@hover-dev/core 0.16.0 → 0.18.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.
- package/README.md +26 -55
- package/dist/agentDirectives.d.ts +55 -0
- package/dist/agentDirectives.d.ts.map +1 -0
- package/dist/agentDirectives.js +276 -0
- package/dist/engine.d.ts +28 -0
- package/dist/engine.d.ts.map +1 -0
- package/dist/engine.js +27 -0
- package/dist/memory/businessMemory.d.ts +29 -0
- package/dist/memory/businessMemory.d.ts.map +1 -0
- package/dist/memory/businessMemory.js +125 -0
- package/dist/playwright/launchChrome.d.ts +18 -0
- package/dist/playwright/launchChrome.d.ts.map +1 -1
- package/dist/playwright/launchChrome.js +46 -3
- package/dist/qa/candidates.d.ts +32 -0
- package/dist/qa/candidates.d.ts.map +1 -0
- package/dist/qa/candidates.js +20 -0
- package/dist/qa/intensity.d.ts +33 -0
- package/dist/qa/intensity.d.ts.map +1 -0
- package/dist/qa/intensity.js +25 -0
- package/dist/qa/qaReport.d.ts +19 -0
- package/dist/qa/qaReport.d.ts.map +1 -0
- package/dist/qa/qaReport.js +50 -0
- package/dist/sessions/sessions.d.ts +125 -0
- package/dist/sessions/sessions.d.ts.map +1 -0
- package/dist/sessions/sessions.js +175 -0
- package/dist/specs/authFixture.d.ts +30 -0
- package/dist/specs/authFixture.d.ts.map +1 -0
- package/dist/specs/authFixture.js +145 -0
- package/dist/specs/detectSharedFlows.d.ts +1 -1
- package/dist/specs/detectSharedFlows.d.ts.map +1 -1
- package/dist/specs/detectSharedFlows.js +20 -21
- package/dist/specs/generatePageObject.d.ts +1 -1
- package/dist/specs/generatePageObject.d.ts.map +1 -1
- package/dist/specs/healPrompt.d.ts +19 -0
- package/dist/specs/healPrompt.d.ts.map +1 -0
- package/dist/specs/healPrompt.js +48 -0
- package/dist/specs/humanSteps.d.ts +4 -8
- package/dist/specs/humanSteps.d.ts.map +1 -1
- package/dist/specs/humanSteps.js +6 -1
- package/dist/specs/optimizeSpec.d.ts +15 -8
- package/dist/specs/optimizeSpec.d.ts.map +1 -1
- package/dist/specs/optimizeSpec.js +71 -41
- package/dist/specs/pageObjectManifest.d.ts +3 -1
- package/dist/specs/pageObjectManifest.d.ts.map +1 -1
- package/dist/specs/pageObjectManifest.js +24 -19
- package/dist/specs/replayGrounded.d.ts +45 -0
- package/dist/specs/replayGrounded.d.ts.map +1 -0
- package/dist/specs/replayGrounded.js +155 -0
- package/dist/specs/runFailures.d.ts +34 -0
- package/dist/specs/runFailures.d.ts.map +1 -0
- package/dist/specs/runFailures.js +93 -0
- package/dist/specs/seeds.d.ts +16 -15
- package/dist/specs/seeds.d.ts.map +1 -1
- package/dist/specs/seeds.js +86 -54
- package/dist/specs/sidecar.d.ts +34 -6
- package/dist/specs/sidecar.d.ts.map +1 -1
- package/dist/specs/sidecar.js +79 -9
- package/dist/specs/specStep.d.ts +21 -0
- package/dist/specs/specStep.d.ts.map +1 -0
- package/dist/specs/specStep.js +1 -0
- package/dist/specs/text.d.ts +8 -6
- package/dist/specs/text.d.ts.map +1 -1
- package/dist/specs/text.js +10 -7
- package/dist/specs/writeSpec.d.ts +62 -1
- package/dist/specs/writeSpec.d.ts.map +1 -1
- package/dist/specs/writeSpec.js +596 -21
- package/package.json +9 -29
- package/dist/agents/aider.d.ts +0 -16
- package/dist/agents/aider.d.ts.map +0 -1
- package/dist/agents/aider.js +0 -161
- package/dist/agents/argv.d.ts +0 -11
- package/dist/agents/argv.d.ts.map +0 -1
- package/dist/agents/argv.js +0 -23
- package/dist/agents/claude.d.ts +0 -3
- package/dist/agents/claude.d.ts.map +0 -1
- package/dist/agents/claude.js +0 -195
- package/dist/agents/codex.d.ts +0 -19
- package/dist/agents/codex.d.ts.map +0 -1
- package/dist/agents/codex.js +0 -216
- package/dist/agents/cursor.d.ts +0 -18
- package/dist/agents/cursor.d.ts.map +0 -1
- package/dist/agents/cursor.js +0 -220
- package/dist/agents/detect.d.ts +0 -46
- package/dist/agents/detect.d.ts.map +0 -1
- package/dist/agents/detect.js +0 -80
- package/dist/agents/gemini.d.ts +0 -17
- package/dist/agents/gemini.d.ts.map +0 -1
- package/dist/agents/gemini.js +0 -186
- package/dist/agents/index.d.ts +0 -6
- package/dist/agents/index.d.ts.map +0 -1
- package/dist/agents/index.js +0 -5
- package/dist/agents/invoke.d.ts +0 -12
- package/dist/agents/invoke.d.ts.map +0 -1
- package/dist/agents/invoke.js +0 -96
- package/dist/agents/qwen.d.ts +0 -17
- package/dist/agents/qwen.d.ts.map +0 -1
- package/dist/agents/qwen.js +0 -172
- package/dist/agents/registry.d.ts +0 -19
- package/dist/agents/registry.d.ts.map +0 -1
- package/dist/agents/registry.js +0 -34
- package/dist/agents/shared.d.ts +0 -28
- package/dist/agents/shared.d.ts.map +0 -1
- package/dist/agents/shared.js +0 -35
- package/dist/agents/types.d.ts +0 -186
- package/dist/agents/types.d.ts.map +0 -1
- package/dist/agents/types.js +0 -23
- package/dist/index.d.ts +0 -3
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -2
- package/dist/mcp/sourceFence.d.ts +0 -23
- package/dist/mcp/sourceFence.d.ts.map +0 -1
- package/dist/mcp/sourceFence.js +0 -75
- package/dist/mcp/sourceServer.d.ts +0 -3
- package/dist/mcp/sourceServer.d.ts.map +0 -1
- package/dist/mcp/sourceServer.js +0 -116
- package/dist/playwright/cdpStatus.d.ts +0 -29
- package/dist/playwright/cdpStatus.d.ts.map +0 -1
- package/dist/playwright/cdpStatus.js +0 -119
- package/dist/playwright/preflight.d.ts +0 -31
- package/dist/playwright/preflight.d.ts.map +0 -1
- package/dist/playwright/preflight.js +0 -82
- package/dist/playwright/preflightCache.d.ts +0 -27
- package/dist/playwright/preflightCache.d.ts.map +0 -1
- package/dist/playwright/preflightCache.js +0 -21
- package/dist/playwright/raiseWindow.d.ts +0 -10
- package/dist/playwright/raiseWindow.d.ts.map +0 -1
- package/dist/playwright/raiseWindow.js +0 -158
- package/dist/playwright/resolveMcpConfig.d.ts +0 -55
- package/dist/playwright/resolveMcpConfig.d.ts.map +0 -1
- package/dist/playwright/resolveMcpConfig.js +0 -66
- package/dist/plugin-api.d.ts +0 -235
- package/dist/plugin-api.d.ts.map +0 -1
- package/dist/plugin-api.js +0 -52
- package/dist/runSession.d.ts +0 -42
- package/dist/runSession.d.ts.map +0 -1
- package/dist/runSession.js +0 -81
- package/dist/scripts/bench-multi-tab.d.ts +0 -2
- package/dist/scripts/bench-multi-tab.d.ts.map +0 -1
- package/dist/scripts/bench-multi-tab.js +0 -192
- package/dist/scripts/bench-ttfb.d.ts +0 -2
- package/dist/scripts/bench-ttfb.d.ts.map +0 -1
- package/dist/scripts/bench-ttfb.js +0 -127
- package/dist/scripts/start-chrome.d.ts +0 -3
- package/dist/scripts/start-chrome.d.ts.map +0 -1
- package/dist/scripts/start-chrome.js +0 -23
- package/dist/service/cdpHandlers.d.ts +0 -44
- package/dist/service/cdpHandlers.d.ts.map +0 -1
- package/dist/service/cdpHandlers.js +0 -85
- package/dist/service/cdpHint.d.ts +0 -48
- package/dist/service/cdpHint.d.ts.map +0 -1
- package/dist/service/cdpHint.js +0 -216
- package/dist/service/conventions.d.ts +0 -8
- package/dist/service/conventions.d.ts.map +0 -1
- package/dist/service/conventions.js +0 -42
- package/dist/service/saveHandlers.d.ts +0 -52
- package/dist/service/saveHandlers.d.ts.map +0 -1
- package/dist/service/saveHandlers.js +0 -75
- package/dist/service/types.d.ts +0 -58
- package/dist/service/types.d.ts.map +0 -1
- package/dist/service/types.js +0 -26
- package/dist/service.d.ts +0 -50
- package/dist/service.d.ts.map +0 -1
- package/dist/service.js +0 -1065
- package/dist/skills/writeSkill.d.ts +0 -27
- package/dist/skills/writeSkill.d.ts.map +0 -1
- package/dist/skills/writeSkill.js +0 -13
- package/dist/specs/extractPageObjects.d.ts +0 -18
- package/dist/specs/extractPageObjects.d.ts.map +0 -1
- package/dist/specs/extractPageObjects.js +0 -98
- package/dist/specs/listSpecs.d.ts +0 -52
- package/dist/specs/listSpecs.d.ts.map +0 -1
- package/dist/specs/listSpecs.js +0 -139
- package/dist/specs/optimizationSuggestion.d.ts +0 -26
- package/dist/specs/optimizationSuggestion.d.ts.map +0 -1
- package/dist/specs/optimizationSuggestion.js +0 -28
- package/dist/specs/optimizeSpecWithAgent.d.ts +0 -11
- package/dist/specs/optimizeSpecWithAgent.d.ts.map +0 -1
- package/dist/specs/optimizeSpecWithAgent.js +0 -40
- package/dist/specs/writeCaseCsv.d.ts +0 -28
- package/dist/specs/writeCaseCsv.d.ts.map +0 -1
- package/dist/specs/writeCaseCsv.js +0 -134
package/dist/agents/gemini.js
DELETED
|
@@ -1,186 +0,0 @@
|
|
|
1
|
-
import { HOVER_PROMPT_PREFACE, stripMcpPrefix } from './shared.js';
|
|
2
|
-
function geminiState(state) {
|
|
3
|
-
if (typeof state.runningTurns !== 'number') {
|
|
4
|
-
state.runningTurns = 0;
|
|
5
|
-
state.runningSessionId = undefined;
|
|
6
|
-
state.runningModel = undefined;
|
|
7
|
-
state.lastAssistantText = undefined;
|
|
8
|
-
state.sawErrorEvent = false;
|
|
9
|
-
state.toolNameByUseId = new Map();
|
|
10
|
-
}
|
|
11
|
-
return state;
|
|
12
|
-
}
|
|
13
|
-
function resetGeminiCounters(s) {
|
|
14
|
-
s.runningTurns = 0;
|
|
15
|
-
s.runningSessionId = undefined;
|
|
16
|
-
s.runningModel = undefined;
|
|
17
|
-
s.lastAssistantText = undefined;
|
|
18
|
-
s.sawErrorEvent = false;
|
|
19
|
-
s.toolNameByUseId.clear();
|
|
20
|
-
}
|
|
21
|
-
/**
|
|
22
|
-
* Extract assistant text from a `message` event whose `content` may be a
|
|
23
|
-
* plain string OR an array of `{type:'text', text}` content blocks. Gemini's
|
|
24
|
-
* docs aren't explicit on which shape ships per build, so we handle both.
|
|
25
|
-
*/
|
|
26
|
-
function extractMessageText(ev) {
|
|
27
|
-
if (typeof ev.content === 'string') {
|
|
28
|
-
const t = ev.content.trim();
|
|
29
|
-
return t.length > 0 ? t : undefined;
|
|
30
|
-
}
|
|
31
|
-
if (Array.isArray(ev.content)) {
|
|
32
|
-
const parts = ev.content
|
|
33
|
-
.filter(b => b.type === 'text' && typeof b.text === 'string')
|
|
34
|
-
.map(b => b.text.trim())
|
|
35
|
-
.filter(t => t.length > 0);
|
|
36
|
-
return parts.length > 0 ? parts.join('\n') : undefined;
|
|
37
|
-
}
|
|
38
|
-
return undefined;
|
|
39
|
-
}
|
|
40
|
-
export const geminiAgent = {
|
|
41
|
-
id: 'gemini',
|
|
42
|
-
binName: 'gemini',
|
|
43
|
-
protocol: 'argv',
|
|
44
|
-
streamFormat: 'json-lines',
|
|
45
|
-
sandboxStrength: 'soft',
|
|
46
|
-
display: {
|
|
47
|
-
label: 'Gemini',
|
|
48
|
-
tagline: 'Google Gemini — soft sandbox (no built-in tool deny-list)',
|
|
49
|
-
homepage: 'https://github.com/google-gemini/gemini-cli',
|
|
50
|
-
installHint: 'npm install -g @google/gemini-cli',
|
|
51
|
-
},
|
|
52
|
-
buildArgs(opts) {
|
|
53
|
-
// Gemini has no --append-system-prompt CLI flag (only the
|
|
54
|
-
// GEMINI_SYSTEM_MD env var which writes a file). Prepend the HOVER-mode
|
|
55
|
-
// preface to the prompt instead — same pattern as cursor.ts / aider.ts.
|
|
56
|
-
const preface = opts.appendSystemPrompt && opts.appendSystemPrompt.trim().length > 0
|
|
57
|
-
? `${HOVER_PROMPT_PREFACE} ${opts.appendSystemPrompt}`
|
|
58
|
-
: HOVER_PROMPT_PREFACE;
|
|
59
|
-
const finalPrompt = `${preface}\n\n${opts.prompt}`;
|
|
60
|
-
const args = ['-p', finalPrompt];
|
|
61
|
-
// NDJSON streaming output.
|
|
62
|
-
args.push('--output-format', 'stream-json');
|
|
63
|
-
// Auto-approve all tool calls so the run doesn't hang. The newer
|
|
64
|
-
// canonical form is --approval-mode=yolo; --yolo is deprecated but
|
|
65
|
-
// still accepted in 2026-05. We use the modern form.
|
|
66
|
-
args.push('--approval-mode', 'yolo');
|
|
67
|
-
if (opts.model) {
|
|
68
|
-
args.push('--model', opts.model);
|
|
69
|
-
}
|
|
70
|
-
if (opts.sessionId) {
|
|
71
|
-
// --resume <id> is the documented form. -r is the alias. The single
|
|
72
|
-
// string 'latest' picks the most recent session; we only pass an
|
|
73
|
-
// explicit id, never the literal 'latest'.
|
|
74
|
-
args.push('--resume', opts.sessionId);
|
|
75
|
-
}
|
|
76
|
-
// MCP servers configured via `gemini mcp add` at install time — no
|
|
77
|
-
// per-invocation --mcp-config equivalent.
|
|
78
|
-
// No equivalent for --max-budget-usd or --allowedTools / --disallowedTools
|
|
79
|
-
// in the disable-built-ins sense.
|
|
80
|
-
return args;
|
|
81
|
-
},
|
|
82
|
-
parseEvent(line, state = {}) {
|
|
83
|
-
if (!line.trim())
|
|
84
|
-
return [];
|
|
85
|
-
let ev;
|
|
86
|
-
try {
|
|
87
|
-
ev = JSON.parse(line);
|
|
88
|
-
}
|
|
89
|
-
catch {
|
|
90
|
-
return [{ kind: 'raw', line }];
|
|
91
|
-
}
|
|
92
|
-
const s = geminiState(state);
|
|
93
|
-
const out = [];
|
|
94
|
-
if (ev.type === 'init') {
|
|
95
|
-
resetGeminiCounters(s);
|
|
96
|
-
s.runningModel = ev.model;
|
|
97
|
-
if (ev.session_id) {
|
|
98
|
-
s.runningSessionId = ev.session_id;
|
|
99
|
-
out.push({ kind: 'session_start', sessionId: ev.session_id, model: ev.model });
|
|
100
|
-
}
|
|
101
|
-
return out;
|
|
102
|
-
}
|
|
103
|
-
if (ev.type === 'message') {
|
|
104
|
-
// Only count and surface assistant messages — user echoes (the
|
|
105
|
-
// role:'user' message events) don't count as turns from our POV.
|
|
106
|
-
if (ev.role === 'assistant' || ev.role === undefined) {
|
|
107
|
-
s.runningTurns += 1;
|
|
108
|
-
out.push({ kind: 'usage', turns: s.runningTurns });
|
|
109
|
-
const text = extractMessageText(ev);
|
|
110
|
-
if (text) {
|
|
111
|
-
s.lastAssistantText = text;
|
|
112
|
-
out.push({ kind: 'text', text });
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
return out;
|
|
116
|
-
}
|
|
117
|
-
if (ev.type === 'tool_use') {
|
|
118
|
-
const rawName = ev.name ?? '';
|
|
119
|
-
const tool = stripMcpPrefix(rawName);
|
|
120
|
-
if (ev.id)
|
|
121
|
-
s.toolNameByUseId.set(ev.id, tool);
|
|
122
|
-
out.push({ kind: 'tool_use', tool, input: ev.input });
|
|
123
|
-
return out;
|
|
124
|
-
}
|
|
125
|
-
if (ev.type === 'tool_result') {
|
|
126
|
-
const isError = ev.is_error === true;
|
|
127
|
-
out.push({ kind: 'tool_result', isError });
|
|
128
|
-
return out;
|
|
129
|
-
}
|
|
130
|
-
if (ev.type === 'result') {
|
|
131
|
-
// result.stats may carry turns; prefer it over our running count.
|
|
132
|
-
const turns = ev.stats?.turns ?? ev.stats?.model?.turns ?? s.runningTurns;
|
|
133
|
-
const isError = ev.is_error === true || ev.error !== undefined && ev.error !== null;
|
|
134
|
-
if (isError)
|
|
135
|
-
s.sawErrorEvent = true;
|
|
136
|
-
out.push({
|
|
137
|
-
kind: 'session_end',
|
|
138
|
-
turns,
|
|
139
|
-
// costUsd intentionally undefined — gemini's stats block does not
|
|
140
|
-
// include $ figures.
|
|
141
|
-
isError,
|
|
142
|
-
summary: ev.response ?? s.lastAssistantText,
|
|
143
|
-
});
|
|
144
|
-
return out;
|
|
145
|
-
}
|
|
146
|
-
if (ev.type === 'error') {
|
|
147
|
-
s.sawErrorEvent = true;
|
|
148
|
-
const msg = ev.message ?? ev.error?.message ?? `[gemini] error`;
|
|
149
|
-
out.push({ kind: 'text', text: msg });
|
|
150
|
-
return out;
|
|
151
|
-
}
|
|
152
|
-
return [];
|
|
153
|
-
},
|
|
154
|
-
/**
|
|
155
|
-
* Gemini's `result` event already produces a session_end via parseEvent;
|
|
156
|
-
* this fallback is for the case where the child exits without emitting a
|
|
157
|
-
* `result` (e.g. crash, signal). Same shape as cursor.ts / qwen.ts.
|
|
158
|
-
*/
|
|
159
|
-
onStreamEnd(exitCode, state = {}) {
|
|
160
|
-
const s = geminiState(state);
|
|
161
|
-
return {
|
|
162
|
-
kind: 'session_end',
|
|
163
|
-
turns: s.runningTurns,
|
|
164
|
-
// costUsd intentionally undefined — see parseEvent note.
|
|
165
|
-
isError: s.sawErrorEvent || (exitCode != null && exitCode !== 0),
|
|
166
|
-
summary: s.lastAssistantText,
|
|
167
|
-
};
|
|
168
|
-
},
|
|
169
|
-
};
|
|
170
|
-
/**
|
|
171
|
-
* Test-only escape hatches, same pattern as cursor.ts / codex.ts.
|
|
172
|
-
*/
|
|
173
|
-
export const __testing = {
|
|
174
|
-
freshState: () => ({}),
|
|
175
|
-
resetCounters: (state) => resetGeminiCounters(geminiState(state)),
|
|
176
|
-
getState: (state) => {
|
|
177
|
-
const s = geminiState(state);
|
|
178
|
-
return {
|
|
179
|
-
runningTurns: s.runningTurns,
|
|
180
|
-
runningSessionId: s.runningSessionId,
|
|
181
|
-
runningModel: s.runningModel,
|
|
182
|
-
lastAssistantText: s.lastAssistantText,
|
|
183
|
-
sawErrorEvent: s.sawErrorEvent,
|
|
184
|
-
};
|
|
185
|
-
},
|
|
186
|
-
};
|
package/dist/agents/index.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/agents/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC;AAC9B,cAAc,WAAW,CAAC"}
|
package/dist/agents/index.js
DELETED
package/dist/agents/invoke.d.ts
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import type { InvokeEvent, InvokeOptions } from './types.js';
|
|
2
|
-
/**
|
|
3
|
-
* Spawn an agent and yield normalized InvokeEvents as they arrive.
|
|
4
|
-
*
|
|
5
|
-
* Lifecycle: the generator owns the child process. Iterating to completion
|
|
6
|
-
* drains stdout; breaking early (e.g. WS disconnect) runs the finally block
|
|
7
|
-
* which closes readline and SIGTERMs the child, so we never leak orphan
|
|
8
|
-
* agent processes that would keep driving the browser (and burning tokens)
|
|
9
|
-
* after the user has navigated away.
|
|
10
|
-
*/
|
|
11
|
-
export declare function invokeAgent(opts: InvokeOptions): AsyncIterable<InvokeEvent>;
|
|
12
|
-
//# sourceMappingURL=invoke.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"invoke.d.ts","sourceRoot":"","sources":["../../src/agents/invoke.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAe,MAAM,YAAY,CAAC;AAE1E;;;;;;;;GAQG;AACH,wBAAuB,WAAW,CAAC,IAAI,EAAE,aAAa,GAAG,aAAa,CAAC,WAAW,CAAC,CA+ElF"}
|
package/dist/agents/invoke.js
DELETED
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
// cross-spawn is a drop-in for child_process.spawn that fixes Windows behaviour
|
|
2
|
-
// around `.cmd`/`.bat` shims (e.g. npm-installed `claude.cmd`). The return type
|
|
3
|
-
// is identical to node:child_process so call sites are unchanged.
|
|
4
|
-
import spawn from 'cross-spawn';
|
|
5
|
-
import { createInterface } from 'node:readline';
|
|
6
|
-
import { buildArgv } from './argv.js';
|
|
7
|
-
import { resolveBinForAgent } from './detect.js';
|
|
8
|
-
import { getAgent } from './registry.js';
|
|
9
|
-
import { AgentNotInstalledError, UnsupportedAgentProtocolError, } from './types.js';
|
|
10
|
-
/**
|
|
11
|
-
* Spawn an agent and yield normalized InvokeEvents as they arrive.
|
|
12
|
-
*
|
|
13
|
-
* Lifecycle: the generator owns the child process. Iterating to completion
|
|
14
|
-
* drains stdout; breaking early (e.g. WS disconnect) runs the finally block
|
|
15
|
-
* which closes readline and SIGTERMs the child, so we never leak orphan
|
|
16
|
-
* agent processes that would keep driving the browser (and burning tokens)
|
|
17
|
-
* after the user has navigated away.
|
|
18
|
-
*/
|
|
19
|
-
export async function* invokeAgent(opts) {
|
|
20
|
-
const descriptor = getAgent(opts.agentId);
|
|
21
|
-
if (!descriptor) {
|
|
22
|
-
throw new UnsupportedAgentProtocolError(`Unknown agent: ${opts.agentId}`);
|
|
23
|
-
}
|
|
24
|
-
const bin = await resolveBinForAgent(descriptor);
|
|
25
|
-
if (!bin)
|
|
26
|
-
throw new AgentNotInstalledError(opts.agentId);
|
|
27
|
-
const argv = buildArgv(descriptor, opts);
|
|
28
|
-
const usesStdinPrompt = descriptor.protocol === 'stdin';
|
|
29
|
-
const child = spawn(bin, argv, {
|
|
30
|
-
stdio: [usesStdinPrompt ? 'pipe' : 'ignore', 'pipe', 'inherit'],
|
|
31
|
-
cwd: opts.cwd,
|
|
32
|
-
// Clear CLAUDECODE so spawning `claude` from inside a Claude Code session
|
|
33
|
-
// doesn't trip the nested-session guard. Harmless for other agents.
|
|
34
|
-
// If the caller supplied an API key and the descriptor names a key env var,
|
|
35
|
-
// inject it so the CLI runs on the key instead of a logged-in subscription.
|
|
36
|
-
// The key lives only in this child's env — never logged, never persisted.
|
|
37
|
-
env: {
|
|
38
|
-
...process.env,
|
|
39
|
-
CLAUDECODE: '',
|
|
40
|
-
...(opts.apiKey && descriptor.apiKeyEnv
|
|
41
|
-
? { [descriptor.apiKeyEnv]: opts.apiKey }
|
|
42
|
-
: {}),
|
|
43
|
-
},
|
|
44
|
-
});
|
|
45
|
-
const onAbort = () => {
|
|
46
|
-
if (!child.killed)
|
|
47
|
-
child.kill('SIGTERM');
|
|
48
|
-
};
|
|
49
|
-
if (opts.signal) {
|
|
50
|
-
if (opts.signal.aborted)
|
|
51
|
-
onAbort();
|
|
52
|
-
else
|
|
53
|
-
opts.signal.addEventListener('abort', onAbort, { once: true });
|
|
54
|
-
}
|
|
55
|
-
if (usesStdinPrompt && child.stdin) {
|
|
56
|
-
child.stdin.write(opts.prompt);
|
|
57
|
-
child.stdin.end();
|
|
58
|
-
}
|
|
59
|
-
const rl = createInterface({ input: child.stdout });
|
|
60
|
-
const exitPromise = new Promise(res => child.on('exit', c => res(c ?? -1)));
|
|
61
|
-
const state = {};
|
|
62
|
-
let sawSessionEnd = false;
|
|
63
|
-
try {
|
|
64
|
-
for await (const line of rl) {
|
|
65
|
-
for (const ev of descriptor.parseEvent(line, state)) {
|
|
66
|
-
if (ev.kind === 'session_end')
|
|
67
|
-
sawSessionEnd = true;
|
|
68
|
-
yield ev;
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
const code = await exitPromise;
|
|
72
|
-
if (!sawSessionEnd && !opts.signal?.aborted) {
|
|
73
|
-
// Give the descriptor a chance to synthesize its own terminator from
|
|
74
|
-
// accumulated state (codex does this — its stream never emits a
|
|
75
|
-
// session_end). Falls back to a generic error session_end if the
|
|
76
|
-
// descriptor declines and the child exited non-zero.
|
|
77
|
-
const synthetic = descriptor.onStreamEnd?.(code, state);
|
|
78
|
-
if (synthetic) {
|
|
79
|
-
yield synthetic;
|
|
80
|
-
}
|
|
81
|
-
else if (code !== 0) {
|
|
82
|
-
yield {
|
|
83
|
-
kind: 'session_end',
|
|
84
|
-
isError: true,
|
|
85
|
-
summary: `agent exited with code ${code}`,
|
|
86
|
-
};
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
finally {
|
|
91
|
-
rl.close();
|
|
92
|
-
if (!child.killed)
|
|
93
|
-
child.kill('SIGTERM');
|
|
94
|
-
opts.signal?.removeEventListener('abort', onAbort);
|
|
95
|
-
}
|
|
96
|
-
}
|
package/dist/agents/qwen.d.ts
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import type { AgentDescriptor, ParserState } from './types.js';
|
|
2
|
-
export declare const qwenAgent: AgentDescriptor;
|
|
3
|
-
/**
|
|
4
|
-
* Test-only escape hatches, same pattern as cursor.ts / codex.ts.
|
|
5
|
-
*/
|
|
6
|
-
export declare const __testing: {
|
|
7
|
-
freshState: () => ParserState;
|
|
8
|
-
resetCounters: (state: ParserState) => void;
|
|
9
|
-
getState: (state: ParserState) => {
|
|
10
|
-
runningTurns: number;
|
|
11
|
-
runningSessionId: string | undefined;
|
|
12
|
-
runningModel: string | undefined;
|
|
13
|
-
lastAssistantText: string | undefined;
|
|
14
|
-
sawErrorEvent: boolean;
|
|
15
|
-
};
|
|
16
|
-
};
|
|
17
|
-
//# sourceMappingURL=qwen.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"qwen.d.ts","sourceRoot":"","sources":["../../src/agents/qwen.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAA8B,WAAW,EAAE,MAAM,YAAY,CAAC;AAoJ3F,eAAO,MAAM,SAAS,EAAE,eAqJvB,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,SAAS;sBACJ,WAAW;2BACJ,WAAW;sBAChB,WAAW;;;;;;;CAU9B,CAAC"}
|
package/dist/agents/qwen.js
DELETED
|
@@ -1,172 +0,0 @@
|
|
|
1
|
-
import { HOVER_PROMPT_PREFACE, stripMcpPrefix } from './shared.js';
|
|
2
|
-
function qwenState(state) {
|
|
3
|
-
if (typeof state.runningTurns !== 'number') {
|
|
4
|
-
state.runningTurns = 0;
|
|
5
|
-
state.runningSessionId = undefined;
|
|
6
|
-
state.runningModel = undefined;
|
|
7
|
-
state.lastAssistantText = undefined;
|
|
8
|
-
state.sawErrorEvent = false;
|
|
9
|
-
state.toolNameByUseId = new Map();
|
|
10
|
-
}
|
|
11
|
-
return state;
|
|
12
|
-
}
|
|
13
|
-
function resetQwenCounters(s) {
|
|
14
|
-
s.runningTurns = 0;
|
|
15
|
-
s.runningSessionId = undefined;
|
|
16
|
-
s.runningModel = undefined;
|
|
17
|
-
s.lastAssistantText = undefined;
|
|
18
|
-
s.sawErrorEvent = false;
|
|
19
|
-
s.toolNameByUseId.clear();
|
|
20
|
-
}
|
|
21
|
-
export const qwenAgent = {
|
|
22
|
-
id: 'qwen',
|
|
23
|
-
binName: 'qwen',
|
|
24
|
-
protocol: 'argv',
|
|
25
|
-
streamFormat: 'json-lines',
|
|
26
|
-
sandboxStrength: 'soft',
|
|
27
|
-
display: {
|
|
28
|
-
label: 'Qwen Code',
|
|
29
|
-
tagline: 'Qwen Code — soft sandbox (no built-in tool deny-list)',
|
|
30
|
-
homepage: 'https://github.com/QwenLM/qwen-code',
|
|
31
|
-
installHint: 'npm install -g @qwen-code/qwen-code@latest',
|
|
32
|
-
},
|
|
33
|
-
buildArgs(opts) {
|
|
34
|
-
const args = ['-p', opts.prompt];
|
|
35
|
-
// NDJSON streaming output.
|
|
36
|
-
args.push('--output-format', 'stream-json');
|
|
37
|
-
// Auto-approve all tool calls so the run doesn't hang. The newer
|
|
38
|
-
// canonical form is --approval-mode=yolo; --yolo is deprecated but
|
|
39
|
-
// still works in 2026-05. We use the modern form.
|
|
40
|
-
args.push('--approval-mode', 'yolo');
|
|
41
|
-
if (opts.model) {
|
|
42
|
-
args.push('--model', opts.model);
|
|
43
|
-
}
|
|
44
|
-
if (opts.sessionId) {
|
|
45
|
-
// --resume <sessionId> is the documented headless form. --continue
|
|
46
|
-
// (no arg) picks the most recent — NOT what we want when a specific
|
|
47
|
-
// session was passed.
|
|
48
|
-
args.push('--resume', opts.sessionId);
|
|
49
|
-
}
|
|
50
|
-
// Qwen has a real --append-system-prompt flag — use it instead of
|
|
51
|
-
// prepending to the user prompt. Concatenate the standing Hover-mode
|
|
52
|
-
// preface with whatever the caller appended.
|
|
53
|
-
const sysPrompt = opts.appendSystemPrompt && opts.appendSystemPrompt.trim().length > 0
|
|
54
|
-
? `${HOVER_PROMPT_PREFACE} ${opts.appendSystemPrompt}`
|
|
55
|
-
: HOVER_PROMPT_PREFACE;
|
|
56
|
-
args.push('--append-system-prompt', sysPrompt);
|
|
57
|
-
// MCP servers configured in ~/.qwen/settings.json — no per-invocation
|
|
58
|
-
// --mcp-config equivalent. Same constraint as cursor / codex.
|
|
59
|
-
// No equivalent for --max-budget-usd or --allowedTools / --disallowedTools.
|
|
60
|
-
return args;
|
|
61
|
-
},
|
|
62
|
-
parseEvent(line, state = {}) {
|
|
63
|
-
if (!line.trim())
|
|
64
|
-
return [];
|
|
65
|
-
let ev;
|
|
66
|
-
try {
|
|
67
|
-
ev = JSON.parse(line);
|
|
68
|
-
}
|
|
69
|
-
catch {
|
|
70
|
-
return [{ kind: 'raw', line }];
|
|
71
|
-
}
|
|
72
|
-
const s = qwenState(state);
|
|
73
|
-
const out = [];
|
|
74
|
-
if (ev.type === 'system' && ev.subtype === 'session_start') {
|
|
75
|
-
resetQwenCounters(s);
|
|
76
|
-
s.runningModel = ev.model;
|
|
77
|
-
if (ev.session_id) {
|
|
78
|
-
s.runningSessionId = ev.session_id;
|
|
79
|
-
out.push({ kind: 'session_start', sessionId: ev.session_id, model: ev.model });
|
|
80
|
-
}
|
|
81
|
-
return out;
|
|
82
|
-
}
|
|
83
|
-
if (ev.type === 'assistant' && ev.message) {
|
|
84
|
-
s.runningTurns += 1;
|
|
85
|
-
// Emit a usage event so the widget can advance its turn counter.
|
|
86
|
-
// costUsd intentionally omitted — qwen doesn't publish $ in stream.
|
|
87
|
-
out.push({ kind: 'usage', turns: s.runningTurns });
|
|
88
|
-
for (const block of ev.message.content ?? []) {
|
|
89
|
-
if (block.type === 'text') {
|
|
90
|
-
const text = block.text?.trim();
|
|
91
|
-
if (text) {
|
|
92
|
-
s.lastAssistantText = text;
|
|
93
|
-
out.push({ kind: 'text', text });
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
else if (block.type === 'tool_use') {
|
|
97
|
-
const rawName = block.name ?? '';
|
|
98
|
-
const tool = stripMcpPrefix(rawName);
|
|
99
|
-
if (block.id)
|
|
100
|
-
s.toolNameByUseId.set(block.id, tool);
|
|
101
|
-
out.push({ kind: 'tool_use', tool, input: block.input });
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
return out;
|
|
105
|
-
}
|
|
106
|
-
// tool_result blocks are wrapped in `user` messages (Anthropic Messages
|
|
107
|
-
// convention). We surface them as tool_result events.
|
|
108
|
-
if (ev.type === 'user' && ev.message) {
|
|
109
|
-
for (const block of ev.message.content ?? []) {
|
|
110
|
-
if (block.type === 'tool_result') {
|
|
111
|
-
const isError = block.is_error === true;
|
|
112
|
-
out.push({ kind: 'tool_result', isError });
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
return out;
|
|
116
|
-
}
|
|
117
|
-
if (ev.type === 'result') {
|
|
118
|
-
const isError = ev.is_error === true ||
|
|
119
|
-
(typeof ev.subtype === 'string' && /error|fail/i.test(ev.subtype));
|
|
120
|
-
if (isError)
|
|
121
|
-
s.sawErrorEvent = true;
|
|
122
|
-
out.push({
|
|
123
|
-
kind: 'session_end',
|
|
124
|
-
turns: s.runningTurns,
|
|
125
|
-
// costUsd intentionally undefined — qwen doesn't publish $.
|
|
126
|
-
isError,
|
|
127
|
-
summary: ev.result ?? s.lastAssistantText,
|
|
128
|
-
});
|
|
129
|
-
return out;
|
|
130
|
-
}
|
|
131
|
-
// Qwen emits various error envelopes mid-stream; surface them as text.
|
|
132
|
-
if (ev.type && /error/i.test(ev.type)) {
|
|
133
|
-
s.sawErrorEvent = true;
|
|
134
|
-
const msg = ev.error?.message ?? ev.text ?? ev.result ?? `[qwen] ${ev.type}`;
|
|
135
|
-
out.push({ kind: 'text', text: msg });
|
|
136
|
-
return out;
|
|
137
|
-
}
|
|
138
|
-
return [];
|
|
139
|
-
},
|
|
140
|
-
/**
|
|
141
|
-
* Qwen's `result` event already produces a session_end via parseEvent;
|
|
142
|
-
* this fallback is for the case where the child exits without emitting a
|
|
143
|
-
* `result` (e.g. crash, signal). Mirrors codex.ts / cursor.ts shape.
|
|
144
|
-
*/
|
|
145
|
-
onStreamEnd(exitCode, state = {}) {
|
|
146
|
-
const s = qwenState(state);
|
|
147
|
-
return {
|
|
148
|
-
kind: 'session_end',
|
|
149
|
-
turns: s.runningTurns,
|
|
150
|
-
// costUsd intentionally undefined — see parseEvent note.
|
|
151
|
-
isError: s.sawErrorEvent || (exitCode != null && exitCode !== 0),
|
|
152
|
-
summary: s.lastAssistantText,
|
|
153
|
-
};
|
|
154
|
-
},
|
|
155
|
-
};
|
|
156
|
-
/**
|
|
157
|
-
* Test-only escape hatches, same pattern as cursor.ts / codex.ts.
|
|
158
|
-
*/
|
|
159
|
-
export const __testing = {
|
|
160
|
-
freshState: () => ({}),
|
|
161
|
-
resetCounters: (state) => resetQwenCounters(qwenState(state)),
|
|
162
|
-
getState: (state) => {
|
|
163
|
-
const s = qwenState(state);
|
|
164
|
-
return {
|
|
165
|
-
runningTurns: s.runningTurns,
|
|
166
|
-
runningSessionId: s.runningSessionId,
|
|
167
|
-
runningModel: s.runningModel,
|
|
168
|
-
lastAssistantText: s.lastAssistantText,
|
|
169
|
-
sawErrorEvent: s.sawErrorEvent,
|
|
170
|
-
};
|
|
171
|
-
},
|
|
172
|
-
};
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import type { AgentDescriptor } from './types.js';
|
|
2
|
-
/**
|
|
3
|
-
* Registry of agents Hover can drive.
|
|
4
|
-
*
|
|
5
|
-
* To add support for another agent (e.g. cline, continue, kilo), implement
|
|
6
|
-
* its AgentDescriptor in its own file and register it here. The rest of the
|
|
7
|
-
* system — detect, argv, invoke, service, widget — works without further
|
|
8
|
-
* changes.
|
|
9
|
-
*
|
|
10
|
-
* Insertion order is the order shown in the widget's agent dropdown, so put
|
|
11
|
-
* the recommended primary first. The two hard-sandbox / first-party agents
|
|
12
|
-
* (claude, codex) lead; the soft-sandbox third-party agents follow in the
|
|
13
|
-
* order they were added.
|
|
14
|
-
*/
|
|
15
|
-
export declare const AGENTS: Record<string, AgentDescriptor>;
|
|
16
|
-
export declare function getAgent(id: string): AgentDescriptor | undefined;
|
|
17
|
-
/** Stable, insertion-ordered list of all registered agents. */
|
|
18
|
-
export declare function listAgents(): AgentDescriptor[];
|
|
19
|
-
//# sourceMappingURL=registry.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/agents/registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAQlD;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAOlD,CAAC;AAEF,wBAAgB,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,eAAe,GAAG,SAAS,CAEhE;AAED,+DAA+D;AAC/D,wBAAgB,UAAU,IAAI,eAAe,EAAE,CAE9C"}
|
package/dist/agents/registry.js
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import { claudeAgent } from './claude.js';
|
|
2
|
-
import { codexAgent } from './codex.js';
|
|
3
|
-
import { cursorAgent } from './cursor.js';
|
|
4
|
-
import { aiderAgent } from './aider.js';
|
|
5
|
-
import { geminiAgent } from './gemini.js';
|
|
6
|
-
import { qwenAgent } from './qwen.js';
|
|
7
|
-
/**
|
|
8
|
-
* Registry of agents Hover can drive.
|
|
9
|
-
*
|
|
10
|
-
* To add support for another agent (e.g. cline, continue, kilo), implement
|
|
11
|
-
* its AgentDescriptor in its own file and register it here. The rest of the
|
|
12
|
-
* system — detect, argv, invoke, service, widget — works without further
|
|
13
|
-
* changes.
|
|
14
|
-
*
|
|
15
|
-
* Insertion order is the order shown in the widget's agent dropdown, so put
|
|
16
|
-
* the recommended primary first. The two hard-sandbox / first-party agents
|
|
17
|
-
* (claude, codex) lead; the soft-sandbox third-party agents follow in the
|
|
18
|
-
* order they were added.
|
|
19
|
-
*/
|
|
20
|
-
export const AGENTS = {
|
|
21
|
-
[claudeAgent.id]: claudeAgent,
|
|
22
|
-
[codexAgent.id]: codexAgent,
|
|
23
|
-
[cursorAgent.id]: cursorAgent,
|
|
24
|
-
[aiderAgent.id]: aiderAgent,
|
|
25
|
-
[geminiAgent.id]: geminiAgent,
|
|
26
|
-
[qwenAgent.id]: qwenAgent,
|
|
27
|
-
};
|
|
28
|
-
export function getAgent(id) {
|
|
29
|
-
return AGENTS[id];
|
|
30
|
-
}
|
|
31
|
-
/** Stable, insertion-ordered list of all registered agents. */
|
|
32
|
-
export function listAgents() {
|
|
33
|
-
return Object.values(AGENTS);
|
|
34
|
-
}
|
package/dist/agents/shared.d.ts
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Cross-agent helpers shared by the soft-sandbox descriptors
|
|
3
|
-
* (codex / cursor / gemini / qwen / aider).
|
|
4
|
-
*
|
|
5
|
-
* These agents all need the same two things:
|
|
6
|
-
* 1. A standing "HOVER-mode" instruction preface that tells the agent to
|
|
7
|
-
* drive the browser via the Playwright MCP tools only and not to touch
|
|
8
|
-
* its built-in shell / file-edit tools. Each agent injects it through a
|
|
9
|
-
* different channel (cursor / gemini / aider prepend it to the prompt,
|
|
10
|
-
* qwen passes it via --append-system-prompt, codex via
|
|
11
|
-
* `-c developer_instructions=`), so this module owns only the *text*, not
|
|
12
|
-
* the injection.
|
|
13
|
-
* 2. Normalising the `mcp__playwright__` / `mcp__hover-playwright__` prefix
|
|
14
|
-
* off a raw tool name so the emitted tool names line up across agents.
|
|
15
|
-
*
|
|
16
|
-
* codex deliberately does NOT use HOVER_PROMPT_PREFACE — it keeps its own
|
|
17
|
-
* wording ("Do NOT call …", "emit a short agent_message summary …") that is
|
|
18
|
-
* tuned to codex's event vocabulary. See codex.ts.
|
|
19
|
-
*/
|
|
20
|
-
/**
|
|
21
|
-
* The standing HOVER-mode instruction shared by cursor / gemini / qwen / aider.
|
|
22
|
-
* codex carries a near-identical but intentionally different variant inline.
|
|
23
|
-
*/
|
|
24
|
-
export declare const HOVER_PROMPT_PREFACE: string;
|
|
25
|
-
/** Strip the `mcp__playwright__` / `mcp__hover-playwright__` prefix so tool
|
|
26
|
-
* names match the normalised names every agent emits. */
|
|
27
|
-
export declare function stripMcpPrefix(raw: string): string;
|
|
28
|
-
//# sourceMappingURL=shared.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../src/agents/shared.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH;;;GAGG;AACH,eAAO,MAAM,oBAAoB,QAMtB,CAAC;AAEZ;0DAC0D;AAC1D,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAElD"}
|
package/dist/agents/shared.js
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Cross-agent helpers shared by the soft-sandbox descriptors
|
|
3
|
-
* (codex / cursor / gemini / qwen / aider).
|
|
4
|
-
*
|
|
5
|
-
* These agents all need the same two things:
|
|
6
|
-
* 1. A standing "HOVER-mode" instruction preface that tells the agent to
|
|
7
|
-
* drive the browser via the Playwright MCP tools only and not to touch
|
|
8
|
-
* its built-in shell / file-edit tools. Each agent injects it through a
|
|
9
|
-
* different channel (cursor / gemini / aider prepend it to the prompt,
|
|
10
|
-
* qwen passes it via --append-system-prompt, codex via
|
|
11
|
-
* `-c developer_instructions=`), so this module owns only the *text*, not
|
|
12
|
-
* the injection.
|
|
13
|
-
* 2. Normalising the `mcp__playwright__` / `mcp__hover-playwright__` prefix
|
|
14
|
-
* off a raw tool name so the emitted tool names line up across agents.
|
|
15
|
-
*
|
|
16
|
-
* codex deliberately does NOT use HOVER_PROMPT_PREFACE — it keeps its own
|
|
17
|
-
* wording ("Do NOT call …", "emit a short agent_message summary …") that is
|
|
18
|
-
* tuned to codex's event vocabulary. See codex.ts.
|
|
19
|
-
*/
|
|
20
|
-
/**
|
|
21
|
-
* The standing HOVER-mode instruction shared by cursor / gemini / qwen / aider.
|
|
22
|
-
* codex carries a near-identical but intentionally different variant inline.
|
|
23
|
-
*/
|
|
24
|
-
export const HOVER_PROMPT_PREFACE = [
|
|
25
|
-
'You are operating in Hover, a browser-testing tool.',
|
|
26
|
-
'Use ONLY the MCP playwright tools (prefixed `mcp__playwright__` / `mcp__hover-playwright__`) to drive the browser.',
|
|
27
|
-
'Do NOT use shell, file-edit, web-search, or any other built-in tool.',
|
|
28
|
-
'Do NOT navigate to a URL the user is already on; check the page state via `browser_snapshot` first.',
|
|
29
|
-
'When the task is complete, emit a short summary and stop.',
|
|
30
|
-
].join(' ');
|
|
31
|
-
/** Strip the `mcp__playwright__` / `mcp__hover-playwright__` prefix so tool
|
|
32
|
-
* names match the normalised names every agent emits. */
|
|
33
|
-
export function stripMcpPrefix(raw) {
|
|
34
|
-
return raw.replace(/^mcp__playwright__/, '').replace(/^mcp__hover-playwright__/, '');
|
|
35
|
-
}
|