coding-tool-x 3.8.1 → 3.8.3

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 (36) hide show
  1. package/CHANGELOG.md +2 -0
  2. package/dist/web/assets/{Analytics-8dRv1aYW.js → Analytics-COEUXmd4.js} +1 -1
  3. package/dist/web/assets/{ConfigTemplates-BQRXSICO.js → ConfigTemplates-DLLkS53H.js} +1 -1
  4. package/dist/web/assets/{Home-BtzWhKIa.js → Home-DCEyLzwX.js} +1 -1
  5. package/dist/web/assets/{PluginManager-BTa-Ygf6.js → PluginManager-Oe1ZHoMO.js} +1 -1
  6. package/dist/web/assets/{ProjectList-F_cB8ldA.js → ProjectList-CWjuAFof.js} +1 -1
  7. package/dist/web/assets/{SessionList-LOrbPQvu.js → SessionList-v3ehGg9U.js} +1 -1
  8. package/dist/web/assets/{SkillManager-BzI_t-yV.js → SkillManager-DUF_qP5S.js} +1 -1
  9. package/dist/web/assets/{WorkspaceManager-Db9RGiz7.js → WorkspaceManager-CjAWBosO.js} +1 -1
  10. package/dist/web/assets/index-D5hyHPj7.js +2 -0
  11. package/dist/web/index.html +1 -1
  12. package/docs/superpowers/plans/2026-08-10-codex-conversion-compatibility.md +251 -0
  13. package/docs/superpowers/plans/2026-08-11-windows-command-window-hiding.md +90 -0
  14. package/docs/superpowers/plans/2026-08-13-omp-restart-recovery.md +48 -0
  15. package/docs/superpowers/specs/2026-08-10-codex-conversion-compatibility-design.md +61 -0
  16. package/docs/superpowers/specs/2026-08-11-windows-command-window-hiding-design.md +44 -0
  17. package/docs/superpowers/specs/2026-08-13-omp-restart-recovery-design.md +22 -0
  18. package/package.json +1 -1
  19. package/src/commands/daemon.js +3 -12
  20. package/src/commands/update.js +1 -1
  21. package/src/index.js +8 -1
  22. package/src/server/omp-proxy-server.js +9 -6
  23. package/src/server/opencode-proxy-server.js +55 -1037
  24. package/src/server/services/claude-wire.js +694 -0
  25. package/src/server/services/codex-wire.js +219 -0
  26. package/src/server/services/gemini-wire.js +759 -0
  27. package/src/server/services/model-detector.js +49 -131
  28. package/src/server/services/omp-channels.js +23 -4
  29. package/src/server/services/omp-gateway.js +29 -1
  30. package/src/server/services/omp-settings-manager.js +27 -3
  31. package/src/server/services/opencode-gateway-adapters.js +22 -1147
  32. package/src/server/services/opencode-normalization.js +546 -0
  33. package/src/server/services/session-history-index.js +1 -1
  34. package/src/server/services/session-history-worker.js +2 -1
  35. package/src/server/services/speed-test.js +85 -303
  36. package/dist/web/assets/index-DIWJmyL6.js +0 -2
@@ -0,0 +1,546 @@
1
+ 'use strict';
2
+
3
+ // ---------------------------------------------------------------------------
4
+ // Neutral OpenCode normalization primitives
5
+ // ---------------------------------------------------------------------------
6
+ // Shared by claude-wire and gemini-wire. Provider-specific conversion,
7
+ // auth, envelope, and URL construction remain in each wire module.
8
+ // ---------------------------------------------------------------------------
9
+
10
+ function extractTextFragments(value, fragments) {
11
+ if (value === null || value === undefined) return;
12
+ if (typeof value === 'string') {
13
+ if (value.trim()) fragments.push(value);
14
+ return;
15
+ }
16
+ if (typeof value === 'number' || typeof value === 'boolean') {
17
+ fragments.push(String(value));
18
+ return;
19
+ }
20
+ if (Array.isArray(value)) {
21
+ value.forEach(item => extractTextFragments(item, fragments));
22
+ return;
23
+ }
24
+ if (typeof value !== 'object') return;
25
+
26
+ if (typeof value.text === 'string') {
27
+ extractTextFragments(value.text, fragments);
28
+ return;
29
+ }
30
+ if (typeof value.input_text === 'string') {
31
+ extractTextFragments(value.input_text, fragments);
32
+ return;
33
+ }
34
+ if (typeof value.output_text === 'string') {
35
+ extractTextFragments(value.output_text, fragments);
36
+ return;
37
+ }
38
+ if (value.content !== undefined) {
39
+ extractTextFragments(value.content, fragments);
40
+ return;
41
+ }
42
+ if (Array.isArray(value.parts)) {
43
+ extractTextFragments(value.parts, fragments);
44
+ }
45
+ }
46
+
47
+ function extractText(value) {
48
+ const fragments = [];
49
+ extractTextFragments(value, fragments);
50
+ return fragments.join('\n').trim();
51
+ }
52
+
53
+ function parseBase64DataUrl(dataUrl = '') {
54
+ const value = typeof dataUrl === 'string' ? dataUrl.trim() : '';
55
+ if (!value) return null;
56
+ const matched = value.match(/^data:([^;,]+)?;base64,(.+)$/i);
57
+ if (!matched) return null;
58
+ return {
59
+ mediaType: String(matched[1] || '').trim(),
60
+ data: String(matched[2] || '')
61
+ };
62
+ }
63
+
64
+ function normalizeOpenAiImageBlock(value) {
65
+ let imageUrl = '';
66
+ if (typeof value === 'string') {
67
+ imageUrl = value;
68
+ } else if (value && typeof value === 'object') {
69
+ if (typeof value.url === 'string') {
70
+ imageUrl = value.url;
71
+ } else if (typeof value.image_url === 'string') {
72
+ imageUrl = value.image_url;
73
+ } else if (
74
+ value.image_url &&
75
+ typeof value.image_url === 'object' &&
76
+ typeof value.image_url.url === 'string'
77
+ ) {
78
+ imageUrl = value.image_url.url;
79
+ }
80
+ }
81
+
82
+ if (!imageUrl || typeof imageUrl !== 'string') return null;
83
+ const trimmed = imageUrl.trim();
84
+ if (!trimmed) return null;
85
+
86
+ const dataUrl = parseBase64DataUrl(trimmed);
87
+ if (dataUrl) {
88
+ return {
89
+ type: 'image',
90
+ source: {
91
+ type: 'base64',
92
+ media_type: dataUrl.mediaType || 'image/png',
93
+ data: dataUrl.data
94
+ }
95
+ };
96
+ }
97
+
98
+ return {
99
+ type: 'image',
100
+ source: {
101
+ type: 'url',
102
+ url: trimmed
103
+ }
104
+ };
105
+ }
106
+
107
+ function normalizeOpenAiFileBlock(value) {
108
+ if (!value || typeof value !== 'object') return null;
109
+
110
+ const fileNode =
111
+ value.file && typeof value.file === 'object' ? value.file : value;
112
+ const fileData =
113
+ typeof fileNode.file_data === 'string' ? fileNode.file_data.trim() : '';
114
+ const fileUrl =
115
+ typeof fileNode.file_url === 'string' ? fileNode.file_url.trim() : '';
116
+ const fileId =
117
+ typeof fileNode.file_id === 'string' ? fileNode.file_id.trim() : '';
118
+
119
+ if (fileData) {
120
+ const dataUrl = parseBase64DataUrl(fileData);
121
+ if (dataUrl) {
122
+ return {
123
+ type: 'document',
124
+ source: {
125
+ type: 'base64',
126
+ media_type: dataUrl.mediaType || 'application/octet-stream',
127
+ data: dataUrl.data
128
+ }
129
+ };
130
+ }
131
+
132
+ return {
133
+ type: 'document',
134
+ source: {
135
+ type: 'base64',
136
+ media_type: 'application/octet-stream',
137
+ data: fileData
138
+ }
139
+ };
140
+ }
141
+
142
+ if (fileUrl) {
143
+ return {
144
+ type: 'document',
145
+ source: {
146
+ type: 'url',
147
+ url: fileUrl
148
+ }
149
+ };
150
+ }
151
+
152
+ if (fileId) {
153
+ return {
154
+ type: 'text',
155
+ text: `[input_file:${fileId}]`
156
+ };
157
+ }
158
+
159
+ return null;
160
+ }
161
+
162
+ function normalizeOpenAiContentItemToClaudeBlocks(item) {
163
+ if (item === null || item === undefined) return [];
164
+
165
+ if (
166
+ typeof item === 'string' ||
167
+ typeof item === 'number' ||
168
+ typeof item === 'boolean'
169
+ ) {
170
+ const text = String(item);
171
+ return text.trim() ? [{ type: 'text', text }] : [];
172
+ }
173
+
174
+ if (Array.isArray(item)) {
175
+ return item.flatMap(normalizeOpenAiContentItemToClaudeBlocks);
176
+ }
177
+
178
+ if (typeof item !== 'object') return [];
179
+
180
+ const itemType = String(item.type || '').trim().toLowerCase();
181
+ if (itemType === 'tool_use') {
182
+ return [item];
183
+ }
184
+ if (itemType === 'tool_result') {
185
+ return [item];
186
+ }
187
+
188
+ if (itemType === 'image' && item.source && typeof item.source === 'object') {
189
+ return [item];
190
+ }
191
+ if (
192
+ itemType === 'document' &&
193
+ item.source &&
194
+ typeof item.source === 'object'
195
+ ) {
196
+ return [item];
197
+ }
198
+
199
+ if (
200
+ itemType === 'text' ||
201
+ itemType === 'input_text' ||
202
+ itemType === 'output_text'
203
+ ) {
204
+ const text = typeof item.text === 'string' ? item.text : '';
205
+ if (!text.trim()) return [];
206
+ const block = { type: 'text', text };
207
+ if (item.cache_control && typeof item.cache_control === 'object') {
208
+ block.cache_control = item.cache_control;
209
+ }
210
+ return [block];
211
+ }
212
+
213
+ if (itemType === 'image_url' || itemType === 'input_image') {
214
+ const imageBlock = normalizeOpenAiImageBlock(item);
215
+ return imageBlock ? [imageBlock] : [];
216
+ }
217
+
218
+ if (itemType === 'file' || itemType === 'input_file') {
219
+ const fileBlock = normalizeOpenAiFileBlock(item);
220
+ return fileBlock ? [fileBlock] : [];
221
+ }
222
+
223
+ if (item.image_url !== undefined || item.url !== undefined) {
224
+ const imageBlock = normalizeOpenAiImageBlock(item);
225
+ if (imageBlock) return [imageBlock];
226
+ }
227
+
228
+ if (
229
+ item.file !== undefined ||
230
+ item.file_data !== undefined ||
231
+ item.file_url !== undefined ||
232
+ item.file_id !== undefined
233
+ ) {
234
+ const fileBlock = normalizeOpenAiFileBlock(item);
235
+ if (fileBlock) return [fileBlock];
236
+ }
237
+
238
+ const fallbackText = extractText(item);
239
+ return fallbackText ? [{ type: 'text', text: fallbackText }] : [];
240
+ }
241
+
242
+ function normalizeOpenAiContentToClaudeBlocks(content) {
243
+ return normalizeOpenAiContentItemToClaudeBlocks(content);
244
+ }
245
+
246
+ function normalizeOpenAiRole(role) {
247
+ const value = String(role || '').trim().toLowerCase();
248
+ if (value === 'assistant' || value === 'model') return 'assistant';
249
+ if (value === 'system' || value === 'developer') return 'system';
250
+ if (value === 'tool') return 'tool';
251
+ return 'user';
252
+ }
253
+
254
+ function isResponsesPath(pathname) {
255
+ const normalized =
256
+ String(pathname || '').trim().replace(/\/+$/, '') || '/';
257
+ return (
258
+ normalized.endsWith('/v1/responses') || normalized.endsWith('/responses')
259
+ );
260
+ }
261
+
262
+ function isChatCompletionsPath(pathname) {
263
+ const normalized =
264
+ String(pathname || '').trim().replace(/\/+$/, '') || '/';
265
+ return (
266
+ normalized.endsWith('/v1/chat/completions') ||
267
+ normalized.endsWith('/chat/completions')
268
+ );
269
+ }
270
+
271
+ function generateToolCallId() {
272
+ return `toolu_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
273
+ }
274
+
275
+ function parseToolArguments(value) {
276
+ if (value && typeof value === 'object' && !Array.isArray(value)) {
277
+ return value;
278
+ }
279
+ if (typeof value === 'string') {
280
+ const trimmed = value.trim();
281
+ if (!trimmed) return {};
282
+ try {
283
+ const parsed = JSON.parse(trimmed);
284
+ if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
285
+ return parsed;
286
+ }
287
+ } catch {
288
+ return {};
289
+ }
290
+ }
291
+ return {};
292
+ }
293
+
294
+ function normalizeToolResultContent(value) {
295
+ if (typeof value === 'string') return value;
296
+ if (value === null || value === undefined) return '';
297
+ if (typeof value === 'number' || typeof value === 'boolean')
298
+ return String(value);
299
+ try {
300
+ return JSON.stringify(value);
301
+ } catch {
302
+ return String(value);
303
+ }
304
+ }
305
+
306
+ function normalizeStopSequences(stopValue) {
307
+ if (!stopValue) return undefined;
308
+ if (typeof stopValue === 'string') {
309
+ const normalized = String(stopValue).trim();
310
+ if (normalized) return [normalized];
311
+ return undefined;
312
+ }
313
+ if (Array.isArray(stopValue)) {
314
+ const sequences = stopValue
315
+ .filter(item => typeof item === 'string')
316
+ .map(item => String(item).trim())
317
+ .filter(Boolean);
318
+ return sequences.length > 0 ? sequences : undefined;
319
+ }
320
+ return undefined;
321
+ }
322
+
323
+ function buildAssistantToolUseMessageFromFunctionCall(item) {
324
+ const functionPayload =
325
+ item?.function && typeof item.function === 'object'
326
+ ? item.function
327
+ : item;
328
+ const rawName = functionPayload?.name || item?.name;
329
+ if (!rawName) return null;
330
+
331
+ const callId =
332
+ functionPayload?.call_id ||
333
+ item?.call_id ||
334
+ functionPayload?.id ||
335
+ item?.id ||
336
+ generateToolCallId();
337
+ const argumentsSource =
338
+ functionPayload?.arguments ??
339
+ item?.arguments ??
340
+ functionPayload?.input ??
341
+ item?.input;
342
+ const input = parseToolArguments(argumentsSource);
343
+
344
+ return {
345
+ role: 'assistant',
346
+ content: [
347
+ {
348
+ type: 'tool_use',
349
+ id: callId,
350
+ name: rawName,
351
+ input
352
+ }
353
+ ]
354
+ };
355
+ }
356
+
357
+ function buildUserToolResultMessage(item) {
358
+ const callId =
359
+ item?.call_id || item?.tool_call_id || item?.id || generateToolCallId();
360
+ const outputSource = item?.output ?? item?.content ?? '';
361
+ const content = normalizeToolResultContent(outputSource);
362
+
363
+ return {
364
+ role: 'user',
365
+ content: [
366
+ {
367
+ type: 'tool_result',
368
+ tool_use_id: callId,
369
+ content
370
+ }
371
+ ]
372
+ };
373
+ }
374
+
375
+ function normalizeOpenCodeMessages(pathname, payload = {}) {
376
+ const systemBlocks = [];
377
+ const messages = [];
378
+
379
+ if (
380
+ isResponsesPath(pathname) &&
381
+ typeof payload.instructions === 'string' &&
382
+ payload.instructions.trim()
383
+ ) {
384
+ systemBlocks.push({
385
+ type: 'text',
386
+ text: payload.instructions.trim()
387
+ });
388
+ }
389
+
390
+ const appendMessage = (role, content, topLevelCacheControl) => {
391
+ const normalizedRole = normalizeOpenAiRole(role);
392
+ const contentBlocks = normalizeOpenAiContentToClaudeBlocks(content);
393
+ if (normalizedRole === 'system') {
394
+ const blocks = contentBlocks.filter(
395
+ block =>
396
+ block &&
397
+ block.type === 'text' &&
398
+ typeof block.text === 'string' &&
399
+ block.text.trim()
400
+ );
401
+ blocks.forEach((block, idx) => {
402
+ const systemBlock = { type: 'text', text: block.text };
403
+ if (block.cache_control && typeof block.cache_control === 'object') {
404
+ systemBlock.cache_control = block.cache_control;
405
+ } else if (
406
+ topLevelCacheControl &&
407
+ typeof topLevelCacheControl === 'object' &&
408
+ idx === blocks.length - 1
409
+ ) {
410
+ systemBlock.cache_control = topLevelCacheControl;
411
+ }
412
+ systemBlocks.push(systemBlock);
413
+ });
414
+ return;
415
+ }
416
+
417
+ if (!Array.isArray(contentBlocks) || contentBlocks.length === 0) return;
418
+ if (
419
+ topLevelCacheControl &&
420
+ typeof topLevelCacheControl === 'object' &&
421
+ contentBlocks.length > 0
422
+ ) {
423
+ const lastBlock = contentBlocks[contentBlocks.length - 1];
424
+ if (!lastBlock.cache_control)
425
+ lastBlock.cache_control = topLevelCacheControl;
426
+ }
427
+ messages.push({
428
+ role: normalizedRole === 'assistant' ? 'assistant' : 'user',
429
+ content: contentBlocks
430
+ });
431
+ };
432
+
433
+ if (isResponsesPath(pathname)) {
434
+ if (typeof payload.input === 'string') {
435
+ appendMessage('user', payload.input);
436
+ } else if (Array.isArray(payload.input)) {
437
+ payload.input.forEach(item => {
438
+ if (!item || typeof item !== 'object') return;
439
+ if (item.type === 'function_call') {
440
+ const assistantToolUse =
441
+ buildAssistantToolUseMessageFromFunctionCall(item);
442
+ if (assistantToolUse) {
443
+ messages.push(assistantToolUse);
444
+ }
445
+ return;
446
+ }
447
+ if (item.type === 'function_call_output') {
448
+ messages.push(buildUserToolResultMessage(item));
449
+ return;
450
+ }
451
+ if (item.type === 'message' || item.role) {
452
+ appendMessage(item.role, item.content, item.cache_control);
453
+ }
454
+ });
455
+ }
456
+ }
457
+
458
+ // Chat-completions conversion also applies to generic non-Responses paths
459
+ // (e.g. probe payloads sent against /v1/messages), so wire callers keep a
460
+ // single conversion path.
461
+ if (
462
+ Array.isArray(payload.messages) &&
463
+ (isChatCompletionsPath(pathname) || !isResponsesPath(pathname))
464
+ ) {
465
+ payload.messages.forEach(message => {
466
+ if (!message || typeof message !== 'object') return;
467
+ if (message.role === 'tool') {
468
+ messages.push(buildUserToolResultMessage(message));
469
+ return;
470
+ }
471
+ if (
472
+ message.role === 'assistant' &&
473
+ Array.isArray(message.tool_calls) &&
474
+ message.tool_calls.length > 0
475
+ ) {
476
+ const assistantContent = normalizeOpenAiContentToClaudeBlocks(
477
+ message.content
478
+ );
479
+
480
+ message.tool_calls.forEach(toolCall => {
481
+ if (!toolCall || typeof toolCall !== 'object') return;
482
+ const functionPayload =
483
+ toolCall.function && typeof toolCall.function === 'object'
484
+ ? toolCall.function
485
+ : toolCall;
486
+ const rawName = functionPayload.name || toolCall.name;
487
+ if (!rawName) return;
488
+ assistantContent.push({
489
+ type: 'tool_use',
490
+ id:
491
+ toolCall.id || functionPayload.call_id || generateToolCallId(),
492
+ name: rawName,
493
+ input: parseToolArguments(
494
+ functionPayload.arguments ?? functionPayload.input
495
+ )
496
+ });
497
+ });
498
+
499
+ if (assistantContent.length > 0) {
500
+ messages.push({
501
+ role: 'assistant',
502
+ content: assistantContent
503
+ });
504
+ }
505
+ return;
506
+ }
507
+ appendMessage(
508
+ message.role,
509
+ message.content,
510
+ message.cache_control
511
+ );
512
+ });
513
+ }
514
+
515
+ if (messages.length === 0) {
516
+ messages.push({
517
+ role: 'user',
518
+ content: [{ type: 'text', text: 'Hello' }]
519
+ });
520
+ }
521
+
522
+ return {
523
+ systemBlocks,
524
+ messages
525
+ };
526
+ }
527
+
528
+ module.exports = {
529
+ extractTextFragments,
530
+ extractText,
531
+ parseBase64DataUrl,
532
+ normalizeOpenAiImageBlock,
533
+ normalizeOpenAiFileBlock,
534
+ normalizeOpenAiContentItemToClaudeBlocks,
535
+ normalizeOpenAiContentToClaudeBlocks,
536
+ normalizeOpenAiRole,
537
+ isResponsesPath,
538
+ isChatCompletionsPath,
539
+ generateToolCallId,
540
+ parseToolArguments,
541
+ normalizeToolResultContent,
542
+ normalizeStopSequences,
543
+ buildAssistantToolUseMessageFromFunctionCall,
544
+ buildUserToolResultMessage,
545
+ normalizeOpenCodeMessages
546
+ };
@@ -10,7 +10,7 @@ const path = require('path');
10
10
  // ---------------------------------------------------------------------------
11
11
 
12
12
  /** @type {number} Maximum time a source inventory is considered fresh */
13
- const INDEX_INVENTORY_TTL_MS = 5000;
13
+ const INDEX_INVENTORY_TTL_MS = 30000;
14
14
 
15
15
  /** @type {number} Max wait for stale-ok on a cold source */
16
16
  const COLD_STALE_WAIT_MS = 2500;
@@ -20,7 +20,8 @@ function runInventoryWorker(source, indexDbPath, options = {}) {
20
20
  CC_TOOL_SESSION_HISTORY_CHILD: '1'
21
21
  },
22
22
  silent: true,
23
- stdio: ['pipe', 'pipe', 'pipe', 'ipc']
23
+ stdio: ['pipe', 'pipe', 'pipe', 'ipc'],
24
+ windowsHide: true
24
25
  });
25
26
 
26
27
  let settled = false;