Skip to content

Commit 1457340

Browse files
authored
Merge pull request #47 from SentienceAPI/improve_agent3
Adapt SDK to wait for loading async function in chrome
2 parents 120b370 + f7051e1 commit 1457340

File tree

2 files changed

+126
-2
lines changed

2 files changed

+126
-2
lines changed

src/global.d.ts

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
/**
2+
* Type definitions for Sentience Chrome Extension API
3+
*
4+
* This file defines the global window.sentience API that is injected
5+
* by the Sentience Chrome Extension into every page.
6+
*
7+
* These types allow TypeScript code to call window.sentience methods
8+
* without using 'as any' casts.
9+
*/
10+
11+
/**
12+
* Sentience Chrome Extension API
13+
*
14+
* The actual return types match the SDK's types.ts definitions,
15+
* but we use 'any' here to avoid conflicts between browser context
16+
* and Node.js context types.
17+
*/
18+
interface SentienceAPI {
19+
/**
20+
* Take a snapshot of the current page
21+
*
22+
* Extracts interactive elements with semantic understanding,
23+
* scores them by importance, and returns structured data.
24+
*
25+
* @param options Snapshot configuration options
26+
* @returns Promise resolving to snapshot data
27+
*
28+
* @example
29+
* ```typescript
30+
* // Basic snapshot
31+
* const result = await window.sentience.snapshot();
32+
* console.log(result.elements); // Top 50 elements
33+
*
34+
* // With options
35+
* const result = await window.sentience.snapshot({
36+
* limit: 100,
37+
* screenshot: true,
38+
* filter: { min_area: 50 }
39+
* });
40+
* ```
41+
*/
42+
snapshot(options?: any): Promise<any>;
43+
44+
/**
45+
* Click an element by its ID
46+
*
47+
* @param id Element ID from snapshot
48+
* @returns true if click succeeded, false otherwise
49+
*/
50+
click(id: number): boolean;
51+
52+
/**
53+
* Get readable text from the page
54+
*
55+
* @param options Read options
56+
* @returns Extracted text content
57+
*/
58+
read(options?: any): any;
59+
60+
/**
61+
* Internal: WASM module reference (may not be exposed)
62+
* @internal
63+
*/
64+
_wasmModule?: any;
65+
}
66+
67+
/**
68+
* Extend the global Window interface
69+
*/
70+
declare global {
71+
interface Window {
72+
/**
73+
* Sentience Chrome Extension API
74+
*
75+
* This API is injected by the Sentience extension and provides
76+
* programmatic access to semantic web page analysis.
77+
*/
78+
sentience: SentienceAPI;
79+
80+
/**
81+
* Internal: Element registry for click tracking
82+
* @internal
83+
*/
84+
sentience_registry: HTMLElement[];
85+
}
86+
}
87+
88+
// This export makes this a module (required for declaration merging)
89+
export {};

src/snapshot.ts

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,28 @@ async function snapshotViaExtension(
4444
): Promise<Snapshot> {
4545
const page = browser.getPage();
4646

47+
// CRITICAL: Wait for extension injection to complete (CSP-resistant architecture)
48+
// The new architecture loads injected_api.js asynchronously, so window.sentience
49+
// may not be immediately available after page load
50+
try {
51+
await page.waitForFunction(
52+
() => typeof window.sentience !== 'undefined',
53+
{ timeout: 5000 }
54+
);
55+
} catch (e) {
56+
// Gather diagnostics if wait fails
57+
const diag = await page.evaluate(() => ({
58+
sentience_defined: typeof window.sentience !== 'undefined',
59+
extension_id: document.documentElement.dataset.sentienceExtensionId || 'not set',
60+
url: window.location.href
61+
})).catch(() => ({ error: 'Could not gather diagnostics' }));
62+
63+
throw new Error(
64+
`Sentience extension failed to inject window.sentience API. ` +
65+
`Is the extension loaded? Diagnostics: ${JSON.stringify(diag)}`
66+
);
67+
}
68+
4769
// Build options object
4870
const opts: any = {};
4971
if (options.screenshot !== undefined) {
@@ -56,9 +78,9 @@ async function snapshotViaExtension(
5678
opts.filter = options.filter;
5779
}
5880

59-
// Call extension API
81+
// Call extension API (no 'as any' needed - types defined in global.d.ts)
6082
const result = await page.evaluate((opts) => {
61-
return (window as any).sentience.snapshot(opts);
83+
return window.sentience.snapshot(opts);
6284
}, opts);
6385

6486
// Basic validation
@@ -77,6 +99,19 @@ async function snapshotViaApi(
7799
): Promise<Snapshot> {
78100
const page = browser.getPage();
79101

102+
// CRITICAL: Wait for extension injection to complete (CSP-resistant architecture)
103+
// Even for API mode, we need the extension to collect raw data locally
104+
try {
105+
await page.waitForFunction(
106+
() => typeof (window as any).sentience !== 'undefined',
107+
{ timeout: 5000 }
108+
);
109+
} catch (e) {
110+
throw new Error(
111+
'Sentience extension failed to inject. Cannot collect raw data for API processing.'
112+
);
113+
}
114+
80115
// Step 1: Get raw data from local extension (always happens locally)
81116
const rawOpts: any = {};
82117
if (options.screenshot !== undefined) {

0 commit comments

Comments
 (0)