@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.
Files changed (181) 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/engine.d.ts +28 -0
  6. package/dist/engine.d.ts.map +1 -0
  7. package/dist/engine.js +27 -0
  8. package/dist/memory/businessMemory.d.ts +29 -0
  9. package/dist/memory/businessMemory.d.ts.map +1 -0
  10. package/dist/memory/businessMemory.js +125 -0
  11. package/dist/playwright/launchChrome.d.ts +18 -0
  12. package/dist/playwright/launchChrome.d.ts.map +1 -1
  13. package/dist/playwright/launchChrome.js +46 -3
  14. package/dist/qa/candidates.d.ts +32 -0
  15. package/dist/qa/candidates.d.ts.map +1 -0
  16. package/dist/qa/candidates.js +20 -0
  17. package/dist/qa/intensity.d.ts +33 -0
  18. package/dist/qa/intensity.d.ts.map +1 -0
  19. package/dist/qa/intensity.js +25 -0
  20. package/dist/qa/qaReport.d.ts +19 -0
  21. package/dist/qa/qaReport.d.ts.map +1 -0
  22. package/dist/qa/qaReport.js +50 -0
  23. package/dist/sessions/sessions.d.ts +125 -0
  24. package/dist/sessions/sessions.d.ts.map +1 -0
  25. package/dist/sessions/sessions.js +175 -0
  26. package/dist/specs/authFixture.d.ts +30 -0
  27. package/dist/specs/authFixture.d.ts.map +1 -0
  28. package/dist/specs/authFixture.js +145 -0
  29. package/dist/specs/detectSharedFlows.d.ts +1 -1
  30. package/dist/specs/detectSharedFlows.d.ts.map +1 -1
  31. package/dist/specs/detectSharedFlows.js +20 -21
  32. package/dist/specs/generatePageObject.d.ts +1 -1
  33. package/dist/specs/generatePageObject.d.ts.map +1 -1
  34. package/dist/specs/healPrompt.d.ts +19 -0
  35. package/dist/specs/healPrompt.d.ts.map +1 -0
  36. package/dist/specs/healPrompt.js +48 -0
  37. package/dist/specs/humanSteps.d.ts +4 -8
  38. package/dist/specs/humanSteps.d.ts.map +1 -1
  39. package/dist/specs/humanSteps.js +6 -1
  40. package/dist/specs/optimizeSpec.d.ts +15 -8
  41. package/dist/specs/optimizeSpec.d.ts.map +1 -1
  42. package/dist/specs/optimizeSpec.js +71 -41
  43. package/dist/specs/pageObjectManifest.d.ts +3 -1
  44. package/dist/specs/pageObjectManifest.d.ts.map +1 -1
  45. package/dist/specs/pageObjectManifest.js +24 -19
  46. package/dist/specs/replayGrounded.d.ts +45 -0
  47. package/dist/specs/replayGrounded.d.ts.map +1 -0
  48. package/dist/specs/replayGrounded.js +155 -0
  49. package/dist/specs/runFailures.d.ts +34 -0
  50. package/dist/specs/runFailures.d.ts.map +1 -0
  51. package/dist/specs/runFailures.js +93 -0
  52. package/dist/specs/seeds.d.ts +16 -15
  53. package/dist/specs/seeds.d.ts.map +1 -1
  54. package/dist/specs/seeds.js +86 -54
  55. package/dist/specs/sidecar.d.ts +34 -6
  56. package/dist/specs/sidecar.d.ts.map +1 -1
  57. package/dist/specs/sidecar.js +79 -9
  58. package/dist/specs/specStep.d.ts +21 -0
  59. package/dist/specs/specStep.d.ts.map +1 -0
  60. package/dist/specs/specStep.js +1 -0
  61. package/dist/specs/text.d.ts +8 -6
  62. package/dist/specs/text.d.ts.map +1 -1
  63. package/dist/specs/text.js +10 -7
  64. package/dist/specs/writeSpec.d.ts +62 -1
  65. package/dist/specs/writeSpec.d.ts.map +1 -1
  66. package/dist/specs/writeSpec.js +596 -21
  67. package/package.json +9 -29
  68. package/dist/agents/aider.d.ts +0 -16
  69. package/dist/agents/aider.d.ts.map +0 -1
  70. package/dist/agents/aider.js +0 -161
  71. package/dist/agents/argv.d.ts +0 -11
  72. package/dist/agents/argv.d.ts.map +0 -1
  73. package/dist/agents/argv.js +0 -23
  74. package/dist/agents/claude.d.ts +0 -3
  75. package/dist/agents/claude.d.ts.map +0 -1
  76. package/dist/agents/claude.js +0 -195
  77. package/dist/agents/codex.d.ts +0 -19
  78. package/dist/agents/codex.d.ts.map +0 -1
  79. package/dist/agents/codex.js +0 -216
  80. package/dist/agents/cursor.d.ts +0 -18
  81. package/dist/agents/cursor.d.ts.map +0 -1
  82. package/dist/agents/cursor.js +0 -220
  83. package/dist/agents/detect.d.ts +0 -46
  84. package/dist/agents/detect.d.ts.map +0 -1
  85. package/dist/agents/detect.js +0 -80
  86. package/dist/agents/gemini.d.ts +0 -17
  87. package/dist/agents/gemini.d.ts.map +0 -1
  88. package/dist/agents/gemini.js +0 -186
  89. package/dist/agents/index.d.ts +0 -6
  90. package/dist/agents/index.d.ts.map +0 -1
  91. package/dist/agents/index.js +0 -5
  92. package/dist/agents/invoke.d.ts +0 -12
  93. package/dist/agents/invoke.d.ts.map +0 -1
  94. package/dist/agents/invoke.js +0 -96
  95. package/dist/agents/qwen.d.ts +0 -17
  96. package/dist/agents/qwen.d.ts.map +0 -1
  97. package/dist/agents/qwen.js +0 -172
  98. package/dist/agents/registry.d.ts +0 -19
  99. package/dist/agents/registry.d.ts.map +0 -1
  100. package/dist/agents/registry.js +0 -34
  101. package/dist/agents/shared.d.ts +0 -28
  102. package/dist/agents/shared.d.ts.map +0 -1
  103. package/dist/agents/shared.js +0 -35
  104. package/dist/agents/types.d.ts +0 -186
  105. package/dist/agents/types.d.ts.map +0 -1
  106. package/dist/agents/types.js +0 -23
  107. package/dist/index.d.ts +0 -3
  108. package/dist/index.d.ts.map +0 -1
  109. package/dist/index.js +0 -2
  110. package/dist/mcp/sourceFence.d.ts +0 -23
  111. package/dist/mcp/sourceFence.d.ts.map +0 -1
  112. package/dist/mcp/sourceFence.js +0 -75
  113. package/dist/mcp/sourceServer.d.ts +0 -3
  114. package/dist/mcp/sourceServer.d.ts.map +0 -1
  115. package/dist/mcp/sourceServer.js +0 -116
  116. package/dist/playwright/cdpStatus.d.ts +0 -29
  117. package/dist/playwright/cdpStatus.d.ts.map +0 -1
  118. package/dist/playwright/cdpStatus.js +0 -119
  119. package/dist/playwright/preflight.d.ts +0 -31
  120. package/dist/playwright/preflight.d.ts.map +0 -1
  121. package/dist/playwright/preflight.js +0 -82
  122. package/dist/playwright/preflightCache.d.ts +0 -27
  123. package/dist/playwright/preflightCache.d.ts.map +0 -1
  124. package/dist/playwright/preflightCache.js +0 -21
  125. package/dist/playwright/raiseWindow.d.ts +0 -10
  126. package/dist/playwright/raiseWindow.d.ts.map +0 -1
  127. package/dist/playwright/raiseWindow.js +0 -158
  128. package/dist/playwright/resolveMcpConfig.d.ts +0 -55
  129. package/dist/playwright/resolveMcpConfig.d.ts.map +0 -1
  130. package/dist/playwright/resolveMcpConfig.js +0 -66
  131. package/dist/plugin-api.d.ts +0 -235
  132. package/dist/plugin-api.d.ts.map +0 -1
  133. package/dist/plugin-api.js +0 -52
  134. package/dist/runSession.d.ts +0 -42
  135. package/dist/runSession.d.ts.map +0 -1
  136. package/dist/runSession.js +0 -81
  137. package/dist/scripts/bench-multi-tab.d.ts +0 -2
  138. package/dist/scripts/bench-multi-tab.d.ts.map +0 -1
  139. package/dist/scripts/bench-multi-tab.js +0 -192
  140. package/dist/scripts/bench-ttfb.d.ts +0 -2
  141. package/dist/scripts/bench-ttfb.d.ts.map +0 -1
  142. package/dist/scripts/bench-ttfb.js +0 -127
  143. package/dist/scripts/start-chrome.d.ts +0 -3
  144. package/dist/scripts/start-chrome.d.ts.map +0 -1
  145. package/dist/scripts/start-chrome.js +0 -23
  146. package/dist/service/cdpHandlers.d.ts +0 -44
  147. package/dist/service/cdpHandlers.d.ts.map +0 -1
  148. package/dist/service/cdpHandlers.js +0 -85
  149. package/dist/service/cdpHint.d.ts +0 -48
  150. package/dist/service/cdpHint.d.ts.map +0 -1
  151. package/dist/service/cdpHint.js +0 -216
  152. package/dist/service/conventions.d.ts +0 -8
  153. package/dist/service/conventions.d.ts.map +0 -1
  154. package/dist/service/conventions.js +0 -42
  155. package/dist/service/saveHandlers.d.ts +0 -52
  156. package/dist/service/saveHandlers.d.ts.map +0 -1
  157. package/dist/service/saveHandlers.js +0 -75
  158. package/dist/service/types.d.ts +0 -58
  159. package/dist/service/types.d.ts.map +0 -1
  160. package/dist/service/types.js +0 -26
  161. package/dist/service.d.ts +0 -50
  162. package/dist/service.d.ts.map +0 -1
  163. package/dist/service.js +0 -1065
  164. package/dist/skills/writeSkill.d.ts +0 -27
  165. package/dist/skills/writeSkill.d.ts.map +0 -1
  166. package/dist/skills/writeSkill.js +0 -13
  167. package/dist/specs/extractPageObjects.d.ts +0 -18
  168. package/dist/specs/extractPageObjects.d.ts.map +0 -1
  169. package/dist/specs/extractPageObjects.js +0 -98
  170. package/dist/specs/listSpecs.d.ts +0 -52
  171. package/dist/specs/listSpecs.d.ts.map +0 -1
  172. package/dist/specs/listSpecs.js +0 -139
  173. package/dist/specs/optimizationSuggestion.d.ts +0 -26
  174. package/dist/specs/optimizationSuggestion.d.ts.map +0 -1
  175. package/dist/specs/optimizationSuggestion.js +0 -28
  176. package/dist/specs/optimizeSpecWithAgent.d.ts +0 -11
  177. package/dist/specs/optimizeSpecWithAgent.d.ts.map +0 -1
  178. package/dist/specs/optimizeSpecWithAgent.js +0 -40
  179. package/dist/specs/writeCaseCsv.d.ts +0 -28
  180. package/dist/specs/writeCaseCsv.d.ts.map +0 -1
  181. package/dist/specs/writeCaseCsv.js +0 -134
package/README.md CHANGED
@@ -4,7 +4,6 @@ The local Node service. Owns:
4
4
 
5
5
  - **Agent invocation** (`src/agents/`) — Local CLI Agent First. Spawns `claude` / `codex` / `cursor-agent` / `aider` / `gemini-cli` / `qwen-code` and normalizes their output into a single `InvokeEvent` stream.
6
6
  - **Playwright preflight** (`src/playwright/`) — verifies CDP connection to the user's Chrome.
7
- - **Smoke test** (`src/smoke.ts`) — end-to-end verification of the whole chain.
8
7
 
9
8
  ## Architecture (agents/)
10
9
 
@@ -41,7 +40,7 @@ per-save path** (reproducible by construction):
41
40
  runnable around it. `countOptimizableMarkers()` reads the count back; `listSpecs`
42
41
  surfaces it as `SpecSummary.optimizableCount`.
43
42
  - Alongside the `.spec.ts`, a **sidecar** is written to
44
- `.hover/<slug>.json` — the structured `SpecStep[]` + observed signals. This is
43
+ `.hover/sidecars/<slug>.json` — the structured `SpecStep[]` + observed signals. This is
45
44
  the machine-readable behavior record the optimization pass reads (it keeps the
46
45
  spec itself clean).
47
46
 
@@ -52,33 +51,32 @@ The **service** (never the sandboxed browser agent) can optionally run an LLM
52
51
  feedback the session observed. Its input is data the service already holds (the
53
52
  draft + sidecar + relevant seeds), not live page content, so it sits outside the
54
53
  agent's prompt-injection surface and needs no filesystem access. It writes an
55
- **optimization candidate** to `.hover/optimized/<slug>.spec.ts.draft` (never
54
+ **optimization candidate** to `.hover/cache/optimized/<slug>.spec.ts.draft` (never
56
55
  `*.spec.ts`, so the test runner can't collect an unreviewed candidate). A human
57
56
  promotes or discards it via diff — **the deterministic original is always
58
57
  preserved**. Off by default.
59
58
 
60
- ### Seed library — extending translation (`.hover/rules/`)
59
+ ### Seed library — translation patterns (`BUILTIN_SEEDS`)
61
60
 
62
- The optimization pass generalizes from **seeds**: human-written worked examples
63
- of "captured steps → the Playwright code they should produce." This is how
64
- coverage of new multi-step patterns grows **without core changes** — you (or the
65
- community) drop a JSON file; the pass picks it up as few-shot.
61
+ The optimization pass generalizes from **seeds**: worked examples of "captured
62
+ steps → the Playwright code they should produce", fed to the pass as few-shot.
63
+ They ship inlined as the `BUILTIN_SEEDS` constant in
64
+ [`src/specs/seeds.ts`](src/specs/seeds.ts) (`download`, `file-upload`, `dialog`,
65
+ `network-gated-assertion`, `oauth-popup`); a seed is a `signature` (tool names)
66
+ + a concrete `example` (`steps` → `code`):
66
67
 
67
- A seed lives at `<projectRoot>/.hover/rules/<name>.json` and matches
68
- [`src/specs/seed.schema.json`](src/specs/seed.schema.json):
69
-
70
- ```json
68
+ ```ts
71
69
  {
72
- "name": "oauth-popup",
73
- "signature": ["browser_click", "browser_tabs:select"],
74
- "note": "sign in through a provider popup that opens a new tab",
75
- "example": {
76
- "steps": [
77
- { "tool": "browser_click", "element": "Sign in with Google button" },
78
- { "tool": "browser_tabs", "action": "select", "idx": 1 }
70
+ name: 'oauth-popup',
71
+ signature: ['browser_click', 'browser_tabs:select'],
72
+ note: 'sign in through a provider popup that opens a new tab',
73
+ example: {
74
+ steps: [
75
+ { tool: 'browser_click', element: 'Sign in with Google button' },
76
+ { tool: 'browser_tabs', action: 'select', idx: 1 },
79
77
  ],
80
- "code": "const [popup] = await Promise.all([\n context.waitForEvent('page'),\n page.getByRole('button', { name: 'Sign in with Google' }).click(),\n]);\nawait popup.getByLabel('Email').fill('user@example.com');"
81
- }
78
+ code: `const [popup] = await Promise.all([ ... ]);`,
79
+ },
82
80
  }
83
81
  ```
84
82
 
@@ -87,43 +85,16 @@ A seed lives at `<projectRoot>/.hover/rules/<name>.json` and matches
87
85
  in the spec being optimized. It is **not** exact-matched.
88
86
  - **`code`** must obey the same rules as generated specs: semantic selectors, no
89
87
  XPath, no `waitForTimeout`.
90
- - **Built-in seeds** ship in `src/specs/seeds.ts` (`BUILTIN_SEEDS`). The bar to
91
- be built-in is high only **highly certain**, app-agnostic, deterministic
92
- patterns qualify (currently just `download`). Semantic / judgement-based
93
- optimizations (e.g. *which* feedback text to assert) are not seeds — they're
94
- standing instructions in the prompt. Popup is hardcoded in `writeSpec.ts`, not
95
- a seed. Speculative or project-specific patterns belong in your own
96
- `.hover/rules/`, where the bar is your call.
97
-
98
- `readSeeds(projectRoot)` returns built-ins + your `.hover/rules/*.json`
99
- (malformed files are skipped, not fatal).
100
-
101
- ## Smoke test
102
-
103
- ```bash
104
- # Terminal 1: run basic-app — also spawns a debug Chrome (the example sets
105
- # `autoLaunchChrome: true`) navigated to http://localhost:5173
106
- pnpm dev:example:basic-app # from repo root
107
-
108
- # Terminal 2: run the smoke test
109
- pnpm smoke # from repo root, defaults to http://localhost:5173
110
- ```
111
-
112
- Need the debug Chrome standalone (no example)? `pnpm smoke:chrome` (or `pnpm exec hover-chrome`).
113
-
114
- Override the target or prompt:
115
-
116
- ```bash
117
- pnpm smoke http://localhost:5173/ "log in then add a todo named 'verify hover'"
118
- ```
88
+ - Adding a pattern = appending a `SeedRule` to `BUILTIN_SEEDS`. There is no
89
+ user-authored-seed file mechanism: the catalogue is curated and ships with
90
+ Hover. Semantic / judgement-based optimizations (e.g. *which* feedback text to
91
+ assert) are not seeds — they're standing instructions in the prompt.
119
92
 
120
- Environment variables:
93
+ ## Exercising the engine
121
94
 
122
- - `HOVER_CDP` CDP URL (default `http://localhost:9222`)
123
- - `HOVER_AGENT` — agent id (omit to auto-detect; tries the user's stated preference, then the first installed agent in registry order — `claude` → `codex` → `cursor-agent` → `aider` → `gemini-cli` → `qwen-code` today)
124
- - `HOVER_MODEL` — model for the agent (default `sonnet`, much cheaper than opus)
95
+ There is no standalone CLI smoke loop — the engine is driven by the VS Code extension. Build + sideload it (`pnpm --filter hover-dev package`), open the Hover chat, and drive any dev server. The extension spawns the isolated debug Chrome on demand and connects to the engine over WS (ports 51789+). Env knobs the engine reads: `HOVER_CDP` (CDP URL, default `http://localhost:9222`), `HOVER_AGENT` (agent id; omit to auto-detect), `HOVER_MODEL` (default `sonnet`).
125
96
 
126
- ## Sandboxing (what the smoke test enforces)
97
+ ## Sandboxing
127
98
 
128
99
  The `claude -p` invocation is locked down so Claude can only drive the browser:
129
100
 
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Agent system-prompt directives — the static prose fragments appended to the
3
+ * coding agent's system prompt to shape how it drives the browser and writes its
4
+ * report. Extracted from service.ts (which assembles them per run) so the prompt
5
+ * engineering lives in one readable place, separate from the run orchestration.
6
+ *
7
+ * Which directives apply when:
8
+ * - ZH_OUTPUT — only when the user's prompt contains CJK.
9
+ * - REPORTING / NARRATION / ASK_FORMAT / EXPLORATION_CHECKPOINT — all modes.
10
+ * - GROUNDED_ACTUATION (directive + DENY list) — modes whose ModeBehavior has
11
+ * groundedActuation = true (Flow, QA). See ./modes.ts.
12
+ */
13
+ /** CJK-presence test — mirrors voice.js's detectLanguage. Any Han character
14
+ * in the prompt flips the agent's prose output to Chinese. */
15
+ export declare const CJK_RE: RegExp;
16
+ /** Appended to the agent's system prompt when the user's prompt contains CJK,
17
+ * so the human-facing prose (verification summary / ## Findings / step
18
+ * narration) comes back in Chinese — matching how Voice mode picks a Chinese
19
+ * TTS voice for the same prompt. Deliberately scoped to PROSE only: the agent
20
+ * must still use the page's real (often English) accessible names, labels,
21
+ * and selectors when driving the browser. */
22
+ export declare const ZH_OUTPUT_DIRECTIVE: string;
23
+ /**
24
+ * Grounded-actuation deny list. The Playwright MCP interaction tools take a
25
+ * free-form `element` description that doesn't round-trip to a replayable
26
+ * selector (it gets crystallized as a confabulated getByText). So in
27
+ * grounded-actuation modes we DENY them and route every interaction through the
28
+ * Hover control MCP, whose role+name/testId/text args come straight from the
29
+ * snapshot and crystallize 1:1. (Plugin modes keep the Playwright tools — they
30
+ * explore to capture traffic, not to crystallize browser steps.)
31
+ */
32
+ export declare const GROUNDED_ACTUATION_DENY: string[];
33
+ export declare const REPORTING_DIRECTIVE: string;
34
+ export declare const NARRATION_DIRECTIVE: string;
35
+ export declare const ASK_FORMAT_DIRECTIVE: string;
36
+ export declare const EXPLORATION_CHECKPOINT_DIRECTIVE: string;
37
+ /** State-reset recon (debt-2 reproducible-state-isolation). Appended for grounded
38
+ * modes ONLY when the extension explicitly requests it (run payload `reconReset`)
39
+ * — recon clears client state, which would wipe a logged-in session, so it never
40
+ * runs unsolicited and never on a plain Flow recording. The agent discovers +
41
+ * validates the reset recipe ONCE, then reports it via record_reset_recipe for
42
+ * the engine to forward to the environment store. */
43
+ export declare const RECON_DIRECTIVE: string;
44
+ /** QA Testing mode — appended on top of the grounded-actuation directive. Turns
45
+ * a directed run into autonomous exploratory testing. (Behavioral effect needs
46
+ * live verification; the wiring just appends this when mode === 'qa'.) */
47
+ export declare const QA_EXPLORATION_DIRECTIVE: string;
48
+ /** Appended to the FIRST (functional verify) pass when a penetration-testing
49
+ * pass is queued to run right after it. Keeps the two passes from both doing
50
+ * security work: the verify pass stays functional-only, all security/vuln work
51
+ * is deferred to the dedicated pentest pass. (Only added when QA has pentest on
52
+ * AND this is the pre-pentest verify phase — see service.ts `splitting`.) */
53
+ export declare const QA_VERIFY_DEFER_SECURITY_DIRECTIVE: string;
54
+ export declare const GROUNDED_ACTUATION_DIRECTIVE: string;
55
+ //# sourceMappingURL=agentDirectives.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agentDirectives.d.ts","sourceRoot":"","sources":["../src/agentDirectives.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH;+DAC+D;AAC/D,eAAO,MAAM,MAAM,QAAU,CAAC;AAE9B;;;;;8CAK8C;AAC9C,eAAO,MAAM,mBAAmB,QAOM,CAAC;AAEvC;;;;;;;;GAQG;AACH,eAAO,MAAM,uBAAuB,UAgBnC,CAAC;AAEF,eAAO,MAAM,mBAAmB,QA0BI,CAAC;AAErC,eAAO,MAAM,mBAAmB,QAK2B,CAAC;AAE5D,eAAO,MAAM,oBAAoB,QAc6B,CAAC;AAE/D,eAAO,MAAM,gCAAgC,QAqB8B,CAAC;AAE5E;;;;;sDAKsD;AACtD,eAAO,MAAM,eAAe,QAcL,CAAC;AAExB;;2EAE2E;AAC3E,eAAO,MAAM,wBAAwB,QAoDmB,CAAC;AAEzD;;;;8EAI8E;AAC9E,eAAO,MAAM,kCAAkC,QASD,CAAC;AAE/C,eAAO,MAAM,4BAA4B,QAmE2C,CAAC"}
@@ -0,0 +1,276 @@
1
+ /**
2
+ * Agent system-prompt directives — the static prose fragments appended to the
3
+ * coding agent's system prompt to shape how it drives the browser and writes its
4
+ * report. Extracted from service.ts (which assembles them per run) so the prompt
5
+ * engineering lives in one readable place, separate from the run orchestration.
6
+ *
7
+ * Which directives apply when:
8
+ * - ZH_OUTPUT — only when the user's prompt contains CJK.
9
+ * - REPORTING / NARRATION / ASK_FORMAT / EXPLORATION_CHECKPOINT — all modes.
10
+ * - GROUNDED_ACTUATION (directive + DENY list) — modes whose ModeBehavior has
11
+ * groundedActuation = true (Flow, QA). See ./modes.ts.
12
+ */
13
+ /** CJK-presence test — mirrors voice.js's detectLanguage. Any Han character
14
+ * in the prompt flips the agent's prose output to Chinese. */
15
+ export const CJK_RE = /[一-鿿]/;
16
+ /** Appended to the agent's system prompt when the user's prompt contains CJK,
17
+ * so the human-facing prose (verification summary / ## Findings / step
18
+ * narration) comes back in Chinese — matching how Voice mode picks a Chinese
19
+ * TTS voice for the same prompt. Deliberately scoped to PROSE only: the agent
20
+ * must still use the page's real (often English) accessible names, labels,
21
+ * and selectors when driving the browser. */
22
+ export const ZH_OUTPUT_DIRECTIVE = '用户使用中文下达指令。请用简体中文撰写【所有】面向用户的文字:最终报告的概述、' +
23
+ '`## Findings` 里的每一条,以及过程中每一步的简短说明。' +
24
+ '这一点【贯穿整个过程,不分顺利与否】:当你在排查、卡住、改变思路、或自言自语式地推理时' +
25
+ '(例如“让我先找一下…”“这个按钮点不到,换个方式”),也必须用中文,绝不要中途切回英文。' +
26
+ '注意:这只影响写给用户看的叙述文字。操作浏览器时仍要使用页面真实的(通常是英文的)' +
27
+ '角色名、标签、可访问名称和选择器——不要翻译成中文;严重级别标记' +
28
+ '(high / medium / low / info)也保持英文。';
29
+ /**
30
+ * Grounded-actuation deny list. The Playwright MCP interaction tools take a
31
+ * free-form `element` description that doesn't round-trip to a replayable
32
+ * selector (it gets crystallized as a confabulated getByText). So in
33
+ * grounded-actuation modes we DENY them and route every interaction through the
34
+ * Hover control MCP, whose role+name/testId/text args come straight from the
35
+ * snapshot and crystallize 1:1. (Plugin modes keep the Playwright tools — they
36
+ * explore to capture traffic, not to crystallize browser steps.)
37
+ */
38
+ export const GROUNDED_ACTUATION_DENY = [
39
+ 'mcp__playwright__browser_click',
40
+ 'mcp__playwright__browser_type',
41
+ 'mcp__playwright__browser_fill_form',
42
+ 'mcp__playwright__browser_select_option',
43
+ // Uploads go through mcp__hovercontrol__upload_file (which crystallizes to a
44
+ // real filechooser + setFiles); Playwright's browser_file_upload would only
45
+ // leave an untranslatable optimizable marker.
46
+ 'mcp__playwright__browser_file_upload',
47
+ // Screenshots go through mcp__hovercontrol__take_screenshot (viewport only).
48
+ // Playwright's browser_take_screenshot does a fullPage capture by RESIZING the
49
+ // live window, which fires a window 'resize' the app may react to (lost
50
+ // transient UI state — e.g. a flipped card snapping back), so the agent never
51
+ // sees the result of its own action. Deny it; the viewport tool has no such
52
+ // side effect.
53
+ 'mcp__playwright__browser_take_screenshot',
54
+ ];
55
+ export const REPORTING_DIRECTIVE = 'YOUR REPORT IS ABOUT THE APP, NOT THE TOOLING. The final summary and any ' +
56
+ '## Findings are for the developer of the app under test — write them in plain ' +
57
+ 'product terms about what the APP did: which user flows worked, and real ' +
58
+ 'defects only (wrong validation, broken navigation, lost data, a genuinely ' +
59
+ 'confusing UX). NEVER mention how you drove the page or any Hover/Playwright ' +
60
+ 'mechanics: no tool names (click_control, check_control, getByRole, ' +
61
+ 'browser_snapshot, upload_file, …), no selectors, no "strict mode", "grounded", ' +
62
+ '"display:none", "filechooser", "tab index", and no internal file names. ' +
63
+ 'Trouble OPERATING a control (a hidden input, a label repeated across groups, a ' +
64
+ 'lingering dialog, any tool quirk) is YOUR technique to work out — do it ' +
65
+ 'silently; it is NOT an app bug and must never appear as a finding. NEVER ' +
66
+ 'propose changes to Hover or its tools, and do not narrate your own environment, ' +
67
+ 'capabilities, or memory. Report only what a user of the app would care about.\n\n' +
68
+ 'WRITE YOUR FINAL REPORT AS PLAIN MARKDOWN — NOT JSON, and NOT wrapped in any ' +
69
+ 'fenced code block. Structure it exactly so:\n' +
70
+ '• ONE short outcome sentence on the first line.\n' +
71
+ '• Then a blank line, then concise `- ` bullets for the key things you checked ' +
72
+ '(one per step / area / flow). Never cram it all into one paragraph.\n' +
73
+ '• ONLY if you found real defects, add a line `## Findings` followed by one ' +
74
+ '`- ` bullet per defect, each written as `- **severity** — what happened and why ' +
75
+ 'it matters` (severity = high / medium / low / info; name the endpoint + method ' +
76
+ 'inline when the defect is about a specific API call). No real defects → omit ' +
77
+ 'the Findings section entirely.\n' +
78
+ 'Use real line breaks (a literal newline, NEVER the characters backslash-n). ' +
79
+ 'Do not output JSON, a "summary"/"findings" object, or any ```fenced``` wrapper — ' +
80
+ 'just the Markdown report itself.';
81
+ export const NARRATION_DIRECTIVE = 'NARRATION — As you work, keep each interim status to ONE short present-tense ' +
82
+ 'line stating your immediate intent before you act ("Filling the address ' +
83
+ 'fields", "Now testing an underage date of birth"). Do not write paragraphs ' +
84
+ 'between actions and do not restate what just happened — the steps are already ' +
85
+ 'shown. Save the full wrap-up for the final report only.';
86
+ export const ASK_FORMAT_DIRECTIVE = 'OFFERING CHOICES — if the user\'s request is NOT a concrete instruction you ' +
87
+ 'can act on (a concrete instruction looks like "test the login flow", "log ' +
88
+ 'in", "register an account", "complete checkout", "run the X flow", "fill the ' +
89
+ 'form") — i.e. it is vague, conversational, or just asks you to ask (e.g. ' +
90
+ '"ask me a question", "what can you do", "test this") — do NOT reply with an ' +
91
+ 'open-ended question like "what would you like me to test?". Instead, LOOK at ' +
92
+ 'the current page first, then PROPOSE 2-4 concrete things you could test on ' +
93
+ 'THIS page. Whenever you offer the user a choice, write the question as a ' +
94
+ 'normal sentence, then put ONLY the options in a fenced block tagged ' +
95
+ 'hover-ask, one per line with a leading "- ":\n' +
96
+ '```hover-ask\n- first concrete option\n- second concrete option\n```\n' +
97
+ 'Each line becomes a clickable button, so keep options short, specific to this ' +
98
+ 'page, and directly actionable. ALWAYS give concrete options this way — never ' +
99
+ 'a bare open question, a numbered list, or inline "A or B".';
100
+ export const EXPLORATION_CHECKPOINT_DIRECTIVE = 'OPEN-ENDED TASKS — CHECK IN BEFORE YOU STOP. When the request is vague or ' +
101
+ 'unscoped (e.g. just "test", "test this", "check the app") YOU chose what to ' +
102
+ 'cover, so you do not actually know when the user considers it done. If you ' +
103
+ 'reach a natural stopping point with MATERIAL scope still untested — whole ' +
104
+ 'sections / flows / steps you noticed but did not exercise — do NOT end the ' +
105
+ 'run on your own. First call mcp__hovercontrol__ask_user: briefly say what ' +
106
+ 'you have covered and what remains, and offer concrete options such as ' +
107
+ 'continuing with a specific untested part, continuing through everything ' +
108
+ 'left, or stopping here. Then act on the answer. Ask at a genuine checkpoint ' +
109
+ '(a finished chunk), not after every step, and ask once per checkpoint — do ' +
110
+ 'not loop. Skip this entirely when the task was explicit and bounded (you ' +
111
+ 'finished exactly what was asked) or when the user already said to stop / ' +
112
+ 'that it is enough — then just finish and report. ' +
113
+ 'IN-APP LIMITS ARE NOT BLOCKERS. When an EXPLICIT task is stopped by something ' +
114
+ 'you can change inside the app — a daily quota reached, a feature behind a ' +
115
+ 'setting/toggle, a smaller default that a control can raise — do the in-app ' +
116
+ 'workaround yourself (open settings, raise the limit, flip the toggle) and ' +
117
+ 'COMPLETE the task. Do NOT stop to offer a menu of choices. Only a truly ' +
118
+ 'EXTERNAL blocker (missing credentials, a file you cannot obtain) justifies ' +
119
+ 'asking; and if the explicit task is already satisfied, just conclude and ' +
120
+ 'report it — never end an explicit task with a "what next?" option list.';
121
+ /** State-reset recon (debt-2 reproducible-state-isolation). Appended for grounded
122
+ * modes ONLY when the extension explicitly requests it (run payload `reconReset`)
123
+ * — recon clears client state, which would wipe a logged-in session, so it never
124
+ * runs unsolicited and never on a plain Flow recording. The agent discovers +
125
+ * validates the reset recipe ONCE, then reports it via record_reset_recipe for
126
+ * the engine to forward to the environment store. */
127
+ export const RECON_DIRECTIVE = 'STATE-RESET RECON — do this ONCE, before you start testing. For saved tests to ' +
128
+ 'be reproducible, Hover needs to know how to reset this app to a clean start. ' +
129
+ '(1) Note which controls/screens reflect the app\'s stored state. (2) Call ' +
130
+ 'mcp__hovercontrol__clear_client_state, then look at the page after it reloads. ' +
131
+ '(3) Decide: did the app return to its INITIAL state (its state is client-side ' +
132
+ '— Tier 1) or did your prior progress come BACK (it is re-hydrated from a ' +
133
+ 'backend / your logged-in account — Tier 2)? Prefer a FULL clear (clear ' +
134
+ 'everything) — if that logged you out and the app needs auth, log back in ' +
135
+ 'using the test account credentials provided for this run, then continue. Only ' +
136
+ 'fall back to naming specific storageKeys if a full clear breaks something you ' +
137
+ 'cannot re-establish. (4) Report it with mcp__hovercontrol__record_reset_recipe: ' +
138
+ 'tier 1 (clear-all, the default; or with storageKeys only if you had to scope), ' +
139
+ 'or tier 2 (not client-resettable). Do this recon only once, at the start; ' +
140
+ 'then test normally.';
141
+ /** QA Testing mode — appended on top of the grounded-actuation directive. Turns
142
+ * a directed run into autonomous exploratory testing. (Behavioral effect needs
143
+ * live verification; the wiring just appends this when mode === 'qa'.) */
144
+ export const QA_EXPLORATION_DIRECTIVE = 'QA TESTING MODE — explore, don\'t just follow. YOU ARE A TESTER: your only job ' +
145
+ 'is to TEST this app. Never merely read out, describe, summarize, or narrate the ' +
146
+ 'page — always EXERCISE the controls (click, fill, submit, toggle, navigate), ' +
147
+ 'try negative / boundary inputs, and verify behavior to find defects; describing ' +
148
+ 'the page is never an acceptable result on its own. A vague or unscoped request ' +
149
+ '("test the app", "test this") MEANS "explore the whole app" — do NOT open with ' +
150
+ 'an ask_user or a list of choices, just START testing what you can see (even on ' +
151
+ 'a login/landing page: empty submit, bad password, invalid input first). Ask the ' +
152
+ 'user (ask_user) ONLY when EXTERNALLY blocked (credentials / a file you cannot ' +
153
+ 'get) or for a decisive business judgment you cannot resolve — never just to ' +
154
+ 'pick scope. An IN-APP limit you can change yourself (a daily quota, a ' +
155
+ 'setting/toggle, a raisable default) is NOT "blocked": adjust it in the app and ' +
156
+ 'finish the task — do not stop to ask. Go BEYOND any single instruction: ' +
157
+ 'systematically exercise every reachable control and state of the app to find ' +
158
+ 'real defects. Maintain a mental frontier of untried controls; try each; do NOT ' +
159
+ 'repeat a state you have already explored. Do NEGATIVE testing too — empty / ' +
160
+ 'invalid / boundary / special-character inputs on forms — to surface validation ' +
161
+ 'gaps, not just happy paths. Flag what you find as Findings (severity high / ' +
162
+ 'medium / low / info) in your report; DO NOT crystallize a spec unless the user ' +
163
+ 'asks. Treat clearly DESTRUCTIVE / irreversible actions (delete account, submit ' +
164
+ 'payment, send email, bulk delete) carefully: confirm with the user once per ' +
165
+ 'action-type before doing them, otherwise flag-and-skip. Stay on the app under ' +
166
+ 'test (never navigate to external origins). Stop when the frontier is exhausted ' +
167
+ 'or you hit the run budget; then WRITE THE FINDINGS REPORT AND END THE RUN. ' +
168
+ 'Your turn ENDS with that report — do NOT close by asking whether to test more ' +
169
+ 'or offering a menu of further areas (no "shall I also test X?" question, no ' +
170
+ 'ask_user, no closing option list). Anything you did not cover belongs in the ' +
171
+ 'report\'s `## Coverage` → `Not covered:` list, never in a closing question.\n' +
172
+ 'REPORT COVERAGE: end your report with a `## Coverage` section — first a short ' +
173
+ '`Tested:` list of the main areas / flows / controls you DID exercise, then a ' +
174
+ '`Not covered:` list of anything you saw but did NOT test (and a few words on ' +
175
+ 'why: out of scope, blocked, ran out of budget, destructive-and-skipped). This ' +
176
+ 'tells the developer exactly what is verified vs still open — be honest about ' +
177
+ 'gaps, do not claim coverage you did not do.\n' +
178
+ 'CAPTURE CLEAN FLOWS: as you exercise the app, whenever you complete a coherent ' +
179
+ 'end-to-end flow worth keeping as a regression test (e.g. "Log in", "Add item ' +
180
+ 'to cart", "Submit the registration form"), call record_candidate with just a ' +
181
+ 'short imperative name — IN ENGLISH (it becomes the spec\'s filename + test ' +
182
+ 'name, even though your report prose is in another language). You do NOT pass ' +
183
+ 'steps: Hover automatically captures the successful click / fill / select / ' +
184
+ 'check / upload actions you did since your last record_candidate, so call it ' +
185
+ 'the MOMENT you finish each distinct flow — before starting the next one or ' +
186
+ 'doing unrelated exploration — so its captured steps are exactly that flow. ' +
187
+ '(record_candidate only OFFERS the user a one-click "Crystallize" later — it ' +
188
+ 'does not write a spec; you never write one yourself.)\n' +
189
+ 'REMEMBER WHAT YOU LEARN: when you confirm a durable business rule about this ' +
190
+ 'app — an expected behavior, a validation rule, an access policy, or the answer ' +
191
+ 'to a "is this a bug or by-design?" you asked the user — call record_fact to ' +
192
+ 'persist it, so neither you nor a future run re-asks it. State it as a clean ' +
193
+ 'self-contained rule. RULES ONLY — never record secrets, passwords, tokens, or ' +
194
+ 'personal data. (Anything in KNOWN BUSINESS KNOWLEDGE above is already ' +
195
+ 'remembered — treat it as settled, do not re-ask it.)';
196
+ /** Appended to the FIRST (functional verify) pass when a penetration-testing
197
+ * pass is queued to run right after it. Keeps the two passes from both doing
198
+ * security work: the verify pass stays functional-only, all security/vuln work
199
+ * is deferred to the dedicated pentest pass. (Only added when QA has pentest on
200
+ * AND this is the pre-pentest verify phase — see service.ts `splitting`.) */
201
+ export const QA_VERIFY_DEFER_SECURITY_DIRECTIVE = 'SECURITY IS A SEPARATE LATER PASS — NOT THIS ONE. A dedicated penetration-' +
202
+ 'testing pass runs right after this one and owns ALL security / vulnerability ' +
203
+ 'work (auth / access control, IDOR, injection, secrets, endpoint abuse, ' +
204
+ 'attacking the backend). In THIS pass do FUNCTIONAL testing ONLY: verify the ' +
205
+ 'app WORKS — flows, forms, navigation, validation, state — and report only ' +
206
+ 'functional defects. Even if the request mentions security, do NOT audit ' +
207
+ 'security, do NOT read source looking for vulnerabilities, and do NOT report ' +
208
+ 'security findings here. Leave every security concern to the pentest pass so ' +
209
+ 'the two passes never duplicate each other.';
210
+ export const GROUNDED_ACTUATION_DIRECTIVE = 'INTERACTING WITH THE PAGE — IMPORTANT: You interact with the page ONLY through ' +
211
+ 'the Hover control tools: mcp__hovercontrol__click_control, fill_control, ' +
212
+ 'select_control, check_control. You ALREADY HAVE FULL PERMISSION to use them — ' +
213
+ 'NEVER ask the user to grant permissions, never stop to request access, never ' +
214
+ 'narrate a permission request. Just call the tools and keep going until the task ' +
215
+ 'is done. Each takes the element\'s accessible role + name exactly as shown in ' +
216
+ 'the latest browser_snapshot (fall back to its testId, then its real visible ' +
217
+ 'text, only when there is no clean role+name). Workflow: browser_snapshot to read ' +
218
+ 'the real roles + names, then call the matching *_control tool for each field / ' +
219
+ 'option / button, snapshotting again after navigation. (The Playwright ' +
220
+ 'interaction tools are disabled — the control tools replace them, so saved ' +
221
+ 'selectors stay grounded.) browser_navigate / browser_snapshot / ' +
222
+ 'browser_wait_for / browser_tabs / browser_press_key remain available.\n\n' +
223
+ 'WHEN A TARGET ISN\'T UNIQUELY ADDRESSABLE — narrow it, don\'t give up. Two ' +
224
+ 'common reasons and the one principle that solves both: (a) its accessible ' +
225
+ 'name/label repeats elsewhere on the page, or (b) its real input is hidden so ' +
226
+ 'it isn\'t in the snapshot as a control and getByRole/check_control would time ' +
227
+ 'out — in which case act on the element you CAN see (its visible label text). ' +
228
+ 'Principle: scope to the smallest container in the snapshot that uniquely ' +
229
+ 'holds your target by passing `within` = that container\'s role + accessible ' +
230
+ 'name, then identify the target inside it (by text when its own name isn\'t ' +
231
+ 'unique). To choose the right container and approach, read the snapshot tree, ' +
232
+ 'take a mcp__hovercontrol__take_screenshot to SEE the real visual layout (the ' +
233
+ 'accessibility tree omits display:none inputs, canvas, and can\'t convey ' +
234
+ 'spatial grouping — the screenshot shows what the user actually sees), and ' +
235
+ 'read the component source if you\'re unsure how it\'s built. take_screenshot ' +
236
+ 'captures the CURRENT VIEWPORT only and never resizes the page — use it, NOT ' +
237
+ 'Playwright\'s browser_take_screenshot (disabled here: its fullPage capture ' +
238
+ 'resizes the live window, which can reset transient app state so you\'d never ' +
239
+ 'see the result of your own action). To see below the fold, scroll first, then ' +
240
+ 'take_screenshot. For FINDING elements, rely on browser_snapshot — its tree ' +
241
+ 'covers the whole page (off-screen included), so a viewport shot never makes ' +
242
+ 'you miss a control. Perceive with the screenshot; ACT through the grounded ' +
243
+ '*_control tools. This is routine; work it ' +
244
+ 'out and keep going rather than reporting it as a limitation.\n\n' +
245
+ 'WHEN YOU ARE TRULY BLOCKED — ASK VIA THE CARD, DON\'T DEAD-END: only after ' +
246
+ 'you\'ve tried to work it out yourself (re-read the snapshot, scope with ' +
247
+ '`within`, read the component source), if something genuinely needs the user — ' +
248
+ 'credentials you don\'t have, a file only they can provide, a decision only ' +
249
+ 'they can make — call mcp__hovercontrol__ask_user. Never surface that as a ' +
250
+ 'plain chat question and end your turn: the user can only answer an ask_user ' +
251
+ 'card, so a bare question dead-ends the run. (WHEN to ask vs. keep going on ' +
252
+ 'your own — and how to start and stop — is governed by the mode directive ' +
253
+ 'below; this paragraph only fixes HOW to ask.) Engine helper: ' +
254
+ 'mcp__hovercontrol__upload_file (path or placeholder) sets a file on an upload ' +
255
+ 'control, since you have no filesystem access yourself.\n\n' +
256
+ 'VOLATILE CONTENT — FLAG IT, DON\'T FREEZE IT. Two kinds of text live on a ' +
257
+ 'page: FIXED UI labels the app ships (button / field / menu text like ' +
258
+ '"Submit", "Email", "Add to cart") and APP DATA the page draws from its ' +
259
+ 'content or state (a word on a card, a product or item title, a person\'s ' +
260
+ 'name, a generated id, an order number, a date, a price, a count). Whenever the ' +
261
+ 'name / text you ground on is APP DATA — NOT a fixed label — you MUST pass ' +
262
+ 'dynamic:true AND anchor on something stable (a testId, the `within` container, ' +
263
+ 'or just the role), never the changing text itself. Quick test before every ' +
264
+ 'click/assert: "would this EXACT text be on the page on a fresh run with ' +
265
+ 'different data?" If no → it is dynamic. (Example: a flashcard heading showing ' +
266
+ 'the current word is APP DATA — click_control({ role: "heading", dynamic: true ' +
267
+ '}), NOT { name: "bathroom" }.) A frozen data value makes the saved test pass ' +
268
+ 'once and fail every run after.\n\n' +
269
+ 'CAPTURE THE INVARIANT — assert what the flow PROVES, not this run\'s value: ' +
270
+ 'when a flow reaches a state worth verifying, call ' +
271
+ 'mcp__hovercontrol__assert_visible, and capture at least the key one before ' +
272
+ 'record_candidate. Assert the CONTRACT (a result appears, a confirmation shows, ' +
273
+ 'the expected number of items render). When the proof is that some APP DATA ' +
274
+ 'showed up (a word, a row, a result), assert THAT element with dynamic:true + ' +
275
+ 'matcher \'non-empty\' or \'text-contains\' — NOT a fixed button sitting next to ' +
276
+ 'it, and NOT the literal value. Use \'text-exact\' only for genuinely fixed text.';
@@ -0,0 +1,28 @@
1
+ /**
2
+ * In-process engine surface for the `@hover-dev/mcp` server (and any other
3
+ * standalone consumer). The MCP server drives the debug Chrome directly via
4
+ * `playwright-core` and buffers grounded steps, then crystallizes them with
5
+ * `writeSpec` — no WebSocket service, no agent spawning. This barrel re-exports
6
+ * exactly those building blocks.
7
+ *
8
+ * (The old staged-engine surface — `runSession`, the WS `service`, the
9
+ * Playwright-MCP config in `resolveMcpConfig` / `buildGroundedMcpConfig`, and
10
+ * the `mcp/actuateServer` + `mcp/sourceServer` relays — has been removed. The
11
+ * MCP-first path doesn't spawn Playwright's MCP; it actuates through
12
+ * `playwright-core` and `groundedLocate` below, which is what keeps
13
+ * record == replay.)
14
+ */
15
+ export { writeSpec } from './specs/writeSpec.js';
16
+ export type { WriteSpecOptions, WriteSpecResult, Redaction } from './specs/writeSpec.js';
17
+ export type { SkillStep } from './specs/specStep.js';
18
+ export { replayGroundedSteps, replayOnPage, applyGroundedStep, groundedLocate } from './specs/replayGrounded.js';
19
+ export type { ReplayResult, ReplayFailure, ReplayStep, GroundedTarget } from './specs/replayGrounded.js';
20
+ export { launchDebugChrome, closeDebugChrome, findChromeBinary } from './playwright/launchChrome.js';
21
+ export type { LaunchOptions, LaunchResult } from './playwright/launchChrome.js';
22
+ export { GROUNDED_ACTUATION_DENY, GROUNDED_ACTUATION_DIRECTIVE } from './agentDirectives.js';
23
+ export { loadMemory, formatMemoryForPrompt, writeFact, memoryDir } from './memory/businessMemory.js';
24
+ export type { BusinessFact } from './memory/businessMemory.js';
25
+ export { RECON_DIRECTIVE, QA_EXPLORATION_DIRECTIVE } from './agentDirectives.js';
26
+ export { QA_INTENSITY, DEFAULT_QA_INTENSITY, asQaIntensity, qaBudgetDirective } from './qa/intensity.js';
27
+ export type { QaIntensity, QaIntensitySpec } from './qa/intensity.js';
28
+ //# sourceMappingURL=engine.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAGH,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,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;AAG/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"}
package/dist/engine.js ADDED
@@ -0,0 +1,27 @@
1
+ /**
2
+ * In-process engine surface for the `@hover-dev/mcp` server (and any other
3
+ * standalone consumer). The MCP server drives the debug Chrome directly via
4
+ * `playwright-core` and buffers grounded steps, then crystallizes them with
5
+ * `writeSpec` — no WebSocket service, no agent spawning. This barrel re-exports
6
+ * exactly those building blocks.
7
+ *
8
+ * (The old staged-engine surface — `runSession`, the WS `service`, the
9
+ * Playwright-MCP config in `resolveMcpConfig` / `buildGroundedMcpConfig`, and
10
+ * the `mcp/actuateServer` + `mcp/sourceServer` relays — has been removed. The
11
+ * MCP-first path doesn't spawn Playwright's MCP; it actuates through
12
+ * `playwright-core` and `groundedLocate` below, which is what keeps
13
+ * record == replay.)
14
+ */
15
+ // ── crystallization + grounded replay ────────────────────────────────────────
16
+ export { writeSpec } from './specs/writeSpec.js';
17
+ // Creation-verification: replay a flow's grounded steps over CDP (no playwright test).
18
+ export { replayGroundedSteps, replayOnPage, applyGroundedStep, groundedLocate } from './specs/replayGrounded.js';
19
+ // ── debug-Chrome lifecycle ───────────────────────────────────────────────────
20
+ export { launchDebugChrome, closeDebugChrome, findChromeBinary } from './playwright/launchChrome.js';
21
+ // ── grounded-actuation knobs (the deny-list + directive the agent runs under) ─
22
+ export { GROUNDED_ACTUATION_DENY, GROUNDED_ACTUATION_DIRECTIVE } from './agentDirectives.js';
23
+ // ── business memory (ask → remember loop) ────────────────────────────────────
24
+ export { loadMemory, formatMemoryForPrompt, writeFact, memoryDir } from './memory/businessMemory.js';
25
+ // ── autonomous-exploration directives + intensity (the test_app workflow) ─────
26
+ export { RECON_DIRECTIVE, QA_EXPLORATION_DIRECTIVE } from './agentDirectives.js';
27
+ export { QA_INTENSITY, DEFAULT_QA_INTENSITY, asQaIntensity, qaBudgetDirective } from './qa/intensity.js';
@@ -0,0 +1,29 @@
1
+ /** A learned business fact about the app under test. */
2
+ export interface BusinessFact {
3
+ /** kebab-case slug; also the filename stem. */
4
+ name: string;
5
+ /** One-line summary used for recall relevance + the index hook. */
6
+ description: string;
7
+ /** What kind of knowledge this is. */
8
+ type: 'business-rule' | 'expected-behavior' | 'validation' | 'access-policy';
9
+ /** The fact itself (markdown). */
10
+ body: string;
11
+ }
12
+ export declare function memoryDir(devRoot: string): string;
13
+ /** kebab-case a title into a safe filename stem. */
14
+ export declare function slugify(s: string): string;
15
+ /** Load every fact under `.hover/memory/` (excluding the MEMORY.md index).
16
+ * Pure + total: a missing dir / unreadable file just yields fewer facts. */
17
+ export declare function loadMemory(devRoot: string): Promise<BusinessFact[]>;
18
+ /** Format loaded facts as a system-prompt block, or '' when there are none (so
19
+ * the caller appends nothing). Grouped nothing-fancy: one bullet per fact. */
20
+ export declare function formatMemoryForPrompt(facts: BusinessFact[]): string;
21
+ /** Write (or overwrite) a fact file + refresh the MEMORY.md index line. NEVER
22
+ * throws — returns the path or an error string for the caller to log. Business
23
+ * RULES only; the caller must never pass secrets / PII / credentials. */
24
+ export declare function writeFact(devRoot: string, fact: BusinessFact): Promise<{
25
+ path: string;
26
+ } | {
27
+ error: string;
28
+ }>;
29
+ //# sourceMappingURL=businessMemory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"businessMemory.d.ts","sourceRoot":"","sources":["../../src/memory/businessMemory.ts"],"names":[],"mappings":"AAwBA,wDAAwD;AACxD,MAAM,WAAW,YAAY;IAC3B,+CAA+C;IAC/C,IAAI,EAAE,MAAM,CAAC;IACb,mEAAmE;IACnE,WAAW,EAAE,MAAM,CAAC;IACpB,sCAAsC;IACtC,IAAI,EAAE,eAAe,GAAG,mBAAmB,GAAG,YAAY,GAAG,eAAe,CAAC;IAC7E,kCAAkC;IAClC,IAAI,EAAE,MAAM,CAAC;CACd;AAED,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAEjD;AAED,oDAAoD;AACpD,wBAAgB,OAAO,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAMzC;AAsBD;6EAC6E;AAC7E,wBAAsB,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,CAkBzE;AAED;+EAC+E;AAC/E,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,YAAY,EAAE,GAAG,MAAM,CAQnE;AAED;;0EAE0E;AAC1E,wBAAsB,SAAS,CAC7B,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,YAAY,GACjB,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAc/C"}