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.
- package/README.md +250 -847
- package/dist/api/server.js +32 -5
- package/dist/cli/v4/aidenCLI.js +379 -53
- package/dist/cli/v4/callbacks.js +248 -0
- package/dist/cli/v4/chatSession.js +292 -4
- package/dist/cli/v4/commands/_runtimeToggleHelpers.js +92 -0
- package/dist/cli/v4/commands/browserDepth.js +45 -0
- package/dist/cli/v4/commands/cron.js +264 -0
- package/dist/cli/v4/commands/daemon.js +541 -0
- package/dist/cli/v4/commands/daemonStatus.js +253 -0
- package/dist/cli/v4/commands/help.js +7 -0
- package/dist/cli/v4/commands/index.js +20 -1
- package/dist/cli/v4/commands/runs.js +203 -0
- package/dist/cli/v4/commands/sandbox.js +48 -0
- package/dist/cli/v4/commands/suggestions.js +68 -0
- package/dist/cli/v4/commands/tce.js +41 -0
- package/dist/cli/v4/commands/trigger.js +378 -0
- package/dist/cli/v4/commands/update.js +95 -3
- package/dist/cli/v4/daemonAgentBuilder.js +142 -0
- package/dist/cli/v4/defaultSoul.js +75 -3
- package/dist/cli/v4/display/capabilityCard.js +26 -0
- package/dist/cli/v4/display/progressBar.js +41 -8
- package/dist/cli/v4/display.js +258 -15
- package/dist/cli/v4/replyRenderer.js +31 -23
- package/dist/cli/v4/toolPreview.js +10 -0
- package/dist/cli/v4/updateBootPrompt.js +170 -0
- package/dist/core/playwrightBridge.js +129 -0
- package/dist/core/toolRegistry.js +7 -1
- package/dist/core/v4/aidenAgent.js +371 -4
- package/dist/core/v4/browserState.js +436 -0
- package/dist/core/v4/checkpoint.js +79 -0
- package/dist/core/v4/daemon/bootstrap.js +604 -0
- package/dist/core/v4/daemon/cleanShutdown.js +154 -0
- package/dist/core/v4/daemon/cron/cronBridge.js +126 -0
- package/dist/core/v4/daemon/cron/cronEmitter.js +173 -0
- package/dist/core/v4/daemon/cron/migration.js +199 -0
- package/dist/core/v4/daemon/cron/misfirePolicy.js +115 -0
- package/dist/core/v4/daemon/daemonConfig.js +90 -0
- package/dist/core/v4/daemon/db/connection.js +106 -0
- package/dist/core/v4/daemon/db/migrations.js +296 -0
- package/dist/core/v4/daemon/db/schema/v1.spec.js +18 -0
- package/dist/core/v4/daemon/dispatcher/agentRunner.js +98 -0
- package/dist/core/v4/daemon/dispatcher/budgetGate.js +127 -0
- package/dist/core/v4/daemon/dispatcher/daemonApproval.js +113 -0
- package/dist/core/v4/daemon/dispatcher/dailyBudgetTracker.js +120 -0
- package/dist/core/v4/daemon/dispatcher/dispatcher.js +389 -0
- package/dist/core/v4/daemon/dispatcher/fireRateLimiter.js +113 -0
- package/dist/core/v4/daemon/dispatcher/index.js +53 -0
- package/dist/core/v4/daemon/dispatcher/promptTemplate.js +95 -0
- package/dist/core/v4/daemon/dispatcher/realAgentRunner.js +356 -0
- package/dist/core/v4/daemon/dispatcher/resolveModel.js +93 -0
- package/dist/core/v4/daemon/dispatcher/sessionId.js +93 -0
- package/dist/core/v4/daemon/drain.js +156 -0
- package/dist/core/v4/daemon/eventLoopLag.js +73 -0
- package/dist/core/v4/daemon/health.js +159 -0
- package/dist/core/v4/daemon/idempotencyStore.js +204 -0
- package/dist/core/v4/daemon/index.js +179 -0
- package/dist/core/v4/daemon/instanceTracker.js +99 -0
- package/dist/core/v4/daemon/resourceRegistry.js +150 -0
- package/dist/core/v4/daemon/restartCode.js +32 -0
- package/dist/core/v4/daemon/restartFailureCounter.js +77 -0
- package/dist/core/v4/daemon/runStore.js +114 -0
- package/dist/core/v4/daemon/runtimeLock.js +167 -0
- package/dist/core/v4/daemon/signals.js +50 -0
- package/dist/core/v4/daemon/supervisor.js +272 -0
- package/dist/core/v4/daemon/triggerBus.js +279 -0
- package/dist/core/v4/daemon/triggers/email/allowlist.js +70 -0
- package/dist/core/v4/daemon/triggers/email/automatedSender.js +78 -0
- package/dist/core/v4/daemon/triggers/email/bodyExtractor.js +0 -0
- package/dist/core/v4/daemon/triggers/email/emailSeenStore.js +99 -0
- package/dist/core/v4/daemon/triggers/email/emailSpec.js +107 -0
- package/dist/core/v4/daemon/triggers/email/imapConnection.js +211 -0
- package/dist/core/v4/daemon/triggers/email/index.js +332 -0
- package/dist/core/v4/daemon/triggers/email/seenUids.js +60 -0
- package/dist/core/v4/daemon/triggers/fileObservationsStore.js +93 -0
- package/dist/core/v4/daemon/triggers/fileWatcher.js +253 -0
- package/dist/core/v4/daemon/triggers/fileWatcherSpec.js +88 -0
- package/dist/core/v4/daemon/triggers/fsIdentity.js +42 -0
- package/dist/core/v4/daemon/triggers/globMatcher.js +100 -0
- package/dist/core/v4/daemon/triggers/reconcile.js +206 -0
- package/dist/core/v4/daemon/triggers/settleStat.js +81 -0
- package/dist/core/v4/daemon/triggers/webhook.js +376 -0
- package/dist/core/v4/daemon/triggers/webhookDeliveriesStore.js +109 -0
- package/dist/core/v4/daemon/triggers/webhookIdempotency.js +72 -0
- package/dist/core/v4/daemon/triggers/webhookRateLimit.js +56 -0
- package/dist/core/v4/daemon/triggers/webhookSpec.js +76 -0
- package/dist/core/v4/daemon/triggers/webhookVerifier.js +128 -0
- package/dist/core/v4/daemon/types.js +15 -0
- package/dist/core/v4/dockerSession.js +461 -0
- package/dist/core/v4/dryRun.js +117 -0
- package/dist/core/v4/failureClassifier.js +779 -0
- package/dist/core/v4/loopTrace.js +257 -0
- package/dist/core/v4/recoveryReport.js +449 -0
- package/dist/core/v4/runtimeToggles.js +187 -0
- package/dist/core/v4/sandboxConfig.js +285 -0
- package/dist/core/v4/sandboxFs.js +316 -0
- package/dist/core/v4/suggestionCatalog.js +41 -0
- package/dist/core/v4/suggestionEngine.js +210 -0
- package/dist/core/v4/toolRegistry.js +18 -0
- package/dist/core/v4/turnState.js +587 -0
- package/dist/core/v4/update/checkUpdate.js +63 -3
- package/dist/core/v4/update/installMethodDetect.js +115 -0
- package/dist/core/v4/update/registryClient.js +121 -0
- package/dist/core/v4/update/skipState.js +75 -0
- package/dist/core/v4/verifier.js +448 -0
- package/dist/core/version.js +1 -1
- package/dist/core/webSearch.js +64 -24
- package/dist/tools/v4/browser/_observer.js +224 -0
- package/dist/tools/v4/browser/browserBlocker.js +396 -0
- package/dist/tools/v4/browser/browserClick.js +18 -1
- package/dist/tools/v4/browser/browserClose.js +18 -1
- package/dist/tools/v4/browser/browserExtract.js +5 -1
- package/dist/tools/v4/browser/browserFill.js +17 -1
- package/dist/tools/v4/browser/browserGetUrl.js +5 -1
- package/dist/tools/v4/browser/browserNavigate.js +16 -1
- package/dist/tools/v4/browser/browserScreenshot.js +5 -1
- package/dist/tools/v4/browser/browserScroll.js +18 -1
- package/dist/tools/v4/browser/browserType.js +17 -1
- package/dist/tools/v4/browser/captchaCheck.js +5 -1
- package/dist/tools/v4/executeCode.js +1 -0
- package/dist/tools/v4/files/fileCopy.js +56 -2
- package/dist/tools/v4/files/fileDelete.js +38 -1
- package/dist/tools/v4/files/fileList.js +12 -1
- package/dist/tools/v4/files/fileMove.js +59 -2
- package/dist/tools/v4/files/filePatch.js +43 -1
- package/dist/tools/v4/files/fileRead.js +12 -1
- package/dist/tools/v4/files/fileWrite.js +41 -1
- package/dist/tools/v4/index.js +71 -58
- package/dist/tools/v4/memory/memoryAdd.js +14 -0
- package/dist/tools/v4/memory/memoryRemove.js +14 -0
- package/dist/tools/v4/memory/memoryReplace.js +15 -0
- package/dist/tools/v4/memory/sessionSummary.js +12 -0
- package/dist/tools/v4/process/processKill.js +19 -0
- package/dist/tools/v4/process/processList.js +1 -0
- package/dist/tools/v4/process/processLogRead.js +1 -0
- package/dist/tools/v4/process/processSpawn.js +13 -0
- package/dist/tools/v4/process/processWait.js +1 -0
- package/dist/tools/v4/sessions/recallSession.js +1 -0
- package/dist/tools/v4/sessions/sessionList.js +1 -0
- package/dist/tools/v4/sessions/sessionSearch.js +1 -0
- package/dist/tools/v4/skills/lookupToolSchema.js +2 -0
- package/dist/tools/v4/skills/skillManage.js +13 -0
- package/dist/tools/v4/skills/skillView.js +1 -0
- package/dist/tools/v4/skills/skillsList.js +1 -0
- package/dist/tools/v4/subagent/subagentFanout.js +1 -0
- package/dist/tools/v4/system/aidenSelfUpdate.js +16 -0
- package/dist/tools/v4/system/appClose.js +13 -0
- package/dist/tools/v4/system/appInput.js +13 -0
- package/dist/tools/v4/system/appLaunch.js +13 -0
- package/dist/tools/v4/system/clipboardRead.js +1 -0
- package/dist/tools/v4/system/clipboardWrite.js +14 -0
- package/dist/tools/v4/system/mediaKey.js +12 -0
- package/dist/tools/v4/system/mediaSessions.js +1 -0
- package/dist/tools/v4/system/mediaTransport.js +13 -0
- package/dist/tools/v4/system/naturalEvents.js +1 -0
- package/dist/tools/v4/system/nowPlaying.js +1 -0
- package/dist/tools/v4/system/osProcessList.js +1 -0
- package/dist/tools/v4/system/screenshot.js +1 -0
- package/dist/tools/v4/system/systemInfo.js +1 -0
- package/dist/tools/v4/system/volumeSet.js +17 -0
- package/dist/tools/v4/terminal/shellExec.js +81 -9
- package/dist/tools/v4/web/deepResearch.js +1 -0
- package/dist/tools/v4/web/openUrl.js +1 -0
- package/dist/tools/v4/web/webFetch.js +1 -0
- package/dist/tools/v4/web/webPage.js +1 -0
- package/dist/tools/v4/web/webSearch.js +1 -0
- package/dist/tools/v4/web/youtubeSearch.js +1 -0
- package/package.json +7 -1
- 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
|
-
|
|
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
|
-
|
|
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);
|