@hover-dev/core 0.15.0 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (169) hide show
  1. package/README.md +26 -55
  2. package/dist/agentDirectives.d.ts +55 -0
  3. package/dist/agentDirectives.d.ts.map +1 -0
  4. package/dist/agentDirectives.js +276 -0
  5. package/dist/agents/claude.d.ts.map +1 -1
  6. package/dist/agents/claude.js +28 -3
  7. package/dist/agents/codex.d.ts.map +1 -1
  8. package/dist/agents/codex.js +38 -18
  9. package/dist/agents/gemini.d.ts.map +1 -1
  10. package/dist/agents/gemini.js +3 -14
  11. package/dist/agents/invoke.d.ts.map +1 -1
  12. package/dist/agents/invoke.js +3 -6
  13. package/dist/agents/qwen.d.ts.map +1 -1
  14. package/dist/agents/qwen.js +3 -14
  15. package/dist/agents/registry.d.ts.map +1 -1
  16. package/dist/agents/registry.js +0 -4
  17. package/dist/agents/shared.d.ts +28 -0
  18. package/dist/agents/shared.d.ts.map +1 -0
  19. package/dist/agents/shared.js +35 -0
  20. package/dist/agents/types.d.ts +19 -11
  21. package/dist/agents/types.d.ts.map +1 -1
  22. package/dist/engine.d.ts +53 -0
  23. package/dist/engine.d.ts.map +1 -0
  24. package/dist/engine.js +78 -0
  25. package/dist/mcp/actuateServer.d.ts +3 -0
  26. package/dist/mcp/actuateServer.d.ts.map +1 -0
  27. package/dist/mcp/actuateServer.js +594 -0
  28. package/dist/mcp/sourceFence.d.ts +23 -0
  29. package/dist/mcp/sourceFence.d.ts.map +1 -0
  30. package/dist/mcp/sourceFence.js +79 -0
  31. package/dist/mcp/sourceServer.d.ts +3 -0
  32. package/dist/mcp/sourceServer.d.ts.map +1 -0
  33. package/dist/mcp/sourceServer.js +191 -0
  34. package/dist/memory/businessMemory.d.ts +29 -0
  35. package/dist/memory/businessMemory.d.ts.map +1 -0
  36. package/dist/memory/businessMemory.js +125 -0
  37. package/dist/modes.d.ts +39 -0
  38. package/dist/modes.d.ts.map +1 -0
  39. package/dist/modes.js +34 -0
  40. package/dist/playwright/cdpStatus.d.ts +0 -15
  41. package/dist/playwright/cdpStatus.d.ts.map +1 -1
  42. package/dist/playwright/cdpStatus.js +0 -67
  43. package/dist/playwright/launchChrome.d.ts +18 -0
  44. package/dist/playwright/launchChrome.d.ts.map +1 -1
  45. package/dist/playwright/launchChrome.js +46 -3
  46. package/dist/playwright/preflight.d.ts.map +1 -1
  47. package/dist/playwright/preflight.js +6 -1
  48. package/dist/playwright/resolveMcpConfig.d.ts +12 -0
  49. package/dist/playwright/resolveMcpConfig.d.ts.map +1 -1
  50. package/dist/playwright/resolveMcpConfig.js +36 -5
  51. package/dist/plugin-api.d.ts +35 -26
  52. package/dist/plugin-api.d.ts.map +1 -1
  53. package/dist/plugin-api.js +2 -2
  54. package/dist/qa/candidates.d.ts +32 -0
  55. package/dist/qa/candidates.d.ts.map +1 -0
  56. package/dist/qa/candidates.js +20 -0
  57. package/dist/qa/classify.d.ts +38 -0
  58. package/dist/qa/classify.d.ts.map +1 -0
  59. package/dist/qa/classify.js +138 -0
  60. package/dist/qa/intensity.d.ts +33 -0
  61. package/dist/qa/intensity.d.ts.map +1 -0
  62. package/dist/qa/intensity.js +25 -0
  63. package/dist/qa/qaReport.d.ts +19 -0
  64. package/dist/qa/qaReport.d.ts.map +1 -0
  65. package/dist/qa/qaReport.js +50 -0
  66. package/dist/runSession.d.ts +14 -3
  67. package/dist/runSession.d.ts.map +1 -1
  68. package/dist/runSession.js +31 -11
  69. package/dist/service/cdpHandlers.d.ts +3 -27
  70. package/dist/service/cdpHandlers.d.ts.map +1 -1
  71. package/dist/service/cdpHandlers.js +6 -53
  72. package/dist/service/cdpHint.d.ts +21 -28
  73. package/dist/service/cdpHint.d.ts.map +1 -1
  74. package/dist/service/cdpHint.js +106 -164
  75. package/dist/service/relayHandlers.d.ts +28 -0
  76. package/dist/service/relayHandlers.d.ts.map +1 -0
  77. package/dist/service/relayHandlers.js +105 -0
  78. package/dist/service/saveHandlers.d.ts +1 -3
  79. package/dist/service/saveHandlers.d.ts.map +1 -1
  80. package/dist/service/saveHandlers.js +17 -15
  81. package/dist/service/types.d.ts +108 -8
  82. package/dist/service/types.d.ts.map +1 -1
  83. package/dist/service.d.ts +13 -3
  84. package/dist/service.d.ts.map +1 -1
  85. package/dist/service.js +1022 -236
  86. package/dist/sessions/sessions.d.ts +125 -0
  87. package/dist/sessions/sessions.d.ts.map +1 -0
  88. package/dist/sessions/sessions.js +175 -0
  89. package/dist/specs/authFixture.d.ts +30 -0
  90. package/dist/specs/authFixture.d.ts.map +1 -0
  91. package/dist/specs/authFixture.js +145 -0
  92. package/dist/specs/businessMap.d.ts +29 -0
  93. package/dist/specs/businessMap.d.ts.map +1 -0
  94. package/dist/specs/businessMap.js +95 -0
  95. package/dist/specs/detectSharedFlows.d.ts +1 -1
  96. package/dist/specs/detectSharedFlows.d.ts.map +1 -1
  97. package/dist/specs/detectSharedFlows.js +20 -21
  98. package/dist/specs/generatePageObject.d.ts +1 -1
  99. package/dist/specs/generatePageObject.d.ts.map +1 -1
  100. package/dist/specs/healPrompt.d.ts +19 -0
  101. package/dist/specs/healPrompt.d.ts.map +1 -0
  102. package/dist/specs/healPrompt.js +48 -0
  103. package/dist/specs/humanSteps.d.ts +4 -8
  104. package/dist/specs/humanSteps.d.ts.map +1 -1
  105. package/dist/specs/humanSteps.js +6 -1
  106. package/dist/specs/optimizeSpec.d.ts +15 -8
  107. package/dist/specs/optimizeSpec.d.ts.map +1 -1
  108. package/dist/specs/optimizeSpec.js +98 -46
  109. package/dist/specs/optimizeSpecWithAgent.d.ts +0 -2
  110. package/dist/specs/optimizeSpecWithAgent.d.ts.map +1 -1
  111. package/dist/specs/optimizeSpecWithAgent.js +0 -1
  112. package/dist/specs/pageObjectManifest.d.ts +3 -1
  113. package/dist/specs/pageObjectManifest.d.ts.map +1 -1
  114. package/dist/specs/pageObjectManifest.js +13 -9
  115. package/dist/specs/replayGrounded.d.ts +45 -0
  116. package/dist/specs/replayGrounded.d.ts.map +1 -0
  117. package/dist/specs/replayGrounded.js +155 -0
  118. package/dist/specs/runFailures.d.ts +34 -0
  119. package/dist/specs/runFailures.d.ts.map +1 -0
  120. package/dist/specs/runFailures.js +93 -0
  121. package/dist/specs/seeds.d.ts +16 -15
  122. package/dist/specs/seeds.d.ts.map +1 -1
  123. package/dist/specs/seeds.js +86 -54
  124. package/dist/specs/sidecar.d.ts +34 -6
  125. package/dist/specs/sidecar.d.ts.map +1 -1
  126. package/dist/specs/sidecar.js +79 -9
  127. package/dist/specs/softBatch.d.ts +14 -0
  128. package/dist/specs/softBatch.d.ts.map +1 -0
  129. package/dist/specs/softBatch.js +177 -0
  130. package/dist/specs/specStep.d.ts +21 -0
  131. package/dist/specs/specStep.d.ts.map +1 -0
  132. package/dist/specs/specStep.js +1 -0
  133. package/dist/specs/text.d.ts +19 -0
  134. package/dist/specs/text.d.ts.map +1 -0
  135. package/dist/specs/text.js +27 -0
  136. package/dist/specs/writeSpec.d.ts +62 -1
  137. package/dist/specs/writeSpec.d.ts.map +1 -1
  138. package/dist/specs/writeSpec.js +598 -30
  139. package/package.json +10 -10
  140. package/dist/agents/aider.d.ts +0 -16
  141. package/dist/agents/aider.d.ts.map +0 -1
  142. package/dist/agents/aider.js +0 -169
  143. package/dist/agents/cursor.d.ts +0 -18
  144. package/dist/agents/cursor.d.ts.map +0 -1
  145. package/dist/agents/cursor.js +0 -229
  146. package/dist/playwright/raiseWindow.d.ts +0 -10
  147. package/dist/playwright/raiseWindow.d.ts.map +0 -1
  148. package/dist/playwright/raiseWindow.js +0 -139
  149. package/dist/scripts/bench-multi-tab.d.ts +0 -2
  150. package/dist/scripts/bench-multi-tab.d.ts.map +0 -1
  151. package/dist/scripts/bench-multi-tab.js +0 -192
  152. package/dist/scripts/bench-ttfb.d.ts +0 -2
  153. package/dist/scripts/bench-ttfb.d.ts.map +0 -1
  154. package/dist/scripts/bench-ttfb.js +0 -127
  155. package/dist/scripts/start-chrome.d.ts +0 -3
  156. package/dist/scripts/start-chrome.d.ts.map +0 -1
  157. package/dist/scripts/start-chrome.js +0 -23
  158. package/dist/skills/writeSkill.d.ts +0 -27
  159. package/dist/skills/writeSkill.d.ts.map +0 -1
  160. package/dist/skills/writeSkill.js +0 -13
  161. package/dist/specs/listSpecs.d.ts +0 -52
  162. package/dist/specs/listSpecs.d.ts.map +0 -1
  163. package/dist/specs/listSpecs.js +0 -139
  164. package/dist/specs/optimizationSuggestion.d.ts +0 -26
  165. package/dist/specs/optimizationSuggestion.d.ts.map +0 -1
  166. package/dist/specs/optimizationSuggestion.js +0 -28
  167. package/dist/specs/writeCaseCsv.d.ts +0 -28
  168. package/dist/specs/writeCaseCsv.d.ts.map +0 -1
  169. package/dist/specs/writeCaseCsv.js +0 -140
@@ -1,3 +1,4 @@
1
+ import { stripMcpPrefix } from './shared.js';
1
2
  /**
2
3
  * Pricing per million tokens. Keep in lockstep with claude.ts's table —
3
4
  * approximate published OpenAI rates as of 2026. We are deliberately
@@ -16,6 +17,9 @@ const PRICE_PER_M_USD = {
16
17
  'gpt-4o': { in: 2.5, out: 10 },
17
18
  'gpt-4': { in: 30, out: 60 },
18
19
  };
20
+ // `modelHint` is currently always passed as undefined — the parser can't see
21
+ // the invocation's --model — so the default tier below is what gets used. The
22
+ // parameter is kept so a future caller that does have the model id can pass it.
19
23
  function estimateCostUsd(modelHint, usage) {
20
24
  const m = (modelHint ?? 'gpt-5.5').toLowerCase();
21
25
  // Match by longest-prefix so 'gpt-5.5-mini' picks up the 'gpt-5.5' tier.
@@ -28,7 +32,7 @@ function codexState(state) {
28
32
  if (typeof state.runningCost !== 'number') {
29
33
  state.runningCost = 0;
30
34
  state.runningTurns = 0;
31
- state.runningModel = undefined;
35
+ state.runningTokens = 0;
32
36
  state.runningSessionId = undefined;
33
37
  state.lastAgentMessage = undefined;
34
38
  state.sawErrorEvent = false;
@@ -39,27 +43,32 @@ function codexState(state) {
39
43
  function resetCodexCounters(s) {
40
44
  s.runningCost = 0;
41
45
  s.runningTurns = 0;
42
- s.runningModel = undefined;
46
+ s.runningTokens = 0;
43
47
  s.runningSessionId = undefined;
44
48
  s.lastAgentMessage = undefined;
45
49
  s.sawErrorEvent = false;
46
50
  s.itemTypeById.clear();
47
51
  }
48
- /** Cap surfaced as a constraint in the system prompt — codex has no CLI flag. */
49
- const CODEX_DEVELOPER_INSTRUCTIONS = [
50
- 'You are operating in Hover, a browser-testing tool.',
51
- 'Use ONLY the MCP playwright tools (prefixed `mcp__playwright__` / `mcp__hover-playwright__`) to drive the browser.',
52
- 'Do NOT call shell, file-edit, web-search, or any other built-in tool.',
53
- 'Do NOT navigate to a URL the user is already on; check the page state via `browser_snapshot` first.',
54
- 'When the task is complete, emit a short agent_message summary and stop.',
55
- ].join(' ');
52
+ /** The tool-restriction is codex's only sandbox (no --allowedTools flag), so it
53
+ * must mirror the hard-sandbox allow-list the service computes per mode — NOT a
54
+ * hardcoded "playwright only", which made codex refuse the active mode's plugin
55
+ * tools (api_request, replay_flow, hover-control, ). Built from opts.allowedTools. */
56
+ function codexDeveloperInstructions(allowedTools) {
57
+ const prefixes = (allowedTools && allowedTools.length ? allowedTools : ['mcp__playwright']).map((p) => `${p}__*`);
58
+ return [
59
+ 'You are operating in Hover, a browser- and API-testing tool.',
60
+ `Use ONLY MCP tools whose name starts with one of these prefixes: ${prefixes.join(', ')}. They cover driving the browser, the Hover control + API-request tools, and reading source — everything the task needs.`,
61
+ 'Do NOT call shell, file-edit, web-search, or any other built-in tool.',
62
+ 'Do NOT navigate to a URL the user is already on; check the page state via `browser_snapshot` first.',
63
+ 'When the task is complete, emit a short agent_message summary and stop.',
64
+ ].join(' ');
65
+ }
56
66
  export const codexAgent = {
57
67
  id: 'codex',
58
68
  binName: 'codex',
59
69
  protocol: 'argv',
60
70
  streamFormat: 'json-lines',
61
71
  sandboxStrength: 'soft',
62
- apiKeyEnv: 'OPENAI_API_KEY',
63
72
  display: {
64
73
  label: 'OpenAI Codex',
65
74
  tagline: 'OpenAI — soft sandbox (no built-in tool deny-list)',
@@ -85,13 +94,19 @@ export const codexAgent = {
85
94
  if (opts.model) {
86
95
  args.push('--model', opts.model);
87
96
  }
97
+ // Reasoning effort → the API's reasoning_effort, set via the `-c` TOML
98
+ // override (no stable long flag exists). Sits alongside the other -c keys.
99
+ if (opts.effort) {
100
+ args.push('-c', `model_reasoning_effort=${opts.effort}`);
101
+ }
88
102
  // System-prompt injection. Codex has no --append-system-prompt; we route
89
103
  // through `-c developer_instructions='...'`. Concatenate the standing
90
104
  // Hover-mode instructions with whatever the caller passes (e.g. "user is
91
105
  // already on http://localhost:5173/").
106
+ const base = codexDeveloperInstructions(opts.allowedTools);
92
107
  const sysPrompt = opts.appendSystemPrompt && opts.appendSystemPrompt.trim().length > 0
93
- ? `${CODEX_DEVELOPER_INSTRUCTIONS} ${opts.appendSystemPrompt}`
94
- : CODEX_DEVELOPER_INSTRUCTIONS;
108
+ ? `${base} ${opts.appendSystemPrompt}`
109
+ : base;
95
110
  args.push('-c', `developer_instructions=${JSON.stringify(sysPrompt)}`);
96
111
  // MCP servers are configured in ~/.codex/config.toml at install time,
97
112
  // not per-invocation. If the user passed an mcpConfig path, we don't
@@ -128,13 +143,13 @@ export const codexAgent = {
128
143
  // The exact field names aren't published. Read defensively: prefer
129
144
  // `name`, fall back to `tool`. Same for input.
130
145
  const rawName = it.name ?? it.tool ?? '';
131
- const tool = rawName.replace(/^mcp__playwright__/, '').replace(/^mcp__hover-playwright__/, '');
132
- out.push({ kind: 'tool_use', tool, input: it.input ?? it.arguments, costUsdSnapshot: s.runningCost });
146
+ const tool = stripMcpPrefix(rawName);
147
+ out.push({ kind: 'tool_use', tool, input: it.input ?? it.arguments, costUsdSnapshot: s.runningCost, tokensSnapshot: s.runningTokens });
133
148
  }
134
149
  else if (it.type === 'command_execution') {
135
150
  // We DISCOURAGED this in developer_instructions but the agent can
136
151
  // still try. Surface it so the user sees it happen.
137
- out.push({ kind: 'tool_use', tool: 'shell', input: { command: it.command }, costUsdSnapshot: s.runningCost });
152
+ out.push({ kind: 'tool_use', tool: 'shell', input: { command: it.command }, costUsdSnapshot: s.runningCost, tokensSnapshot: s.runningTokens });
138
153
  }
139
154
  return out;
140
155
  }
@@ -158,9 +173,13 @@ export const codexAgent = {
158
173
  if (ev.type === 'turn.completed') {
159
174
  s.runningTurns += 1;
160
175
  if (ev.usage) {
161
- s.runningCost += estimateCostUsd(s.runningModel, ev.usage);
176
+ // The parser has no access to the invocation's --model, so we let
177
+ // estimateCostUsd fall back to its fixed default tier. Cost is a
178
+ // high-water "should I hit Stop now" signal, not an invoice.
179
+ s.runningCost += estimateCostUsd(undefined, ev.usage);
180
+ s.runningTokens += (ev.usage.input_tokens ?? 0) + (ev.usage.output_tokens ?? 0);
162
181
  }
163
- out.push({ kind: 'usage', costUsd: s.runningCost, turns: s.runningTurns });
182
+ out.push({ kind: 'usage', costUsd: s.runningCost, turns: s.runningTurns, tokens: s.runningTokens });
164
183
  return out;
165
184
  }
166
185
  // Codex emits various error envelopes; we conservatively match anything
@@ -185,6 +204,7 @@ export const codexAgent = {
185
204
  kind: 'session_end',
186
205
  turns: s.runningTurns,
187
206
  costUsd: s.runningCost,
207
+ tokens: s.runningTokens,
188
208
  isError: s.sawErrorEvent || (exitCode != null && exitCode !== 0),
189
209
  summary: s.lastAgentMessage,
190
210
  };
@@ -1 +1 @@
1
- {"version":3,"file":"gemini.d.ts","sourceRoot":"","sources":["../../src/agents/gemini.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAA8B,WAAW,EAAE,MAAM,YAAY,CAAC;AA4K3F,eAAO,MAAM,WAAW,EAAE,eAiJzB,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,SAAS;sBACJ,WAAW;2BACJ,WAAW;sBAChB,WAAW;;;;;;;CAU9B,CAAC"}
1
+ {"version":3,"file":"gemini.d.ts","sourceRoot":"","sources":["../../src/agents/gemini.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAA8B,WAAW,EAAE,MAAM,YAAY,CAAC;AA+J3F,eAAO,MAAM,WAAW,EAAE,eAiJzB,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,SAAS;sBACJ,WAAW;2BACJ,WAAW;sBAChB,WAAW;;;;;;;CAU9B,CAAC"}
@@ -1,3 +1,4 @@
1
+ import { HOVER_PROMPT_PREFACE, stripMcpPrefix } from './shared.js';
1
2
  function geminiState(state) {
2
3
  if (typeof state.runningTurns !== 'number') {
3
4
  state.runningTurns = 0;
@@ -17,11 +18,6 @@ function resetGeminiCounters(s) {
17
18
  s.sawErrorEvent = false;
18
19
  s.toolNameByUseId.clear();
19
20
  }
20
- /** Strip the `mcp__playwright__` / `mcp__hover-playwright__` prefix so tool
21
- * names match the normalised names claude / codex / cursor / qwen emit. */
22
- function stripMcpPrefix(raw) {
23
- return raw.replace(/^mcp__playwright__/, '').replace(/^mcp__hover-playwright__/, '');
24
- }
25
21
  /**
26
22
  * Extract assistant text from a `message` event whose `content` may be a
27
23
  * plain string OR an array of `{type:'text', text}` content blocks. Gemini's
@@ -41,13 +37,6 @@ function extractMessageText(ev) {
41
37
  }
42
38
  return undefined;
43
39
  }
44
- const GEMINI_PROMPT_PREFACE = [
45
- 'You are operating in Hover, a browser-testing tool.',
46
- 'Use ONLY the MCP playwright tools (prefixed `mcp__playwright__` / `mcp__hover-playwright__`) to drive the browser.',
47
- 'Do NOT use shell, file-edit, web-search, or any other built-in tool.',
48
- 'Do NOT navigate to a URL the user is already on; check the page state via `browser_snapshot` first.',
49
- 'When the task is complete, emit a short summary and stop.',
50
- ].join(' ');
51
40
  export const geminiAgent = {
52
41
  id: 'gemini',
53
42
  binName: 'gemini',
@@ -65,8 +54,8 @@ export const geminiAgent = {
65
54
  // GEMINI_SYSTEM_MD env var which writes a file). Prepend the HOVER-mode
66
55
  // preface to the prompt instead — same pattern as cursor.ts / aider.ts.
67
56
  const preface = opts.appendSystemPrompt && opts.appendSystemPrompt.trim().length > 0
68
- ? `${GEMINI_PROMPT_PREFACE} ${opts.appendSystemPrompt}`
69
- : GEMINI_PROMPT_PREFACE;
57
+ ? `${HOVER_PROMPT_PREFACE} ${opts.appendSystemPrompt}`
58
+ : HOVER_PROMPT_PREFACE;
70
59
  const finalPrompt = `${preface}\n\n${opts.prompt}`;
71
60
  const args = ['-p', finalPrompt];
72
61
  // NDJSON streaming output.
@@ -1 +1 @@
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"}
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,CA4ElF"}
@@ -31,15 +31,12 @@ export async function* invokeAgent(opts) {
31
31
  cwd: opts.cwd,
32
32
  // Clear CLAUDECODE so spawning `claude` from inside a Claude Code session
33
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.
34
+ // The CLI authenticates via its own logged-in subscription (or inherits any
35
+ // key already in process.env); opts.env carries the Local LLM endpoint vars.
37
36
  env: {
38
37
  ...process.env,
39
38
  CLAUDECODE: '',
40
- ...(opts.apiKey && descriptor.apiKeyEnv
41
- ? { [descriptor.apiKeyEnv]: opts.apiKey }
42
- : {}),
39
+ ...(opts.env ?? {}),
43
40
  },
44
41
  });
45
42
  const onAbort = () => {
@@ -1 +1 @@
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;AAiK3F,eAAO,MAAM,SAAS,EAAE,eAqJvB,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,SAAS;sBACJ,WAAW;2BACJ,WAAW;sBAChB,WAAW;;;;;;;CAU9B,CAAC"}
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"}
@@ -1,3 +1,4 @@
1
+ import { HOVER_PROMPT_PREFACE, stripMcpPrefix } from './shared.js';
1
2
  function qwenState(state) {
2
3
  if (typeof state.runningTurns !== 'number') {
3
4
  state.runningTurns = 0;
@@ -17,18 +18,6 @@ function resetQwenCounters(s) {
17
18
  s.sawErrorEvent = false;
18
19
  s.toolNameByUseId.clear();
19
20
  }
20
- /** Strip the `mcp__playwright__` / `mcp__hover-playwright__` prefix so tool
21
- * names match the normalised names claude / codex / cursor emit. */
22
- function stripMcpPrefix(raw) {
23
- return raw.replace(/^mcp__playwright__/, '').replace(/^mcp__hover-playwright__/, '');
24
- }
25
- const QWEN_PROMPT_PREFACE = [
26
- 'You are operating in Hover, a browser-testing tool.',
27
- 'Use ONLY the MCP playwright tools (prefixed `mcp__playwright__` / `mcp__hover-playwright__`) to drive the browser.',
28
- 'Do NOT use shell, file-edit, web-search, or any other built-in tool.',
29
- 'Do NOT navigate to a URL the user is already on; check the page state via `browser_snapshot` first.',
30
- 'When the task is complete, emit a short summary and stop.',
31
- ].join(' ');
32
21
  export const qwenAgent = {
33
22
  id: 'qwen',
34
23
  binName: 'qwen',
@@ -62,8 +51,8 @@ export const qwenAgent = {
62
51
  // prepending to the user prompt. Concatenate the standing Hover-mode
63
52
  // preface with whatever the caller appended.
64
53
  const sysPrompt = opts.appendSystemPrompt && opts.appendSystemPrompt.trim().length > 0
65
- ? `${QWEN_PROMPT_PREFACE} ${opts.appendSystemPrompt}`
66
- : QWEN_PROMPT_PREFACE;
54
+ ? `${HOVER_PROMPT_PREFACE} ${opts.appendSystemPrompt}`
55
+ : HOVER_PROMPT_PREFACE;
67
56
  args.push('--append-system-prompt', sysPrompt);
68
57
  // MCP servers configured in ~/.qwen/settings.json — no per-invocation
69
58
  // --mcp-config equivalent. Same constraint as cursor / codex.
@@ -1 +1 @@
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"}
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/agents/registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAMlD;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAKlD,CAAC;AAEF,wBAAgB,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,eAAe,GAAG,SAAS,CAEhE;AAED,+DAA+D;AAC/D,wBAAgB,UAAU,IAAI,eAAe,EAAE,CAE9C"}
@@ -1,7 +1,5 @@
1
1
  import { claudeAgent } from './claude.js';
2
2
  import { codexAgent } from './codex.js';
3
- import { cursorAgent } from './cursor.js';
4
- import { aiderAgent } from './aider.js';
5
3
  import { geminiAgent } from './gemini.js';
6
4
  import { qwenAgent } from './qwen.js';
7
5
  /**
@@ -20,8 +18,6 @@ import { qwenAgent } from './qwen.js';
20
18
  export const AGENTS = {
21
19
  [claudeAgent.id]: claudeAgent,
22
20
  [codexAgent.id]: codexAgent,
23
- [cursorAgent.id]: cursorAgent,
24
- [aiderAgent.id]: aiderAgent,
25
21
  [geminiAgent.id]: geminiAgent,
26
22
  [qwenAgent.id]: qwenAgent,
27
23
  };
@@ -0,0 +1,28 @@
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
@@ -0,0 +1 @@
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"}
@@ -0,0 +1,35 @@
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
+ }
@@ -23,7 +23,15 @@ export interface InvokeOptions {
23
23
  allowedTools?: string[];
24
24
  disallowedTools?: string[];
25
25
  maxBudgetUsd?: number;
26
+ /** Hard ceiling on agent turns (~steps) — claude `--max-turns`. Used by QA
27
+ * intensity to bound an exploratory run by step count. */
28
+ maxTurns?: number;
26
29
  model?: string;
30
+ /** Reasoning-effort level for the run. claude → `--effort <level>` (low /
31
+ * medium / high / xhigh / max); codex → `-c model_reasoning_effort=<level>`
32
+ * (minimal / low / medium / high / xhigh). The caller is responsible for
33
+ * passing a level the chosen model actually supports. */
34
+ effort?: string;
27
35
  cwd?: string;
28
36
  sessionId?: string;
29
37
  /** Extra text appended to the agent's system prompt (claude: via
@@ -31,11 +39,10 @@ export interface InvokeOptions {
31
39
  * "the user's current Chrome tab is already on http://localhost:5173/,
32
40
  * don't browser_navigate there". */
33
41
  appendSystemPrompt?: string;
34
- /** Optional model API key. Injected into the spawned CLI's environment under
35
- * the descriptor's `apiKeyEnv` var (e.g. ANTHROPIC_API_KEY) so a user without
36
- * a logged-in subscription can drive Hover with their own key. Never logged,
37
- * never persisted server-side — held only for the lifetime of the spawn. */
38
- apiKey?: string;
42
+ /** Extra environment variables for the spawned CLI (merged over process.env).
43
+ * Used for the "Local LLM" path: qwen-code reads OPENAI_BASE_URL /
44
+ * OPENAI_API_KEY to target a user's self-hosted OpenAI-compatible endpoint. */
45
+ env?: Record<string, string>;
39
46
  /** Aborts the spawned child if signaled. Used to stop an orphan run when
40
47
  * the WebSocket caller disconnects (e.g. user reloads the dev page). */
41
48
  signal?: AbortSignal;
@@ -57,6 +64,7 @@ export type InvokeEvent = {
57
64
  tool: string;
58
65
  input: unknown;
59
66
  costUsdSnapshot?: number;
67
+ tokensSnapshot?: number;
60
68
  } | {
61
69
  kind: 'tool_result';
62
70
  isError?: boolean;
@@ -73,6 +81,7 @@ export type InvokeEvent = {
73
81
  kind: 'usage';
74
82
  costUsd?: number;
75
83
  turns?: number;
84
+ tokens?: number;
76
85
  }
77
86
  /** End-of-session event. Three terminal states the widget renders distinctly:
78
87
  *
@@ -87,6 +96,7 @@ export type InvokeEvent = {
87
96
  kind: 'session_end';
88
97
  turns?: number;
89
98
  costUsd?: number;
99
+ tokens?: number;
90
100
  isError?: boolean;
91
101
  cancelled?: boolean;
92
102
  summary?: string;
@@ -156,12 +166,10 @@ export interface AgentDescriptor {
156
166
  * per-CLI deny list live alongside its descriptor instead of as a magic
157
167
  * array in the service. Soft-sandbox agents leave this undefined. */
158
168
  defaultDisallowedTools?: readonly string[];
159
- /** Environment variable this CLI reads its model API key from
160
- * (claude: ANTHROPIC_API_KEY, codex: OPENAI_API_KEY). When set and the
161
- * caller supplies `InvokeOptions.apiKey`, the key is injected into the spawn
162
- * env so the user can run on a raw key instead of a logged-in subscription.
163
- * Undefined for agents that have no API-key env path. */
164
- apiKeyEnv?: string;
169
+ /** A cheap/fast model id for non-interactive refinement work (the F7 optimize
170
+ * pass) where the big model isn't worth the cost. Used when the user hasn't
171
+ * set `hover.optimizeModel`; undefined fall back to the session model. */
172
+ cheapModel?: string;
165
173
  buildArgs(opts: InvokeOptions): string[];
166
174
  /**
167
175
  * Parse a single line of agent stdout into normalised InvokeEvents.
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/agents/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/agents/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,53 @@
1
+ export { runSession } from './runSession.js';
2
+ export type { RunSessionOptions, RunSessionResult } from './runSession.js';
3
+ export { writeSpec } from './specs/writeSpec.js';
4
+ export type { WriteSpecOptions, WriteSpecResult, Redaction } from './specs/writeSpec.js';
5
+ export type { SkillStep } from './specs/specStep.js';
6
+ export { replayGroundedSteps, replayOnPage, applyGroundedStep, groundedLocate } from './specs/replayGrounded.js';
7
+ export type { ReplayResult, ReplayFailure, ReplayStep, GroundedTarget } from './specs/replayGrounded.js';
8
+ export { resolveMcpConfig, mcpToolPrefix } from './playwright/resolveMcpConfig.js';
9
+ export { launchDebugChrome, closeDebugChrome, findChromeBinary } from './playwright/launchChrome.js';
10
+ export type { LaunchOptions, LaunchResult } from './playwright/launchChrome.js';
11
+ export { GROUNDED_ACTUATION_DENY, GROUNDED_ACTUATION_DIRECTIVE } from './agentDirectives.js';
12
+ export { loadMemory, formatMemoryForPrompt, writeFact, memoryDir } from './memory/businessMemory.js';
13
+ export type { BusinessFact } from './memory/businessMemory.js';
14
+ export { RECON_DIRECTIVE, QA_EXPLORATION_DIRECTIVE } from './agentDirectives.js';
15
+ export { QA_INTENSITY, DEFAULT_QA_INTENSITY, asQaIntensity, qaBudgetDirective } from './qa/intensity.js';
16
+ export type { QaIntensity, QaIntensitySpec } from './qa/intensity.js';
17
+ /**
18
+ * The always-on control-actuation MCP server id. Alphanumeric on purpose — a
19
+ * hyphen would make the hard-sandbox allow prefix (`mcp__hovercontrol`) fail to
20
+ * match the tool names (`mcp__hovercontrol__click_control`), so every grounded
21
+ * actuation would be denied. (Same constant + reasoning as service.ts.)
22
+ */
23
+ export declare const CONTROL_MCP_ID = "hovercontrol";
24
+ /** The hard-sandbox allow prefix for the control server — pass in
25
+ * `runSession({ allowedToolsExtra: [CONTROL_MCP_TOOL_PREFIX] })`. */
26
+ export declare const CONTROL_MCP_TOOL_PREFIX: string;
27
+ export interface GroundedMcpOptions {
28
+ /** CDP endpoint of the debug Chrome the agent drives. */
29
+ cdpUrl: string;
30
+ /** Port used only to namespace the generated config filename. */
31
+ port: number;
32
+ /** Project root — where the control server resolves relative paths / writes
33
+ * its placeholder upload fixture, and where `@playwright/mcp` resolves from. */
34
+ devRoot: string;
35
+ /** The dev-server origin the run targets (defaults to `cdpUrl`). */
36
+ devUrl?: string;
37
+ /** WS port the control server's back-channel (`ask_user` / `record_*`)
38
+ * connects to. Omit when there's no listener — actuation still works; the
39
+ * back-channel features silently no-op (actuateServer fails soft). */
40
+ approvalPort?: number;
41
+ /** Directory the Playwright MCP + control screenshots write into. */
42
+ outputDir?: string;
43
+ }
44
+ /**
45
+ * Build an MCP config mirroring the service's grounded/normal mode: the
46
+ * Playwright MCP over CDP PLUS the hover-control actuation server. Pair it in
47
+ * `runSession` with `allowedToolsExtra: [CONTROL_MCP_TOOL_PREFIX]`,
48
+ * `disallowedToolsExtra: GROUNDED_ACTUATION_DENY`, and append
49
+ * `GROUNDED_ACTUATION_DIRECTIVE` to the system prompt — then the selectors the
50
+ * agent actuates with are the ones crystallized (record==replay).
51
+ */
52
+ export declare function buildGroundedMcpConfig(opts: GroundedMcpOptions): string;
53
+ //# sourceMappingURL=engine.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"AAmBA,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,YAAY,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAC3E,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACjD,YAAY,EAAE,gBAAgB,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACzF,YAAY,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAErD,OAAO,EAAE,mBAAmB,EAAE,YAAY,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AACjH,YAAY,EAAE,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAGzG,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,kCAAkC,CAAC;AACnF,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AACrG,YAAY,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAGhF,OAAO,EAAE,uBAAuB,EAAE,4BAA4B,EAAE,MAAM,sBAAsB,CAAC;AAG7F,OAAO,EAAE,UAAU,EAAE,qBAAqB,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AACrG,YAAY,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAM/D,OAAO,EAAE,eAAe,EAAE,wBAAwB,EAAE,MAAM,sBAAsB,CAAC;AACjF,OAAO,EAAE,YAAY,EAAE,oBAAoB,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACzG,YAAY,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEtE;;;;;GAKG;AACH,eAAO,MAAM,cAAc,iBAAiB,CAAC;AAE7C;sEACsE;AACtE,eAAO,MAAM,uBAAuB,QAAgC,CAAC;AAMrE,MAAM,WAAW,kBAAkB;IACjC,yDAAyD;IACzD,MAAM,EAAE,MAAM,CAAC;IACf,iEAAiE;IACjE,IAAI,EAAE,MAAM,CAAC;IACb;qFACiF;IACjF,OAAO,EAAE,MAAM,CAAC;IAChB,oEAAoE;IACpE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;2EAEuE;IACvE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,qEAAqE;IACrE,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,kBAAkB,GAAG,MAAM,CAqBvE"}
package/dist/engine.js ADDED
@@ -0,0 +1,78 @@
1
+ /**
2
+ * In-process engine surface for non-service consumers — the `hover` CLI and the
3
+ * `@hover-dev/mcp` server. The VS Code extension drives core through the WS
4
+ * service (`./service`); a standalone process instead calls `runSession`
5
+ * directly. This barrel exposes the same building blocks the service composes —
6
+ * the session run, the GROUNDED MCP config (so a CLI-authored spec gets the
7
+ * record==replay fidelity the extension does), and spec crystallization —
8
+ * without the WebSocket layer.
9
+ *
10
+ * Keep this a thin re-export + the one `buildGroundedMcpConfig` helper. The
11
+ * grounded path here MUST stay in lock-step with `service.ts`'s normal-mode
12
+ * assembly (the control-actuation server + `GROUNDED_ACTUATION_DENY` /
13
+ * `GROUNDED_ACTUATION_DIRECTIVE`), or CLI specs would drift from extension specs.
14
+ */
15
+ import { resolve, dirname } from 'node:path';
16
+ import { fileURLToPath } from 'node:url';
17
+ import { resolveMcpConfig, mcpToolPrefix } from './playwright/resolveMcpConfig.js';
18
+ // ── in-process session + crystallization ─────────────────────────────────────
19
+ export { runSession } from './runSession.js';
20
+ export { writeSpec } from './specs/writeSpec.js';
21
+ // Creation-verification: replay a flow's grounded steps over CDP (no playwright test).
22
+ export { replayGroundedSteps, replayOnPage, applyGroundedStep, groundedLocate } from './specs/replayGrounded.js';
23
+ // ── browser / MCP plumbing ───────────────────────────────────────────────────
24
+ export { resolveMcpConfig, mcpToolPrefix } from './playwright/resolveMcpConfig.js';
25
+ export { launchDebugChrome, closeDebugChrome, findChromeBinary } from './playwright/launchChrome.js';
26
+ // ── grounded-actuation knobs (must match service.ts) ─────────────────────────
27
+ export { GROUNDED_ACTUATION_DENY, GROUNDED_ACTUATION_DIRECTIVE } from './agentDirectives.js';
28
+ // ── business memory (ask → remember loop; shared with the extension's QA mode) ─
29
+ export { loadMemory, formatMemoryForPrompt, writeFact, memoryDir } from './memory/businessMemory.js';
30
+ // ── autonomous-exploration directives (QA mode reuse) ────────────────────────
31
+ // The CLI's "explore → discover business flows → record_candidate" loop is the
32
+ // same mechanism QA mode drives the agent with; reuse the directives verbatim
33
+ // so behaviour stays in lock-step.
34
+ export { RECON_DIRECTIVE, QA_EXPLORATION_DIRECTIVE } from './agentDirectives.js';
35
+ export { QA_INTENSITY, DEFAULT_QA_INTENSITY, asQaIntensity, qaBudgetDirective } from './qa/intensity.js';
36
+ /**
37
+ * The always-on control-actuation MCP server id. Alphanumeric on purpose — a
38
+ * hyphen would make the hard-sandbox allow prefix (`mcp__hovercontrol`) fail to
39
+ * match the tool names (`mcp__hovercontrol__click_control`), so every grounded
40
+ * actuation would be denied. (Same constant + reasoning as service.ts.)
41
+ */
42
+ export const CONTROL_MCP_ID = 'hovercontrol';
43
+ /** The hard-sandbox allow prefix for the control server — pass in
44
+ * `runSession({ allowedToolsExtra: [CONTROL_MCP_TOOL_PREFIX] })`. */
45
+ export const CONTROL_MCP_TOOL_PREFIX = mcpToolPrefix(CONTROL_MCP_ID);
46
+ // dist/engine.js sits beside dist/service.js, so mcp/actuateServer.js resolves
47
+ // the same way it does from service.ts.
48
+ const CONTROL_MCP_SCRIPT = resolve(dirname(fileURLToPath(import.meta.url)), 'mcp', 'actuateServer.js');
49
+ /**
50
+ * Build an MCP config mirroring the service's grounded/normal mode: the
51
+ * Playwright MCP over CDP PLUS the hover-control actuation server. Pair it in
52
+ * `runSession` with `allowedToolsExtra: [CONTROL_MCP_TOOL_PREFIX]`,
53
+ * `disallowedToolsExtra: GROUNDED_ACTUATION_DENY`, and append
54
+ * `GROUNDED_ACTUATION_DIRECTIVE` to the system prompt — then the selectors the
55
+ * agent actuates with are the ones crystallized (record==replay).
56
+ */
57
+ export function buildGroundedMcpConfig(opts) {
58
+ return resolveMcpConfig({
59
+ cdpUrl: opts.cdpUrl,
60
+ port: opts.port,
61
+ cwd: opts.devRoot,
62
+ outputDir: opts.outputDir,
63
+ extra: [
64
+ {
65
+ id: CONTROL_MCP_ID,
66
+ command: process.execPath,
67
+ args: [CONTROL_MCP_SCRIPT],
68
+ env: {
69
+ HOVER_CDP_URL: opts.cdpUrl,
70
+ HOVER_DEV_URL: opts.devUrl ?? opts.cdpUrl,
71
+ HOVER_PROJECT_ROOT: opts.devRoot,
72
+ ...(opts.approvalPort ? { HOVER_APPROVAL_PORT: String(opts.approvalPort) } : {}),
73
+ ...(opts.outputDir ? { HOVER_SHOT_DIR: opts.outputDir } : {}),
74
+ },
75
+ },
76
+ ],
77
+ });
78
+ }
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=actuateServer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"actuateServer.d.ts","sourceRoot":"","sources":["../../src/mcp/actuateServer.ts"],"names":[],"mappings":""}