@adhdev/daemon-core 0.6.56 → 0.6.58

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 (37) hide show
  1. package/dist/index.d.ts +23 -0
  2. package/dist/index.js +423 -95
  3. package/dist/index.js.map +1 -1
  4. package/package.json +1 -1
  5. package/providers/_builtin/cli/aider-cli/scripts/1.0/parse_output.js +51 -3
  6. package/providers/_builtin/cli/claude-cli/provider.json +18 -6
  7. package/providers/_builtin/cli/claude-cli/scripts/1.0/detect_status.js +68 -16
  8. package/providers/_builtin/cli/claude-cli/scripts/1.0/parse_approval.js +81 -22
  9. package/providers/_builtin/cli/claude-cli/scripts/1.0/parse_output.js +347 -94
  10. package/providers/_builtin/cli/codex-cli/provider.json +2 -0
  11. package/providers/_builtin/cli/codex-cli/scripts/1.0/detect_status.js +44 -10
  12. package/providers/_builtin/cli/codex-cli/scripts/1.0/parse_approval.js +83 -7
  13. package/providers/_builtin/cli/codex-cli/scripts/1.0/parse_output.js +501 -47
  14. package/providers/_builtin/cli/cursor-cli/scripts/1.0/parse_output.js +1 -1
  15. package/providers/_builtin/cli/github-copilot-cli/scripts/1.0/parse_output.js +1 -1
  16. package/providers/_builtin/cli/goose-cli/scripts/1.0/parse_output.js +1 -1
  17. package/providers/_builtin/cli/opencode-cli/scripts/1.0/parse_output.js +1 -1
  18. package/providers/_builtin/ide/vscode/provider.json +5 -1
  19. package/providers/_builtin/ide/vscode/scripts/1.0/focus_editor.js +1 -0
  20. package/providers/_builtin/ide/vscode/scripts/1.0/list_models.js +1 -0
  21. package/providers/_builtin/ide/vscode/scripts/1.0/list_sessions.js +1 -0
  22. package/providers/_builtin/ide/vscode/scripts/1.0/new_session.js +1 -0
  23. package/providers/_builtin/ide/vscode/scripts/1.0/open_panel.js +1 -0
  24. package/providers/_builtin/ide/vscode/scripts/1.0/read_chat.js +1 -0
  25. package/providers/_builtin/ide/vscode/scripts/1.0/resolve_action.js +1 -0
  26. package/providers/_builtin/ide/vscode/scripts/1.0/scripts.js +25 -0
  27. package/providers/_builtin/ide/vscode/scripts/1.0/send_message.js +1 -0
  28. package/providers/_builtin/ide/vscode/scripts/1.0/set_model.js +1 -0
  29. package/providers/_builtin/ide/vscode/scripts/1.0/switch_session.js +1 -0
  30. package/providers/_builtin/registry.json +1 -1
  31. package/src/cli-adapters/provider-cli-adapter.ts +410 -65
  32. package/src/commands/chat-commands.ts +7 -1
  33. package/src/config/chat-history.ts +53 -1
  34. package/src/daemon/dev-server.ts +7 -9
  35. package/src/providers/cli-provider-instance.ts +10 -23
  36. package/src/providers/provider-instance.ts +1 -0
  37. package/src/providers/version-archive.ts +4 -1
@@ -2,67 +2,521 @@
2
2
  * Codex CLI — parse_output
3
3
  */
4
4
  'use strict';
5
- const detectStatus = require('./detect_status.js');
5
+
6
+ const detectStatus = require('./detect_status.js');
6
7
  const parseApproval = require('./parse_approval.js');
7
8
 
8
- function splitTurns(buffer) {
9
- const messages = [];
10
- if (!buffer || buffer.length < 5) return messages;
11
- const lines = buffer.split('\n');
12
- let currentRole = null;
13
- let currentContent = [];
14
- let msgIndex = 0;
15
- let started = false;
9
+ function splitLines(text) {
10
+ return String(text || '')
11
+ .replace(/\u0007/g, '')
12
+ .split(/\r\n|\n|\r/g)
13
+ .map(line => line.replace(/\s+$/, ''));
14
+ }
16
15
 
17
- for (const line of lines) {
18
- const trimmed = line.trim();
19
- const userMatch = trimmed.match(/^[❯›>$]\s+(.+)$/);
20
- if (userMatch && userMatch[1].length > 1) {
21
- started = true;
22
- if (currentRole && currentContent.length > 0) {
23
- const text = currentContent.join('\n').trim();
24
- if (text.length > 1) {
25
- messages.push({ id: `msg_${msgIndex}`, role: currentRole, content: text.slice(0, 6000), index: msgIndex, kind: 'standard' });
26
- msgIndex++;
27
- }
16
+ function normalize(line) {
17
+ return String(line || '')
18
+ .replace(/\u0007/g, '')
19
+ .replace(/^\d+;/, '')
20
+ .replace(/\s+/g, ' ')
21
+ .trim();
22
+ }
23
+
24
+ function tokenizePrompt(text) {
25
+ return String(text || '')
26
+ .replace(/\s+/g, ' ')
27
+ .trim()
28
+ .split(/[^A-Za-z0-9_.:/-]+/)
29
+ .map(token => token.trim().toLowerCase())
30
+ .filter(token => token.length >= 4);
31
+ }
32
+
33
+ function findPromptLineIndex(lines, promptText) {
34
+ const tokens = tokenizePrompt(promptText);
35
+ if (tokens.length === 0) return -1;
36
+
37
+ for (let index = lines.length - 1; index >= 0; index -= 1) {
38
+ const line = normalize(lines[index]).toLowerCase();
39
+ if (!line) continue;
40
+ const matched = tokens.filter(token => line.includes(token)).length;
41
+ if (matched >= Math.min(tokens.length, 3)) return index;
42
+ }
43
+ return -1;
44
+ }
45
+
46
+ function sliceAfterLatestPrompt(text, promptText) {
47
+ const lines = splitLines(text);
48
+ const index = findPromptLineIndex(lines, promptText);
49
+ if (index < 0) return text;
50
+ return lines.slice(index + 1).join('\n');
51
+ }
52
+
53
+ function isBoxLine(line) {
54
+ return /^[─═╭╮╰╯│┌┐└┘├┤┬┴┼]+$/.test(line);
55
+ }
56
+
57
+ function isHeaderLine(line) {
58
+ return /^OpenAI Codex\b/i.test(line)
59
+ || /^>_ OpenAI Codex\b/i.test(line)
60
+ || /(?:^|[│\s])model:\s+/i.test(line)
61
+ || /(?:^|[│\s])directory:\s+/i.test(line);
62
+ }
63
+
64
+ function isFooterLine(line) {
65
+ return /⏎\s+send/i.test(line)
66
+ || /⌃J\s+newline/i.test(line)
67
+ || /⌃T\s+transcript/i.test(line)
68
+ || /⌃C\s+quit/i.test(line)
69
+ || /\b\d+(?:\.\d+)?[KM]?\s+tokens used\b/i.test(line)
70
+ || /\b\d+% context left\b/i.test(line);
71
+ }
72
+
73
+ function isWelcomeLine(line) {
74
+ return /To get started, describe a task/i.test(line)
75
+ || /^\/(?:init|status|approvals|model)\b/.test(line)
76
+ || /create an AGENTS\.md file/i.test(line)
77
+ || /show current session configuration/i.test(line)
78
+ || /choose what Codex can do without approval/i.test(line)
79
+ || /choose what model and reasoning effort to use/i.test(line)
80
+ || /Update available!/i.test(line)
81
+ || /npm install -g @openai\/codex@latest/i.test(line);
82
+ }
83
+
84
+ function isStatusLine(line) {
85
+ return /Esc to interrupt/i.test(line)
86
+ || /(?:Thinking|Planning|Searching|Reading|Working|Analyzing|Inspecting|Responding|Following instructions clearly)[^\n]*\(\d+s\b/i.test(line)
87
+ || /^[⠁-⣿]+$/.test(line);
88
+ }
89
+
90
+ function isApprovalLine(line) {
91
+ return /You are running Codex in/i.test(line)
92
+ || /Allow Codex to (?:run|apply)/i.test(line)
93
+ || /Press Enter to continue/i.test(line)
94
+ || /^(?:[>▌]\s*)?\d+\.\s+/.test(line);
95
+ }
96
+
97
+ function isInputLine(line) {
98
+ return /^▌\s*/.test(line) || /^>\s*$/.test(line);
99
+ }
100
+
101
+ function isPlaceholderLine(line) {
102
+ return /^(?:Write tests for @filename|Explain this codebase|Summarize recent commits|Implement \{feature\})$/i.test(line);
103
+ }
104
+
105
+ function isAssistantLeadLine(line) {
106
+ return /^>\s+/.test(line) || /^•\s+/.test(line);
107
+ }
108
+
109
+ function stripAssistantLead(line) {
110
+ return String(line || '').replace(/^(?:>\s+|•\s+)/, '').trim();
111
+ }
112
+
113
+ function isTranscriptNoise(line) {
114
+ return !line
115
+ || isBoxLine(line)
116
+ || isHeaderLine(line)
117
+ || isFooterLine(line)
118
+ || isWelcomeLine(line)
119
+ || isStatusLine(line)
120
+ || isApprovalLine(line)
121
+ || isInputLine(line)
122
+ || /^…\s+\+\d+\s+lines\b/i.test(line)
123
+ || isPlaceholderLine(line);
124
+ }
125
+
126
+ function cleanContentLine(rawLine) {
127
+ const normalized = normalize(rawLine);
128
+ if (!normalized || isTranscriptNoise(normalized)) return '';
129
+ let cleaned = normalized
130
+ .replace(/^✔\s+/, '')
131
+ .replace(/^\s*│\s*/, '')
132
+ .replace(/▌.*$/g, '')
133
+ .replace(/⏎\s+send.*$/i, '')
134
+ .replace(/\b\d+(?:\.\d+)?[KM]?\s+tokens used\b.*$/i, '')
135
+ .replace(/\b\d+% context left\b.*$/i, '')
136
+ .replace(/\b(?:Working|Thinking|Planning|Searching|Reading|Analyzing|Inspecting|Responding)[^.!?]*$/i, '')
137
+ .replace(/Write tests for @filename.*$/i, '')
138
+ .replace(/([.!?])(?:[A-Za-z0-9]{3,}){3,}$/g, '$1')
139
+ .trim();
140
+ if (/^[{\[]/.test(cleaned)) {
141
+ cleaned = cleaned.replace(/([}\]])[A-Za-z0-9]+$/, '$1');
142
+ }
143
+ return cleaned;
144
+ }
145
+
146
+ function cleanAssistantLeadContent(line) {
147
+ return cleanContentLine(line)
148
+ .replace(/^>\s+/, '')
149
+ .replace(/^•\s+/, '')
150
+ .trim();
151
+ }
152
+
153
+ function isWelcomeScreen(text) {
154
+ return /OpenAI Codex/i.test(text)
155
+ && /To get started, describe a task/i.test(text);
156
+ }
157
+
158
+ function collectAssistantLines(lines) {
159
+ const blocks = [];
160
+ let current = null;
161
+ let collecting = false;
162
+
163
+ for (const rawLine of lines) {
164
+ const line = normalize(rawLine);
165
+
166
+ if (!line) {
167
+ if (collecting && current && current.lines.length > 0 && current.lines[current.lines.length - 1] !== '') {
168
+ current.lines.push('');
28
169
  }
29
- currentRole = 'user';
30
- currentContent = [userMatch[1]];
31
170
  continue;
32
171
  }
33
- if (!started) continue;
34
- if (currentRole === 'user' && trimmed && !userMatch) {
35
- const text = currentContent.join('\n').trim();
36
- if (text.length > 1) {
37
- messages.push({ id: `msg_${msgIndex}`, role: 'user', content: text, index: msgIndex, kind: 'standard' });
38
- msgIndex++;
172
+
173
+ if (isInputLine(line)) {
174
+ collecting = false;
175
+ current = null;
176
+ continue;
177
+ }
178
+
179
+ if (isAssistantLeadLine(line)) {
180
+ const stripped = cleanAssistantLeadContent(stripAssistantLead(line));
181
+ collecting = true;
182
+ current = {
183
+ kind: /^>\s+/.test(line) ? 'assistant' : 'tool',
184
+ lines: [],
185
+ };
186
+ blocks.push(current);
187
+ if (stripped) current.lines.push(stripped);
188
+ continue;
189
+ }
190
+
191
+ if (!collecting) continue;
192
+
193
+ const cleaned = cleanContentLine(rawLine);
194
+ if (!cleaned) continue;
195
+ if (current && current.lines[current.lines.length - 1] !== cleaned) current.lines.push(cleaned);
196
+ }
197
+
198
+ const preferred = [...blocks].reverse().find(block => block.kind === 'assistant' && block.lines.some(Boolean))
199
+ || [...blocks].reverse().find(block => block.lines.some(Boolean));
200
+ const result = preferred ? preferred.lines.slice() : [];
201
+ while (result[0] === '') result.shift();
202
+ while (result[result.length - 1] === '') result.pop();
203
+
204
+ return result;
205
+ }
206
+
207
+ function cleanFallbackLines(lines) {
208
+ const result = [];
209
+ for (const rawLine of lines) {
210
+ const line = normalize(rawLine);
211
+ if (!isAssistantLeadLine(line)) continue;
212
+ const stripped = stripAssistantLead(line);
213
+ if (stripped && result[result.length - 1] !== stripped) result.push(stripped);
214
+ }
215
+ return result;
216
+ }
217
+
218
+ function extractAssistantText(text) {
219
+ return collectAssistantLines(splitLines(text)).join('\n').trim();
220
+ }
221
+
222
+ function extractTrailingLeadAnswer(text) {
223
+ const lines = splitLines(text);
224
+ for (let index = lines.length - 1; index >= 0; index -= 1) {
225
+ const line = normalize(lines[index]);
226
+ if (!isAssistantLeadLine(line)) continue;
227
+ const cleaned = cleanAssistantLeadContent(stripAssistantLead(line));
228
+ if (!cleaned || shouldSuppressAssistantText(cleaned)) continue;
229
+ return cleaned;
230
+ }
231
+ return '';
232
+ }
233
+
234
+ function extractFallbackText(text) {
235
+ return cleanFallbackLines(splitLines(text)).join('\n').trim();
236
+ }
237
+
238
+ function chooseRicherText(primary, secondary) {
239
+ const a = String(primary || '').trim();
240
+ const b = String(secondary || '').trim();
241
+ if (!a) return b;
242
+ if (!b) return a;
243
+ const aLines = a.split('\n').length;
244
+ const bLines = b.split('\n').length;
245
+ if (aLines > bLines + 1 || a.length > b.length + 60) return a;
246
+ if (bLines > aLines + 1 || b.length > a.length + 60) return b;
247
+ return a.length >= b.length ? a : b;
248
+ }
249
+
250
+ function extractVisibleContent(text) {
251
+ const blocks = [];
252
+ let current = [];
253
+ for (const rawLine of splitLines(text)) {
254
+ const cleaned = cleanContentLine(rawLine);
255
+ if (!cleaned) {
256
+ if (current.length > 0) {
257
+ blocks.push(current);
258
+ current = [];
39
259
  }
40
- currentRole = 'assistant';
41
- currentContent = [];
260
+ continue;
42
261
  }
43
- if (!trimmed) continue;
44
- if (/^[─═╭╮╰╯│]+$/.test(trimmed)) continue;
45
- if (/^[\u2800-\u28ff]+$/.test(trimmed)) continue;
46
- if (currentRole) currentContent.push(trimmed);
262
+ current.push(cleaned);
47
263
  }
48
- if (currentRole && currentContent.length > 0) {
49
- const text = currentContent.join('\n').trim();
50
- if (text.length > 1) {
51
- messages.push({ id: `msg_${msgIndex}`, role: currentRole, content: text.slice(0, 6000), index: msgIndex, kind: 'standard' });
264
+ if (current.length > 0) blocks.push(current);
265
+ const chosen = [...blocks].reverse().find(block => block.length > 1) || blocks[blocks.length - 1] || [];
266
+ return chosen.join('\n').trim();
267
+ }
268
+
269
+ function mergeLineContent(existing, incoming) {
270
+ const left = String(existing || '').trim();
271
+ const right = String(incoming || '').trim();
272
+ if (!left) return right;
273
+ if (!right) return left;
274
+ if (left === right) return right;
275
+
276
+ const leftLines = left.split('\n');
277
+ const rightLines = right.split('\n');
278
+ const leftNorm = leftLines.map(line => normalize(line));
279
+ const rightNorm = rightLines.map(line => normalize(line));
280
+ const maxOverlap = Math.min(leftLines.length, rightLines.length);
281
+
282
+ for (let overlap = maxOverlap; overlap >= 1; overlap--) {
283
+ const leftTail = leftNorm.slice(leftNorm.length - overlap);
284
+ const rightHead = rightNorm.slice(0, overlap);
285
+ if (leftTail.every((line, index) => line === rightHead[index])) {
286
+ return [...leftLines.slice(0, leftLines.length - overlap), ...rightLines].join('\n').trim();
52
287
  }
53
288
  }
54
- return messages.length > 50 ? messages.slice(-50) : messages;
289
+
290
+ if (left.includes(right)) return left;
291
+ if (right.includes(left)) return right;
292
+ return chooseRicherText(left, right);
293
+ }
294
+
295
+ function finalizeAssistantText(text) {
296
+ const value = String(text || '').trim();
297
+ if (!value) return '';
298
+ const lines = value.split('\n');
299
+ if (/^>\s*[{[]/.test(lines[0] || '')) {
300
+ lines[0] = lines[0].replace(/^>\s*/, '');
301
+ }
302
+ return lines.join('\n').trim();
303
+ }
304
+
305
+ function looksLikeStructuredAnswer(text) {
306
+ const value = String(text || '').trim();
307
+ if (!value) return false;
308
+ const firstLine = value.split('\n')[0] || '';
309
+ return /^TITLE=/.test(firstLine)
310
+ || /^NEWS=/.test(firstLine)
311
+ || /^[A-Z][A-Z0-9_]*=/.test(firstLine)
312
+ || /^[{\[]/.test(firstLine);
313
+ }
314
+
315
+ function isStructuredAnswerLine(line) {
316
+ const value = String(line || '').trim();
317
+ return /^TITLE=/.test(value)
318
+ || /^NEWS=/.test(value)
319
+ || /^[A-Z][A-Z0-9_]*=/.test(value)
320
+ || /^[{\[]/.test(value);
321
+ }
322
+
323
+ function extractStructuredAnswer(text) {
324
+ const lines = splitLines(text)
325
+ .map(cleanContentLine)
326
+ .filter(Boolean);
327
+ if (lines.length === 0) return '';
328
+
329
+ const trailing = [];
330
+ for (let i = lines.length - 1; i >= 0; i -= 1) {
331
+ const line = lines[i];
332
+ if (isStructuredAnswerLine(line)) {
333
+ trailing.unshift(line);
334
+ continue;
335
+ }
336
+ if (trailing.length > 0) break;
337
+ }
338
+ if (trailing.length >= 2) return trailing.join('\n').trim();
339
+
340
+ const blocks = [];
341
+ let current = [];
342
+ for (const line of lines) {
343
+ if (isStructuredAnswerLine(line)) {
344
+ current.push(line);
345
+ } else if (current.length > 0) {
346
+ blocks.push(current);
347
+ current = [];
348
+ }
349
+ }
350
+ if (current.length > 0) blocks.push(current);
351
+ const chosen = [...blocks].reverse().find(block => block.length >= 2) || [];
352
+ return chosen.join('\n').trim();
353
+ }
354
+
355
+ function looksLikeCorruptToolText(text) {
356
+ const value = String(text || '');
357
+ if (!value) return false;
358
+ return /Ran zsh -lc/i.test(value)
359
+ || /• Added /i.test(value)
360
+ || /… \+\d+ lines/.test(value)
361
+ || /└ /.test(value)
362
+ || /�/.test(value);
363
+ }
364
+
365
+ function shouldSuppressAssistantText(text) {
366
+ const value = String(text || '').trim();
367
+ if (!value) return true;
368
+ return /^>_ OpenAI Codex\b/.test(value)
369
+ || /^OpenAI Codex\b/.test(value)
370
+ || looksLikeCorruptToolText(value) && !looksLikeStructuredAnswer(value);
371
+ }
372
+
373
+ function shouldDropPartialText(text) {
374
+ const value = String(text || '').trim();
375
+ if (!value) return true;
376
+ return /^>_ OpenAI Codex\b/.test(value)
377
+ || /^OpenAI Codex\b/.test(value)
378
+ || /^[│\s]+$/.test(value)
379
+ || /(?:Working|Planning|Searching|Reading|Analyzing|Considering)/i.test(value) && !/\n/.test(value);
380
+ }
381
+
382
+ function shouldKeepPartialText(text) {
383
+ const value = String(text || '').trim();
384
+ if (!value || shouldDropPartialText(value) || looksLikeCorruptToolText(value)) return false;
385
+ return looksLikeStructuredAnswer(value) || value.split('\n').length >= 3;
386
+ }
387
+
388
+ function containsUiNoise(text) {
389
+ return /⏎\s+send|⌃J\s+newline|⌃T\s+transcript|⌃C\s+quit|\b\d+(?:\.\d+)?[KM]?\s+tokens used\b|\b\d+% context left\b|Working\(\d+s/i.test(String(text || ''));
390
+ }
391
+
392
+ function extractInlineLeadAnswer(text) {
393
+ const lines = splitLines(text);
394
+ for (let index = lines.length - 1; index >= 0; index -= 1) {
395
+ const rawLine = lines[index];
396
+ const match = rawLine.match(/>\s+(.+)$/);
397
+ if (!match) continue;
398
+ const cleaned = cleanAssistantLeadContent(`> ${match[1]}`);
399
+ if (cleaned && !shouldSuppressAssistantText(cleaned)) return cleaned;
400
+ }
401
+ return '';
402
+ }
403
+
404
+ function buildMessages(previousMessages, assistantText, partialText) {
405
+ const base = Array.isArray(previousMessages)
406
+ ? previousMessages
407
+ .filter(message => message && (message.role === 'user' || message.role === 'assistant'))
408
+ .map(message => ({
409
+ role: message.role,
410
+ content: typeof message.content === 'string' ? message.content : String(message.content || ''),
411
+ timestamp: message.timestamp,
412
+ }))
413
+ : [];
414
+
415
+ const lastUser = [...base].reverse().find(message => message.role === 'user')?.content || '';
416
+ const rawCandidate = finalizeAssistantText(assistantText || partialText);
417
+ const inlineLead = extractInlineLeadAnswer(rawCandidate);
418
+ const promptEcho = tokenizePrompt(lastUser)
419
+ .slice(0, 6)
420
+ .filter(token => normalize(rawCandidate).toLowerCase().includes(token))
421
+ .length >= 2;
422
+ const candidate = (inlineLead && (containsUiNoise(rawCandidate) || promptEcho))
423
+ ? inlineLead
424
+ : rawCandidate;
425
+ if (!candidate || shouldSuppressAssistantText(candidate)) return base;
426
+
427
+ const last = base[base.length - 1];
428
+ if (last && last.role === 'assistant') {
429
+ if (looksLikeStructuredAnswer(candidate) && !looksLikeStructuredAnswer(last.content)) {
430
+ last.content = candidate;
431
+ } else {
432
+ const merged = mergeLineContent(last.content, candidate);
433
+ if (last.content !== merged) last.content = merged;
434
+ }
435
+ } else {
436
+ base.push({ role: 'assistant', content: candidate });
437
+ }
438
+ return base;
439
+ }
440
+
441
+ function toMessageObjects(messages, status) {
442
+ return messages.slice(-50).map((message, index, slice) => ({
443
+ id: `msg_${index}`,
444
+ role: message.role,
445
+ content: typeof message.content === 'string' && message.content.length > 6000
446
+ ? `${message.content.slice(0, 6000)}\n[... truncated]`
447
+ : message.content,
448
+ index,
449
+ kind: 'standard',
450
+ ...(status === 'generating' && index === slice.length - 1 && message.role === 'assistant'
451
+ ? { meta: { streaming: true } }
452
+ : {}),
453
+ }));
55
454
  }
56
455
 
57
456
  module.exports = function parseOutput(input) {
58
- const { buffer, recentBuffer, partialResponse, screenText } = input;
457
+ const screenText = String(input?.screenText || '');
458
+ const buffer = String(input?.buffer || '');
59
459
  const transcript = screenText || buffer;
60
- const tail = recentBuffer || (transcript || '').slice(-500);
61
- const status = detectStatus({ tail });
62
- const activeModal = status === 'waiting_approval' ? parseApproval({ buffer: transcript, tail }) : null;
63
- const messages = splitTurns(transcript);
64
- if (status === 'generating' && partialResponse && partialResponse.trim().length > 2) {
65
- messages.push({ id: 'msg_partial', role: 'assistant', content: partialResponse.trim().slice(0, 6000), index: messages.length, kind: 'standard', meta: { streaming: true } });
460
+ const tail = String(input?.recentBuffer || transcript.slice(-500));
461
+ const previousMessages = Array.isArray(input?.messages) ? input.messages : [];
462
+ const lastUserMessage = [...previousMessages].reverse().find(message => message && message.role === 'user');
463
+ const promptScope = lastUserMessage?.content || '';
464
+ const scopedScreenText = sliceAfterLatestPrompt(screenText, promptScope);
465
+ const scopedBufferText = sliceAfterLatestPrompt(buffer, promptScope);
466
+
467
+ const status = detectStatus({
468
+ tail,
469
+ screenText,
470
+ rawBuffer: input?.rawBuffer || '',
471
+ });
472
+
473
+ const activeModal = status === 'waiting_approval'
474
+ ? parseApproval({ buffer: transcript, rawBuffer: input?.rawBuffer || '', tail })
475
+ : null;
476
+
477
+ if (status === 'waiting_approval' || (status === 'idle' && isWelcomeScreen(transcript))) {
478
+ return {
479
+ id: 'cli_session',
480
+ status,
481
+ title: 'Codex CLI',
482
+ messages: toMessageObjects(previousMessages, status),
483
+ activeModal,
484
+ };
66
485
  }
67
- return { id: 'cli_session', status, title: 'Codex CLI', messages, activeModal };
486
+
487
+ const bufferAssistantText = finalizeAssistantText(extractAssistantText(scopedBufferText || buffer));
488
+ const screenAssistantText = finalizeAssistantText(extractAssistantText(scopedScreenText || screenText));
489
+ const trailingLeadText = finalizeAssistantText(
490
+ extractTrailingLeadAnswer(scopedScreenText || screenText) || extractTrailingLeadAnswer(scopedBufferText || buffer),
491
+ );
492
+ const visibleContentText = finalizeAssistantText(extractVisibleContent(scopedScreenText || screenText));
493
+ const structuredAnswerText = finalizeAssistantText(
494
+ extractStructuredAnswer(scopedScreenText || screenText) || extractStructuredAnswer(scopedBufferText || buffer),
495
+ );
496
+ const assistantText = structuredAnswerText
497
+ || trailingLeadText
498
+ || looksLikeStructuredAnswer(screenAssistantText) || looksLikeCorruptToolText(bufferAssistantText)
499
+ ? structuredAnswerText || trailingLeadText || screenAssistantText || visibleContentText || bufferAssistantText
500
+ : finalizeAssistantText(mergeLineContent(
501
+ mergeLineContent(
502
+ chooseRicherText(bufferAssistantText, screenAssistantText),
503
+ visibleContentText,
504
+ ),
505
+ screenAssistantText,
506
+ ));
507
+ const partialText = status === 'generating'
508
+ ? finalizeAssistantText(chooseRicherText(
509
+ extractFallbackText(buffer),
510
+ extractFallbackText(String(input?.partialResponse || '')),
511
+ ))
512
+ : '';
513
+ const messages = buildMessages(previousMessages, assistantText, shouldKeepPartialText(partialText) ? partialText : '');
514
+
515
+ return {
516
+ id: 'cli_session',
517
+ status,
518
+ title: 'Codex CLI',
519
+ messages: toMessageObjects(messages, status),
520
+ activeModal,
521
+ };
68
522
  };
@@ -36,7 +36,7 @@ function splitTurns(buffer) {
36
36
 
37
37
  module.exports = function parseOutput(input) {
38
38
  const { buffer, recentBuffer, partialResponse, screenText } = input;
39
- const transcript = screenText || buffer;
39
+ const transcript = buffer || screenText;
40
40
  const tail = recentBuffer || (transcript || '').slice(-500);
41
41
  const status = detectStatus({ tail });
42
42
  const activeModal = status === 'waiting_approval' ? parseApproval({ buffer: transcript, tail }) : null;
@@ -36,7 +36,7 @@ function splitTurns(buffer) {
36
36
 
37
37
  module.exports = function parseOutput(input) {
38
38
  const { buffer, recentBuffer, partialResponse, screenText } = input;
39
- const transcript = screenText || buffer;
39
+ const transcript = buffer || screenText;
40
40
  const tail = recentBuffer || (transcript || '').slice(-500);
41
41
  const status = detectStatus({ tail });
42
42
  const activeModal = status === 'waiting_approval' ? parseApproval({ buffer: transcript, tail }) : null;
@@ -36,7 +36,7 @@ function splitTurns(buffer) {
36
36
 
37
37
  module.exports = function parseOutput(input) {
38
38
  const { buffer, recentBuffer, partialResponse, screenText } = input;
39
- const transcript = screenText || buffer;
39
+ const transcript = buffer || screenText;
40
40
  const tail = recentBuffer || (transcript || '').slice(-500);
41
41
  const status = detectStatus({ tail });
42
42
  const activeModal = status === 'waiting_approval' ? parseApproval({ buffer: transcript, tail }) : null;
@@ -36,7 +36,7 @@ function splitTurns(buffer) {
36
36
 
37
37
  module.exports = function parseOutput(input) {
38
38
  const { buffer, recentBuffer, partialResponse, screenText } = input;
39
- const transcript = screenText || buffer;
39
+ const transcript = buffer || screenText;
40
40
  const tail = recentBuffer || (transcript || '').slice(-500);
41
41
  const status = detectStatus({ tail });
42
42
  const activeModal = status === 'waiting_approval' ? parseApproval({ buffer: transcript, tail }) : null;
@@ -37,7 +37,11 @@
37
37
  },
38
38
  "inputMethod": "cdp-type-and-send",
39
39
  "inputSelector": "[contenteditable=\"true\"][role=\"textbox\"]",
40
- "versionCommand": "code --version",
40
+ "versionCommand": {
41
+ "darwin": "\"/Applications/Visual Studio Code.app/Contents/Resources/app/bin/code\" --version",
42
+ "win32": "\"C:\\Program Files\\Microsoft VS Code\\bin\\code\" --version",
43
+ "linux": "code --version"
44
+ },
41
45
  "providerVersion": "0.0.0",
42
46
  "compatibility": [
43
47
  { "ideVersion": ">=1.0.0", "scriptDir": "scripts/1.0" }
@@ -0,0 +1 @@
1
+ module.exports = async function(args, context) { return { error: "Not implemented" }; };
@@ -0,0 +1 @@
1
+ module.exports = async function(args, context) { return { error: "Not implemented" }; };
@@ -0,0 +1 @@
1
+ module.exports = async function(args, context) { return { error: "Not implemented" }; };
@@ -0,0 +1 @@
1
+ module.exports = async function(args, context) { return { error: "Not implemented" }; };
@@ -0,0 +1 @@
1
+ module.exports = async function(args, context) { return { error: "Not implemented" }; };
@@ -0,0 +1 @@
1
+ module.exports = async function(args, context) { return { error: "Not implemented" }; };
@@ -0,0 +1 @@
1
+ module.exports = async function(args, context) { return { error: "Not implemented" }; };
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Visual Studio Code (Native GitHub Copilot Chat) — Scripts
3
+ */
4
+
5
+ module.exports = {
6
+ // ─── IDE Core ───
7
+ openPanel: require('./open_panel.js'),
8
+ focusEditor: require('./focus_editor.js'),
9
+
10
+ // ─── Chat Interaction ───
11
+ sendMessage: require('./send_message.js'),
12
+ readChat: require('./read_chat.js'),
13
+
14
+ // ─── Session Management ───
15
+ newSession: require('./new_session.js'),
16
+ listSessions: require('./list_sessions.js'),
17
+ switchSession: require('./switch_session.js'),
18
+
19
+ // ─── Resolution ───
20
+ resolveAction: require('./resolve_action.js'),
21
+
22
+ // ─── Models ───
23
+ listModels: require('./list_models.js'),
24
+ setModel: require('./set_model.js'),
25
+ };
@@ -0,0 +1 @@
1
+ module.exports = async function(args, context) { return { error: "Not implemented" }; };