@nuanu-ai/agentbrowse 0.2.24 → 0.2.26

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.
@@ -1,5 +1,5 @@
1
1
  import { type AcceptanceProbe, type PageObservation } from './action-acceptance.js';
2
- export type SubmitResultClaimKind = 'hard_result' | 'hard_blocker' | 'soft_result_candidate' | 'noisy_change';
2
+ export type SubmitResultClaimKind = 'hard_result' | 'hard_blocker' | 'intermediate_progress' | 'soft_result_candidate' | 'noisy_change';
3
3
  export type SubmitResultClaim = {
4
4
  kind: SubmitResultClaimKind;
5
5
  source: string;
@@ -7,7 +7,7 @@ export type SubmitResultClaim = {
7
7
  ownerScopeRef?: string;
8
8
  };
9
9
  export type SubmitResultResolution = {
10
- finalVerdict: 'outcome' | 'blocked' | 'none';
10
+ finalVerdict: 'outcome' | 'blocked' | 'progress' | 'none';
11
11
  claims: SubmitResultClaim[];
12
12
  decisiveClaims: SubmitResultClaim[];
13
13
  acceptAsProgress: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"action-result-resolution.d.ts","sourceRoot":"","sources":["../../src/commands/action-result-resolution.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,eAAe,EACpB,KAAK,eAAe,EACrB,MAAM,wBAAwB,CAAC;AAGhC,MAAM,MAAM,qBAAqB,GAC7B,aAAa,GACb,cAAc,GACd,uBAAuB,GACvB,cAAc,CAAC;AAEnB,MAAM,MAAM,iBAAiB,GAAG;IAC9B,IAAI,EAAE,qBAAqB,CAAC;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAAG;IACnC,YAAY,EAAE,SAAS,GAAG,SAAS,GAAG,MAAM,CAAC;IAC7C,MAAM,EAAE,iBAAiB,EAAE,CAAC;IAC5B,cAAc,EAAE,iBAAiB,EAAE,CAAC;IACpC,gBAAgB,EAAE,OAAO,CAAC;CAC3B,CAAC;AAEF,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,aAAa,CAAC,iBAAiB,CAAC,GACvC,IAAI,CAAC,sBAAsB,EAAE,QAAQ,GAAG,kBAAkB,CAAC,CAqB7D;AAmBD,wBAAsB,mBAAmB,CACvC,KAAK,EAAE,eAAe,EACtB,oBAAoB,EAAE,eAAe,GAAG,IAAI,GAC3C,OAAO,CAAC,sBAAsB,CAAC,CAsEjC"}
1
+ {"version":3,"file":"action-result-resolution.d.ts","sourceRoot":"","sources":["../../src/commands/action-result-resolution.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,eAAe,EACpB,KAAK,eAAe,EACrB,MAAM,wBAAwB,CAAC;AAGhC,MAAM,MAAM,qBAAqB,GAC7B,aAAa,GACb,cAAc,GACd,uBAAuB,GACvB,uBAAuB,GACvB,cAAc,CAAC;AAEnB,MAAM,MAAM,iBAAiB,GAAG;IAC9B,IAAI,EAAE,qBAAqB,CAAC;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAAG;IACnC,YAAY,EAAE,SAAS,GAAG,SAAS,GAAG,UAAU,GAAG,MAAM,CAAC;IAC1D,MAAM,EAAE,iBAAiB,EAAE,CAAC;IAC5B,cAAc,EAAE,iBAAiB,EAAE,CAAC;IACpC,gBAAgB,EAAE,OAAO,CAAC;CAC3B,CAAC;AAEF,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,aAAa,CAAC,iBAAiB,CAAC,GACvC,IAAI,CAAC,sBAAsB,EAAE,QAAQ,GAAG,kBAAkB,CAAC,CA+B7D;AAmBD,wBAAsB,mBAAmB,CACvC,KAAK,EAAE,eAAe,EACtB,oBAAoB,EAAE,eAAe,GAAG,IAAI,GAC3C,OAAO,CAAC,sBAAsB,CAAC,CAoFjC"}
@@ -15,6 +15,13 @@ export function reduceSubmitResultClaims(claims) {
15
15
  decisiveClaims: hardResults,
16
16
  };
17
17
  }
18
+ const intermediateProgressClaims = claims.filter((claim) => claim.kind === 'intermediate_progress');
19
+ if (intermediateProgressClaims.length > 0) {
20
+ return {
21
+ finalVerdict: 'progress',
22
+ decisiveClaims: intermediateProgressClaims,
23
+ };
24
+ }
18
25
  return {
19
26
  finalVerdict: 'none',
20
27
  decisiveClaims: [],
@@ -62,6 +69,8 @@ export async function resolveSubmitResult(probe, afterPageObservation) {
62
69
  }
63
70
  const resultSignalChanged = beforePageObservation.resultSignalHash !== afterPageObservation.resultSignalHash &&
64
71
  afterPageObservation.resultSignalHash !== null;
72
+ const submitSignalChanged = beforePageObservation.submitSignalHash !== afterPageObservation.submitSignalHash &&
73
+ afterPageObservation.submitSignalHash !== null;
65
74
  const afterSurfaceContextHash = await captureAfterSurfaceContextHash(probe);
66
75
  const ownerScopeChanged = probe.beforeSurfaceContextHash !== null &&
67
76
  afterSurfaceContextHash !== null &&
@@ -77,18 +86,29 @@ export async function resolveSubmitResult(probe, afterPageObservation) {
77
86
  });
78
87
  }
79
88
  else if (ownerScopeChanged) {
80
- claims.push({
81
- kind: 'noisy_change',
82
- source: 'owner-scope',
83
- reason: 'Owner scope changed after submit without a result-bearing signal.',
84
- ownerScopeRef: probe.surface?.ref,
85
- });
89
+ if (submitSignalChanged) {
90
+ claims.push({
91
+ kind: 'intermediate_progress',
92
+ source: 'owner-scope',
93
+ reason: 'Owner scope changed after submit with a submit-relevant follow-up state change.',
94
+ ownerScopeRef: probe.surface?.ref,
95
+ });
96
+ }
97
+ else {
98
+ claims.push({
99
+ kind: 'noisy_change',
100
+ source: 'owner-scope',
101
+ reason: 'Owner scope changed after submit without a result-bearing signal.',
102
+ ownerScopeRef: probe.surface?.ref,
103
+ });
104
+ }
86
105
  }
87
106
  const reduced = reduceSubmitResultClaims(claims);
88
107
  return {
89
108
  ...reduced,
90
109
  claims,
91
110
  acceptAsProgress: reduced.finalVerdict === 'outcome' ||
111
+ reduced.finalVerdict === 'progress' ||
92
112
  (reduced.finalVerdict === 'none' &&
93
113
  claims.some((claim) => claim.kind === 'soft_result_candidate')),
94
114
  };
@@ -1 +1 @@
1
- {"version":3,"file":"extract.d.ts","sourceRoot":"","sources":["../../src/commands/extract.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAmMnD,wBAAsB,OAAO,CAC3B,OAAO,EAAE,aAAa,EACtB,UAAU,EAAE,MAAM,EAClB,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC,CAkMf"}
1
+ {"version":3,"file":"extract.d.ts","sourceRoot":"","sources":["../../src/commands/extract.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAoMnD,wBAAsB,OAAO,CAC3B,OAAO,EAAE,aAAa,EACtB,UAAU,EAAE,MAAM,EAClB,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC,CAyMf"}
@@ -6,7 +6,7 @@ import { AgentpayStructuredOutputTruncatedError } from '../agentpay-stagehand-ll
6
6
  import { saveSession } from '../session.js';
7
7
  import { getSurface, getTarget, markSurfaceLifecycle, markTargetLifecycle, setCurrentPage, } from '../runtime-state.js';
8
8
  import { outputContractFailure, outputJSON } from '../output.js';
9
- import { connectPlaywright, disconnectPlaywright, resolvePageByRef as resolvePlaywrightPageByRef, syncSessionPage, } from '../playwright-runtime.js';
9
+ import { connectPlaywright, disconnectPlaywright, resolveCurrentPageContext, resolvePageByRef as resolvePlaywrightPageByRef, syncSessionPage, } from '../playwright-runtime.js';
10
10
  import { normalizePageSignature } from './descriptor-validation.js';
11
11
  import { readScopedDialogText } from './extract-scoped-dialog-text.js';
12
12
  import { resolveScopedExtractContext } from './extract-scope-resolution.js';
@@ -174,7 +174,7 @@ export async function extract(session, schemaJson, scopeRef) {
174
174
  const targetScope = scopeRef ? getTarget(session, scopeRef) : null;
175
175
  const surfaceScope = !targetScope && scopeRef ? getSurface(session, scopeRef) : null;
176
176
  const scopeTarget = targetScope ?? surfaceScope;
177
- const pageRef = scopeTarget?.pageRef ?? session.runtime?.currentPageRef ?? 'p0';
177
+ let pageRef = scopeTarget?.pageRef ?? session.runtime?.currentPageRef ?? 'p0';
178
178
  if (scopeRef && !scopeTarget) {
179
179
  return outputContractFailure({
180
180
  error: 'unknown_scope_ref',
@@ -221,7 +221,15 @@ export async function extract(session, schemaJson, scopeRef) {
221
221
  });
222
222
  }
223
223
  try {
224
- const sourcePage = await resolvePlaywrightPageByRef(browser, session, pageRef);
224
+ let sourcePage;
225
+ if (scopeTarget) {
226
+ sourcePage = await resolvePlaywrightPageByRef(browser, session, pageRef);
227
+ }
228
+ else {
229
+ const resolvedPage = await resolveCurrentPageContext(browser, session);
230
+ pageRef = resolvedPage.pageRef;
231
+ sourcePage = resolvedPage.page;
232
+ }
225
233
  let page = sourcePage;
226
234
  let scopedResolution = null;
227
235
  const { url, title } = await syncSessionPage(session, pageRef, sourcePage);
@@ -1 +1 @@
1
- {"version":3,"file":"navigate.d.ts","sourceRoot":"","sources":["../../src/commands/navigate.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAiBnD,wBAAsB,QAAQ,CAAC,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAyDvF"}
1
+ {"version":3,"file":"navigate.d.ts","sourceRoot":"","sources":["../../src/commands/navigate.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAgBnD,wBAAsB,QAAQ,CAAC,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAwDvF"}
@@ -1,11 +1,11 @@
1
1
  /**
2
2
  * browse navigate <url> — Navigate the current tab to a URL.
3
3
  */
4
- import { clearProtectedExposure } from '../runtime-state.js';
4
+ import { clearProtectedExposure, setCurrentPage } from '../runtime-state.js';
5
5
  import { saveSession } from '../session.js';
6
6
  import { outputContractFailure, outputJSON, info } from '../output.js';
7
7
  import { summarizeSecretCatalog, syncSecretCatalogForUrl, tryResolveCatalogHost, } from '../secrets/catalog-sync.js';
8
- import { connectPlaywright, disconnectPlaywright, listPages, resolvePageByRef, syncSessionPage, } from '../playwright-runtime.js';
8
+ import { connectPlaywright, disconnectPlaywright, resolveCurrentPageContext, syncSessionPage, } from '../playwright-runtime.js';
9
9
  export async function navigate(session, targetUrl) {
10
10
  let browser = null;
11
11
  let failureMessage = null;
@@ -22,18 +22,18 @@ export async function navigate(session, targetUrl) {
22
22
  });
23
23
  }
24
24
  try {
25
- const pageRef = session.runtime?.currentPageRef ?? 'p0';
25
+ let pageRef = session.runtime?.currentPageRef ?? 'p0';
26
+ const resolvedPage = await resolveCurrentPageContext(browser, session);
27
+ pageRef = resolvedPage.pageRef;
28
+ const page = resolvedPage.page;
26
29
  const previousUrl = session.runtime?.pages?.[pageRef]?.url;
27
30
  const previousHost = tryResolveCatalogHost(previousUrl);
28
- const pages = listPages(browser);
29
- const page = session.runtime?.pages[pageRef] || pages.length > 1
30
- ? await resolvePageByRef(browser, session, pageRef)
31
- : pages[0];
32
31
  if (!page) {
33
32
  throw new Error('no_open_pages');
34
33
  }
35
34
  await page.goto(targetUrl, { waitUntil: 'domcontentloaded' });
36
35
  const { url, title } = await syncSessionPage(session, pageRef, page);
36
+ setCurrentPage(session, pageRef);
37
37
  clearProtectedExposure(session, pageRef);
38
38
  const nextHost = tryResolveCatalogHost(url);
39
39
  if (nextHost && nextHost !== previousHost) {
@@ -1 +1 @@
1
- {"version":3,"file":"observe.d.ts","sourceRoot":"","sources":["../../src/commands/observe.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAqDnD,OAAO,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAU/D,eAAO,MAAM,yBAAyB;;;;iBA4TwQ,CAAC;gBAAwB,CAAC;;;;CA5TnQ,CAAC;AACtE,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAGrC,CAAC;AAEF,wBAAsB,OAAO,CAAC,OAAO,EAAE,aAAa,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAqTzF"}
1
+ {"version":3,"file":"observe.d.ts","sourceRoot":"","sources":["../../src/commands/observe.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AA0DnD,OAAO,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAU/D,eAAO,MAAM,yBAAyB;;;;iBA6T7B,CAAC;gBAEE,CAAA;;;;CA/TyD,CAAC;AACtE,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAGrC,CAAC;AAEF,wBAAsB,OAAO,CAAC,OAAO,EAAE,aAAa,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA8TzF"}
@@ -2,8 +2,8 @@
2
2
  * browse observe ["<instruction>"] — Discover available actions on the page.
3
3
  */
4
4
  import { saveSession } from '../session.js';
5
- import { ensureRuntimeState, incrementMetric, replaceTargetsForPage } from '../runtime-state.js';
6
- import { connectPlaywright, disconnectPlaywright, resolvePageByRef, syncSessionPage, } from '../playwright-runtime.js';
5
+ import { ensureRuntimeState, incrementMetric, replaceTargetsForPage, setCurrentPage, } from '../runtime-state.js';
6
+ import { connectPlaywright, disconnectPlaywright, resolveCurrentPageContext, syncSessionPage, } from '../playwright-runtime.js';
7
7
  import { outputContractFailure, outputJSON } from '../output.js';
8
8
  import { domRuntimeResolution, stagehandRuntimeResolution } from '../runtime-resolution.js';
9
9
  import { withStagehand } from '../stagehand-runtime.js';
@@ -26,7 +26,7 @@ export const __testStagehandDescriptor = {
26
26
  };
27
27
  export async function observe(session, instruction) {
28
28
  const runtime = ensureRuntimeState(session);
29
- const pageRef = runtime.currentPageRef;
29
+ let pageRef = runtime.currentPageRef;
30
30
  let domPassError = null;
31
31
  let stagehandFallbackReason = null;
32
32
  let browser = null;
@@ -34,8 +34,11 @@ export async function observe(session, instruction) {
34
34
  if (!instruction) {
35
35
  try {
36
36
  browser = await connectPlaywright(session.cdpUrl);
37
- const page = await resolvePageByRef(browser, session, pageRef);
37
+ const resolvedPage = await resolveCurrentPageContext(browser, session);
38
+ pageRef = resolvedPage.pageRef;
39
+ const page = resolvedPage.page;
38
40
  const { url, title } = await syncSessionPage(session, pageRef, page);
41
+ setCurrentPage(session, pageRef);
39
42
  const collectedTargets = await collectDomTargets(page);
40
43
  let observeAccessibilityStats;
41
44
  const domTargets = compressSemanticallyDuplicateTargets(orderBySurfaceCompetition(annotateDomTargets(await enrichDomTargetsWithAccessibility(page, collectedTargets, {
@@ -96,8 +99,11 @@ export async function observe(session, instruction) {
96
99
  if (!browser) {
97
100
  browser = await connectPlaywright(session.cdpUrl);
98
101
  }
99
- const page = await resolvePageByRef(browser, session, pageRef);
102
+ const resolvedPage = await resolveCurrentPageContext(browser, session);
103
+ pageRef = resolvedPage.pageRef;
104
+ const page = resolvedPage.page;
100
105
  const { url, title } = await syncSessionPage(session, pageRef, page);
106
+ setCurrentPage(session, pageRef);
101
107
  const collectedTargets = await collectDomTargets(page, {
102
108
  includeActivationAffordances: true,
103
109
  });
@@ -198,8 +204,11 @@ export async function observe(session, instruction) {
198
204
  });
199
205
  }
200
206
  try {
201
- const page = await resolvePageByRef(browser, session, pageRef);
207
+ const resolvedPage = await resolveCurrentPageContext(browser, session);
208
+ pageRef = resolvedPage.pageRef;
209
+ const page = resolvedPage.page;
202
210
  const { url, title } = await syncSessionPage(session, pageRef, page);
211
+ setCurrentPage(session, pageRef);
203
212
  const signals = compactSignals(await collectPageSignals(page).catch(() => []));
204
213
  const actions = await withStagehand(session, async (stagehand) => {
205
214
  incrementMetric(session, 'stagehandCalls');
@@ -1 +1 @@
1
- {"version":3,"file":"screenshot.d.ts","sourceRoot":"","sources":["../../src/commands/screenshot.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAYnD,wBAAsB,UAAU,CAAC,OAAO,EAAE,aAAa,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAsDzF"}
1
+ {"version":3,"file":"screenshot.d.ts","sourceRoot":"","sources":["../../src/commands/screenshot.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAWnD,wBAAsB,UAAU,CAAC,OAAO,EAAE,aAAa,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAsEzF"}
@@ -1,20 +1,19 @@
1
1
  /**
2
2
  * browse screenshot [--path <file>] — Capture a screenshot of the current page.
3
3
  */
4
- import { getProtectedExposure } from '../runtime-state.js';
4
+ import { getProtectedExposure, setCurrentPage } from '../runtime-state.js';
5
5
  import { saveSession } from '../session.js';
6
6
  import { outputFailure, outputJSON, outputContractFailure, info } from '../output.js';
7
- import { connectPlaywright, disconnectPlaywright, listPages, resolvePageByRef, syncSessionPage, } from '../playwright-runtime.js';
7
+ import { connectPlaywright, disconnectPlaywright, resolveCurrentPageContext, syncSessionPage, } from '../playwright-runtime.js';
8
8
  import { buildProtectedScreenshotBlockedResult } from '../secrets/protected-artifact-guard.js';
9
9
  export async function screenshot(session, filePath) {
10
10
  const outputPath = filePath ?? `/tmp/browse-screenshot-${Date.now()}.png`;
11
- const pageRef = session.runtime?.currentPageRef ?? 'p0';
12
- const protectedExposure = getProtectedExposure(session, pageRef);
13
- if (protectedExposure) {
14
- return outputFailure(buildProtectedScreenshotBlockedResult(protectedExposure));
15
- }
11
+ const initialPageRef = session.runtime?.currentPageRef ?? 'p0';
12
+ let pageRef = initialPageRef;
13
+ const initialProtectedExposure = getProtectedExposure(session, initialPageRef);
16
14
  let browser = null;
17
15
  let failureMessage = null;
16
+ let page = null;
18
17
  try {
19
18
  browser = await connectPlaywright(session.cdpUrl);
20
19
  }
@@ -27,16 +26,33 @@ export async function screenshot(session, filePath) {
27
26
  });
28
27
  }
29
28
  try {
30
- const pages = listPages(browser);
31
- const page = session.runtime?.pages[pageRef] || pages.length > 1
32
- ? await resolvePageByRef(browser, session, pageRef)
33
- : pages[0];
29
+ const resolvedPage = await resolveCurrentPageContext(browser, session);
30
+ pageRef = resolvedPage.pageRef;
31
+ page = resolvedPage.page;
32
+ }
33
+ catch (err) {
34
+ failureMessage = `Screenshot failed: ${err instanceof Error ? err.message : String(err)}`;
35
+ }
36
+ const recoveredProtectedExposure = getProtectedExposure(session, pageRef) ?? (!page ? initialProtectedExposure : null);
37
+ if (recoveredProtectedExposure) {
38
+ if (page && pageRef !== initialPageRef) {
39
+ setCurrentPage(session, pageRef);
40
+ saveSession(session);
41
+ }
42
+ if (browser) {
43
+ disconnectPlaywright(browser);
44
+ browser = null;
45
+ }
46
+ return outputFailure(buildProtectedScreenshotBlockedResult(recoveredProtectedExposure));
47
+ }
48
+ try {
34
49
  if (!page) {
35
- throw new Error('no_open_pages');
50
+ throw new Error(failureMessage?.replace(/^Screenshot failed:\s*/, '') ?? 'no_open_pages');
36
51
  }
37
52
  await page.screenshot({ path: outputPath });
38
53
  info(`Screenshot saved: ${outputPath}`);
39
54
  const { url, title } = await syncSessionPage(session, pageRef, page);
55
+ setCurrentPage(session, pageRef);
40
56
  saveSession(session);
41
57
  outputJSON({ success: true, pageRef, path: outputPath, url, title });
42
58
  }
@@ -1 +1 @@
1
- {"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/commands/status.ts"],"names":[],"mappings":"AAAA;;GAEG;AAoDH,wBAAsB,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CA8F5C"}
1
+ {"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/commands/status.ts"],"names":[],"mappings":"AAAA;;GAEG;AAmKH,wBAAsB,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CAgG5C"}
@@ -3,6 +3,21 @@
3
3
  */
4
4
  import { loadSession, isSessionAlive, getSessionPort, supportsCaptchaSolve } from '../session.js';
5
5
  import { outputJSON } from '../output.js';
6
+ function normalizeComparableUrl(url) {
7
+ const raw = url?.trim();
8
+ if (!raw) {
9
+ return null;
10
+ }
11
+ try {
12
+ const parsed = new URL(raw);
13
+ parsed.hash = '';
14
+ parsed.search = '';
15
+ return `${parsed.origin}${parsed.pathname}`;
16
+ }
17
+ catch {
18
+ return raw;
19
+ }
20
+ }
6
21
  function tryResolveHost(url) {
7
22
  if (!url) {
8
23
  return undefined;
@@ -23,6 +38,7 @@ function buildProtectedStatusPayload(params) {
23
38
  ? { captchaSolveCapable: params.captchaSolveCapable }
24
39
  : {}),
25
40
  runtime: params.runtimeSummary,
41
+ ...(params.currentPageMismatch ? { currentPageMismatch: params.currentPageMismatch } : {}),
26
42
  protectedExposureActive: true,
27
43
  pageRef: params.protectedExposure.pageRef,
28
44
  url: params.pageUrl ?? 'unknown',
@@ -36,6 +52,70 @@ function buildProtectedStatusPayload(params) {
36
52
  reason: 'AgentBrowse is treating the current page as sensitive because a protected fill was executed and values may still be visible.',
37
53
  };
38
54
  }
55
+ function matchesStoredPage(page, livePage) {
56
+ const storedUrl = normalizeComparableUrl(page?.url);
57
+ const currentUrl = normalizeComparableUrl(livePage.url);
58
+ if (!storedUrl || !currentUrl || storedUrl !== currentUrl) {
59
+ return null;
60
+ }
61
+ if (page?.title && livePage.title && page.title === livePage.title) {
62
+ return 'url-title-match';
63
+ }
64
+ return 'url-match';
65
+ }
66
+ function resolveCurrentPageMismatch(session, livePage) {
67
+ const runtime = session?.runtime;
68
+ if (!runtime || !livePage) {
69
+ return undefined;
70
+ }
71
+ const persistedPageRef = runtime.currentPageRef;
72
+ const currentPage = runtime.pages[persistedPageRef];
73
+ if (!currentPage) {
74
+ return {
75
+ persistedPageRef,
76
+ };
77
+ }
78
+ if (matchesStoredPage(currentPage, livePage)) {
79
+ return undefined;
80
+ }
81
+ const openerPageRef = currentPage.openerPageRef;
82
+ if (openerPageRef && matchesStoredPage(runtime.pages[openerPageRef], livePage)) {
83
+ return {
84
+ persistedPageRef,
85
+ livePageRef: openerPageRef,
86
+ recoveredVia: 'opener',
87
+ };
88
+ }
89
+ const exactMatches = Object.values(runtime.pages).filter((page) => {
90
+ if (page.pageRef === persistedPageRef || page.pageRef === openerPageRef) {
91
+ return false;
92
+ }
93
+ return matchesStoredPage(page, livePage) === 'url-title-match';
94
+ });
95
+ if (exactMatches.length === 1) {
96
+ return {
97
+ persistedPageRef,
98
+ livePageRef: exactMatches[0].pageRef,
99
+ recoveredVia: 'url-title-match',
100
+ };
101
+ }
102
+ const urlOnlyMatches = Object.values(runtime.pages).filter((page) => {
103
+ if (page.pageRef === persistedPageRef || page.pageRef === openerPageRef) {
104
+ return false;
105
+ }
106
+ return matchesStoredPage(page, livePage) === 'url-match';
107
+ });
108
+ if (urlOnlyMatches.length === 1) {
109
+ return {
110
+ persistedPageRef,
111
+ livePageRef: urlOnlyMatches[0].pageRef,
112
+ recoveredVia: 'url-match',
113
+ };
114
+ }
115
+ return {
116
+ persistedPageRef,
117
+ };
118
+ }
39
119
  export async function status() {
40
120
  const session = loadSession();
41
121
  const port = getSessionPort(session);
@@ -57,15 +137,17 @@ export async function status() {
57
137
  const listRes = await fetch(`http://localhost:${port}/json/list`);
58
138
  const targets = (await listRes.json());
59
139
  const page = targets.find((t) => t.type === 'page' && !t.url.startsWith('devtools://'));
60
- const currentPageRef = session?.runtime?.currentPageRef;
61
- const protectedExposure = currentPageRef
62
- ? session?.runtime?.protectedExposureByPage?.[currentPageRef]
140
+ const currentPageMismatch = resolveCurrentPageMismatch(session, page);
141
+ const effectivePageRef = currentPageMismatch?.livePageRef ?? session?.runtime?.currentPageRef;
142
+ const protectedExposure = effectivePageRef
143
+ ? session?.runtime?.protectedExposureByPage?.[effectivePageRef]
63
144
  : undefined;
64
145
  if (protectedExposure) {
65
146
  outputJSON(buildProtectedStatusPayload({
66
147
  runtimeSummary,
67
148
  pageUrl: page?.url,
68
149
  pageTitle: page?.title,
150
+ currentPageMismatch,
69
151
  protectedExposure,
70
152
  }));
71
153
  return;
@@ -76,6 +158,7 @@ export async function status() {
76
158
  url: page?.url ?? 'unknown',
77
159
  title: page?.title ?? 'unknown',
78
160
  runtime: runtimeSummary,
161
+ ...(currentPageMismatch ? { currentPageMismatch } : {}),
79
162
  });
80
163
  return;
81
164
  }
@@ -90,9 +173,10 @@ export async function status() {
90
173
  const listRes = await fetch(`http://localhost:${port}/json/list`);
91
174
  const targets = (await listRes.json());
92
175
  const page = targets.find((t) => t.type === 'page' && !t.url.startsWith('devtools://'));
93
- const currentPageRef = session.runtime?.currentPageRef;
94
- const protectedExposure = currentPageRef
95
- ? session.runtime?.protectedExposureByPage?.[currentPageRef]
176
+ const currentPageMismatch = resolveCurrentPageMismatch(session, page);
177
+ const effectivePageRef = currentPageMismatch?.livePageRef ?? session.runtime?.currentPageRef;
178
+ const protectedExposure = effectivePageRef
179
+ ? session.runtime?.protectedExposureByPage?.[effectivePageRef]
96
180
  : undefined;
97
181
  if (protectedExposure) {
98
182
  outputJSON(buildProtectedStatusPayload({
@@ -100,6 +184,7 @@ export async function status() {
100
184
  pageUrl: page?.url,
101
185
  pageTitle: page?.title,
102
186
  captchaSolveCapable: supportsCaptchaSolve(session),
187
+ currentPageMismatch,
103
188
  protectedExposure,
104
189
  }));
105
190
  return;
@@ -111,6 +196,7 @@ export async function status() {
111
196
  url: page?.url ?? 'unknown',
112
197
  title: page?.title ?? 'unknown',
113
198
  runtime: runtimeSummary,
199
+ ...(currentPageMismatch ? { currentPageMismatch } : {}),
114
200
  });
115
201
  return;
116
202
  }
@@ -9,6 +9,11 @@ type LaunchPageMetadata = {
9
9
  title: string;
10
10
  targetId?: string;
11
11
  };
12
+ export type ResolvedCurrentPageContext = {
13
+ pageRef: string;
14
+ page: Page;
15
+ recoveredVia?: 'opener' | 'sole-live-page';
16
+ };
12
17
  export declare function readLaunchPageMetadata(browser: Browser, options?: {
13
18
  requestedUrl?: string;
14
19
  fallbackUrl?: string;
@@ -26,6 +31,7 @@ export declare function syncLaunchPage(session: BrowseSession, browser: Browser,
26
31
  targetId?: string;
27
32
  }>;
28
33
  export declare function resolvePageByRef(browser: Browser, session: BrowseSession, pageRef: string): Promise<Page>;
34
+ export declare function resolveCurrentPageContext(browser: Browser, session: BrowseSession): Promise<ResolvedCurrentPageContext>;
29
35
  export declare function syncSessionPage(session: BrowseSession, pageRef: string, page: Page, options?: {
30
36
  settleTimeoutMs?: number;
31
37
  }): Promise<{
@@ -1 +1 @@
1
- {"version":3,"file":"playwright-runtime.d.ts","sourceRoot":"","sources":["../src/playwright-runtime.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,KAAK,OAAO,EAAE,KAAK,IAAI,EAAE,MAAM,iBAAiB,CAAC;AACpE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAGlD,wBAAsB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAExE;AAED,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAO3D;AAED,wBAAgB,SAAS,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,EAAE,CAElD;AAmCD,KAAK,kBAAkB,GAAG;IACxB,IAAI,EAAE,IAAI,GAAG,IAAI,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAkJF,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,OAAO,EAChB,OAAO,GAAE;IACP,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;CACf,GACL,OAAO,CAAC,kBAAkB,CAAC,CA+C7B;AAED,wBAAsB,cAAc,CAClC,OAAO,EAAE,aAAa,EACtB,OAAO,EAAE,OAAO,EAChB,OAAO,GAAE;IACP,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;CACf,GACL,OAAO,CAAC;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAgC5D;AAED,wBAAsB,gBAAgB,CACpC,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,aAAa,EACtB,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,IAAI,CAAC,CA8Cf;AAED,wBAAsB,eAAe,CACnC,OAAO,EAAE,aAAa,EACtB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,IAAI,EACV,OAAO,GAAE;IACP,eAAe,CAAC,EAAE,MAAM,CAAC;CACrB,GACL,OAAO,CAAC;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAgB5D"}
1
+ {"version":3,"file":"playwright-runtime.d.ts","sourceRoot":"","sources":["../src/playwright-runtime.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,KAAK,OAAO,EAAE,KAAK,IAAI,EAAE,MAAM,iBAAiB,CAAC;AACpE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAGlD,wBAAsB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAExE;AAED,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAO3D;AAED,wBAAgB,SAAS,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,EAAE,CAElD;AA8CD,KAAK,kBAAkB,GAAG;IACxB,IAAI,EAAE,IAAI,GAAG,IAAI,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAQF,MAAM,MAAM,0BAA0B,GAAG;IACvC,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,IAAI,CAAC;IACX,YAAY,CAAC,EAAE,QAAQ,GAAG,gBAAgB,CAAC;CAC5C,CAAC;AA4IF,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,OAAO,EAChB,OAAO,GAAE;IACP,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;CACf,GACL,OAAO,CAAC,kBAAkB,CAAC,CA+C7B;AAED,wBAAsB,cAAc,CAClC,OAAO,EAAE,aAAa,EACtB,OAAO,EAAE,OAAO,EAChB,OAAO,GAAE;IACP,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;CACf,GACL,OAAO,CAAC;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAgC5D;AAED,wBAAsB,gBAAgB,CACpC,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,aAAa,EACtB,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,IAAI,CAAC,CA8Cf;AA+CD,wBAAsB,yBAAyB,CAC7C,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,aAAa,GACrB,OAAO,CAAC,0BAA0B,CAAC,CAqCrC;AAED,wBAAsB,eAAe,CACnC,OAAO,EAAE,aAAa,EACtB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,IAAI,EACV,OAAO,GAAE;IACP,eAAe,CAAC,EAAE,MAAM,CAAC;CACrB,GACL,OAAO,CAAC;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAgB5D"}
@@ -17,6 +17,10 @@ export function listPages(browser) {
17
17
  function buildPageResolutionError(kind, pageRef) {
18
18
  return new Error(`${kind === 'unknown' ? 'unknown_page_ref' : 'stale_page_ref'}:${pageRef}`);
19
19
  }
20
+ function isPageResolutionError(error, kind, pageRef) {
21
+ return (error instanceof Error &&
22
+ error.message === `${kind === 'unknown' ? 'unknown_page_ref' : 'stale_page_ref'}:${pageRef}`);
23
+ }
20
24
  function sleep(ms) {
21
25
  return new Promise((resolve) => setTimeout(resolve, ms));
22
26
  }
@@ -250,6 +254,78 @@ export async function resolvePageByRef(browser, session, pageRef) {
250
254
  }
251
255
  throw buildPageResolutionError('stale', pageRef);
252
256
  }
257
+ async function findRecoverableCurrentPageRef(browser, session, stalePageRef) {
258
+ const runtimePages = session.runtime?.pages ?? {};
259
+ const stalePageState = runtimePages[stalePageRef];
260
+ const openerPageRef = stalePageState?.openerPageRef;
261
+ if (openerPageRef && openerPageRef !== stalePageRef) {
262
+ try {
263
+ await resolvePageByRef(browser, session, openerPageRef);
264
+ return {
265
+ pageRef: openerPageRef,
266
+ recoveredVia: 'opener',
267
+ };
268
+ }
269
+ catch {
270
+ // Keep recovery fail-closed unless opener or a single live alternative is provably valid.
271
+ }
272
+ }
273
+ const liveCandidateRefs = [];
274
+ for (const candidatePageRef of Object.keys(runtimePages)) {
275
+ if (candidatePageRef === stalePageRef || candidatePageRef === openerPageRef) {
276
+ continue;
277
+ }
278
+ try {
279
+ await resolvePageByRef(browser, session, candidatePageRef);
280
+ liveCandidateRefs.push(candidatePageRef);
281
+ }
282
+ catch {
283
+ // Ignore stale or unknown candidates while looking for a single surviving live page.
284
+ }
285
+ }
286
+ if (liveCandidateRefs.length === 1) {
287
+ return {
288
+ pageRef: liveCandidateRefs[0],
289
+ recoveredVia: 'sole-live-page',
290
+ };
291
+ }
292
+ return null;
293
+ }
294
+ export async function resolveCurrentPageContext(browser, session) {
295
+ const currentPageRef = session.runtime?.currentPageRef ?? 'p0';
296
+ try {
297
+ const page = await resolvePageByRef(browser, session, currentPageRef);
298
+ return {
299
+ pageRef: currentPageRef,
300
+ page,
301
+ };
302
+ }
303
+ catch (error) {
304
+ if (isPageResolutionError(error, 'unknown', currentPageRef)) {
305
+ const pages = listPages(browser);
306
+ if (pages.length === 1) {
307
+ return {
308
+ pageRef: currentPageRef,
309
+ page: pages[0],
310
+ recoveredVia: 'sole-live-page',
311
+ };
312
+ }
313
+ throw error;
314
+ }
315
+ if (!isPageResolutionError(error, 'stale', currentPageRef)) {
316
+ throw error;
317
+ }
318
+ const recovered = await findRecoverableCurrentPageRef(browser, session, currentPageRef);
319
+ if (!recovered) {
320
+ throw error;
321
+ }
322
+ return {
323
+ pageRef: recovered.pageRef,
324
+ page: await resolvePageByRef(browser, session, recovered.pageRef),
325
+ recoveredVia: recovered.recoveredVia,
326
+ };
327
+ }
328
+ }
253
329
  export async function syncSessionPage(session, pageRef, page, options = {}) {
254
330
  ensureRuntimeState(session);
255
331
  const { url, title, targetId } = await readSettledSessionPageMetadata(session, pageRef, page, options);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nuanu-ai/agentbrowse",
3
- "version": "0.2.24",
3
+ "version": "0.2.26",
4
4
  "type": "module",
5
5
  "description": "Browser automation CLI for AI agents: control a CDP browser, observe UI surfaces, act on refs, extract data, capture screenshots, complete protected fills, and solve captchas",
6
6
  "keywords": [