diff --git a/src/resources/extensions/browser-tools/core.d.ts b/src/resources/extensions/browser-tools/core.d.ts new file mode 100644 index 000000000..3f740e355 --- /dev/null +++ b/src/resources/extensions/browser-tools/core.d.ts @@ -0,0 +1,205 @@ +/** + * Type declarations for core.js — runtime-neutral helper logic for browser-tools. + */ + +export interface ActionTimeline { + limit: number; + nextId: number; + entries: ActionEntry[]; +} + +export interface ActionEntry { + id: number; + tool: string; + paramsSummary: string; + startedAt: number; + finishedAt: number | null; + status: string; + beforeUrl: string; + afterUrl: string; + verificationSummary?: string; + warningSummary?: string; + diffSummary?: string; + changed?: boolean; + error?: string; +} + +export interface ActionPartial { + tool: string; + paramsSummary?: string; + startedAt?: number; + beforeUrl?: string; + afterUrl?: string; + verificationSummary?: string; + warningSummary?: string; + diffSummary?: string; + changed?: boolean; + error?: string; +} + +export interface ActionUpdates { + finishedAt?: number; + status?: string; + afterUrl?: string; + verificationSummary?: string; + warningSummary?: string; + diffSummary?: string; + changed?: boolean; + error?: string; +} + +export interface DiffResult { + changed: boolean; + changes: Array<{ type: string; before: unknown; after: unknown }>; + summary: string; +} + +export interface Threshold { + op: string; + n: number; +} + +export interface PageRegistry { + pages: PageEntry[]; + activePageId: number | null; + nextId: number; +} + +export interface PageEntry { + id: number; + page: any; + title: string; + url: string; + opener: number | null; +} + +export interface PageListEntry { + id: number; + title: string; + url: string; + opener: number | null; + isActive: boolean; +} + +export interface SnapshotModeConfig { + tags: string[]; + roles: string[]; + selectors: string[]; + ariaAttributes: string[]; + useInteractiveFilter: boolean; + visibleOnly?: boolean; + containerExpand?: boolean; +} + +export interface AssertionCheckResult { + name: string; + passed: boolean; + actual: unknown; + expected: unknown; + selector?: string; + text?: string; +} + +export interface AssertionEvaluation { + verified: boolean; + checks: AssertionCheckResult[]; + summary: string; + agentHint: string; +} + +export interface WaitValidationError { + error: string; +} + +export interface BatchStepResult { + ok: boolean; + stopReason: string | null; + failedStepIndex: number | null; + stepResults: unknown[]; + summary: string; +} + +export interface FormattedTimeline { + entries: Array<{ + id: number | null; + tool: string; + status: string; + durationMs: number | null; + beforeUrl: string; + afterUrl: string; + line: string; + }>; + retained: number; + totalRecorded: number; + bounded: boolean; + summary: string; +} + +export interface FailureHypothesis { + hasFailures: boolean; + categories: string[]; + summary: string; + signals: Array<{ category: string; source: string; detail: string }>; +} + +export interface SessionSummary { + counts: { + pages: number; + actions: { total: number; retained: number; success: number; error: number; running: number }; + waits: { total: number; success: number; error: number; running: number }; + assertions: { total: number; passed: number; failed: number; running: number }; + consoleErrors: number; + failedRequests: number; + dialogs: number; + }; + activePage: { id: number | null; title: string; url: string } | null; + caveats: string[]; + failureHypothesis: FailureHypothesis; + summary: string; +} + +export function createActionTimeline(limit?: number): ActionTimeline; +export function beginAction(timeline: ActionTimeline, partial: ActionPartial): ActionEntry; +export function finishAction(timeline: ActionTimeline, actionId: number, updates?: ActionUpdates): ActionEntry | null; +export function findAction(timeline: ActionTimeline, actionId: number): ActionEntry | null; +export function toActionParamsSummary(params: unknown): string; +export function diffCompactStates(before: unknown, after: unknown): DiffResult; +export function includesNeedle(haystack: string, needle: string): boolean; +export function parseThreshold(value: string | null | undefined): Threshold | null; +export function meetsThreshold(count: number, threshold: Threshold): boolean; +export function getEntriesSince( + entries: Array<{ timestamp?: number }>, + sinceActionId: number | undefined, + timeline: ActionTimeline, +): unknown[]; +export function evaluateAssertionChecks(args: { checks: unknown[]; state: unknown }): AssertionEvaluation; +export function validateWaitParams(params: { condition: string; value?: string; threshold?: string }): WaitValidationError | null; +export function createRegionStableScript(selector: string): string; +export function createPageRegistry(): PageRegistry; +export function registryAddPage( + registry: PageRegistry, + info: { page: unknown; title?: string; url?: string; opener?: number | null }, +): PageEntry; +export function registryRemovePage(registry: PageRegistry, pageId: number): { removed: PageEntry; newActiveId: number | null }; +export function registrySetActive(registry: PageRegistry, pageId: number): void; +export function registryGetActive(registry: PageRegistry): PageEntry; +export function registryGetPage(registry: PageRegistry, pageId: number): PageEntry | null; +export function registryListPages(registry: PageRegistry): PageListEntry[]; +export function createBoundedLogPusher(maxSize: number): (array: unknown[], entry: unknown) => void; +export function runBatchSteps(args: { + steps: unknown[]; + executeStep: (step: unknown, index: number) => Promise<{ ok: boolean; [key: string]: unknown }>; + stopOnFailure?: boolean; +}): Promise; + +export declare const SNAPSHOT_MODES: Record; +export function getSnapshotModeConfig(mode: string): SnapshotModeConfig | null; +export function computeContentHash(text: string): string; +export function computeStructuralSignature(tag: string, role: string, childTags: string[]): string; +export function matchFingerprint( + stored: { contentHash?: string; structuralSignature?: string }, + candidate: { contentHash?: string; structuralSignature?: string }, +): boolean; +export function formatTimelineEntries(entries?: unknown[], options?: Record): FormattedTimeline; +export function buildFailureHypothesis(session?: Record): FailureHypothesis; +export function summarizeBrowserSession(session?: Record): SessionSummary; diff --git a/src/resources/extensions/browser-tools/index.ts b/src/resources/extensions/browser-tools/index.ts index b9753ba0e..11c062584 100644 --- a/src/resources/extensions/browser-tools/index.ts +++ b/src/resources/extensions/browser-tools/index.ts @@ -28,11 +28,11 @@ export default function (pi: ExtensionAPI) { parseRef: u.parseRef, formatVersionedRef: u.formatVersionedRef, staleRefGuidance: u.staleRefGuidance, beginTrackedAction: u.beginTrackedAction, finishTrackedAction: u.finishTrackedAction, truncateText: u.truncateText, verificationFromChecks: u.verificationFromChecks, - verificationLine: u.verificationLine, collectAssertionState: u.collectAssertionState, + verificationLine: u.verificationLine, collectAssertionState: (p, checks, target?) => u.collectAssertionState(p, checks, captureCompactPageState, target), formatAssertionText: u.formatAssertionText, formatDiffText: u.formatDiffText, getUrlHash: u.getUrlHash, captureClickTargetState: u.captureClickTargetState, readInputLikeValue: u.readInputLikeValue, - firstErrorLine: u.firstErrorLine, captureAccessibilityMarkdown: u.captureAccessibilityMarkdown, + firstErrorLine: u.firstErrorLine, captureAccessibilityMarkdown: (selector?) => u.captureAccessibilityMarkdown(getActiveTarget(), selector), resolveAccessibilityScope: u.resolveAccessibilityScope, getLivePagesSnapshot: u.createGetLivePagesSnapshot(ensureBrowser), getSinceTimestamp: u.getSinceTimestamp, getConsoleEntriesSince: u.getConsoleEntriesSince, diff --git a/src/resources/extensions/browser-tools/refs.ts b/src/resources/extensions/browser-tools/refs.ts index 18339482c..09d80095c 100644 --- a/src/resources/extensions/browser-tools/refs.ts +++ b/src/resources/extensions/browser-tools/refs.ts @@ -124,7 +124,7 @@ export async function buildRefSnapshot( sib = sib.previousElementSibling; } // Check if the parent itself is a heading (unlikely but possible) - const parent = current.parentElement; + const parent: Element | null = current.parentElement; if (parent && (headingTags.has(parent.tagName) || parent.getAttribute("role") === "heading")) { return (parent.textContent || "").trim().replace(/\s+/g, " ").slice(0, 80); } diff --git a/src/resources/extensions/browser-tools/tools/session.ts b/src/resources/extensions/browser-tools/tools/session.ts index 43fb5b996..19ef2cb20 100644 --- a/src/resources/extensions/browser-tools/tools/session.ts +++ b/src/resources/extensions/browser-tools/tools/session.ts @@ -379,7 +379,7 @@ export function registerSessionTools(pi: ExtensionAPI, deps: ToolDeps): void { console: consoleLogs.length, network: networkLogs.length, dialog: dialogLogs.length, - actions: timeline.count, + actions: timeline.retained, pages: pages.length, }, elapsedMs: Date.now() - startedAt, diff --git a/tsconfig.extensions.json b/tsconfig.extensions.json index b2f249376..07b44e356 100644 --- a/tsconfig.extensions.json +++ b/tsconfig.extensions.json @@ -3,8 +3,10 @@ "compilerOptions": { "noEmit": true, "allowImportingTsExtensions": true, + "allowJs": true, + "checkJs": false, "rootDir": "." }, - "include": ["src/resources/extensions/gsd"], + "include": ["src/resources/extensions"], "exclude": [] }