@adhdev/daemon-core 0.6.47 → 0.6.49
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/index.js +50 -23
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/providers/_builtin/cli/claude-cli/provider.json +1 -7
- package/providers/_builtin/ide/kiro/provider.json +0 -1
- package/providers/_builtin/ide/kiro/scripts/1.0/webview_dump_html.js +1 -0
- package/providers/_builtin/ide/kiro/scripts/1.0/webview_list_models.js +25 -22
- package/providers/_builtin/ide/kiro/scripts/1.0/webview_list_modes.js +10 -27
- package/providers/_builtin/ide/kiro/scripts/1.0/webview_read_chat.js +103 -10
- package/providers/_builtin/ide/kiro/scripts/1.0/webview_resolve_action.js +62 -0
- package/providers/_builtin/ide/kiro/scripts/1.0/webview_send_message.js +23 -1
- package/providers/_builtin/ide/kiro/scripts/1.0/webview_set_mode.js +25 -6
- package/providers/_builtin/ide/kiro/scripts/1.0/webview_set_model.js +38 -6
- package/providers/_builtin/ide/windsurf/scripts/1.0/read_chat.js +1 -1
- package/providers/_builtin/registry.json +1 -1
- package/src/cli-adapters/provider-cli-adapter.ts +4 -7
- package/src/providers/acp-provider-instance.ts +4 -1
- package/src/providers/cli-provider-instance.ts +5 -1
- package/src/providers/extension-provider-instance.ts +8 -2
- package/src/providers/ide-provider-instance.ts +7 -1
- package/src/providers/status-monitor.ts +30 -5
package/package.json
CHANGED
|
@@ -67,9 +67,6 @@
|
|
|
67
67
|
"patterns": {
|
|
68
68
|
"prompt": [
|
|
69
69
|
{ "source": "Type your message", "flags": "i" },
|
|
70
|
-
{ "source": "^>\\s*$", "flags": "m" },
|
|
71
|
-
{ "source": "[›❯]\\s*[\\r\\n]", "flags": "" },
|
|
72
|
-
{ "source": "[›❯]\\s*$", "flags": "m" },
|
|
73
70
|
{ "source": "for\\s*shortcuts", "flags": "i" },
|
|
74
71
|
{ "source": "\\?\\s*for\\s*help", "flags": "i" },
|
|
75
72
|
{ "source": "Press enter", "flags": "i" }
|
|
@@ -85,10 +82,7 @@
|
|
|
85
82
|
{ "source": "Allow\\s*once", "flags": "i" },
|
|
86
83
|
{ "source": "Always\\s*allow", "flags": "i" },
|
|
87
84
|
{ "source": "\\(y/n\\)", "flags": "i" },
|
|
88
|
-
{ "source": "\\[Y/n\\]", "flags": "i" }
|
|
89
|
-
{ "source": "Run\\s+\\w+\\s+command", "flags": "i" },
|
|
90
|
-
{ "source": "Yes,?\\s*don'?t\\s*ask", "flags": "i" },
|
|
91
|
-
{ "source": "\\bDeny\\b", "flags": "i" }
|
|
85
|
+
{ "source": "\\[Y/n\\]", "flags": "i" }
|
|
92
86
|
],
|
|
93
87
|
"ready": [
|
|
94
88
|
{ "source": "for\\s*shortcuts", "flags": "i" },
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
(() => { return document.documentElement.outerHTML; })()
|
|
@@ -1,34 +1,37 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Kiro — webview_list_models
|
|
3
3
|
*/
|
|
4
|
-
(() => {
|
|
4
|
+
(async () => {
|
|
5
5
|
try {
|
|
6
|
-
const
|
|
7
|
-
|
|
6
|
+
const trigger = document.querySelector('.kiro-dropdown-trigger');
|
|
7
|
+
if (!trigger) {
|
|
8
|
+
return JSON.stringify({ models: [], current: 'Default', error: 'No dropdown found' });
|
|
9
|
+
}
|
|
8
10
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
}
|
|
18
|
-
}
|
|
11
|
+
const current = (trigger.querySelector('.kiro-dropdown-selected-text')?.textContent || '').trim();
|
|
12
|
+
|
|
13
|
+
// Check if menu is already open
|
|
14
|
+
const wasExpanded = trigger.getAttribute('aria-expanded') === 'true';
|
|
15
|
+
|
|
16
|
+
if (!wasExpanded) {
|
|
17
|
+
trigger.click();
|
|
18
|
+
await new Promise(r => setTimeout(r, 150));
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
current = txt;
|
|
28
|
-
}
|
|
21
|
+
const models = [];
|
|
22
|
+
const items = document.querySelectorAll('.kiro-dropdown-item, [role="menuitem"], [role="option"]');
|
|
23
|
+
for (const item of items) {
|
|
24
|
+
const txt = (item.textContent || '').trim();
|
|
25
|
+
if (txt && txt.length < 50) {
|
|
26
|
+
models.push(txt);
|
|
29
27
|
}
|
|
30
28
|
}
|
|
31
29
|
|
|
30
|
+
// Close menu
|
|
31
|
+
if (!wasExpanded) {
|
|
32
|
+
trigger.click();
|
|
33
|
+
}
|
|
34
|
+
|
|
32
35
|
return JSON.stringify({
|
|
33
36
|
models: [...new Set(models)],
|
|
34
37
|
current: current || 'Default'
|
|
@@ -1,39 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Kiro — webview_list_modes
|
|
3
|
+
* Maps the Autopilot toggle to modes "Autopilot" and "Manual".
|
|
3
4
|
*/
|
|
4
5
|
(() => {
|
|
5
6
|
try {
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
const sel = document.querySelectorAll('select, [class*="model"], [id*="model"]');
|
|
11
|
-
for (const el of sel) {
|
|
12
|
-
const txt = (el.textContent || '').trim();
|
|
13
|
-
if (txt && /claude|gpt|gemini|sonnet|opus/i.test(txt)) {
|
|
14
|
-
if (txt.length < 50) {
|
|
15
|
-
models.push(txt);
|
|
16
|
-
if (!current) current = txt;
|
|
17
|
-
}
|
|
18
|
-
}
|
|
7
|
+
const toggle = document.querySelector('#autonomy-mode-toggle-switch');
|
|
8
|
+
if (!toggle) {
|
|
9
|
+
// Fallback for older versions or if UI changed
|
|
10
|
+
return JSON.stringify({ modes: ['Default'], current: 'Default' });
|
|
19
11
|
}
|
|
20
12
|
|
|
21
|
-
|
|
22
|
-
const btns = document.querySelectorAll('button');
|
|
23
|
-
for (const b of btns) {
|
|
24
|
-
const txt = (b.textContent || '').trim();
|
|
25
|
-
if (txt && /claude|gpt|gemini|sonnet/i.test(txt) && txt.length < 30) {
|
|
26
|
-
models.push(txt);
|
|
27
|
-
current = txt;
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
}
|
|
13
|
+
const isAutopilot = toggle.checked;
|
|
31
14
|
|
|
32
15
|
return JSON.stringify({
|
|
33
|
-
|
|
34
|
-
current:
|
|
16
|
+
modes: ['Autopilot', 'Manual'],
|
|
17
|
+
current: isAutopilot ? 'Autopilot' : 'Manual'
|
|
35
18
|
});
|
|
36
19
|
} catch (e) {
|
|
37
|
-
return JSON.stringify({
|
|
20
|
+
return JSON.stringify({ modes: [], current: '', error: e.message });
|
|
38
21
|
}
|
|
39
22
|
})()
|
|
@@ -20,30 +20,122 @@
|
|
|
20
20
|
msgElements.forEach((msg, idx) => {
|
|
21
21
|
const roleMeta = msg.querySelector('.kiro-chat-message-role');
|
|
22
22
|
const roleText = (roleMeta?.textContent || '').trim();
|
|
23
|
-
const isKiro = roleText.toLowerCase()
|
|
23
|
+
const isKiro = roleText.toLowerCase().includes('kiro');
|
|
24
24
|
const role = isKiro ? 'assistant' : 'user';
|
|
25
25
|
|
|
26
|
+
let parts = [];
|
|
27
|
+
|
|
28
|
+
// A helper to traverse and build rough markdown
|
|
29
|
+
const parseNode = (node) => {
|
|
30
|
+
if (node.nodeType === Node.TEXT_NODE) {
|
|
31
|
+
return node.nodeValue;
|
|
32
|
+
}
|
|
33
|
+
const tag = node.tagName?.toLowerCase();
|
|
34
|
+
if (!tag) return '';
|
|
35
|
+
|
|
36
|
+
// Kiro Thought / Tool executions
|
|
37
|
+
if (tag === 'div' && node.className.includes('kiro-thought')) {
|
|
38
|
+
const toggle = node.querySelector('.summary')?.textContent || 'thought';
|
|
39
|
+
const content = Array.from(node.querySelectorAll('.details')).map(n => n.textContent).join('\n');
|
|
40
|
+
parts.push({ kind: 'thought', content });
|
|
41
|
+
return `\n<details><summary>${toggle}</summary>\n${content}\n</details>\n`;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Kiro Agent Outcomes (Terminal / Command)
|
|
45
|
+
if (tag === 'div' && node.className.includes('agent-outcome')) {
|
|
46
|
+
const label = (node.querySelector('.agent-outcome-label')?.textContent || '').toLowerCase();
|
|
47
|
+
const pre = node.querySelector('.agent-outcome-details pre, .agent-outcome-details code');
|
|
48
|
+
const codeContent = (pre?.textContent || '').trim();
|
|
49
|
+
if (codeContent) {
|
|
50
|
+
const kind = label.includes('command') || label.includes('terminal') ? 'terminal' : 'tool';
|
|
51
|
+
// Add directly to parts since this is a structured execution
|
|
52
|
+
parts.push({ kind, content: codeContent });
|
|
53
|
+
return `\n> [${kind}] ${codeContent}\n`;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (tag === 'pre') {
|
|
58
|
+
const code = node.querySelector('code');
|
|
59
|
+
const lang = code?.className?.replace('language-', '') || '';
|
|
60
|
+
return `\n\`\`\`${lang}\n${node.textContent}\n\`\`\`\n`;
|
|
61
|
+
}
|
|
62
|
+
if (tag === 'code') {
|
|
63
|
+
return `\`${node.textContent}\``;
|
|
64
|
+
}
|
|
65
|
+
if (tag === 'table') {
|
|
66
|
+
let str = '\n';
|
|
67
|
+
const rows = Array.from(node.querySelectorAll('tr'));
|
|
68
|
+
rows.forEach((row, i) => {
|
|
69
|
+
const cells = Array.from(row.querySelectorAll('td, th')).map(c => c.textContent.trim());
|
|
70
|
+
str += '| ' + cells.join(' | ') + ' |\n';
|
|
71
|
+
if (i === 0 && row.querySelector('th')) {
|
|
72
|
+
str += '|' + cells.map(() => '---').join('|') + '|\n';
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
return str + '\n';
|
|
76
|
+
}
|
|
77
|
+
if (tag === 'p') {
|
|
78
|
+
const text = Array.from(node.childNodes).map(parseNode).join('');
|
|
79
|
+
return text + '\n\n';
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Recursively parse children for span, div, etc
|
|
83
|
+
return Array.from(node.childNodes).map(parseNode).join('');
|
|
84
|
+
};
|
|
85
|
+
|
|
26
86
|
const body = msg.querySelector('.kiro-chat-message-body');
|
|
27
87
|
let content = '';
|
|
28
88
|
if (body) {
|
|
29
|
-
|
|
30
|
-
content = (
|
|
89
|
+
// Parse the ENTIRE body recursively so we don't skip elements!
|
|
90
|
+
content = parseNode(body).trim();
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Fallback content parsing if recursive failed
|
|
94
|
+
if (!content) {
|
|
95
|
+
content = body?.textContent?.trim() || '';
|
|
31
96
|
}
|
|
32
97
|
|
|
33
98
|
if (content) {
|
|
34
|
-
|
|
99
|
+
parts.push({ kind: 'text', content });
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (parts.length > 0) {
|
|
103
|
+
messages.push({ role, content: parts.map(p => p.content).join('\n'), parts });
|
|
35
104
|
}
|
|
36
105
|
});
|
|
37
106
|
|
|
38
|
-
// 상태 감지
|
|
107
|
+
// 상태 감지 및 activeModal 추출
|
|
39
108
|
let status = 'idle';
|
|
109
|
+
let activeModal = undefined;
|
|
40
110
|
|
|
41
111
|
// "Working" / "Cancel" 버튼 → generating
|
|
42
|
-
const
|
|
43
|
-
if (
|
|
44
|
-
const barText = (
|
|
112
|
+
const snackbar = document.querySelector('.kiro-snackbar');
|
|
113
|
+
if (snackbar && snackbar.offsetWidth > 0) {
|
|
114
|
+
const barText = (snackbar.textContent || '').toLowerCase();
|
|
45
115
|
if (barText.includes('working') || barText.includes('cancel')) {
|
|
46
116
|
status = 'generating';
|
|
117
|
+
} else if (barText.includes('waiting') || barText.includes('input')) {
|
|
118
|
+
// 승인 대기 중 (waiting on your input)
|
|
119
|
+
const titleEl = snackbar.querySelector('.kiro-snackbar-title, .thinking-text');
|
|
120
|
+
const actionsEl = snackbar.querySelectorAll('.kiro-snackbar-actions button');
|
|
121
|
+
const buttons = Array.from(actionsEl).map(b => (b.textContent || '').trim());
|
|
122
|
+
|
|
123
|
+
if (buttons.length > 0) {
|
|
124
|
+
activeModal = {
|
|
125
|
+
title: (titleEl?.textContent || '').trim(),
|
|
126
|
+
buttons,
|
|
127
|
+
type: 'approval'
|
|
128
|
+
};
|
|
129
|
+
status = 'waiting_approval';
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// If snackbar didn't catch it, fallback to Stop button checking
|
|
135
|
+
if (status === 'idle') {
|
|
136
|
+
const hasStopBtn = document.querySelector('.codicon-debug-stop, [aria-label*="stop" i], [title*="stop" i], [title*="cancel generation" i], .kiro-button[data-loading="true"]');
|
|
137
|
+
if (hasStopBtn) {
|
|
138
|
+
status = 'generating';
|
|
47
139
|
}
|
|
48
140
|
}
|
|
49
141
|
|
|
@@ -59,8 +151,9 @@
|
|
|
59
151
|
id: title || 'kiro-default',
|
|
60
152
|
status,
|
|
61
153
|
messages,
|
|
62
|
-
title: title ||
|
|
63
|
-
inputContent: inputContent ||
|
|
154
|
+
title: title || '',
|
|
155
|
+
inputContent: inputContent || '',
|
|
156
|
+
...(activeModal ? { activeModal } : {})
|
|
64
157
|
});
|
|
65
158
|
} catch (e) {
|
|
66
159
|
return JSON.stringify({ id: '', status: 'error', messages: [], error: e.message });
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Kiro — webview_resolve_action
|
|
3
|
+
* Kiro's approval dialog uses buttons in the kiro-snackbar.
|
|
4
|
+
* 파라미터: ${ BUTTON_TEXT }
|
|
5
|
+
*/
|
|
6
|
+
(() => {
|
|
7
|
+
try {
|
|
8
|
+
const want = ${ BUTTON_TEXT };
|
|
9
|
+
const wantNorm = (want || '').replace(/\s+/g, ' ').trim().toLowerCase();
|
|
10
|
+
|
|
11
|
+
function matches(el) {
|
|
12
|
+
const t = (el.textContent || '').replace(/\s+/g, ' ').trim().toLowerCase();
|
|
13
|
+
if (!t) return false;
|
|
14
|
+
if (t === wantNorm || t.startsWith(wantNorm) || wantNorm.startsWith(t)) return true;
|
|
15
|
+
if (/^(run|approve|allow|accept|yes|trust)\b/.test(wantNorm)) {
|
|
16
|
+
if (/^(run|allow|accept|approve|trust)\b/.test(t)) return true;
|
|
17
|
+
}
|
|
18
|
+
if (/^(reject|deny|no|abort|cancel)\b/.test(wantNorm)) {
|
|
19
|
+
if (/^(reject|deny|cancel)\b/.test(t)) return true;
|
|
20
|
+
}
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const btns = Array.from(document.querySelectorAll('button, [role="button"]'));
|
|
25
|
+
let found = null;
|
|
26
|
+
for (const b of btns.slice().reverse()) {
|
|
27
|
+
const attrText = (b.getAttribute('title') || b.getAttribute('aria-label') || '').replace(/\s+/g, ' ').trim().toLowerCase();
|
|
28
|
+
const hasPlay = b.querySelector('.codicon-play, .codicon-check') !== null;
|
|
29
|
+
const hasReject = b.querySelector('.codicon-chrome-close, .codicon-close') !== null;
|
|
30
|
+
|
|
31
|
+
// Check direct match
|
|
32
|
+
if (matches(b)) {
|
|
33
|
+
found = b;
|
|
34
|
+
break;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Checks for 'run/approve' intent
|
|
38
|
+
if (/^(run|approve|allow|accept|yes|trust)\b/.test(wantNorm)) {
|
|
39
|
+
if (hasPlay || attrText.includes('run') || attrText.includes('approve') || attrText.includes('allow') || attrText.includes('trust')) {
|
|
40
|
+
found = b;
|
|
41
|
+
break;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Checks for 'reject/deny/cancel' intent
|
|
46
|
+
if (/^(reject|deny|no|abort|cancel)\b/.test(wantNorm)) {
|
|
47
|
+
if (hasReject || attrText.includes('reject') || attrText.includes('deny') || attrText.includes('cancel')) {
|
|
48
|
+
found = b;
|
|
49
|
+
break;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (found) {
|
|
55
|
+
found.click();
|
|
56
|
+
return JSON.stringify({ resolved: true, method: 'webview_button_click' });
|
|
57
|
+
}
|
|
58
|
+
return JSON.stringify({ resolved: false, want: wantNorm, error: 'Button not found' });
|
|
59
|
+
} catch (e) {
|
|
60
|
+
return JSON.stringify({ resolved: false, error: e.message });
|
|
61
|
+
}
|
|
62
|
+
})()
|
|
@@ -54,7 +54,29 @@
|
|
|
54
54
|
editor.dispatchEvent(new Event('input', { bubbles: true }));
|
|
55
55
|
await new Promise(r => setTimeout(r, 400));
|
|
56
56
|
|
|
57
|
-
// ─── 3. Enter
|
|
57
|
+
// ─── 3. 전송 버튼 클릭 (Enter 키가 안 먹힐 때 대비) ───
|
|
58
|
+
const sendBtns = Array.from(document.querySelectorAll('button, div[role="button"], span[role="button"]'))
|
|
59
|
+
.filter(b => {
|
|
60
|
+
const aria = (b.getAttribute('aria-label') || '').toLowerCase();
|
|
61
|
+
const title = (b.getAttribute('title') || '').toLowerCase();
|
|
62
|
+
const text = (b.textContent || '').toLowerCase();
|
|
63
|
+
const className = (b.className || '').toLowerCase();
|
|
64
|
+
return aria.includes('send') || aria.includes('submit') ||
|
|
65
|
+
title.includes('send') || title.includes('submit') ||
|
|
66
|
+
className.includes('send') || className.includes('submit') ||
|
|
67
|
+
b.querySelector('svg'); // Fallback for icon-only buttons next to input
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
// Find the button closest to the editor
|
|
71
|
+
let submitBtn = null;
|
|
72
|
+
if (sendBtns.length > 0) {
|
|
73
|
+
// grab the one visually right/bottom to the editor, or just the last svg button
|
|
74
|
+
submitBtn = sendBtns[sendBtns.length - 1];
|
|
75
|
+
submitBtn.click();
|
|
76
|
+
await new Promise(r => setTimeout(r, 100));
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// ─── 4. Enter 키 전송 (Fallback) ───
|
|
58
80
|
const enterOpts = {
|
|
59
81
|
key: 'Enter', code: 'Enter',
|
|
60
82
|
keyCode: 13, which: 13,
|
|
@@ -1,14 +1,33 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
2
|
+
* Kiro — webview_set_mode
|
|
3
|
+
* Toggles the Autopilot switch based on requested mode.
|
|
4
|
+
* 파라미터: ${ MODE }
|
|
4
5
|
*/
|
|
5
6
|
(() => {
|
|
6
7
|
try {
|
|
7
|
-
const
|
|
8
|
-
|
|
8
|
+
const mode = ${ MODE };
|
|
9
|
+
if (!mode) return JSON.stringify({ success: false, error: 'No mode specified' });
|
|
9
10
|
|
|
10
|
-
|
|
11
|
-
|
|
11
|
+
const toggle = document.querySelector('#autonomy-mode-toggle-switch');
|
|
12
|
+
if (!toggle) {
|
|
13
|
+
return JSON.stringify({ success: false, error: 'Autonomy toggle not found' });
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const isAutopilot = toggle.checked;
|
|
17
|
+
|
|
18
|
+
const wantAutopilot = mode.toLowerCase() === 'autopilot';
|
|
19
|
+
const wantManual = mode.toLowerCase() === 'manual';
|
|
20
|
+
|
|
21
|
+
if (wantAutopilot && !isAutopilot) {
|
|
22
|
+
toggle.click();
|
|
23
|
+
return JSON.stringify({ success: true });
|
|
24
|
+
} else if (wantManual && isAutopilot) {
|
|
25
|
+
toggle.click();
|
|
26
|
+
return JSON.stringify({ success: true });
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Already in the right mode
|
|
30
|
+
return JSON.stringify({ success: true });
|
|
12
31
|
} catch (e) {
|
|
13
32
|
return JSON.stringify({ success: false, error: e.message });
|
|
14
33
|
}
|
|
@@ -1,14 +1,46 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* ${ MODEL }
|
|
2
|
+
* Kiro — webview_set_model
|
|
3
|
+
* 파라미터: ${ MODEL }
|
|
4
4
|
*/
|
|
5
|
-
(() => {
|
|
5
|
+
(async () => {
|
|
6
6
|
try {
|
|
7
7
|
const want = ${ MODEL } || '';
|
|
8
|
-
const norm = (t) => t.toLowerCase().trim();
|
|
8
|
+
const norm = (t) => (t || '').toLowerCase().trim();
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
if (!want) return JSON.stringify({ success: false, error: 'No model specified' });
|
|
11
|
+
|
|
12
|
+
const trigger = document.querySelector('.kiro-dropdown-trigger');
|
|
13
|
+
if (!trigger) return JSON.stringify({ success: false, error: 'Trigger not found' });
|
|
14
|
+
|
|
15
|
+
const current = norm(trigger.querySelector('.kiro-dropdown-selected-text')?.textContent);
|
|
16
|
+
if (current === norm(want) || current.includes(norm(want))) {
|
|
17
|
+
return JSON.stringify({ success: true, already: true });
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const wasExpanded = trigger.getAttribute('aria-expanded') === 'true';
|
|
21
|
+
if (!wasExpanded) {
|
|
22
|
+
trigger.click();
|
|
23
|
+
await new Promise(r => setTimeout(r, 150));
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const items = document.querySelectorAll('.kiro-dropdown-item, [role="menuitem"], [role="option"]');
|
|
27
|
+
let found = null;
|
|
28
|
+
for (const item of items) {
|
|
29
|
+
const txt = norm(item.textContent);
|
|
30
|
+
if (txt === norm(want) || txt.includes(norm(want)) || norm(want).includes(txt)) {
|
|
31
|
+
found = item;
|
|
32
|
+
break;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (found) {
|
|
37
|
+
found.click();
|
|
38
|
+
return JSON.stringify({ success: true });
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (!wasExpanded) trigger.click(); // Close if we opened but didn't find
|
|
42
|
+
|
|
43
|
+
return JSON.stringify({ success: false, error: 'Model not found in list' });
|
|
12
44
|
} catch (e) {
|
|
13
45
|
return JSON.stringify({ success: false, error: e.message });
|
|
14
46
|
}
|
|
@@ -137,7 +137,7 @@
|
|
|
137
137
|
status = 'waiting_approval';
|
|
138
138
|
activeModal = {
|
|
139
139
|
message: normalize(cascade.querySelector('button')?.closest('[class*="terminal"],[class*="shadow-step"],.monaco-dialog-box,[role="dialog"]')?.innerText || 'Approval required'),
|
|
140
|
-
|
|
140
|
+
buttons: approvalActions,
|
|
141
141
|
};
|
|
142
142
|
} else {
|
|
143
143
|
const stopButton = Array.from(cascade.querySelectorAll('button, [role="button"]')).find(el => {
|
|
@@ -194,12 +194,11 @@ function coercePatternArray(raw: unknown, fallbacks: RegExp[]): RegExp[] {
|
|
|
194
194
|
/** Defaults tuned for Claude Code / similar agent CLIs when provider.json patterns are empty. */
|
|
195
195
|
const FALLBACK_PROMPT: RegExp[] = [
|
|
196
196
|
/Type your message/i,
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
/[›❯]\s*$/m, // prompt char at end of line (multiline)
|
|
200
|
-
/for\s*shortcuts/i, // Claude Code prompt (ANSI strip may remove spaces → 'forshortcuts')
|
|
201
|
-
/\?\s*for\s*help/i,
|
|
197
|
+
/for\s*shortcuts/i, // Claude Code prompt
|
|
198
|
+
/\?\s*for\s*help/i, // Claude Code help prompt
|
|
202
199
|
/Press enter/i,
|
|
200
|
+
/^[>›❯]\s*$/i, // Prompt char as the complete evaluated string
|
|
201
|
+
/[>›❯]\s*$/, // Prompt char at the very end of evaluated string
|
|
203
202
|
];
|
|
204
203
|
|
|
205
204
|
const FALLBACK_GENERATING: RegExp[] = [
|
|
@@ -214,9 +213,7 @@ const FALLBACK_APPROVAL: RegExp[] = [
|
|
|
214
213
|
/Always\s*allow/i,
|
|
215
214
|
/\(y\/n\)/i,
|
|
216
215
|
/\[Y\/n\]/i,
|
|
217
|
-
/Run\s+\w+\s+command/i, // "Run bash command" etc — requires surrounding words to avoid false matches
|
|
218
216
|
/Yes,?\s*don'?t\s*ask/i, // "Yes, don't ask again" (Claude Code)
|
|
219
|
-
/\bDeny\b/i, // Word-boundary match — avoids "allow & deny" in /permissions menu text
|
|
220
217
|
];
|
|
221
218
|
|
|
222
219
|
function defaultCleanOutput(raw: string, _lastUserInput?: string): string {
|
|
@@ -1088,6 +1088,9 @@ export class AcpProviderInstance implements ProviderInstance {
|
|
|
1088
1088
|
const newStatus = this.currentStatus;
|
|
1089
1089
|
const dirName = this.workingDir.split('/').filter(Boolean).pop() || 'session';
|
|
1090
1090
|
const chatTitle = `${this.provider.name} · ${dirName}`;
|
|
1091
|
+
const progressFingerprint = newStatus === 'generating'
|
|
1092
|
+
? `${this.partialContent}::${JSON.stringify(this.partialBlocks)}::${JSON.stringify(this.activeToolCalls.map(t => ({ name: t.name, status: t.status })))}`.slice(-2000)
|
|
1093
|
+
: undefined;
|
|
1091
1094
|
|
|
1092
1095
|
if (newStatus !== this.lastStatus) {
|
|
1093
1096
|
if (this.lastStatus === 'idle' && newStatus === 'generating') {
|
|
@@ -1111,7 +1114,7 @@ export class AcpProviderInstance implements ProviderInstance {
|
|
|
1111
1114
|
|
|
1112
1115
|
// Monitor check
|
|
1113
1116
|
const agentKey = `${this.type}:acp`;
|
|
1114
|
-
const monitorEvents = this.monitor.check(agentKey, newStatus, now);
|
|
1117
|
+
const monitorEvents = this.monitor.check(agentKey, newStatus, now, progressFingerprint);
|
|
1115
1118
|
for (const me of monitorEvents) {
|
|
1116
1119
|
this.pushEvent({ event: me.type, agentKey: me.agentKey, message: me.message, elapsedSec: me.elapsedSec, timestamp: me.timestamp });
|
|
1117
1120
|
}
|
|
@@ -159,6 +159,10 @@ export class CliProviderInstance implements ProviderInstance {
|
|
|
159
159
|
const newStatus = adapterStatus.status;
|
|
160
160
|
const dirName = this.workingDir.split('/').filter(Boolean).pop() || 'session';
|
|
161
161
|
const chatTitle = `${this.provider.name} · ${dirName}`;
|
|
162
|
+
const partial = this.adapter.getPartialResponse();
|
|
163
|
+
const progressFingerprint = newStatus === 'generating'
|
|
164
|
+
? `${partial || ''}::${adapterStatus.messages.at(-1)?.content || ''}`.slice(-2000)
|
|
165
|
+
: undefined;
|
|
162
166
|
|
|
163
167
|
if (newStatus !== this.lastStatus) {
|
|
164
168
|
LOG.info('CLI', `[${this.type}] status: ${this.lastStatus} → ${newStatus}`);
|
|
@@ -241,7 +245,7 @@ export class CliProviderInstance implements ProviderInstance {
|
|
|
241
245
|
|
|
242
246
|
// Monitor check (cooldown based notification, IDE/CLI common)
|
|
243
247
|
const agentKey = `${this.type}:cli`;
|
|
244
|
-
const monitorEvents = this.monitor.check(agentKey, newStatus, now);
|
|
248
|
+
const monitorEvents = this.monitor.check(agentKey, newStatus, now, progressFingerprint);
|
|
245
249
|
for (const me of monitorEvents) {
|
|
246
250
|
this.pushEvent({ event: me.type, agentKey: me.agentKey, message: me.message, elapsedSec: me.elapsedSec, timestamp: me.timestamp });
|
|
247
251
|
}
|
|
@@ -123,11 +123,17 @@ export class ExtensionProviderInstance implements ProviderInstance {
|
|
|
123
123
|
// via its own detectAgentTransitions(). Emitting here would cause
|
|
124
124
|
// duplicate toasts with slightly different content.
|
|
125
125
|
|
|
126
|
-
private detectTransition(newStatus: string,
|
|
126
|
+
private detectTransition(newStatus: string, data: any): void {
|
|
127
127
|
const now = Date.now();
|
|
128
128
|
const agentStatus = (newStatus === 'streaming' || newStatus === 'generating') ? 'generating'
|
|
129
129
|
: newStatus === 'waiting_approval' ? 'waiting_approval'
|
|
130
130
|
: 'idle';
|
|
131
|
+
const lastMsg = Array.isArray(data?.messages) && data.messages.length > 0
|
|
132
|
+
? data.messages[data.messages.length - 1]
|
|
133
|
+
: null;
|
|
134
|
+
const progressFingerprint = agentStatus === 'generating'
|
|
135
|
+
? `${lastMsg?.role || ''}:${typeof lastMsg?.content === 'string' ? lastMsg.content : JSON.stringify(lastMsg?.content || '')}`.slice(-2000)
|
|
136
|
+
: undefined;
|
|
131
137
|
|
|
132
138
|
if (agentStatus !== this.lastAgentStatus) {
|
|
133
139
|
// Track generating start time (for monitor elapsed calculation)
|
|
@@ -142,7 +148,7 @@ export class ExtensionProviderInstance implements ProviderInstance {
|
|
|
142
148
|
|
|
143
149
|
// Monitor check (cooldown based notification) — keep monitor events (long_generating etc)
|
|
144
150
|
const agentKey = `${this.type}:ext`;
|
|
145
|
-
const monitorEvents = this.monitor.check(agentKey, agentStatus, now);
|
|
151
|
+
const monitorEvents = this.monitor.check(agentKey, agentStatus, now, progressFingerprint);
|
|
146
152
|
for (const me of monitorEvents) {
|
|
147
153
|
this.pushEvent({ event: me.type, agentKey: me.agentKey, message: me.message, elapsedSec: me.elapsedSec, timestamp: me.timestamp });
|
|
148
154
|
}
|
|
@@ -334,6 +334,12 @@ export class IdeProviderInstance implements ProviderInstance {
|
|
|
334
334
|
const agentStatus = (chatStatus === 'streaming' || chatStatus === 'generating') ? 'generating'
|
|
335
335
|
: chatStatus === 'waiting_approval' ? 'waiting_approval'
|
|
336
336
|
: 'idle';
|
|
337
|
+
const lastMsg = Array.isArray(chatData?.messages) && chatData.messages.length > 0
|
|
338
|
+
? chatData.messages[chatData.messages.length - 1]
|
|
339
|
+
: null;
|
|
340
|
+
const progressFingerprint = agentStatus === 'generating'
|
|
341
|
+
? `${lastMsg?.role || ''}:${typeof lastMsg?.content === 'string' ? lastMsg.content : JSON.stringify(lastMsg?.content || '')}`.slice(-2000)
|
|
342
|
+
: undefined;
|
|
337
343
|
|
|
338
344
|
this.currentStatus = agentStatus;
|
|
339
345
|
const lastStatus = this.lastAgentStatuses.get(agentKey) || 'idle';
|
|
@@ -368,7 +374,7 @@ export class IdeProviderInstance implements ProviderInstance {
|
|
|
368
374
|
}
|
|
369
375
|
|
|
370
376
|
// Monitor check (cooldown based notification)
|
|
371
|
-
const monitorEvents = this.monitor.check(agentKey, agentStatus, now);
|
|
377
|
+
const monitorEvents = this.monitor.check(agentKey, agentStatus, now, progressFingerprint);
|
|
372
378
|
for (const me of monitorEvents) {
|
|
373
379
|
this.pushEvent({ event: me.type, agentKey: me.agentKey, message: me.message, elapsedSec: me.elapsedSec, timestamp: me.timestamp });
|
|
374
380
|
}
|