aiden-runtime 4.1.4 → 4.5.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 +250 -847
  2. package/dist/api/server.js +32 -5
  3. package/dist/cli/v4/aidenCLI.js +379 -53
  4. package/dist/cli/v4/callbacks.js +248 -0
  5. package/dist/cli/v4/chatSession.js +292 -4
  6. package/dist/cli/v4/commands/_runtimeToggleHelpers.js +92 -0
  7. package/dist/cli/v4/commands/browserDepth.js +45 -0
  8. package/dist/cli/v4/commands/cron.js +264 -0
  9. package/dist/cli/v4/commands/daemon.js +541 -0
  10. package/dist/cli/v4/commands/daemonStatus.js +253 -0
  11. package/dist/cli/v4/commands/help.js +7 -0
  12. package/dist/cli/v4/commands/index.js +20 -1
  13. package/dist/cli/v4/commands/runs.js +203 -0
  14. package/dist/cli/v4/commands/sandbox.js +48 -0
  15. package/dist/cli/v4/commands/suggestions.js +68 -0
  16. package/dist/cli/v4/commands/tce.js +41 -0
  17. package/dist/cli/v4/commands/trigger.js +378 -0
  18. package/dist/cli/v4/commands/update.js +95 -3
  19. package/dist/cli/v4/daemonAgentBuilder.js +142 -0
  20. package/dist/cli/v4/defaultSoul.js +75 -3
  21. package/dist/cli/v4/display/capabilityCard.js +26 -0
  22. package/dist/cli/v4/display/progressBar.js +41 -8
  23. package/dist/cli/v4/display.js +258 -15
  24. package/dist/cli/v4/replyRenderer.js +31 -23
  25. package/dist/cli/v4/toolPreview.js +10 -0
  26. package/dist/cli/v4/updateBootPrompt.js +170 -0
  27. package/dist/core/playwrightBridge.js +129 -0
  28. package/dist/core/toolRegistry.js +7 -1
  29. package/dist/core/v4/aidenAgent.js +371 -4
  30. package/dist/core/v4/browserState.js +436 -0
  31. package/dist/core/v4/checkpoint.js +79 -0
  32. package/dist/core/v4/daemon/bootstrap.js +604 -0
  33. package/dist/core/v4/daemon/cleanShutdown.js +154 -0
  34. package/dist/core/v4/daemon/cron/cronBridge.js +126 -0
  35. package/dist/core/v4/daemon/cron/cronEmitter.js +173 -0
  36. package/dist/core/v4/daemon/cron/migration.js +199 -0
  37. package/dist/core/v4/daemon/cron/misfirePolicy.js +115 -0
  38. package/dist/core/v4/daemon/daemonConfig.js +90 -0
  39. package/dist/core/v4/daemon/db/connection.js +106 -0
  40. package/dist/core/v4/daemon/db/migrations.js +296 -0
  41. package/dist/core/v4/daemon/db/schema/v1.spec.js +18 -0
  42. package/dist/core/v4/daemon/dispatcher/agentRunner.js +98 -0
  43. package/dist/core/v4/daemon/dispatcher/budgetGate.js +127 -0
  44. package/dist/core/v4/daemon/dispatcher/daemonApproval.js +113 -0
  45. package/dist/core/v4/daemon/dispatcher/dailyBudgetTracker.js +120 -0
  46. package/dist/core/v4/daemon/dispatcher/dispatcher.js +389 -0
  47. package/dist/core/v4/daemon/dispatcher/fireRateLimiter.js +113 -0
  48. package/dist/core/v4/daemon/dispatcher/index.js +53 -0
  49. package/dist/core/v4/daemon/dispatcher/promptTemplate.js +95 -0
  50. package/dist/core/v4/daemon/dispatcher/realAgentRunner.js +356 -0
  51. package/dist/core/v4/daemon/dispatcher/resolveModel.js +93 -0
  52. package/dist/core/v4/daemon/dispatcher/sessionId.js +93 -0
  53. package/dist/core/v4/daemon/drain.js +156 -0
  54. package/dist/core/v4/daemon/eventLoopLag.js +73 -0
  55. package/dist/core/v4/daemon/health.js +159 -0
  56. package/dist/core/v4/daemon/idempotencyStore.js +204 -0
  57. package/dist/core/v4/daemon/index.js +179 -0
  58. package/dist/core/v4/daemon/instanceTracker.js +99 -0
  59. package/dist/core/v4/daemon/resourceRegistry.js +150 -0
  60. package/dist/core/v4/daemon/restartCode.js +32 -0
  61. package/dist/core/v4/daemon/restartFailureCounter.js +77 -0
  62. package/dist/core/v4/daemon/runStore.js +114 -0
  63. package/dist/core/v4/daemon/runtimeLock.js +167 -0
  64. package/dist/core/v4/daemon/signals.js +50 -0
  65. package/dist/core/v4/daemon/supervisor.js +272 -0
  66. package/dist/core/v4/daemon/triggerBus.js +279 -0
  67. package/dist/core/v4/daemon/triggers/email/allowlist.js +70 -0
  68. package/dist/core/v4/daemon/triggers/email/automatedSender.js +78 -0
  69. package/dist/core/v4/daemon/triggers/email/bodyExtractor.js +0 -0
  70. package/dist/core/v4/daemon/triggers/email/emailSeenStore.js +99 -0
  71. package/dist/core/v4/daemon/triggers/email/emailSpec.js +107 -0
  72. package/dist/core/v4/daemon/triggers/email/imapConnection.js +211 -0
  73. package/dist/core/v4/daemon/triggers/email/index.js +332 -0
  74. package/dist/core/v4/daemon/triggers/email/seenUids.js +60 -0
  75. package/dist/core/v4/daemon/triggers/fileObservationsStore.js +93 -0
  76. package/dist/core/v4/daemon/triggers/fileWatcher.js +253 -0
  77. package/dist/core/v4/daemon/triggers/fileWatcherSpec.js +88 -0
  78. package/dist/core/v4/daemon/triggers/fsIdentity.js +42 -0
  79. package/dist/core/v4/daemon/triggers/globMatcher.js +100 -0
  80. package/dist/core/v4/daemon/triggers/reconcile.js +206 -0
  81. package/dist/core/v4/daemon/triggers/settleStat.js +81 -0
  82. package/dist/core/v4/daemon/triggers/webhook.js +376 -0
  83. package/dist/core/v4/daemon/triggers/webhookDeliveriesStore.js +109 -0
  84. package/dist/core/v4/daemon/triggers/webhookIdempotency.js +72 -0
  85. package/dist/core/v4/daemon/triggers/webhookRateLimit.js +56 -0
  86. package/dist/core/v4/daemon/triggers/webhookSpec.js +76 -0
  87. package/dist/core/v4/daemon/triggers/webhookVerifier.js +128 -0
  88. package/dist/core/v4/daemon/types.js +15 -0
  89. package/dist/core/v4/dockerSession.js +461 -0
  90. package/dist/core/v4/dryRun.js +117 -0
  91. package/dist/core/v4/failureClassifier.js +779 -0
  92. package/dist/core/v4/loopTrace.js +257 -0
  93. package/dist/core/v4/recoveryReport.js +449 -0
  94. package/dist/core/v4/runtimeToggles.js +187 -0
  95. package/dist/core/v4/sandboxConfig.js +285 -0
  96. package/dist/core/v4/sandboxFs.js +316 -0
  97. package/dist/core/v4/suggestionCatalog.js +41 -0
  98. package/dist/core/v4/suggestionEngine.js +210 -0
  99. package/dist/core/v4/toolRegistry.js +18 -0
  100. package/dist/core/v4/turnState.js +587 -0
  101. package/dist/core/v4/update/checkUpdate.js +63 -3
  102. package/dist/core/v4/update/installMethodDetect.js +115 -0
  103. package/dist/core/v4/update/registryClient.js +121 -0
  104. package/dist/core/v4/update/skipState.js +75 -0
  105. package/dist/core/v4/verifier.js +448 -0
  106. package/dist/core/version.js +1 -1
  107. package/dist/core/webSearch.js +64 -24
  108. package/dist/tools/v4/browser/_observer.js +224 -0
  109. package/dist/tools/v4/browser/browserBlocker.js +396 -0
  110. package/dist/tools/v4/browser/browserClick.js +18 -1
  111. package/dist/tools/v4/browser/browserClose.js +18 -1
  112. package/dist/tools/v4/browser/browserExtract.js +5 -1
  113. package/dist/tools/v4/browser/browserFill.js +17 -1
  114. package/dist/tools/v4/browser/browserGetUrl.js +5 -1
  115. package/dist/tools/v4/browser/browserNavigate.js +16 -1
  116. package/dist/tools/v4/browser/browserScreenshot.js +5 -1
  117. package/dist/tools/v4/browser/browserScroll.js +18 -1
  118. package/dist/tools/v4/browser/browserType.js +17 -1
  119. package/dist/tools/v4/browser/captchaCheck.js +5 -1
  120. package/dist/tools/v4/executeCode.js +1 -0
  121. package/dist/tools/v4/files/fileCopy.js +56 -2
  122. package/dist/tools/v4/files/fileDelete.js +38 -1
  123. package/dist/tools/v4/files/fileList.js +12 -1
  124. package/dist/tools/v4/files/fileMove.js +59 -2
  125. package/dist/tools/v4/files/filePatch.js +43 -1
  126. package/dist/tools/v4/files/fileRead.js +12 -1
  127. package/dist/tools/v4/files/fileWrite.js +41 -1
  128. package/dist/tools/v4/index.js +71 -58
  129. package/dist/tools/v4/memory/memoryAdd.js +14 -0
  130. package/dist/tools/v4/memory/memoryRemove.js +14 -0
  131. package/dist/tools/v4/memory/memoryReplace.js +15 -0
  132. package/dist/tools/v4/memory/sessionSummary.js +12 -0
  133. package/dist/tools/v4/process/processKill.js +19 -0
  134. package/dist/tools/v4/process/processList.js +1 -0
  135. package/dist/tools/v4/process/processLogRead.js +1 -0
  136. package/dist/tools/v4/process/processSpawn.js +13 -0
  137. package/dist/tools/v4/process/processWait.js +1 -0
  138. package/dist/tools/v4/sessions/recallSession.js +1 -0
  139. package/dist/tools/v4/sessions/sessionList.js +1 -0
  140. package/dist/tools/v4/sessions/sessionSearch.js +1 -0
  141. package/dist/tools/v4/skills/lookupToolSchema.js +2 -0
  142. package/dist/tools/v4/skills/skillManage.js +13 -0
  143. package/dist/tools/v4/skills/skillView.js +1 -0
  144. package/dist/tools/v4/skills/skillsList.js +1 -0
  145. package/dist/tools/v4/subagent/subagentFanout.js +1 -0
  146. package/dist/tools/v4/system/aidenSelfUpdate.js +16 -0
  147. package/dist/tools/v4/system/appClose.js +13 -0
  148. package/dist/tools/v4/system/appInput.js +13 -0
  149. package/dist/tools/v4/system/appLaunch.js +13 -0
  150. package/dist/tools/v4/system/clipboardRead.js +1 -0
  151. package/dist/tools/v4/system/clipboardWrite.js +14 -0
  152. package/dist/tools/v4/system/mediaKey.js +12 -0
  153. package/dist/tools/v4/system/mediaSessions.js +1 -0
  154. package/dist/tools/v4/system/mediaTransport.js +13 -0
  155. package/dist/tools/v4/system/naturalEvents.js +1 -0
  156. package/dist/tools/v4/system/nowPlaying.js +1 -0
  157. package/dist/tools/v4/system/osProcessList.js +1 -0
  158. package/dist/tools/v4/system/screenshot.js +1 -0
  159. package/dist/tools/v4/system/systemInfo.js +1 -0
  160. package/dist/tools/v4/system/volumeSet.js +17 -0
  161. package/dist/tools/v4/terminal/shellExec.js +81 -9
  162. package/dist/tools/v4/web/deepResearch.js +1 -0
  163. package/dist/tools/v4/web/openUrl.js +1 -0
  164. package/dist/tools/v4/web/webFetch.js +1 -0
  165. package/dist/tools/v4/web/webPage.js +1 -0
  166. package/dist/tools/v4/web/webSearch.js +1 -0
  167. package/dist/tools/v4/web/youtubeSearch.js +1 -0
  168. package/package.json +7 -1
  169. package/plugins/aiden-plugin-cdp-browser/.granted-permissions.json +8 -0
@@ -0,0 +1,224 @@
1
+ "use strict";
2
+ /**
3
+ * Copyright (c) 2026 Shiva Deore (Taracod).
4
+ * Licensed under AGPL-3.0. See LICENSE for details.
5
+ *
6
+ * Aiden — local-first agent.
7
+ */
8
+ /**
9
+ * tools/v4/browser/_observer.ts — v4.3 Phase 1 + 2: shared BrowserState
10
+ * observer + stale-ref retry HOC for browser ToolHandlers.
11
+ *
12
+ * One BrowserState lives per server process (lifecycle matches the
13
+ * persistent playwrightBridge context). Every browser tool wraps its
14
+ * ToolHandler in `withBrowserState(...)` so the observer's pre/post
15
+ * snapshot capture happens automatically.
16
+ *
17
+ * Phase 1 — observer captures pre/post snapshots and embeds them as
18
+ * a `browserState` sidecar on the tool result when
19
+ * browser depth is enabled (default ON; opt-out via
20
+ * AIDEN_BROWSER_DEPTH=0). No-op when disabled.
21
+ *
22
+ * Phase 2 — stale-ref recovery. When an interactive browser tool
23
+ * (browser_click / browser_type / browser_fill) returns a
24
+ * resolution-class failure (`element not found`, `not visible`,
25
+ * `not attached`, `timeout`, `target closed`), the HOC resnapshots
26
+ * and retries the inner execute ONCE with the same args. The retry
27
+ * logic is reactive only — no preflight tax on success paths. The
28
+ * retry attempt + outcome lands on `ActionResult.staleRefRetry`
29
+ * for Phase 5's classifier to consume.
30
+ *
31
+ * The one-retry hard cap is the consult-derived non-negotiable: a
32
+ * second retry doesn't help (the cause isn't transient) and starts
33
+ * looking like agent thrashing. If the retry fails, the original
34
+ * failure result is preserved — same error message, but with the
35
+ * `staleRefRetry: { attempted: true, succeeded: false, ... }`
36
+ * sidecar so the classifier can recognise the pattern.
37
+ */
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.STALE_REF_PATTERNS = exports.STALE_REF_RETRYABLE = exports.browserState = void 0;
40
+ exports.detectStaleRefError = detectStaleRefError;
41
+ exports.withBrowserState = withBrowserState;
42
+ const browserState_1 = require("../../../core/v4/browserState");
43
+ const browserBlocker_1 = require("./browserBlocker");
44
+ const playwrightBridge_1 = require("../../../core/playwrightBridge");
45
+ /**
46
+ * Shared observer — one instance per server process. The HOC closes
47
+ * over this reference so all 9 browser tools share the same snapshot
48
+ * counter and gating decision.
49
+ *
50
+ * Tests can construct their own BrowserState with a stubbed bridge
51
+ * loader and call `withBrowserState(handler, customState)` directly.
52
+ */
53
+ exports.browserState = (0, browserState_1.createBrowserState)();
54
+ // ── Phase 2 — stale-ref retry primitives ─────────────────────────────
55
+ /**
56
+ * Interactive browser tools that operate on a selector. Stale-ref
57
+ * retry only fires for these — other tools either don't take a
58
+ * selector (browser_navigate, browser_close, browser_get_url) or
59
+ * are read-only (browser_extract, browser_screenshot, browser_scroll).
60
+ */
61
+ exports.STALE_REF_RETRYABLE = new Set([
62
+ 'browser_click',
63
+ 'browser_type',
64
+ 'browser_fill',
65
+ ]);
66
+ /**
67
+ * Error-message patterns that indicate a resolution-class failure
68
+ * (DOM lookup failed BEFORE any side-effect-producing action fired).
69
+ * Phase 2 retries only on these — never on action-failure messages
70
+ * (network errors, permission denials, etc.).
71
+ *
72
+ * The patterns are case-insensitive substrings; one match is enough.
73
+ * False positives are tolerable — retry-once costs ~200ms and produces
74
+ * the same result on the second attempt. False negatives miss the
75
+ * common transient-race case, so bias toward sensitivity.
76
+ */
77
+ exports.STALE_REF_PATTERNS = [
78
+ /element not found/i,
79
+ /not visible/i,
80
+ /not attached/i,
81
+ /detached from the DOM/i,
82
+ /target closed/i,
83
+ /timeout \d+ms exceeded/i,
84
+ ];
85
+ /**
86
+ * Check if a tool result represents a resolution-class failure.
87
+ * Returns the matched pattern (as a short string) when stale, null
88
+ * otherwise. Pure helper, exported for tests.
89
+ */
90
+ function detectStaleRefError(result) {
91
+ if (result === null || result === undefined || typeof result !== 'object')
92
+ return null;
93
+ const r = result;
94
+ if (r.success !== false)
95
+ return null;
96
+ if (typeof r.error !== 'string' || r.error.length === 0)
97
+ return null;
98
+ for (const pattern of exports.STALE_REF_PATTERNS) {
99
+ if (pattern.test(r.error)) {
100
+ return pattern.source;
101
+ }
102
+ }
103
+ return null;
104
+ }
105
+ /**
106
+ * Test whether a tool result represents success. Used by the HOC to
107
+ * decide whether the retry "succeeded" and should become canonical.
108
+ */
109
+ function isSuccessResult(result) {
110
+ if (result === null || result === undefined || typeof result !== 'object')
111
+ return false;
112
+ return result.success === true;
113
+ }
114
+ const defaultPageTextFetcher = () => (0, playwrightBridge_1.pwSnapshot)();
115
+ function withBrowserState(handler, state = exports.browserState,
116
+ /**
117
+ * Optional page-text fetcher. Production code uses pwSnapshot;
118
+ * tests inject a stub returning canned text for the blocker
119
+ * detection tier. The fetcher is called ONCE per action when
120
+ * browser depth is enabled — disabled path skips entirely.
121
+ */
122
+ pageTextFetcher = defaultPageTextFetcher) {
123
+ return {
124
+ ...handler,
125
+ async execute(args, ctx) {
126
+ if (!state.isEnabled()) {
127
+ return handler.execute(args, ctx);
128
+ }
129
+ const pre = await state.captureState();
130
+ let result = await handler.execute(args, ctx);
131
+ // v4.3 Phase 3 — manual-blocker detection. Runs on every
132
+ // browser-tool result when enabled. Uses the configured
133
+ // page-text fetcher (pwSnapshot in production). Detection
134
+ // never breaks the inner tool — pwSnapshot is wrapped in
135
+ // try/catch via the fetcher itself; failures produce no
136
+ // blocker and no observer sidecar field.
137
+ //
138
+ // The detected blocker is BOTH embedded on the result sidecar
139
+ // (Phase 5 + chat layer consumers) AND used to suppress
140
+ // Phase 2's stale-ref retry below. Pause-and-surface contract
141
+ // (Q-CDP5) — never auto-action a blocker.
142
+ let blocker;
143
+ try {
144
+ const snap = await pageTextFetcher();
145
+ if (snap.ok && snap.text) {
146
+ const url = result?.url ?? '';
147
+ const detected = (0, browserBlocker_1.detectBlocker)({ text: snap.text, url });
148
+ if (detected)
149
+ blocker = detected;
150
+ }
151
+ }
152
+ catch { /* detection never breaks the inner tool */ }
153
+ // v4.3 Phase 4 — propagate blocker (or its absence) to the
154
+ // active tab's metadata in BrowserState. Cross-tab queries can
155
+ // then ask "is there a pending blocker on any tab" without
156
+ // re-running detection. No-op when state is disabled or when
157
+ // the tabs map has no active entry (the reconciliation in
158
+ // captureState above sets activeTabId).
159
+ try {
160
+ state.updateActiveTabBlocker(blocker
161
+ ? {
162
+ kind: blocker.kind,
163
+ subtype: blocker.subtype,
164
+ url: blocker.url,
165
+ confidence: blocker.confidence,
166
+ }
167
+ : null);
168
+ }
169
+ catch { /* defensive — tab updates never break the inner tool */ }
170
+ // v4.3 Phase 2 — stale-ref retry. Reactive: fires only after a
171
+ // resolution-class failure on an interactive tool. One retry
172
+ // hard cap. Safe because the resolution-class errors fire
173
+ // BEFORE any DOM event is dispatched, so retry can't double-act.
174
+ //
175
+ // v4.3 Phase 3 suppression: skip the retry when a manual
176
+ // blocker is present (`!blocker` gate). A blocker means the
177
+ // page is asking for human action — retrying the same tool
178
+ // call against a sign-in wall or 2FA prompt won't help and
179
+ // looks like agent thrashing.
180
+ let staleRefRetry;
181
+ if (pre && !blocker &&
182
+ exports.STALE_REF_RETRYABLE.has(handler.schema.name)) {
183
+ const staleReason = detectStaleRefError(result);
184
+ if (staleReason !== null) {
185
+ // Resnapshot — the "between" state. We use it for the
186
+ // diagnostic state_delta. The retry fires unconditionally
187
+ // (per Q-P2-3 single-signal rule): even when DOM hash
188
+ // hasn't changed, a transient race condition (element
189
+ // attached one tick after the original timeout) is the
190
+ // common case the retry catches.
191
+ const between = await state.captureState();
192
+ const state_delta = state.computeStateDelta(pre, between);
193
+ const retryResult = await handler.execute(args, ctx);
194
+ const retryOk = isSuccessResult(retryResult);
195
+ staleRefRetry = {
196
+ attempted: true,
197
+ succeeded: retryOk,
198
+ reason: staleReason,
199
+ state_delta,
200
+ };
201
+ // If retry succeeded, the retry result becomes canonical.
202
+ // If retry failed, keep the original failure — its error
203
+ // context is what the model needs to see, and a same-error
204
+ // retry would just look like duplicated chrome.
205
+ if (retryOk)
206
+ result = retryResult;
207
+ }
208
+ }
209
+ const post = await state.captureState();
210
+ const observerMeta = state.buildActionResult({ pre, post });
211
+ if (observerMeta &&
212
+ result !== null && result !== undefined &&
213
+ typeof result === 'object' && !Array.isArray(result)) {
214
+ const sidecar = {
215
+ ...observerMeta,
216
+ ...(staleRefRetry && { staleRefRetry }),
217
+ ...(blocker && { blocker }),
218
+ };
219
+ return { ...result, browserState: sidecar };
220
+ }
221
+ return result;
222
+ },
223
+ };
224
+ }
@@ -0,0 +1,396 @@
1
+ "use strict";
2
+ /**
3
+ * Copyright (c) 2026 Shiva Deore (Taracod).
4
+ * Licensed under AGPL-3.0. See LICENSE for details.
5
+ *
6
+ * Aiden — local-first agent.
7
+ */
8
+ /**
9
+ * tools/v4/browser/browserBlocker.ts — v4.3 Phase 3: Manual-blocker
10
+ * detection.
11
+ *
12
+ * Five-tier substring + URL pattern pipeline that surfaces "the
13
+ * agent needs your help" situations: CAPTCHA challenges, sign-in
14
+ * walls, 2FA prompts, identity verification, cookie/GDPR consent.
15
+ *
16
+ * Distinct from Phase 1's BrowserState observer (which records
17
+ * state-delta evidence) and Phase 2's stale-ref retry (which auto-
18
+ * corrects transient races). Phase 3 detects situations that
19
+ * **cannot be solved by retry** — the page is asking for a human
20
+ * action — and surfaces them as structured cards so the model
21
+ * doesn't grind through retry attempts.
22
+ *
23
+ * Non-negotiable: NEVER auto-solve a CAPTCHA. The pause-and-surface
24
+ * contract from Q-CDP5(c) is enforced structurally — this module
25
+ * only detects + surfaces. No vision-attempt fallback, no clicking
26
+ * "I'm not a robot" tickboxes, no submitting forms blindly. The
27
+ * model reads `result.browserState.blocker` in its tool message and
28
+ * tells the user.
29
+ *
30
+ * The CAPTCHA tier reuses Aiden's existing `captchaCheck.ts` 24-
31
+ * marker scan — Phase 3 doesn't refactor it, only wraps it as one
32
+ * of five detectors. The other four tiers (2FA, login, verification,
33
+ * consent) are new in Phase 3.
34
+ *
35
+ * Priority order: captcha > 2fa > login > verification > consent.
36
+ * Only ONE blocker reported per detection (highest priority wins).
37
+ * Pages that match multiple tiers (a 2FA challenge inside an
38
+ * obscured cookie banner) report the most-blocking kind — the
39
+ * lower-priority match can wait until the higher-priority is
40
+ * cleared.
41
+ *
42
+ * Pure module — types + 5 detectors + 1 orchestrator. No I/O, no
43
+ * async, no Playwright dependency. Easy to unit-test against
44
+ * canonical page-text fixtures. The observer HOC consumes the
45
+ * `pwSnapshot()` text via the existing playwrightBridge helper.
46
+ */
47
+ Object.defineProperty(exports, "__esModule", { value: true });
48
+ exports.detectBlocker = detectBlocker;
49
+ const captchaCheck_1 = require("./captchaCheck");
50
+ // ── Pattern tables ──────────────────────────────────────────────────────────
51
+ // CAPTCHA tier reuses captchaCheck.ts markers + adds subtype detection.
52
+ const CAPTCHA_SUBTYPE_MARKERS = [
53
+ ['recaptcha', 'recaptcha'],
54
+ ['g-recaptcha', 'recaptcha'],
55
+ ['hcaptcha.com', 'hcaptcha'],
56
+ ['cf-turnstile', 'turnstile'],
57
+ ['cloudflare', 'cloudflare'],
58
+ ['just a moment', 'cloudflare'],
59
+ ['checking your browser', 'cloudflare'],
60
+ ];
61
+ // 2FA / MFA tier — input attributes, text, and URL.
62
+ const TWOFA_TEXT_PATTERNS = [
63
+ 'enter the code',
64
+ 'enter your code',
65
+ 'verification code',
66
+ 'authentication code',
67
+ '6-digit code',
68
+ 'two-factor',
69
+ 'two factor',
70
+ '2-step verification',
71
+ 'two-step verification',
72
+ 'authenticator app',
73
+ 'sms code',
74
+ 'sent a code',
75
+ 'we sent you a code',
76
+ 'text message',
77
+ 'security code',
78
+ 'one-time code',
79
+ 'one-time password',
80
+ ];
81
+ const TWOFA_URL_PATTERNS = [
82
+ '/2fa',
83
+ '/mfa',
84
+ '/verify-code',
85
+ '/verify_code',
86
+ '/auth/challenge',
87
+ '/two-factor',
88
+ '/two_factor',
89
+ ];
90
+ const TWOFA_DOM_HINTS = [
91
+ 'autocomplete="one-time-code"',
92
+ "autocomplete='one-time-code'",
93
+ 'inputmode="numeric"',
94
+ ];
95
+ const TWOFA_SUBTYPE_HINTS = [
96
+ ['authenticator app', 'totp'],
97
+ ['totp', 'totp'],
98
+ ['sms', 'sms'],
99
+ ['text message', 'sms'],
100
+ ['phone', 'sms'],
101
+ ];
102
+ // Login tier — sign-in forms, OAuth, SSO.
103
+ const LOGIN_TEXT_PATTERNS = [
104
+ 'sign in',
105
+ 'sign-in',
106
+ 'log in',
107
+ 'log-in',
108
+ 'login',
109
+ 'enter your password',
110
+ 'enter password',
111
+ 'continue with google',
112
+ 'continue with apple',
113
+ 'continue with github',
114
+ 'continue with microsoft',
115
+ 'sign in with',
116
+ 'single sign-on',
117
+ 'single sign on',
118
+ ];
119
+ const LOGIN_URL_PATTERNS = [
120
+ '/login',
121
+ '/signin',
122
+ '/sign-in',
123
+ '/auth',
124
+ '/oauth',
125
+ '/sso',
126
+ '/account/login',
127
+ ];
128
+ const LOGIN_DOM_HINTS = [
129
+ 'type="password"',
130
+ "type='password'",
131
+ 'input password', // generic fallback
132
+ ];
133
+ const LOGIN_SUBTYPE_HINTS = [
134
+ ['oauth', 'oauth'],
135
+ ['sso', 'oauth'],
136
+ ['continue with google', 'oauth'],
137
+ ['continue with apple', 'oauth'],
138
+ ['continue with github', 'oauth'],
139
+ ];
140
+ // Verification tier — identity / phone / email confirmation.
141
+ const VERIFY_TEXT_PATTERNS = [
142
+ 'verify your phone',
143
+ 'phone verification',
144
+ 'verify your email',
145
+ 'email verification',
146
+ 'confirm your email',
147
+ 'verify your identity',
148
+ 'identity verification',
149
+ 'identity check',
150
+ 'prove your identity',
151
+ ];
152
+ const VERIFY_URL_PATTERNS = [
153
+ '/verify',
154
+ '/verification',
155
+ '/confirm',
156
+ '/confirm-email',
157
+ '/confirm_email',
158
+ ];
159
+ const VERIFY_SUBTYPE_HINTS = [
160
+ ['phone', 'phone'],
161
+ ['email', 'email'],
162
+ ['identity', 'identity'],
163
+ ];
164
+ // Consent tier — cookie banners, GDPR.
165
+ const CONSENT_TEXT_PATTERNS = [
166
+ 'accept all cookies',
167
+ 'reject all cookies',
168
+ 'manage cookies',
169
+ 'manage your preferences',
170
+ 'we use cookies',
171
+ 'this site uses cookies',
172
+ 'gdpr',
173
+ 'cookie preferences',
174
+ 'cookie consent',
175
+ 'privacy preferences',
176
+ ];
177
+ const CONSENT_URL_PATTERNS = [
178
+ '/cookie',
179
+ '/consent',
180
+ ];
181
+ // ── Helpers ─────────────────────────────────────────────────────────────────
182
+ function lowercase(s) {
183
+ return (s ?? '').toLowerCase();
184
+ }
185
+ function matchesAny(haystack, patterns) {
186
+ for (const p of patterns) {
187
+ if (haystack.includes(p))
188
+ return p;
189
+ }
190
+ return null;
191
+ }
192
+ function pickSubtype(haystack, hints, fallback) {
193
+ for (const [marker, subtype] of hints) {
194
+ if (haystack.includes(marker))
195
+ return subtype;
196
+ }
197
+ return fallback;
198
+ }
199
+ function hostnameOf(url) {
200
+ try {
201
+ return new URL(url).hostname || url;
202
+ }
203
+ catch {
204
+ return url;
205
+ }
206
+ }
207
+ // ── Per-tier detectors ─────────────────────────────────────────────────────
208
+ /**
209
+ * Markers from captchaCheck.ts that aren't CAPTCHA-specific —
210
+ * they overlap semantically with the Verification tier (phone /
211
+ * email / identity confirmation). When captchaCheck's only hit
212
+ * is one of these, defer to the Verification tier; CAPTCHA needs
213
+ * a more specific signal.
214
+ */
215
+ const AMBIGUOUS_CAPTCHA_MARKERS = new Set([
216
+ 'verify your identity',
217
+ ]);
218
+ function detectCaptcha(text, url) {
219
+ const result = (0, captchaCheck_1.detectCaptchaMarkers)(text);
220
+ if (!result.detected)
221
+ return null;
222
+ // Demote: when the only matched marker is an ambiguous one (e.g.
223
+ // "verify your identity" — used by both CAPTCHAs and identity-
224
+ // verification flows), let the Verification tier handle it.
225
+ // CAPTCHA needs at least one specific signal.
226
+ if (result.markers.length === 1 &&
227
+ AMBIGUOUS_CAPTCHA_MARKERS.has(result.markers[0])) {
228
+ return null;
229
+ }
230
+ const lowerText = lowercase(text);
231
+ const subtype = pickSubtype(lowerText, CAPTCHA_SUBTYPE_MARKERS, 'unknown');
232
+ const evidence = result.markers.map((m) => `captcha-marker:${m}`);
233
+ // Multi-marker boosts confidence.
234
+ const confidence = result.markers.length >= 3 ? 0.95 :
235
+ result.markers.length === 2 ? 0.85 : 0.7;
236
+ return {
237
+ kind: 'captcha',
238
+ subtype,
239
+ url,
240
+ confidence,
241
+ evidence,
242
+ message: `${subtype === 'unknown' ? 'CAPTCHA' : subtype} challenge at ${hostnameOf(url)}. Solve it in the browser tab, then tell me when ready.`,
243
+ };
244
+ }
245
+ function detect2FA(text, url) {
246
+ const lowerText = lowercase(text);
247
+ const lowerUrl = lowercase(url);
248
+ const textHit = matchesAny(lowerText, TWOFA_TEXT_PATTERNS);
249
+ const urlHit = matchesAny(lowerUrl, TWOFA_URL_PATTERNS);
250
+ const domHit = matchesAny(lowerText, TWOFA_DOM_HINTS);
251
+ // Need at least ONE strong signal — text or URL match. DOM hint
252
+ // alone isn't enough (the text may be misleading on a docs page).
253
+ if (!textHit && !urlHit)
254
+ return null;
255
+ const evidence = [];
256
+ if (textHit)
257
+ evidence.push(`text:${textHit}`);
258
+ if (urlHit)
259
+ evidence.push(`url:${urlHit}`);
260
+ if (domHit)
261
+ evidence.push(`dom:${domHit}`);
262
+ const subtype = pickSubtype(lowerText, TWOFA_SUBTYPE_HINTS, 'unknown');
263
+ // Confidence: text+url+dom = 0.9, text+url = 0.8, text+dom = 0.75,
264
+ // text-only or url-only = 0.6.
265
+ let confidence = 0.6;
266
+ const signals = (textHit ? 1 : 0) + (urlHit ? 1 : 0) + (domHit ? 1 : 0);
267
+ if (signals === 2)
268
+ confidence = textHit && urlHit ? 0.8 : 0.75;
269
+ if (signals === 3)
270
+ confidence = 0.9;
271
+ return {
272
+ kind: '2fa',
273
+ subtype,
274
+ url,
275
+ confidence,
276
+ evidence,
277
+ message: `Two-factor code required at ${hostnameOf(url)}. Enter your ${subtype === 'totp' ? 'authenticator app code' : subtype === 'sms' ? 'SMS code' : 'verification code'} in the browser tab and tell me when complete.`,
278
+ };
279
+ }
280
+ function detectLogin(text, url) {
281
+ const lowerText = lowercase(text);
282
+ const lowerUrl = lowercase(url);
283
+ const textHit = matchesAny(lowerText, LOGIN_TEXT_PATTERNS);
284
+ const urlHit = matchesAny(lowerUrl, LOGIN_URL_PATTERNS);
285
+ const domHit = matchesAny(lowerText, LOGIN_DOM_HINTS);
286
+ if (!textHit && !urlHit)
287
+ return null;
288
+ const evidence = [];
289
+ if (textHit)
290
+ evidence.push(`text:${textHit}`);
291
+ if (urlHit)
292
+ evidence.push(`url:${urlHit}`);
293
+ if (domHit)
294
+ evidence.push(`dom:${domHit}`);
295
+ // Subtype: prefer text-marker match, then check URL for oauth/sso
296
+ // indicators (a `/oauth` or `/sso` URL is a strong OAuth signal
297
+ // even when the visible text is generic like "Authorize access").
298
+ let subtype = pickSubtype(lowerText, LOGIN_SUBTYPE_HINTS, '');
299
+ if (!subtype) {
300
+ if (lowerUrl.includes('oauth') || lowerUrl.includes('sso')) {
301
+ subtype = 'oauth';
302
+ }
303
+ else {
304
+ subtype = 'password';
305
+ }
306
+ }
307
+ let confidence = 0.6;
308
+ const signals = (textHit ? 1 : 0) + (urlHit ? 1 : 0) + (domHit ? 1 : 0);
309
+ if (signals === 2)
310
+ confidence = 0.8;
311
+ if (signals === 3)
312
+ confidence = 0.9;
313
+ return {
314
+ kind: 'login',
315
+ subtype,
316
+ url,
317
+ confidence,
318
+ evidence,
319
+ message: `Sign-in required at ${hostnameOf(url)}. Authenticate in the browser tab and let me know when done.`,
320
+ };
321
+ }
322
+ function detectVerify(text, url) {
323
+ const lowerText = lowercase(text);
324
+ const lowerUrl = lowercase(url);
325
+ const textHit = matchesAny(lowerText, VERIFY_TEXT_PATTERNS);
326
+ const urlHit = matchesAny(lowerUrl, VERIFY_URL_PATTERNS);
327
+ if (!textHit && !urlHit)
328
+ return null;
329
+ const evidence = [];
330
+ if (textHit)
331
+ evidence.push(`text:${textHit}`);
332
+ if (urlHit)
333
+ evidence.push(`url:${urlHit}`);
334
+ const subtype = pickSubtype(lowerText, VERIFY_SUBTYPE_HINTS, 'unknown');
335
+ // Url-only is the weaker case here (lots of /verify URLs do other
336
+ // things), so URL-only matches need text-marker reinforcement to
337
+ // reach 0.6+ confidence.
338
+ let confidence = 0.5;
339
+ if (textHit && urlHit)
340
+ confidence = 0.85;
341
+ else if (textHit)
342
+ confidence = 0.7;
343
+ return {
344
+ kind: 'verification',
345
+ subtype,
346
+ url,
347
+ confidence,
348
+ evidence,
349
+ message: `Identity verification required at ${hostnameOf(url)}. Complete the verification in the browser tab and tell me when done.`,
350
+ };
351
+ }
352
+ function detectConsent(text, url) {
353
+ const lowerText = lowercase(text);
354
+ const lowerUrl = lowercase(url);
355
+ const textHit = matchesAny(lowerText, CONSENT_TEXT_PATTERNS);
356
+ const urlHit = matchesAny(lowerUrl, CONSENT_URL_PATTERNS);
357
+ if (!textHit && !urlHit)
358
+ return null;
359
+ const evidence = [];
360
+ if (textHit)
361
+ evidence.push(`text:${textHit}`);
362
+ if (urlHit)
363
+ evidence.push(`url:${urlHit}`);
364
+ let confidence = 0.5;
365
+ if (textHit && urlHit)
366
+ confidence = 0.8;
367
+ else if (textHit)
368
+ confidence = 0.65;
369
+ return {
370
+ kind: 'consent',
371
+ subtype: 'cookies',
372
+ url,
373
+ confidence,
374
+ evidence,
375
+ message: `Consent banner at ${hostnameOf(url)}. Dismiss the cookie/privacy banner in the browser tab — I can retry afterward.`,
376
+ };
377
+ }
378
+ // ── Orchestrator ───────────────────────────────────────────────────────────
379
+ /**
380
+ * Detect the highest-priority manual blocker on a page. Priority
381
+ * order: captcha > 2fa > login > verification > consent. Returns
382
+ * null when no tier matches.
383
+ *
384
+ * Pure function — same inputs always produce the same output.
385
+ * Public for unit tests + the HOC's per-action detection call.
386
+ */
387
+ function detectBlocker(input) {
388
+ const text = input.text ?? '';
389
+ const url = input.url ?? '';
390
+ // Priority pipeline — first non-null wins.
391
+ return (detectCaptcha(text, url) ??
392
+ detect2FA(text, url) ??
393
+ detectLogin(text, url) ??
394
+ detectVerify(text, url) ??
395
+ detectConsent(text, url));
396
+ }
@@ -17,7 +17,8 @@
17
17
  Object.defineProperty(exports, "__esModule", { value: true });
18
18
  exports.browserClickTool = void 0;
19
19
  const playwrightBridge_1 = require("../../../core/playwrightBridge");
20
- exports.browserClickTool = {
20
+ const _observer_1 = require("./_observer");
21
+ const _browserClickTool = {
21
22
  schema: {
22
23
  name: 'browser_click',
23
24
  description: 'Click an element by CSS selector or visible text. Use target="first_result" to click the first organic search result on supported engines.',
@@ -35,6 +36,18 @@ exports.browserClickTool = {
35
36
  category: 'browser',
36
37
  mutates: true,
37
38
  toolset: 'browser',
39
+ riskTier: 'caution', // v4.4 Phase 1
40
+ buildPreview(args) {
41
+ const target = String(args.target ?? args.selector ?? '');
42
+ return {
43
+ tool: 'browser_click',
44
+ args,
45
+ riskTier: 'caution',
46
+ sideEffects: [{ type: 'browser_action', action: 'click', target }],
47
+ detectedRisks: [],
48
+ summary: `Would click browser element: ${target}`,
49
+ };
50
+ },
38
51
  async execute(args) {
39
52
  const target = String(args.target ?? args.selector ?? '').trim();
40
53
  if (!target)
@@ -51,3 +64,7 @@ exports.browserClickTool = {
51
64
  return { success: false, error: r.error, target };
52
65
  },
53
66
  };
67
+ // v4.3 Phase 1 — observer HOC captures pre/post page state when
68
+ // browser depth is enabled (default ON; opt-out via
69
+ // AIDEN_BROWSER_DEPTH=0) and embeds it as a `browserState` sidecar.
70
+ exports.browserClickTool = (0, _observer_1.withBrowserState)(_browserClickTool);
@@ -16,7 +16,8 @@
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
17
  exports.browserCloseTool = void 0;
18
18
  const playwrightBridge_1 = require("../../../core/playwrightBridge");
19
- exports.browserCloseTool = {
19
+ const _observer_1 = require("./_observer");
20
+ const _browserCloseTool = {
20
21
  schema: {
21
22
  name: 'browser_close',
22
23
  description: 'Close the persistent browser session. The next browser tool will start a new one.',
@@ -25,6 +26,17 @@ exports.browserCloseTool = {
25
26
  category: 'browser',
26
27
  mutates: true,
27
28
  toolset: 'browser',
29
+ riskTier: 'caution', // v4.4 Phase 1
30
+ buildPreview(args) {
31
+ return {
32
+ tool: 'browser_close',
33
+ args,
34
+ riskTier: 'caution',
35
+ sideEffects: [{ type: 'browser_action', action: 'close' }],
36
+ detectedRisks: [],
37
+ summary: 'Would close the browser session',
38
+ };
39
+ },
28
40
  async execute() {
29
41
  try {
30
42
  await (0, playwrightBridge_1.pwClose)();
@@ -36,3 +48,8 @@ exports.browserCloseTool = {
36
48
  }
37
49
  },
38
50
  };
51
+ // v4.3 Phase 1 — observer HOC. After close the post-snapshot will
52
+ // either spin up a fresh context (and return null on the first call
53
+ // since the page is blank) or fail cleanly — either way the sidecar
54
+ // just doesn't embed. Tiny overhead, uniform pattern.
55
+ exports.browserCloseTool = (0, _observer_1.withBrowserState)(_browserCloseTool);