@renseiai/agentfactory 0.8.18 → 0.8.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/governor/decision-engine-adapter.d.ts +43 -0
- package/dist/src/governor/decision-engine-adapter.d.ts.map +1 -0
- package/dist/src/governor/decision-engine-adapter.js +422 -0
- package/dist/src/governor/decision-engine-adapter.test.d.ts +2 -0
- package/dist/src/governor/decision-engine-adapter.test.d.ts.map +1 -0
- package/dist/src/governor/decision-engine-adapter.test.js +363 -0
- package/dist/src/governor/index.d.ts +1 -0
- package/dist/src/governor/index.d.ts.map +1 -1
- package/dist/src/governor/index.js +1 -0
- package/dist/src/manifest/route-manifest.d.ts.map +1 -1
- package/dist/src/manifest/route-manifest.js +4 -0
- package/dist/src/orchestrator/orchestrator.d.ts +27 -0
- package/dist/src/orchestrator/orchestrator.d.ts.map +1 -1
- package/dist/src/orchestrator/orchestrator.js +289 -86
- package/dist/src/providers/claude-provider.d.ts.map +1 -1
- package/dist/src/providers/claude-provider.js +11 -0
- package/dist/src/providers/codex-app-server-provider.d.ts +201 -0
- package/dist/src/providers/codex-app-server-provider.d.ts.map +1 -0
- package/dist/src/providers/codex-app-server-provider.js +786 -0
- package/dist/src/providers/codex-app-server-provider.test.d.ts +2 -0
- package/dist/src/providers/codex-app-server-provider.test.d.ts.map +1 -0
- package/dist/src/providers/codex-app-server-provider.test.js +529 -0
- package/dist/src/providers/codex-provider.d.ts +24 -4
- package/dist/src/providers/codex-provider.d.ts.map +1 -1
- package/dist/src/providers/codex-provider.js +58 -6
- package/dist/src/providers/index.d.ts +1 -0
- package/dist/src/providers/index.d.ts.map +1 -1
- package/dist/src/providers/index.js +1 -0
- package/dist/src/routing/observation-recorder.test.js +1 -1
- package/dist/src/routing/observation-store.d.ts +15 -1
- package/dist/src/routing/observation-store.d.ts.map +1 -1
- package/dist/src/routing/observation-store.test.js +17 -11
- package/dist/src/templates/index.d.ts +2 -1
- package/dist/src/templates/index.d.ts.map +1 -1
- package/dist/src/templates/index.js +1 -0
- package/dist/src/templates/registry.d.ts +23 -0
- package/dist/src/templates/registry.d.ts.map +1 -1
- package/dist/src/templates/registry.js +80 -0
- package/dist/src/templates/schema.d.ts +31 -0
- package/dist/src/templates/schema.d.ts.map +1 -0
- package/dist/src/templates/schema.js +139 -0
- package/dist/src/templates/schema.test.d.ts +2 -0
- package/dist/src/templates/schema.test.d.ts.map +1 -0
- package/dist/src/templates/schema.test.js +215 -0
- package/package.json +2 -2
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codex-app-server-provider.test.d.ts","sourceRoot":"","sources":["../../../src/providers/codex-app-server-provider.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,529 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { mapAppServerNotification, mapAppServerItemEvent, } from './codex-app-server-provider.js';
|
|
3
|
+
function freshState() {
|
|
4
|
+
return {
|
|
5
|
+
sessionId: null,
|
|
6
|
+
totalInputTokens: 0,
|
|
7
|
+
totalOutputTokens: 0,
|
|
8
|
+
turnCount: 0,
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
// ---------------------------------------------------------------------------
|
|
12
|
+
// mapAppServerNotification
|
|
13
|
+
// ---------------------------------------------------------------------------
|
|
14
|
+
describe('mapAppServerNotification', () => {
|
|
15
|
+
// --- Thread lifecycle ---
|
|
16
|
+
it('maps thread/started to init', () => {
|
|
17
|
+
const state = freshState();
|
|
18
|
+
const notification = {
|
|
19
|
+
method: 'thread/started',
|
|
20
|
+
params: { thread: { id: 'thr_abc' } },
|
|
21
|
+
};
|
|
22
|
+
const result = mapAppServerNotification(notification, state);
|
|
23
|
+
expect(result).toHaveLength(1);
|
|
24
|
+
expect(result[0]).toMatchObject({
|
|
25
|
+
type: 'init',
|
|
26
|
+
sessionId: 'thr_abc',
|
|
27
|
+
});
|
|
28
|
+
expect(state.sessionId).toBe('thr_abc');
|
|
29
|
+
});
|
|
30
|
+
it('maps thread/started without thread to empty array', () => {
|
|
31
|
+
const state = freshState();
|
|
32
|
+
const notification = {
|
|
33
|
+
method: 'thread/started',
|
|
34
|
+
params: {},
|
|
35
|
+
};
|
|
36
|
+
const result = mapAppServerNotification(notification, state);
|
|
37
|
+
expect(result).toHaveLength(0);
|
|
38
|
+
});
|
|
39
|
+
it('maps thread/closed to system event', () => {
|
|
40
|
+
const state = freshState();
|
|
41
|
+
const notification = {
|
|
42
|
+
method: 'thread/closed',
|
|
43
|
+
params: { threadId: 'thr_abc' },
|
|
44
|
+
};
|
|
45
|
+
const result = mapAppServerNotification(notification, state);
|
|
46
|
+
expect(result).toHaveLength(1);
|
|
47
|
+
expect(result[0]).toMatchObject({
|
|
48
|
+
type: 'system',
|
|
49
|
+
subtype: 'thread_closed',
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
it('maps thread/status/changed to system event', () => {
|
|
53
|
+
const state = freshState();
|
|
54
|
+
const notification = {
|
|
55
|
+
method: 'thread/status/changed',
|
|
56
|
+
params: { threadId: 'thr_abc', status: 'idle' },
|
|
57
|
+
};
|
|
58
|
+
const result = mapAppServerNotification(notification, state);
|
|
59
|
+
expect(result[0]).toMatchObject({
|
|
60
|
+
type: 'system',
|
|
61
|
+
subtype: 'thread_status_changed',
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
// --- Turn lifecycle ---
|
|
65
|
+
it('maps turn/started with incrementing turn count', () => {
|
|
66
|
+
const state = freshState();
|
|
67
|
+
const notification = {
|
|
68
|
+
method: 'turn/started',
|
|
69
|
+
params: { turn: { id: 'turn_1' } },
|
|
70
|
+
};
|
|
71
|
+
const r1 = mapAppServerNotification(notification, state);
|
|
72
|
+
expect(r1[0]).toMatchObject({
|
|
73
|
+
type: 'system',
|
|
74
|
+
subtype: 'turn_started',
|
|
75
|
+
message: 'Turn 1 started (turn_1)',
|
|
76
|
+
});
|
|
77
|
+
expect(state.turnCount).toBe(1);
|
|
78
|
+
const r2 = mapAppServerNotification(notification, state);
|
|
79
|
+
expect(r2[0]).toMatchObject({
|
|
80
|
+
message: 'Turn 2 started (turn_1)',
|
|
81
|
+
});
|
|
82
|
+
expect(state.turnCount).toBe(2);
|
|
83
|
+
});
|
|
84
|
+
it('maps turn/started without turn id', () => {
|
|
85
|
+
const state = freshState();
|
|
86
|
+
const notification = {
|
|
87
|
+
method: 'turn/started',
|
|
88
|
+
params: {},
|
|
89
|
+
};
|
|
90
|
+
const result = mapAppServerNotification(notification, state);
|
|
91
|
+
expect(result[0]).toMatchObject({
|
|
92
|
+
type: 'system',
|
|
93
|
+
subtype: 'turn_started',
|
|
94
|
+
message: 'Turn 1 started',
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
it('maps turn/completed (success) with usage', () => {
|
|
98
|
+
const state = freshState();
|
|
99
|
+
state.turnCount = 1;
|
|
100
|
+
const notification = {
|
|
101
|
+
method: 'turn/completed',
|
|
102
|
+
params: {
|
|
103
|
+
turn: {
|
|
104
|
+
id: 'turn_1',
|
|
105
|
+
status: 'completed',
|
|
106
|
+
usage: { input_tokens: 100, output_tokens: 50 },
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
};
|
|
110
|
+
const result = mapAppServerNotification(notification, state);
|
|
111
|
+
expect(result).toHaveLength(1);
|
|
112
|
+
expect(result[0]).toMatchObject({
|
|
113
|
+
type: 'result',
|
|
114
|
+
success: true,
|
|
115
|
+
cost: {
|
|
116
|
+
inputTokens: 100,
|
|
117
|
+
outputTokens: 50,
|
|
118
|
+
numTurns: 1,
|
|
119
|
+
},
|
|
120
|
+
});
|
|
121
|
+
expect(state.totalInputTokens).toBe(100);
|
|
122
|
+
expect(state.totalOutputTokens).toBe(50);
|
|
123
|
+
});
|
|
124
|
+
it('accumulates usage across multiple turns', () => {
|
|
125
|
+
const state = freshState();
|
|
126
|
+
state.turnCount = 2;
|
|
127
|
+
mapAppServerNotification({
|
|
128
|
+
method: 'turn/completed',
|
|
129
|
+
params: { turn: { id: 't1', status: 'completed', usage: { input_tokens: 100, output_tokens: 50 } } },
|
|
130
|
+
}, state);
|
|
131
|
+
const result = mapAppServerNotification({
|
|
132
|
+
method: 'turn/completed',
|
|
133
|
+
params: { turn: { id: 't2', status: 'completed', usage: { input_tokens: 200, output_tokens: 80 } } },
|
|
134
|
+
}, state);
|
|
135
|
+
expect(result[0]).toMatchObject({
|
|
136
|
+
type: 'result',
|
|
137
|
+
success: true,
|
|
138
|
+
cost: { inputTokens: 300, outputTokens: 130 },
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
it('maps turn/completed (failed) with error', () => {
|
|
142
|
+
const state = freshState();
|
|
143
|
+
const notification = {
|
|
144
|
+
method: 'turn/completed',
|
|
145
|
+
params: {
|
|
146
|
+
turn: {
|
|
147
|
+
id: 'turn_1',
|
|
148
|
+
status: 'failed',
|
|
149
|
+
error: { message: 'Context window exceeded', codexErrorInfo: 'ContextWindowExceeded' },
|
|
150
|
+
},
|
|
151
|
+
},
|
|
152
|
+
};
|
|
153
|
+
const result = mapAppServerNotification(notification, state);
|
|
154
|
+
expect(result[0]).toMatchObject({
|
|
155
|
+
type: 'result',
|
|
156
|
+
success: false,
|
|
157
|
+
errors: ['Context window exceeded'],
|
|
158
|
+
errorSubtype: 'ContextWindowExceeded',
|
|
159
|
+
});
|
|
160
|
+
});
|
|
161
|
+
it('maps turn/completed (failed) without error message', () => {
|
|
162
|
+
const state = freshState();
|
|
163
|
+
const result = mapAppServerNotification({
|
|
164
|
+
method: 'turn/completed',
|
|
165
|
+
params: { turn: { id: 'turn_1', status: 'failed' } },
|
|
166
|
+
}, state);
|
|
167
|
+
expect(result[0]).toMatchObject({
|
|
168
|
+
type: 'result',
|
|
169
|
+
success: false,
|
|
170
|
+
errors: ['Turn failed'],
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
it('maps turn/completed (interrupted)', () => {
|
|
174
|
+
const state = freshState();
|
|
175
|
+
const result = mapAppServerNotification({
|
|
176
|
+
method: 'turn/completed',
|
|
177
|
+
params: { turn: { id: 'turn_1', status: 'interrupted' } },
|
|
178
|
+
}, state);
|
|
179
|
+
expect(result[0]).toMatchObject({
|
|
180
|
+
type: 'result',
|
|
181
|
+
success: false,
|
|
182
|
+
errors: ['Turn was interrupted'],
|
|
183
|
+
errorSubtype: 'interrupted',
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
it('maps turn/completed without turn object (defaults to completed)', () => {
|
|
187
|
+
const state = freshState();
|
|
188
|
+
const result = mapAppServerNotification({
|
|
189
|
+
method: 'turn/completed',
|
|
190
|
+
params: {},
|
|
191
|
+
}, state);
|
|
192
|
+
expect(result[0]).toMatchObject({
|
|
193
|
+
type: 'result',
|
|
194
|
+
success: true,
|
|
195
|
+
});
|
|
196
|
+
});
|
|
197
|
+
// --- Item deltas (streaming) ---
|
|
198
|
+
it('maps item/agentMessage/delta to assistant_text', () => {
|
|
199
|
+
const state = freshState();
|
|
200
|
+
const result = mapAppServerNotification({
|
|
201
|
+
method: 'item/agentMessage/delta',
|
|
202
|
+
params: { text: 'Hello world' },
|
|
203
|
+
}, state);
|
|
204
|
+
expect(result[0]).toMatchObject({
|
|
205
|
+
type: 'assistant_text',
|
|
206
|
+
text: 'Hello world',
|
|
207
|
+
});
|
|
208
|
+
});
|
|
209
|
+
it('maps item/agentMessage/delta with empty text to empty array', () => {
|
|
210
|
+
const state = freshState();
|
|
211
|
+
const result = mapAppServerNotification({
|
|
212
|
+
method: 'item/agentMessage/delta',
|
|
213
|
+
params: {},
|
|
214
|
+
}, state);
|
|
215
|
+
expect(result).toHaveLength(0);
|
|
216
|
+
});
|
|
217
|
+
it('maps item/reasoning/summaryTextDelta to reasoning', () => {
|
|
218
|
+
const state = freshState();
|
|
219
|
+
const result = mapAppServerNotification({
|
|
220
|
+
method: 'item/reasoning/summaryTextDelta',
|
|
221
|
+
params: { text: 'Thinking about the problem...' },
|
|
222
|
+
}, state);
|
|
223
|
+
expect(result[0]).toMatchObject({
|
|
224
|
+
type: 'system',
|
|
225
|
+
subtype: 'reasoning',
|
|
226
|
+
message: 'Thinking about the problem...',
|
|
227
|
+
});
|
|
228
|
+
});
|
|
229
|
+
it('maps item/commandExecution/outputDelta to command_progress', () => {
|
|
230
|
+
const state = freshState();
|
|
231
|
+
const result = mapAppServerNotification({
|
|
232
|
+
method: 'item/commandExecution/outputDelta',
|
|
233
|
+
params: { delta: 'Installing packages...' },
|
|
234
|
+
}, state);
|
|
235
|
+
expect(result[0]).toMatchObject({
|
|
236
|
+
type: 'system',
|
|
237
|
+
subtype: 'command_progress',
|
|
238
|
+
message: 'Installing packages...',
|
|
239
|
+
});
|
|
240
|
+
});
|
|
241
|
+
// --- Turn diff/plan ---
|
|
242
|
+
it('maps turn/diff/updated to system event', () => {
|
|
243
|
+
const state = freshState();
|
|
244
|
+
const result = mapAppServerNotification({
|
|
245
|
+
method: 'turn/diff/updated',
|
|
246
|
+
params: { diff: '--- a/file.ts\n+++ b/file.ts' },
|
|
247
|
+
}, state);
|
|
248
|
+
expect(result[0]).toMatchObject({
|
|
249
|
+
type: 'system',
|
|
250
|
+
subtype: 'diff_updated',
|
|
251
|
+
message: '--- a/file.ts\n+++ b/file.ts',
|
|
252
|
+
});
|
|
253
|
+
});
|
|
254
|
+
it('maps turn/plan/updated to system event', () => {
|
|
255
|
+
const state = freshState();
|
|
256
|
+
const result = mapAppServerNotification({
|
|
257
|
+
method: 'turn/plan/updated',
|
|
258
|
+
params: { plan: { steps: ['step1'] } },
|
|
259
|
+
}, state);
|
|
260
|
+
expect(result[0]).toMatchObject({
|
|
261
|
+
type: 'system',
|
|
262
|
+
subtype: 'plan_updated',
|
|
263
|
+
});
|
|
264
|
+
});
|
|
265
|
+
// --- Unknown notifications ---
|
|
266
|
+
it('handles unknown notification method', () => {
|
|
267
|
+
const state = freshState();
|
|
268
|
+
const result = mapAppServerNotification({
|
|
269
|
+
method: 'some/unknown/method',
|
|
270
|
+
params: {},
|
|
271
|
+
}, state);
|
|
272
|
+
expect(result[0]).toMatchObject({
|
|
273
|
+
type: 'system',
|
|
274
|
+
subtype: 'unknown',
|
|
275
|
+
message: 'Unhandled App Server notification: some/unknown/method',
|
|
276
|
+
});
|
|
277
|
+
});
|
|
278
|
+
// --- Item lifecycle (delegates) ---
|
|
279
|
+
it('delegates item/started to mapAppServerItemEvent', () => {
|
|
280
|
+
const state = freshState();
|
|
281
|
+
const result = mapAppServerNotification({
|
|
282
|
+
method: 'item/started',
|
|
283
|
+
params: {
|
|
284
|
+
item: { id: 'cmd-1', type: 'commandExecution', command: 'ls -la' },
|
|
285
|
+
},
|
|
286
|
+
}, state);
|
|
287
|
+
expect(result[0]).toMatchObject({
|
|
288
|
+
type: 'tool_use',
|
|
289
|
+
toolName: 'shell',
|
|
290
|
+
toolUseId: 'cmd-1',
|
|
291
|
+
input: { command: 'ls -la' },
|
|
292
|
+
});
|
|
293
|
+
});
|
|
294
|
+
it('delegates item/completed to mapAppServerItemEvent', () => {
|
|
295
|
+
const state = freshState();
|
|
296
|
+
const result = mapAppServerNotification({
|
|
297
|
+
method: 'item/completed',
|
|
298
|
+
params: {
|
|
299
|
+
item: { id: 'msg-1', type: 'agentMessage', text: 'Done!' },
|
|
300
|
+
},
|
|
301
|
+
}, state);
|
|
302
|
+
expect(result[0]).toMatchObject({
|
|
303
|
+
type: 'assistant_text',
|
|
304
|
+
text: 'Done!',
|
|
305
|
+
});
|
|
306
|
+
});
|
|
307
|
+
});
|
|
308
|
+
// ---------------------------------------------------------------------------
|
|
309
|
+
// mapAppServerItemEvent
|
|
310
|
+
// ---------------------------------------------------------------------------
|
|
311
|
+
describe('mapAppServerItemEvent', () => {
|
|
312
|
+
it('maps agentMessage item.completed to assistant_text', () => {
|
|
313
|
+
const result = mapAppServerItemEvent('item/completed', {
|
|
314
|
+
item: { id: 'msg-1', type: 'agentMessage', text: 'Analysis complete' },
|
|
315
|
+
});
|
|
316
|
+
expect(result).toEqual([{
|
|
317
|
+
type: 'assistant_text',
|
|
318
|
+
text: 'Analysis complete',
|
|
319
|
+
raw: { method: 'item/completed', params: { item: { id: 'msg-1', type: 'agentMessage', text: 'Analysis complete' } } },
|
|
320
|
+
}]);
|
|
321
|
+
});
|
|
322
|
+
it('returns empty for agentMessage item.started', () => {
|
|
323
|
+
const result = mapAppServerItemEvent('item/started', {
|
|
324
|
+
item: { id: 'msg-1', type: 'agentMessage', text: '' },
|
|
325
|
+
});
|
|
326
|
+
expect(result).toHaveLength(0);
|
|
327
|
+
});
|
|
328
|
+
it('maps reasoning item to system event', () => {
|
|
329
|
+
const result = mapAppServerItemEvent('item/completed', {
|
|
330
|
+
item: { id: 'r-1', type: 'reasoning', summary: 'Thinking about it...' },
|
|
331
|
+
});
|
|
332
|
+
expect(result[0]).toMatchObject({
|
|
333
|
+
type: 'system',
|
|
334
|
+
subtype: 'reasoning',
|
|
335
|
+
message: 'Thinking about it...',
|
|
336
|
+
});
|
|
337
|
+
});
|
|
338
|
+
it('maps commandExecution item.started to tool_use', () => {
|
|
339
|
+
const result = mapAppServerItemEvent('item/started', {
|
|
340
|
+
item: { id: 'cmd-1', type: 'commandExecution', command: 'npm test' },
|
|
341
|
+
});
|
|
342
|
+
expect(result[0]).toMatchObject({
|
|
343
|
+
type: 'tool_use',
|
|
344
|
+
toolName: 'shell',
|
|
345
|
+
toolUseId: 'cmd-1',
|
|
346
|
+
input: { command: 'npm test' },
|
|
347
|
+
});
|
|
348
|
+
});
|
|
349
|
+
it('maps commandExecution item.completed to tool_result (success)', () => {
|
|
350
|
+
const result = mapAppServerItemEvent('item/completed', {
|
|
351
|
+
item: {
|
|
352
|
+
id: 'cmd-1',
|
|
353
|
+
type: 'commandExecution',
|
|
354
|
+
command: 'echo hello',
|
|
355
|
+
text: 'hello',
|
|
356
|
+
exitCode: 0,
|
|
357
|
+
status: 'completed',
|
|
358
|
+
},
|
|
359
|
+
});
|
|
360
|
+
expect(result[0]).toMatchObject({
|
|
361
|
+
type: 'tool_result',
|
|
362
|
+
toolName: 'shell',
|
|
363
|
+
toolUseId: 'cmd-1',
|
|
364
|
+
content: 'hello',
|
|
365
|
+
isError: false,
|
|
366
|
+
});
|
|
367
|
+
});
|
|
368
|
+
it('maps commandExecution item.completed to tool_result (failure)', () => {
|
|
369
|
+
const result = mapAppServerItemEvent('item/completed', {
|
|
370
|
+
item: {
|
|
371
|
+
id: 'cmd-1',
|
|
372
|
+
type: 'commandExecution',
|
|
373
|
+
command: 'false',
|
|
374
|
+
text: '',
|
|
375
|
+
exitCode: 1,
|
|
376
|
+
status: 'failed',
|
|
377
|
+
},
|
|
378
|
+
});
|
|
379
|
+
expect(result[0]).toMatchObject({
|
|
380
|
+
type: 'tool_result',
|
|
381
|
+
isError: true,
|
|
382
|
+
});
|
|
383
|
+
});
|
|
384
|
+
it('maps fileChange item.completed to tool_result', () => {
|
|
385
|
+
const result = mapAppServerItemEvent('item/completed', {
|
|
386
|
+
item: {
|
|
387
|
+
id: 'fc-1',
|
|
388
|
+
type: 'fileChange',
|
|
389
|
+
changes: [
|
|
390
|
+
{ path: 'src/index.ts', kind: 'update' },
|
|
391
|
+
{ path: 'src/new.ts', kind: 'add' },
|
|
392
|
+
],
|
|
393
|
+
status: 'completed',
|
|
394
|
+
},
|
|
395
|
+
});
|
|
396
|
+
expect(result[0]).toMatchObject({
|
|
397
|
+
type: 'tool_result',
|
|
398
|
+
toolName: 'file_change',
|
|
399
|
+
content: 'update: src/index.ts\nadd: src/new.ts',
|
|
400
|
+
isError: false,
|
|
401
|
+
});
|
|
402
|
+
});
|
|
403
|
+
it('maps mcpToolCall item.started to tool_use', () => {
|
|
404
|
+
const result = mapAppServerItemEvent('item/started', {
|
|
405
|
+
item: {
|
|
406
|
+
id: 'mcp-1',
|
|
407
|
+
type: 'mcpToolCall',
|
|
408
|
+
server: 'linear',
|
|
409
|
+
tool: 'create_issue',
|
|
410
|
+
arguments: { title: 'Test' },
|
|
411
|
+
status: 'in_progress',
|
|
412
|
+
},
|
|
413
|
+
});
|
|
414
|
+
expect(result[0]).toMatchObject({
|
|
415
|
+
type: 'tool_use',
|
|
416
|
+
toolName: 'mcp:linear/create_issue',
|
|
417
|
+
toolUseId: 'mcp-1',
|
|
418
|
+
input: { title: 'Test' },
|
|
419
|
+
});
|
|
420
|
+
});
|
|
421
|
+
it('maps mcpToolCall item.completed to tool_result (success)', () => {
|
|
422
|
+
const result = mapAppServerItemEvent('item/completed', {
|
|
423
|
+
item: {
|
|
424
|
+
id: 'mcp-1',
|
|
425
|
+
type: 'mcpToolCall',
|
|
426
|
+
server: 'linear',
|
|
427
|
+
tool: 'create_issue',
|
|
428
|
+
arguments: {},
|
|
429
|
+
result: { content: [{ text: 'Created' }] },
|
|
430
|
+
status: 'completed',
|
|
431
|
+
},
|
|
432
|
+
});
|
|
433
|
+
expect(result[0]).toMatchObject({
|
|
434
|
+
type: 'tool_result',
|
|
435
|
+
toolName: 'mcp:linear/create_issue',
|
|
436
|
+
content: '[{"text":"Created"}]',
|
|
437
|
+
isError: false,
|
|
438
|
+
});
|
|
439
|
+
});
|
|
440
|
+
it('maps mcpToolCall item.completed to tool_result (error)', () => {
|
|
441
|
+
const result = mapAppServerItemEvent('item/completed', {
|
|
442
|
+
item: {
|
|
443
|
+
id: 'mcp-1',
|
|
444
|
+
type: 'mcpToolCall',
|
|
445
|
+
server: 'linear',
|
|
446
|
+
tool: 'create_issue',
|
|
447
|
+
arguments: {},
|
|
448
|
+
error: { message: 'Auth failed' },
|
|
449
|
+
status: 'failed',
|
|
450
|
+
},
|
|
451
|
+
});
|
|
452
|
+
expect(result[0]).toMatchObject({
|
|
453
|
+
type: 'tool_result',
|
|
454
|
+
isError: true,
|
|
455
|
+
content: 'Auth failed',
|
|
456
|
+
});
|
|
457
|
+
});
|
|
458
|
+
it('maps plan item to system event', () => {
|
|
459
|
+
const result = mapAppServerItemEvent('item/completed', {
|
|
460
|
+
item: { id: 'p-1', type: 'plan', text: 'Step 1: Read code' },
|
|
461
|
+
});
|
|
462
|
+
expect(result[0]).toMatchObject({
|
|
463
|
+
type: 'system',
|
|
464
|
+
subtype: 'plan',
|
|
465
|
+
message: 'Step 1: Read code',
|
|
466
|
+
});
|
|
467
|
+
});
|
|
468
|
+
it('maps webSearch item to system event', () => {
|
|
469
|
+
const result = mapAppServerItemEvent('item/started', {
|
|
470
|
+
item: { id: 'ws-1', type: 'webSearch', text: 'codex app server docs' },
|
|
471
|
+
});
|
|
472
|
+
expect(result[0]).toMatchObject({
|
|
473
|
+
type: 'system',
|
|
474
|
+
subtype: 'web_search',
|
|
475
|
+
message: 'Web search: codex app server docs',
|
|
476
|
+
});
|
|
477
|
+
});
|
|
478
|
+
it('maps contextCompaction item to system event', () => {
|
|
479
|
+
const result = mapAppServerItemEvent('item/completed', {
|
|
480
|
+
item: { id: 'cc-1', type: 'contextCompaction' },
|
|
481
|
+
});
|
|
482
|
+
expect(result[0]).toMatchObject({
|
|
483
|
+
type: 'system',
|
|
484
|
+
subtype: 'context_compaction',
|
|
485
|
+
message: 'Context history compacted',
|
|
486
|
+
});
|
|
487
|
+
});
|
|
488
|
+
it('returns empty when no item in params', () => {
|
|
489
|
+
const result = mapAppServerItemEvent('item/completed', {});
|
|
490
|
+
expect(result).toHaveLength(0);
|
|
491
|
+
});
|
|
492
|
+
it('handles unknown item type', () => {
|
|
493
|
+
const result = mapAppServerItemEvent('item/completed', {
|
|
494
|
+
item: { id: 'x', type: 'novelItemType' },
|
|
495
|
+
});
|
|
496
|
+
expect(result[0]).toMatchObject({
|
|
497
|
+
type: 'system',
|
|
498
|
+
subtype: 'unknown_item',
|
|
499
|
+
});
|
|
500
|
+
});
|
|
501
|
+
});
|
|
502
|
+
// ---------------------------------------------------------------------------
|
|
503
|
+
// CodexAppServerProvider class
|
|
504
|
+
// ---------------------------------------------------------------------------
|
|
505
|
+
describe('CodexAppServerProvider', () => {
|
|
506
|
+
it('exports CodexAppServerProvider class', async () => {
|
|
507
|
+
const { CodexAppServerProvider } = await import('./codex-app-server-provider.js');
|
|
508
|
+
const provider = new CodexAppServerProvider();
|
|
509
|
+
expect(provider.name).toBe('codex');
|
|
510
|
+
});
|
|
511
|
+
it('exports createCodexAppServerProvider factory', async () => {
|
|
512
|
+
const { createCodexAppServerProvider } = await import('./codex-app-server-provider.js');
|
|
513
|
+
const provider = createCodexAppServerProvider();
|
|
514
|
+
expect(provider.name).toBe('codex');
|
|
515
|
+
expect(provider.capabilities.supportsMessageInjection).toBe(false);
|
|
516
|
+
expect(provider.capabilities.supportsSessionResume).toBe(true);
|
|
517
|
+
});
|
|
518
|
+
});
|
|
519
|
+
// ---------------------------------------------------------------------------
|
|
520
|
+
// AppServerProcessManager
|
|
521
|
+
// ---------------------------------------------------------------------------
|
|
522
|
+
describe('AppServerProcessManager', () => {
|
|
523
|
+
it('exports AppServerProcessManager class', async () => {
|
|
524
|
+
const { AppServerProcessManager } = await import('./codex-app-server-provider.js');
|
|
525
|
+
const manager = new AppServerProcessManager({ cwd: '/tmp' });
|
|
526
|
+
expect(manager.isHealthy()).toBe(false);
|
|
527
|
+
expect(manager.pid).toBeUndefined();
|
|
528
|
+
});
|
|
529
|
+
});
|
|
@@ -1,10 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* OpenAI Codex Agent Provider
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* its JSONL event stream into normalized AgentEvents.
|
|
4
|
+
* Unified Codex provider with two execution modes:
|
|
6
5
|
*
|
|
7
|
-
*
|
|
6
|
+
* 1. **App Server mode** (default when CODEX_USE_APP_SERVER=1):
|
|
7
|
+
* Uses a long-lived `codex app-server` process communicating via JSON-RPC 2.0
|
|
8
|
+
* over stdio. Supports multiple concurrent threads on a single process.
|
|
9
|
+
*
|
|
10
|
+
* 2. **Exec fallback mode** (backward compatibility):
|
|
11
|
+
* Spawns the `codex` CLI as a child process and parses its JSONL event stream.
|
|
12
|
+
* Used for CI/CD or when app-server is unavailable.
|
|
13
|
+
*
|
|
14
|
+
* Mode selection:
|
|
15
|
+
* - CODEX_USE_APP_SERVER=1 → App Server mode (JSON-RPC 2.0)
|
|
16
|
+
* - CODEX_USE_APP_SERVER=0 → Exec fallback mode (CLI JSONL)
|
|
17
|
+
* - Not set → Exec fallback mode (default, backward compatible)
|
|
18
|
+
*
|
|
19
|
+
* Exec CLI invocation patterns:
|
|
8
20
|
* New session: codex exec --json --full-auto -C <cwd> "<prompt>"
|
|
9
21
|
* Resume: codex exec resume --json --full-auto <session_id> "<prompt>"
|
|
10
22
|
*
|
|
@@ -126,9 +138,17 @@ export declare class CodexProvider implements AgentProvider {
|
|
|
126
138
|
readonly supportsMessageInjection: false;
|
|
127
139
|
readonly supportsSessionResume: true;
|
|
128
140
|
};
|
|
141
|
+
/** App Server delegate — created lazily when App Server mode is enabled */
|
|
142
|
+
private appServerProvider;
|
|
129
143
|
spawn(config: AgentSpawnConfig): AgentHandle;
|
|
130
144
|
resume(sessionId: string, config: AgentSpawnConfig): AgentHandle;
|
|
131
|
-
|
|
145
|
+
/**
|
|
146
|
+
* Shut down the App Server process if running.
|
|
147
|
+
* No-op if in exec fallback mode.
|
|
148
|
+
*/
|
|
149
|
+
shutdown(): Promise<void>;
|
|
150
|
+
private getAppServerProvider;
|
|
151
|
+
private createExecHandle;
|
|
132
152
|
}
|
|
133
153
|
/**
|
|
134
154
|
* Create a new Codex provider instance
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"codex-provider.d.ts","sourceRoot":"","sources":["../../../src/providers/codex-provider.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"codex-provider.d.ts","sourceRoot":"","sources":["../../../src/providers/codex-provider.ts"],"names":[],"mappings":"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"}
|