# Talox

> Local-first, stateful browser runtime for AI agents built on Playwright. Talox provides persistent browser sessions, human-paced interaction, structured page state, resilient automation, and deep observability.

This file is a compact agent-facing reference for the current v9 public API. Prefer the explicit settings model below. Legacy `mode` values still exist only as constructor compatibility aliases; do not plan new integrations around runtime mode switching.

## Quick start

```typescript
import { TaloxController } from 'talox';

const talox = new TaloxController('./profiles', {
  settings: { verbosity: 0 },
});

await talox.launch('agent-id', 'ops', 'chromium');
const state = await talox.navigate('https://example.com');
console.log(state.title);
await talox.stop();
```

`launch(profileId, profileClass, browserType?)` accepts profile classes `ops | qa | sandbox` and browser types such as `chromium`, `firefox`, or `webkit`. The third argument is a browser type, not a Talox mode.

## Runtime settings

Talox uses `TaloxSettings` rather than runtime mode switching. Common constructor settings:

```typescript
const talox = new TaloxController('./profiles', {
  settings: {
    headed: false,
    verbosity: 0,
    safeMode: false,
    navigationWaitUntil: 'domcontentloaded',
    contentSafety: 'warn',
  },
});
```

Useful runtime controls:

```typescript
talox.setVerbosity(2);
await talox.setHeaded(true);
talox.setSafeMode(true);
```

There is no current `talox.setMode()` API. Legacy constructor `mode` values are retained for backwards compatibility and are mapped to settings by `resolveLegacyMode()`.

## TaloxPageState

Every `navigate()`, `click()`, `type()`, and full `getState()` call returns Talox's structured page-state contract.

```typescript
{
  url: string;
  title: string;
  timestamp: string;

  console: {
    errors: string[];
    warnings?: string[];
    logs?: string[];
  };

  network: {
    failedRequests: Array<{ url: string; status: number; type?: string }>;
    exceptions?: any[];
  };

  nodes: TaloxNode[];
  interactiveElements: Array<{
    id: string;
    tagName: string;
    role?: string;
    text?: string;
    boundingBox: { x: number; y: number; width: number; height: number };
    isActionable?: boolean;
    cursorDetected?: boolean;
    detectionMethod?: 'cursor-style' | 'onclick-attr' | 'tabindex';
    trust?: 'first-party' | 'external';
  }>;

  bugs: TaloxBug[];

  axTree?: TaloxNode;
  timing?: TaloxStateTiming;
  diff?: TaloxStateDiff;
  profileId?: string;
  domainHints?: string[];
  screenshots?: {
    fullPage?: string;
    crops?: Array<{ id: string; path: string; reason: string }>;
  };
}
```

The frozen v1 core fields are `url`, `title`, `timestamp`, `console`, `network`, `nodes`, `interactiveElements`, and `bugs`. Do not expect a `mode` field in `TaloxPageState`.

For lower token usage, request compact state variants:

```typescript
const agentState = await talox.getState('agent');
const debugState = await talox.getState('debug');
const fullState = await talox.getState('full');
```

## Core actions

```typescript
await talox.navigate('https://example.com');
await talox.click('button#submit');
await talox.type('input[name="q"]', 'hello');

const state = await talox.getState('agent');
const description = await talox.describePage();
const intent = await talox.getIntentState();

await talox.scrollTo('#footer', 'center');
const rows = await talox.extractTable('table.results');
await talox.waitForLoadState('domcontentloaded', 30_000);

const element = await talox.findElement('Submit', 'button');
const title = await talox.evaluate<string>('document.title');

await talox.screenshot();
await talox.screenshot({ selector: '#hero', path: 'hero.png' });
```

Selectors passed to `click()` and `type()` are strings. Do not pass object-shaped `{ selector: ... }` interaction arguments.

## Function-calling tools

`getTaloxTools()` returns 16 schemas aligned to public `TaloxController` methods:

- `talox_navigate`
- `talox_click`
- `talox_type`
- `talox_get_state`
- `talox_describe_page`
- `talox_get_intent_state`
- `talox_screenshot`
- `talox_scroll_to`
- `talox_extract_table`
- `talox_wait_for_load_state`
- `talox_set_verbosity`
- `talox_set_headed`
- `talox_set_safe_mode`
- `talox_verify_visual`
- `talox_find_element`
- `talox_evaluate`

These schemas intentionally do not expose removed `set_mode` behavior or phantom per-call navigation/click/type options.

## MCP

Talox also provides a separate MCP stdio bridge. Its tool surface is intentionally smaller than `getTaloxTools()` and manages one persistent browser session through launch, navigate, click, type, state, screenshot, and stop operations. Do not assume the function-calling and MCP tool lists are identical.

## Events

Event handlers receive the typed payload directly, not an `{ data: ... }` wrapper.

```typescript
talox.on('navigation', (event) => console.log(event.url));
talox.on('consoleError', (event) => console.log(event.error));
talox.on('bugDetected', (bug) => console.log(bug.description));
```

## Profile classes

- `ops` — persistent authenticated sessions and policy-controlled operations
- `qa` — testing, visual verification, and debugging workflows
- `sandbox` — low-risk experimentation

## Navigation readiness

The v9 default `navigationWaitUntil` is `domcontentloaded`. This is deliberate: modern SPAs may keep analytics, WebSocket, long-poll, or background fetch traffic alive indefinitely, making `networkidle` unreachable even when the UI is usable.

Override it explicitly only when a target needs a stricter readiness contract:

```typescript
const talox = new TaloxController('./profiles', {
  settings: { navigationWaitUntil: 'networkidle' },
});
```

## Safety and takeover

Content safety defaults to `warn`. Talox also supports human takeover and headed escalation for steps such as login, 2FA, CAPTCHA, policy blocks, or agent uncertainty.

```typescript
const talox = new TaloxController('./profiles', {
  settings: {
    headed: true,
    humanTakeoverEnabled: true,
  },
});

await talox.launch('agent-id', 'ops', 'chromium');
await talox.requestHumanTakeover('2fa-required');
// Human completes the step, then:
talox.resumeAgent();
```

## Source of truth

- `src/types/config.ts` — constructor configuration
- `src/types/settings.ts` — runtime settings and defaults
- `src/types/index.ts` — `TaloxPageState` and public data contracts
- `src/types/events.ts` — typed event payloads
- `src/core/controller/TaloxController.ts` — public controller API
- `src/core/TaloxTools.ts` — function-calling schemas
- `src/core/mcp/TaloxMcpServer.ts` — MCP tool surface
- `src/schema/TaloxPageState.schema.json` — JSON Schema
- `docs/TALOX-CONTRACTS.md` — contract documentation
- `docs/TALOX-ARCHITECTURE.md` — architecture documentation

Talox v9 requires Node.js 20+.
