# Agotalk Page Agent API (v1)

This is the runtime contract for a self-contained local `static_browser` page
that exposes structured reads and actions to its Agotalk role agent.

Producer: the Agotalk App injects `window.agotalk.expose(...)` into a local
browser page.

Consumer: a role documents the methods its page exposes, then invokes them
through `invoke_page_method`.

Owner: the page owns its domain state and rules. Agotalk transports a
pure-JSON call and result; it does not interpret the domain.

## Register one function table

Register exactly once for each loaded document:

```js
window.agotalk.expose({
  read_current: () => pageState.read(),
  apply_change: async (args) => pageState.apply(args)
});
```

`expose(...)` accepts one non-empty, flat object whose values are functions.
Each function may be synchronous or asynchronous. Its result must be pure JSON.

`read_current` and `apply_change` are examples only. Method names, arguments,
results, and domain rules belong entirely to the role-page contract: a design
tool, tutor, game, or simulator may each expose different methods. The runtime
does not accept API metadata such as `version`, `methods`, `description`,
`effect`, or `inputSchema`; document those semantics in the role package
instead.

Reloading or navigating replaces the document and clears its registration. A
document cannot merge or replace a registration after it succeeds.

## Agent tool

The role invokes a method it has documented for the current local page:

```text
invoke_page_method({ tabId, method, args? })
```

`tabId` and `method` are required. `args` is an optional JSON object and
defaults to `{}`. There is no general JavaScript evaluation surface and no
runtime method-discovery tool. A role must document its own method table.

For example, a role that documents `read_current` and `apply_change` can call:

```text
invoke_page_method({ tabId, method: "read_current", args: {} })
invoke_page_method({ tabId, method: "apply_change", args: { change: { kind: "move", target: "header" }, expectedRevision: 7 } })
```

The result is the registered function's JSON value.

## State and writes

The page remains the only owner of state. Human UI actions and page methods
must call the same domain commit path.

For state that both a human and an agent can change, expose a monotonic
`revision` from the read method. Every write accepts `expectedRevision` and
rejects stale input without changing state:

```json
{
  "ok": false,
  "error": { "code": "STALE_STATE" },
  "state": { "revision": 8 }
}
```

A successful write should return the new state in the same result. This gives
the agent an authoritative receipt without another read call.

Large history or audit data should use a separate read method with an offset.
Keep the ordinary observation bounded.

## Page-initiated thread notification

In Agotalk App builds that support `thread_notify`, a local file page opened
in a thread-owned tab can ask that same thread to take another turn:

```js
async function notifyReview() {
  if (typeof window.agotalk?.post !== "function") return false;
  try {
    const result = await window.agotalk.post("thread_notify", {
      message: "请点评作文"
    });
    return result.ok === true && result.data?.disposition === "queued";
  } catch {
    return false;
  }
}
```

The message must be non-empty and at most 8192 UTF-8 bytes. The page supplies
only `message`; Agotalk identifies the owning thread and tab from the sending
page. Standalone tabs and external URLs cannot use this topic. A successful
response is `{ ok: true, data: { disposition: "queued", deliveryId } }`:
`queued` means the thread input was accepted, not that the agent has finished
the requested work. Unsupported App builds and failed deliveries return
`{ ok: false, error }`. If `notifyReview()` returns `false`, keep the request
visible and tell the child to ask for a review in the conversation.

The notification carries the tab ID to the agent. Document in the role's
instructions which exposed read method the agent should call after waking,
then which write method completes the action. For example, a writing page
can save a review request locally, notify its thread, and let the role call
`observe` followed by `submit_review`. Human actions and agent writes still
use the same page-owned commit path and revision checks.

This is a one-way wakeup, not an event subscription or a completion callback.
Keep the page usable when the notification cannot be sent.

## Failure semantics

Agotalk reports transport or runtime failures with stable `PAGE_API_*` codes,
including:

```text
PAGE_API_NOT_EXPOSED
PAGE_API_ALREADY_EXPOSED
PAGE_API_CONTRACT_INVALID
PAGE_API_METHOD_NOT_FOUND
PAGE_API_ARGS_INVALID
PAGE_API_METHOD_FAILED
PAGE_API_RESULT_NOT_JSON
PAGE_API_TIMEOUT
PAGE_API_PAGE_CHANGED
PAGE_API_TAB_NOT_FOUND
PAGE_API_CALL_FAILED
```

Domain refusals belong to the page and should be returned, rather than thrown,
as JSON values with their own stable codes. A failed write must not change
domain state.

## Non-goals

Page Agent API does not provide Node.js, general Electron APIs, shell access,
MCP, event subscriptions, method discovery, or domain intelligence. Its
structured calls transport only the domain contract that the page explicitly
declares; `thread_notify` is the narrow page-to-thread notification above.
