remote-codex 0.1.10 → 0.11.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.
Files changed (40) hide show
  1. package/apps/supervisor-api/dist/index.js +11159 -27875
  2. package/apps/supervisor-web/dist/assets/{highlighted-body-OFNGDK62-CyMcatlD.js → highlighted-body-OFNGDK62-ChrwAL9u.js} +1 -1
  3. package/apps/supervisor-web/dist/assets/index-DHf2HOXx.js +381 -0
  4. package/apps/supervisor-web/dist/assets/index-DpWxXCgt.css +32 -0
  5. package/apps/supervisor-web/dist/assets/{xterm-DbYWMNQ0.js → xterm-D4sevve4.js} +1 -1
  6. package/apps/supervisor-web/dist/index.html +2 -2
  7. package/package.json +2 -3
  8. package/packages/agent-runtime/src/index.ts +4 -0
  9. package/packages/agent-runtime/src/management-errors.ts +11 -0
  10. package/packages/agent-runtime/src/model-pricing.ts +312 -0
  11. package/packages/agent-runtime/src/registry.ts +19 -4
  12. package/packages/agent-runtime/src/runtime-errors.ts +97 -0
  13. package/packages/agent-runtime/src/types.ts +36 -3
  14. package/packages/agent-runtime/src/unavailable-runtime.ts +169 -0
  15. package/packages/claude/src/runtimeAdapter.test.ts +95 -6
  16. package/packages/claude/src/runtimeAdapter.ts +421 -65
  17. package/packages/codex/src/historyItems.test.ts +110 -0
  18. package/packages/codex/src/historyItems.ts +96 -15
  19. package/packages/codex/src/hookHistory.test.ts +59 -0
  20. package/packages/codex/src/index.ts +7 -0
  21. package/packages/codex/src/local-session-store.ts +390 -0
  22. package/packages/codex/src/management/codex-management-service.ts +454 -0
  23. package/packages/codex/src/management/codexHostConfig.test.ts +88 -0
  24. package/packages/codex/src/management/codexHostConfig.ts +188 -0
  25. package/packages/codex/src/management/errors.ts +20 -0
  26. package/packages/codex/src/modelPricing.test.ts +184 -0
  27. package/packages/codex/src/modelPricing.ts +9 -0
  28. package/packages/codex/src/runtime-errors.test.ts +72 -0
  29. package/packages/codex/src/runtime-errors.ts +37 -0
  30. package/packages/codex/src/runtimeAdapter.ts +15 -0
  31. package/packages/codex/src/thread-title.ts +1 -0
  32. package/packages/opencode/src/historyItems.test.ts +504 -0
  33. package/packages/opencode/src/historyItems.ts +896 -0
  34. package/packages/opencode/src/index.ts +2 -0
  35. package/packages/opencode/src/runtimeAdapter.test.ts +1355 -0
  36. package/packages/opencode/src/runtimeAdapter.ts +1469 -0
  37. package/packages/shared/src/agent-providers.ts +56 -0
  38. package/packages/shared/src/index.ts +170 -35
  39. package/apps/supervisor-web/dist/assets/index-BlAhoIuq.js +0 -379
  40. package/apps/supervisor-web/dist/assets/index-DI0NRNgr.css +0 -32
@@ -0,0 +1,504 @@
1
+ import { describe, expect, it } from 'vitest';
2
+
3
+ import {
4
+ openCodeMessagesToTurns,
5
+ openCodeMessageToHistoryItems,
6
+ openCodeMessagesToPlanUpdate,
7
+ } from './historyItems';
8
+
9
+ describe('OpenCode history item mapping', () => {
10
+ it('maps projected OpenCode message kinds to timeline items', () => {
11
+ const messages = [
12
+ {
13
+ id: 'user-1',
14
+ type: 'user',
15
+ text: 'Implement the feature',
16
+ time: { created: 1_700_000_000_000 },
17
+ },
18
+ {
19
+ id: 'assistant-1',
20
+ type: 'assistant',
21
+ time: { created: 1_700_000_001_000 },
22
+ agent: 'build',
23
+ model: { id: 'gpt-5', providerID: 'openai', variant: 'default' },
24
+ content: [
25
+ { type: 'reasoning', id: 'reasoning-1', text: 'Need to inspect files.' },
26
+ { type: 'text', text: 'I will update the module.' },
27
+ {
28
+ type: 'tool',
29
+ id: 'tool-1',
30
+ name: 'bash',
31
+ state: {
32
+ status: 'completed',
33
+ input: { command: 'pnpm test' },
34
+ content: [{ type: 'text', text: 'ok' }],
35
+ structured: {},
36
+ },
37
+ },
38
+ ],
39
+ },
40
+ {
41
+ id: 'shell-1',
42
+ type: 'shell',
43
+ command: 'git status',
44
+ output: 'clean',
45
+ time: { created: 1_700_000_002_000, completed: 1_700_000_003_000 },
46
+ },
47
+ {
48
+ id: 'synthetic-1',
49
+ type: 'synthetic',
50
+ text: 'System note',
51
+ time: { created: 1_700_000_004_000 },
52
+ },
53
+ {
54
+ id: 'agent-1',
55
+ type: 'agent-switched',
56
+ agent: 'review',
57
+ time: { created: 1_700_000_005_000 },
58
+ },
59
+ {
60
+ id: 'model-1',
61
+ type: 'model-switched',
62
+ model: { id: 'claude-sonnet', providerID: 'anthropic', variant: 'default' },
63
+ time: { created: 1_700_000_006_000 },
64
+ },
65
+ {
66
+ id: 'compact-1',
67
+ type: 'compaction',
68
+ reason: 'manual',
69
+ summary: 'Summary text',
70
+ time: { created: 1_700_000_007_000 },
71
+ },
72
+ ];
73
+
74
+ const turns = openCodeMessagesToTurns(messages);
75
+ expect(turns).toHaveLength(1);
76
+ expect(turns[0]!.items.map((item) => item.kind)).toEqual([
77
+ 'userMessage',
78
+ 'reasoning',
79
+ 'agentMessage',
80
+ 'commandExecution',
81
+ 'commandExecution',
82
+ 'other',
83
+ 'other',
84
+ 'other',
85
+ 'contextCompaction',
86
+ ]);
87
+ });
88
+
89
+ it('maps tool state variants without dropping bubbles', () => {
90
+ const toolStates = ['pending', 'running', 'completed', 'error'].map((status, index) => ({
91
+ id: `assistant-${index}`,
92
+ type: 'assistant',
93
+ content: [
94
+ {
95
+ type: 'tool',
96
+ id: `tool-${index}`,
97
+ name: index === 0 ? 'read' : index === 1 ? 'web_search' : index === 2 ? 'edit' : 'custom',
98
+ state: {
99
+ status,
100
+ input: { path: 'src/index.ts', query: 'docs' },
101
+ content: [{ type: 'text', text: 'result' }],
102
+ structured: {},
103
+ error: status === 'error' ? { message: 'failed' } : undefined,
104
+ },
105
+ },
106
+ ],
107
+ }));
108
+
109
+ const items = toolStates.flatMap((message) => openCodeMessageToHistoryItems(message));
110
+ expect(items.map((item) => item.kind)).toEqual([
111
+ 'fileRead',
112
+ 'webSearch',
113
+ 'fileChange',
114
+ 'toolCall',
115
+ ]);
116
+ expect(items.map((item) => item.status)).toEqual([
117
+ 'running',
118
+ 'running',
119
+ 'completed',
120
+ 'failed',
121
+ ]);
122
+ });
123
+
124
+ it('summarizes bash tools with the command instead of the description', () => {
125
+ const items = openCodeMessageToHistoryItems({
126
+ id: 'assistant-bash',
127
+ type: 'assistant',
128
+ content: [
129
+ {
130
+ type: 'tool',
131
+ id: 'bash-1',
132
+ tool: 'bash',
133
+ state: {
134
+ status: 'completed',
135
+ input: {
136
+ command: 'npx tsx --test packages/frontend/src/tokenUsage.test.ts',
137
+ description: 'Runs TypeScript token usage tests',
138
+ timeout: 120000,
139
+ workdir: '/home/u/dev/ElAgente/graphchat',
140
+ },
141
+ output: 'ok',
142
+ },
143
+ },
144
+ ],
145
+ });
146
+
147
+ expect(items).toEqual([
148
+ expect.objectContaining({
149
+ id: 'bash-1',
150
+ kind: 'commandExecution',
151
+ text: 'npx tsx --test packages/frontend/src/tokenUsage.test.ts',
152
+ previewText: 'npx tsx --test packages/frontend/src/tokenUsage.test.ts',
153
+ }),
154
+ ]);
155
+ });
156
+
157
+ it('maps OpenCode todo tools to plan updates and apply_patch tools to useful bubbles', () => {
158
+ const message = {
159
+ id: 'assistant-tools',
160
+ type: 'assistant',
161
+ content: [
162
+ {
163
+ type: 'tool',
164
+ id: 'todo-1',
165
+ tool: 'todowrite',
166
+ state: {
167
+ status: 'completed',
168
+ input: {
169
+ todos: [
170
+ { content: 'Inspect code', status: 'in_progress', priority: 'high' },
171
+ { content: 'Patch code', status: 'pending', priority: 'high' },
172
+ ],
173
+ },
174
+ output: '[]',
175
+ title: '2 todos',
176
+ metadata: {},
177
+ },
178
+ },
179
+ {
180
+ type: 'tool',
181
+ id: 'patch-1',
182
+ name: 'apply_patch',
183
+ state: {
184
+ status: 'completed',
185
+ input: {
186
+ patchText: [
187
+ '*** Begin Patch',
188
+ '*** Update File: /tmp/project/src/index.ts',
189
+ '@@',
190
+ '+const ok = true;',
191
+ '-const ok = false;',
192
+ '*** End Patch',
193
+ ].join('\n'),
194
+ },
195
+ output: 'Success. Updated the following files:\nM src/index.ts',
196
+ title: 'Success. Updated the following files',
197
+ metadata: {
198
+ files: [
199
+ {
200
+ filePath: '/tmp/project/src/index.ts',
201
+ type: 'update',
202
+ additions: 1,
203
+ deletions: 1,
204
+ },
205
+ ],
206
+ },
207
+ },
208
+ },
209
+ ],
210
+ };
211
+ const items = openCodeMessageToHistoryItems({
212
+ ...message,
213
+ });
214
+
215
+ expect(items).toEqual([
216
+ expect.objectContaining({
217
+ id: 'patch-1',
218
+ kind: 'fileChange',
219
+ text: '/tmp/project/src/index.ts',
220
+ previewText: '/tmp/project/src/index.ts',
221
+ changedFiles: 1,
222
+ addedLines: 1,
223
+ removedLines: 1,
224
+ }),
225
+ ]);
226
+ expect(openCodeMessagesToPlanUpdate([message])).toEqual({
227
+ explanation: null,
228
+ plan: [
229
+ { step: 'Inspect code', status: 'in_progress' },
230
+ { step: 'Patch code', status: 'pending' },
231
+ ],
232
+ });
233
+ });
234
+
235
+ it('extracts file read paths and suppresses empty pending apply_patch tools', () => {
236
+ const items = openCodeMessageToHistoryItems({
237
+ id: 'assistant-read-patch',
238
+ type: 'assistant',
239
+ content: [
240
+ {
241
+ type: 'tool',
242
+ id: 'read-1',
243
+ name: 'read',
244
+ state: {
245
+ status: 'completed',
246
+ input: {
247
+ filePath: '/tmp/project/src/tokenUsage.ts',
248
+ limit: 260,
249
+ offset: 1,
250
+ },
251
+ output: '<path>/tmp/project/src/tokenUsage.ts</path>',
252
+ },
253
+ },
254
+ {
255
+ type: 'tool',
256
+ id: 'patch-pending-1',
257
+ name: 'apply_patch',
258
+ state: {
259
+ status: 'pending',
260
+ input: {},
261
+ raw: '',
262
+ },
263
+ },
264
+ ],
265
+ });
266
+
267
+ expect(items).toEqual([
268
+ expect.objectContaining({
269
+ id: 'read-1',
270
+ kind: 'fileRead',
271
+ text: '/tmp/project/src/tokenUsage.ts',
272
+ previewText: '/tmp/project/src/tokenUsage.ts',
273
+ }),
274
+ ]);
275
+ });
276
+
277
+ it('uses workspace-relative paths for file bubbles and keeps external absolute paths', () => {
278
+ const items = openCodeMessageToHistoryItems({
279
+ id: 'assistant-paths',
280
+ type: 'assistant',
281
+ content: [
282
+ {
283
+ type: 'tool',
284
+ id: 'read-in-root',
285
+ name: 'read',
286
+ state: {
287
+ status: 'completed',
288
+ input: {
289
+ filePath: '/tmp/project/src/tokenUsage.ts',
290
+ },
291
+ output: '',
292
+ },
293
+ },
294
+ {
295
+ type: 'tool',
296
+ id: 'read-outside-root',
297
+ name: 'read',
298
+ state: {
299
+ status: 'completed',
300
+ input: {
301
+ filePath: '/tmp/other/src/tokenUsage.ts',
302
+ },
303
+ output: '',
304
+ },
305
+ },
306
+ {
307
+ type: 'tool',
308
+ id: 'patch-in-root',
309
+ name: 'apply_patch',
310
+ state: {
311
+ status: 'completed',
312
+ input: {},
313
+ metadata: {
314
+ files: [
315
+ {
316
+ filePath: '/tmp/project/src/index.ts',
317
+ additions: 2,
318
+ deletions: 1,
319
+ },
320
+ ],
321
+ },
322
+ },
323
+ },
324
+ ],
325
+ }, {
326
+ workspacePath: '/tmp/project',
327
+ });
328
+
329
+ expect(items).toEqual([
330
+ expect.objectContaining({
331
+ id: 'read-in-root',
332
+ kind: 'fileRead',
333
+ text: 'src/tokenUsage.ts',
334
+ previewText: 'src/tokenUsage.ts',
335
+ }),
336
+ expect.objectContaining({
337
+ id: 'read-outside-root',
338
+ kind: 'fileRead',
339
+ text: '/tmp/other/src/tokenUsage.ts',
340
+ previewText: '/tmp/other/src/tokenUsage.ts',
341
+ }),
342
+ expect.objectContaining({
343
+ id: 'patch-in-root',
344
+ kind: 'fileChange',
345
+ text: 'src/index.ts',
346
+ previewText: 'src/index.ts',
347
+ }),
348
+ ]);
349
+ });
350
+
351
+ it('maps OpenCode SDK part variants without falling back to raw JSON', () => {
352
+ const items = openCodeMessageToHistoryItems({
353
+ id: 'assistant-parts',
354
+ type: 'assistant',
355
+ content: [
356
+ {
357
+ id: 'file-1',
358
+ type: 'file',
359
+ filename: 'src/index.ts',
360
+ mime: 'text/typescript',
361
+ url: 'file:///tmp/project/src/index.ts',
362
+ source: {
363
+ type: 'file',
364
+ path: 'src/index.ts',
365
+ text: { value: 'export {}', start: 0, end: 9 },
366
+ },
367
+ },
368
+ {
369
+ id: 'patch-part-1',
370
+ type: 'patch',
371
+ hash: 'abc',
372
+ files: ['src/index.ts', 'src/app.ts'],
373
+ },
374
+ {
375
+ id: 'step-start-1',
376
+ type: 'step-start',
377
+ },
378
+ {
379
+ id: 'step-finish-1',
380
+ type: 'step-finish',
381
+ reason: 'stop',
382
+ cost: 0.01,
383
+ tokens: {
384
+ input: 10,
385
+ output: 5,
386
+ reasoning: 2,
387
+ cache: { read: 1, write: 0 },
388
+ },
389
+ },
390
+ {
391
+ id: 'agent-1',
392
+ type: 'agent',
393
+ name: 'build',
394
+ source: { value: 'Use build agent', start: 0, end: 15 },
395
+ },
396
+ {
397
+ id: 'subtask-1',
398
+ type: 'subtask',
399
+ prompt: 'Inspect files',
400
+ description: 'Inspect repository',
401
+ agent: 'build',
402
+ },
403
+ {
404
+ id: 'retry-1',
405
+ type: 'retry',
406
+ attempt: 2,
407
+ error: { message: 'rate limited' },
408
+ time: { created: 1 },
409
+ },
410
+ {
411
+ id: 'compact-1',
412
+ type: 'compaction',
413
+ auto: true,
414
+ },
415
+ ],
416
+ });
417
+
418
+ expect(items.map((item) => item.kind)).toEqual([
419
+ 'fileRead',
420
+ 'fileChange',
421
+ 'agentToolCall',
422
+ 'agentToolCall',
423
+ 'other',
424
+ 'contextCompaction',
425
+ ]);
426
+ expect(items[0]).toMatchObject({
427
+ text: 'src/index.ts',
428
+ detailText: 'export {}',
429
+ });
430
+ expect(items[1]).toMatchObject({
431
+ text: 'src/index.ts\nsrc/app.ts',
432
+ changedFiles: 2,
433
+ });
434
+ expect(items[4]).toMatchObject({
435
+ text: 'Retry 2: rate limited',
436
+ status: 'failed',
437
+ });
438
+ });
439
+
440
+ it('maps legacy SDK message wrappers to timeline items', () => {
441
+ const turns = openCodeMessagesToTurns([
442
+ {
443
+ info: {
444
+ id: 'user-1',
445
+ role: 'user',
446
+ time: { created: 1_700_000_000_000 },
447
+ },
448
+ parts: [{ type: 'text', text: 'hi' }],
449
+ },
450
+ {
451
+ info: {
452
+ id: 'assistant-1',
453
+ role: 'assistant',
454
+ time: { created: 1_700_000_001_000, completed: 1_700_000_002_000 },
455
+ providerID: 'openai',
456
+ modelID: 'gpt-5.5',
457
+ },
458
+ parts: [
459
+ { id: 'text-1', type: 'text', text: 'hello' },
460
+ {
461
+ id: 'tool-1',
462
+ type: 'tool',
463
+ tool: 'bash',
464
+ state: {
465
+ status: 'completed',
466
+ input: { command: 'pwd' },
467
+ output: '/tmp/project',
468
+ },
469
+ },
470
+ ],
471
+ },
472
+ ]);
473
+
474
+ expect(turns).toHaveLength(1);
475
+ expect(turns[0]!.items.map((item) => item.kind)).toEqual([
476
+ 'userMessage',
477
+ 'agentMessage',
478
+ 'commandExecution',
479
+ ]);
480
+ expect(turns[0]!.items[1]).toMatchObject({
481
+ kind: 'agentMessage',
482
+ text: 'hello',
483
+ });
484
+ });
485
+
486
+ it('keeps user-only legacy turns in progress', () => {
487
+ const turns = openCodeMessagesToTurns([
488
+ {
489
+ info: {
490
+ id: 'user-1',
491
+ role: 'user',
492
+ time: { created: 1_700_000_000_000 },
493
+ },
494
+ parts: [{ type: 'text', text: 'hi' }],
495
+ },
496
+ ]);
497
+
498
+ expect(turns).toHaveLength(1);
499
+ expect(turns[0]).toMatchObject({
500
+ status: 'inProgress',
501
+ items: [{ kind: 'userMessage', text: 'hi' }],
502
+ });
503
+ });
504
+ });