@mearl/native-host 3.0.0-beta.0 → 3.0.0-beta.10

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.
Files changed (44) hide show
  1. package/README.md +119 -168
  2. package/README.zh-CN.md +319 -0
  3. package/dist/THIRD_PARTY_NOTICES.txt +22 -655
  4. package/dist/agent/chat-visualization.d.ts +3 -0
  5. package/dist/agent/managed-programs.d.ts +24 -0
  6. package/dist/agent/manager.d.ts +15 -2
  7. package/dist/agent/skill-manager.d.ts +8 -1
  8. package/dist/app-authorization.d.ts +8 -0
  9. package/dist/app-profile-catalog.d.ts +3 -1
  10. package/dist/browser-enhancement-gateway.d.ts +24 -0
  11. package/dist/browser-identity.d.ts +11 -0
  12. package/dist/browser-registry.d.ts +1 -0
  13. package/dist/browser-socket-request.d.ts +4 -0
  14. package/dist/capability-contracts.d.ts +15 -52
  15. package/dist/capability-hooks.d.ts +42 -0
  16. package/dist/cdp-backend/buffers.d.ts +11 -11
  17. package/dist/cdp-backend/capability-handlers.d.ts +1 -0
  18. package/dist/cdp-backend/cdp-session.d.ts +2 -0
  19. package/dist/cdp-backend/cdp-utils.d.ts +3 -8
  20. package/dist/cdp-backend/handlers.d.ts +2 -1
  21. package/dist/cdp-backend/http-handlers.d.ts +12 -9
  22. package/dist/cdp-backend/network-control.d.ts +5 -0
  23. package/dist/cdp-backend/page-dialogs.d.ts +14 -0
  24. package/dist/cdp-backend/request-cookies.d.ts +8 -0
  25. package/dist/cdp-backend/response-cookies.d.ts +8 -0
  26. package/dist/cdp-backend/send-request.d.ts +3 -2
  27. package/dist/control-session-manager.d.ts +1 -1
  28. package/dist/index.js +182 -217
  29. package/dist/install.js +1 -1
  30. package/dist/native-capability-services.d.ts +5 -2
  31. package/dist/node-capabilities.d.ts +1 -0
  32. package/dist/node-capabilities.js +2 -2
  33. package/dist/page-runtime-store.d.ts +2 -27
  34. package/dist/plugin-task-host.js +30 -45
  35. package/dist/plugin-worker.js +23 -38
  36. package/dist/pluginModelSessionWorker.js +1 -1
  37. package/dist/preset-auto-update.d.ts +18 -0
  38. package/dist/preset-components.d.ts +3 -1
  39. package/dist/provider-app-resources.d.ts +32 -0
  40. package/dist/provider-control.d.ts +1 -1
  41. package/dist/provider-enhancements.d.ts +21 -0
  42. package/package.json +17 -10
  43. package/dist/alipay-auth-code.d.ts +0 -13
  44. package/dist/cdp-backend/mtop-environment.d.ts +0 -7
package/README.md CHANGED
@@ -1,67 +1,71 @@
1
1
  # @mearl/native-host
2
2
 
3
- Mearl 的 Native Messaging Host —— 桥接 Chrome 扩展与本地 socket 服务。
3
+ [English](README.md) | [简体中文](README.zh-CN.md)
4
4
 
5
- 浏览器扩展通过 `chrome.runtime.connectNative()` 拉起本进程;进程对内通过 CDP(Chrome DevTools Protocol)与浏览器交互,对外通过 **Unix Socket** 暴露给 `@mearl/client` 等本地调用方,并可作为云端链路的本地落点。
5
+ Mearl's Native Messaging Host bridges the Chrome extension and local socket services.
6
6
 
7
- ## 动态能力
7
+ The extension starts this process through `chrome.runtime.connectNative()`. Hosts interact with browsers through Extension or CDP and serve callers such as `@mearl/client` over local IPC: Unix sockets on macOS/Linux and named pipes on Windows. Cloud Connector uses the same host connection.
8
8
 
9
- 公开宿主只内置四个基础 UA/版本档案。完整模拟、业务请求与事件解析、身份和特殊授权通过独立 Node 包动态接入;原动作入口、原 App ID 和授权进度保留。资源校验、更新/回滚和代码引用合同见 [Node 能力包](../../docs/node-capabilities.md)。当前整套 preset 的安装、默认值应用与网页一键入口仍待接通。
9
+ ## Dynamic capabilities
10
10
 
11
- ## 安装与注册
11
+ Hosts provide browser control, device simulation, plugin execution, and default analytics parsing. Node capability packages supply declared hook handlers; see [Node capabilities](../../docs/node-capabilities.md) for resources, activation, and rollback. Setup and web installation share the [installation host](../../docs/preset-installation.md) coordinator.
12
12
 
13
- 注册 Native Messaging Host manifest(让浏览器能够拉起本进程):
13
+ ## Installation and registration
14
+
15
+ Register Native Messaging Host manifests so browsers can launch this process:
14
16
 
15
17
  ```bash
16
- # 同时注册 Skills 与 MCP 两种集成模式(默认)
18
+ # Register both Skills and MCP integration modes (default)
17
19
  mearl-native-host --init
18
20
 
19
- # 仅注册某一种模式
21
+ # Register one mode only
20
22
  mearl-native-host --init --mode skills
21
23
  mearl-native-host --init --mode mcp
22
24
 
23
- # 指定自定义扩展 ID(开发版)
25
+ # Custom extension ID for development
24
26
  mearl-native-host --init --extension-id <your-extension-id>
25
27
  ```
26
28
 
27
- > Skills 与 MCP 两个 host 运行同一份二进制、共享同一个 Unix socket,因此可同时启用。
29
+ > Skills and MCP hosts use the same binary and share one Unix socket, so both can be enabled.
28
30
 
29
- 也可在代码中调用安装入口:
31
+ You can also invoke the installer in code:
30
32
 
31
33
  ```typescript
32
34
  import { installNativeHost } from '@mearl/native-host';
33
35
 
34
- await installNativeHost(); // 默认注册两种模式
36
+ await installNativeHost(); // Register both modes by default
35
37
  ```
36
38
 
37
- 运行时入口(被浏览器拉起、或手动启动)通过子路径导出:
39
+ The runtime entry, launched by browsers or manually, is exported through a subpath:
38
40
 
39
41
  ```typescript
40
42
  import '@mearl/native-host/runtime';
41
43
  ```
42
44
 
43
- ## CLI 选项
45
+ ## CLI options
44
46
 
45
47
  ```bash
46
48
  mearl-native-host [options]
47
49
 
48
- --init [--mode <skills|mcp|both>] [--extension-id <id>] 注册 host manifest
49
- --daemon 独立 CDP daemon
50
- --cdp-port <port> 指定 CDP 端口
51
- --cdp-user-data-dir <dir> 指定 Chrome Profile
52
- --cdp-endpoint-file <path> 从文件读取私有远端 CDP WebSocket URL
53
- --skip-browser-registration 不把 Provider 私有 CDP 运行时注册为独立浏览器
54
- --browser-id <id> 指定浏览器注册 ID
55
- --browser-name <name> 指定浏览器显示名称
56
- --version, -v 查看版本
57
- --help, -h 查看帮助
50
+ --init [--mode <skills|mcp|both>] [--extension-id <id>] Register host manifests
51
+ --daemon Standalone CDP daemon
52
+ --cdp-port <port> CDP port
53
+ --cdp-user-data-dir <dir> Chrome profile
54
+ --cdp-endpoint-file <path> File containing private remote CDP WebSocket URL
55
+ --skip-browser-registration Do not expose Provider-private CDP as a separate browser
56
+ --browser-id <id> Browser registration ID
57
+ --browser-name <name> Browser display name
58
+ --version, -v Show version
59
+ --help, -h Show help
58
60
  ```
59
61
 
60
- daemon 参数只对当前进程生效。client 自动拉起 discovery daemon 时先读取 Chrome 默认 Profile 的 `DevToolsActivePort`,再探测本地 CDP 端口 9222;自定义 CDP 端口或浏览器标识需要在每次手工启动时显式传入。发现进程使用自己的实例 Socket 接收查询,不依赖固定 Socket 的归属。
62
+ Daemon arguments apply only to the current process. Client-started discovery daemons first read the default Chrome profile's `DevToolsActivePort`, then probe local CDP port 9222. Custom ports/browser identities must be supplied on each manual startup. Discovery uses its own instance Socket rather than depending on ownership of a fixed Socket.
63
+
64
+ Chrome authorization mode enabled at `chrome://inspect/#remote-debugging` disables HTTP discovery. If `DevToolsActivePort` is missing/unreadable and `/json/version` returns 404, Mearl connects to `/devtools/browser` WebSocket on the same port and waits for Chrome authorization. Allow the browser prompt. This path reads no browser profile files. Failures retain discovery-file read errors to distinguish filesystem permissions, ports, and authorization issues.
61
65
 
62
- 通过 `chrome://inspect/#remote-debugging` 开启的 Chrome 授权模式会关闭 HTTP 发现接口。若 `DevToolsActivePort` 缺失或无法读取,且 `/json/version` 返回 404,Mearl 会连接同一端口的 `/devtools/browser` WebSocket,并等待 Chrome 的连接授权。请在 Chrome 提示时允许连接;该路径不读取浏览器 Profile 文件。连接失败时会保留发现文件的读取错误,便于区分文件权限、端口和连接授权问题。
66
+ ## Architecture
63
67
 
64
- ## 架构
68
+ On macOS, extension connections first inspect the Native Messaging host's direct parent application's bundle ID to distinguish EgoLite, Chrome, and Edge, including supported test channels. Results cache per host process. Identification does not guess from page UA, application directory names, or browser versions, so device/App simulation does not change names. Other systems, unknown applications, and read failures retain extension-reported names. Names support display/selection, not page association, ownership, or authorization.
65
69
 
66
70
  ```
67
71
  Chrome Extension
@@ -71,48 +75,36 @@ Chrome Extension
71
75
  @mearl/client / @mearl/cloud-connector
72
76
  ```
73
77
 
74
- `browser_launch` 会为每个托管 Chrome 启动一个带独立 CDP 配置的 native-host 子进程,使多个 headless/Profile 实例可以同时被现有浏览器注册表寻址。外部 Provider 也可使用私有远端 CDP 模式复用同一套通用页面 action;远端服务的凭据、生命周期和文件中转仍由 Provider 自己负责。
78
+ `browser_launch` starts a native-host child with independent CDP configuration for each managed Chrome, making concurrent headless/profile instances addressable in the browser registry. External Providers may reuse generic page actions through private remote CDP. Providers still own remote credentials, lifecycle, and file staging.
75
79
 
76
- ## 侧边栏 Agent Chat
80
+ ## Sidebar Agent Chat
77
81
 
78
- 扩展侧边栏通过 Native Messaging 调用 native-host,再按用户选择启动持久的 Codex app-server 或 Qoder ACP 进程。两种 Provider 都支持流式消息、思考与工具进度、中断和审批,并复用各自 CLI 已保存的本地登录态。
82
+ The extension sidebar calls native-host through Native Messaging, then starts persistent Codex app-server or installed Agent-capability processes according to user selection. Providers support streaming messages, reasoning/tool progress, interruption, and approval, reusing CLI sign-in sessions.
79
83
 
80
- 需要安装并登录支持 `app-server` 的独立 Codex CLI 或 Codex App。native-host 会查找安装时的 Node 可执行文件目录、常见全局安装目录、版本管理器、`PATH` 和 Codex App 内置运行时,并发验证兼容性后选择语义版本最新的一项。自动选择结果会在当前 native-host 进程中缓存;显式指定的路径始终优先:
84
+ Install and sign into a standalone Codex CLI or Codex App supporting `app-server`. native-host searches the installation-time Node executable directory, common global directories, version managers, `PATH`, and Codex App's bundled runtime, validates compatibility concurrently, and selects the newest semantic version. Automatic selection caches within the process; explicit paths always win:
81
85
 
82
86
  ```bash
83
87
  MEARL_CODEX_PATH=/absolute/path/to/codex
84
88
  ```
85
89
 
86
- Qoder 使用官方 ACP 接口,需要先安装 Qoder CLI 并完成登录:
87
-
88
- ```bash
89
- # macOS / Linux
90
- curl -fsSL https://qoder.com/install | bash
91
- qodercli
92
- # 进入交互界面后输入 /login
93
-
94
- # 可选:指定非标准安装位置
95
- MEARL_QODER_PATH=/absolute/path/to/qodercli
96
- ```
90
+ The Agent Chat setup screen installs missing Codex or Qoder components through npm under `~/.mearl/agent-programs`. For Qoder, the local Agent adapter comes from [`@mearl/plugin-qoder`](../plugin-qoder/README.md). A Qoder adapter supplied by the selected preset takes precedence. The menu installation leaves the preset selection intact and enables the local Agent only.
97
91
 
98
- 侧边栏 Agent 不使用只读或工作区沙箱限制,文件、命令和网络操作由 Provider 的审批机制控制。用户选择的本地目录只作为对话的参考上下文和相对路径起点,不限制 Agent 可访问的范围;未选择时从用户主目录开始,Codex 可通过 `MEARL_AGENT_CWD` 覆盖默认起点。Agent 接入由统一 Provider 接口承载,后续可新增 Claude 等实现而无需修改扩展与 native-host 之间的 Chat 协议。侧边栏会在没有找到兼容的独立 CLI 或 Codex App 运行时时展示安装与登录指引。
92
+ ## Plugin host capabilities
99
93
 
100
- ## 插件宿主能力实施方案
94
+ The plugin host manages commands, tasks, page instances, events, models, and state. Toolbox, CLI, SDK, MCP, and Agent are entry points; tool-page lifetime does not determine background capability lifetime. Protocols evolve with tools without legacy interface adapters.
101
95
 
102
- 插件宿主统一管理命令执行、任务、页面实例、事件、模型执行和状态。工具箱、CLI、SDK、MCP 与 Agent 是调用入口;工具页面的存活不决定后台能力的存活。协议与已有工具一起更新,不提供旧接口适配层。
96
+ ### Ownership and identity
103
97
 
104
- ### 所有权和身份
98
+ - Execution environments have persistent `hostId` and per-process `epoch`. hostId lives in the plugin registry directory and identifies environments without granting permissions.
99
+ - Each environment's user-level execution service owns tasks/executors. Browser Native Hosts adapt connections and targets. Browser targets and model execution locations are independently selected and pinned after launch, unaffected by default-browser changes.
100
+ - Commands have `invocationId`, source, plugin, and optional parent invocation. Trusted entries construct context; business input cannot replace it. Nested calls inherit host/source and receive new invocation IDs.
101
+ - Installation IDs, logical names, and immutable code revisions are separate. Tasks/page instances pin revisions; updates affect new execution only. Active work completes, drains, or stops by policy. Complete source snapshots include local dependencies; entry metadata cannot substitute for revisions.
105
102
 
106
- - 执行环境使用持久 `hostId`;每次宿主进程启动产生独立 `epoch`。`hostId` 保存在插件注册目录,身份只描述执行环境,不授予权限。
107
- - 最终由每个执行环境的用户级执行服务持有任务和执行器;浏览器 Native Host 负责连接与目标适配。浏览器目标与模型执行位置独立选择,启动后固定,不随默认浏览器变化。
108
- - 每次命令拥有 `invocationId`、来源、插件及可选父调用。可信入口构造上下文,业务 input 无法替换。嵌套插件调用继承宿主和来源,产生新的调用 ID。
109
- - 注册安装 ID、逻辑名称与不可变代码 revision 分开。任务和页面实例固定 revision;更新仅影响新执行,活动执行按完成、排空或停止策略处理。完整源码快照需要覆盖本地依赖,入口文件元数据不能代替代码 revision。
103
+ ### Command executors
110
104
 
111
- ### 命令执行器
105
+ Handlers receive `(input, context)` with `identity`, `signal`, `invoke`, and `progress`, expiring after execution. JS plugins are trusted native code. Executor isolation enables scheduling and termination, not an OS permission sandbox.
112
106
 
113
- 命令 handler 接收 `(input, context)`。上下文提供 `identity`、`signal`、`invoke` 与 `progress`,在该次执行结束后失效。JS 插件是可信本机代码,执行器隔离保障宿主可调度和可终止,不构成操作系统权限沙箱。
114
-
115
- `executeRegisteredPlugin` 为每次调用建立监督进程与业务子进程,Python 入口通过业务执行器转接到 Python 子进程。监督进程独立观察宿主连接,业务代码阻塞也不影响拥有者断线清理。浏览器与 Agent 调用由父宿主处理,工作目录和环境继承当前执行宿主。公开调用与工具页面命令白名单分别校验,来源标签不参与授权。Python 注册信息和执行终态均须经宿主 ACK 后才退出,宿主按进程 exit 结算并释放父端管道,后代继承 stdio 不阻塞完成;已确认终态后仍未退出的 Python 进程有 1 秒退出宽限,随后强制停止,最终仍须监督进程确认全树清理。
107
+ `executeRegisteredPlugin` creates a supervisor and business child for each call; Python entries bridge through the business executor to Python children. Supervisors independently watch host connections, so blocked business code cannot prevent owner-disconnect cleanup. Parent hosts handle browser/Agent calls. cwd/environment inherit from the execution host. Public commands and tool-page allowlists validate separately; source labels grant no authorization. Python registration and terminal results require host ACK before exit. Hosts settle on process exit and release parent pipes, so descendant-inherited stdio cannot block completion. Python processes surviving acknowledged terminal results get one second to exit before forced termination; supervisors must still confirm full-tree cleanup.
116
108
 
117
109
  ```typescript
118
110
  await executeRegisteredPlugin('example', { command: 'read', input }, hostContext, {
@@ -122,27 +114,27 @@ await executeRegisteredPlugin('example', { command: 'read', input }, hostContext
122
114
  });
123
115
  ```
124
116
 
125
- 等待超时、执行期限、调用方断线和取消分别处理。取消先通知执行器,宽限期后终止;执行器退出确认后才报告停止。已收到的成功结果先于后续取消生效。停止时返回仍待完成的宿主 action 名称,以便调用方核对外部操作;命令未等待宿主调用就返回时明确失败。
117
+ Wait timeouts, execution deadlines, caller disconnects, and cancellation are separate. Cancellation notifies executors, then terminates after a grace period; stopped status requires confirmed exit. Already received successful results take precedence over later cancellation. Stops return pending host-action names for external reconciliation. Returning without awaiting host calls fails explicitly.
126
118
 
127
- 执行器提交结果后保持进程归属,父宿主接收结果后统一清理:POSIX 收割进程组,Windows 在监督进程仍存活时执行 `taskkill /T`。拥有者断线由独立监督进程处理,清理无法确认时报告 `PLUGIN_EXECUTOR_CLEANUP_FAILED`。外部服务已提交的操作无法被本机终止撤销。
119
+ Executors retain process ownership after submitting results; parent hosts clean up after receipt: POSIX process groups are reaped, and Windows runs `taskkill /T` while supervisors remain alive. Independent supervisors handle owner disconnects. Unconfirmed cleanup reports `PLUGIN_EXECUTOR_CLEANUP_FAILED`. Local termination cannot undo operations submitted to external services.
128
120
 
129
- 结果经独立 JSON 通道完整提交,当前执行器上限 64 MiB,超限明确失败。诊断可裁剪,最终业务结果不能从裁剪后的事件重建。任务结果保存为独立 JSON 资源,Native Messaging 每次传输最多 192 KiB 原始数据的 Base64 分块,读取方校验总长度和 SHA-256。
121
+ Results submit completely through a separate JSON channel, limited to 64 MiB with explicit overflow failure. Diagnostics may truncate, but final business results cannot be reconstructed from truncated events. Task results persist as separate JSON resources; Native Messaging transfers Base64 chunks of at most 192 KiB raw bytes, with total-length and SHA-256 verification.
130
122
 
131
- ### 任务和模型执行
123
+ ### Tasks and model execution
132
124
 
133
- 任务服务提供 `start/get/list/events/subscribe/wait/cancel/result`,统一执行状态与递增事件序号。状态区分排队、运行、取消中、成功、失败、已取消和中断。记录和终态持久化,进度有界保留;重启后原执行代次未结束的任务标为中断,不自动重做外部操作。父子模型任务共享取消和终态清理;命令可声明检查点和显式恢复入口。
125
+ Task services provide `start/get/list/events/subscribe/wait/cancel/result`, unified statuses, and increasing event sequences. States distinguish queued, running, cancelling, succeeded, failed, cancelled, and interrupted. Records/terminal states persist; progress is bounded. Unfinished generations become interrupted after restart without redoing external operations. Parent/child model tasks share cancellation and terminal cleanup. Commands may declare checkpoints and explicit recovery.
134
126
 
135
- 用户级服务使用独占所有权及执行租约;多个浏览器进程不直接并发写同一任务库。任务检查点由任务服务持有,插件声明恢复入口和 revision 迁移条件。长结果保存为带归属、校验和保留期的资源,读取无损分块。
127
+ User-level services use exclusive ownership and execution leases; multiple browser processes do not concurrently write one task store. Services own checkpoints; plugins declare recovery entries and revision-migration requirements. Long results use owned, checksummed, expiring resources with lossless chunked reads.
136
128
 
137
- 当前服务按插件注册目录分区,监听本机 Unix socket / Windows named pipe;socket 和数据文件限制为当前用户访问。lease 记录 PID、OS 进程创建身份、协议版本、hostId 和 epoch。启动锁避免多个调用入口同时拉起服务;停止接口等待执行器清理与 lease 释放。协议不匹配要求先停止旧服务,不静默连接错误版本。
129
+ Services partition by plugin registry directory and listen on local Unix sockets/Windows named pipes. Sockets/data files are current-user-only. Leases record PID, OS process creation identity, protocol version, hostId, and epoch. Startup locks prevent duplicate launches; stop waits for executor cleanup and lease release. Protocol mismatches require stopping the old service rather than silently connecting.
138
130
 
139
- 接受任务时固定工作目录、环境、安装 ID、源码 revision 和浏览器路由。环境只用于子进程,不写进任务记录。源码与相对资源以内容 hash 保存为只读快照;静态 JS import/require 必须是 Node 内置模块或快照内相对文件,外部包需先打包,越界和缺失依赖在接受前拒绝。快照不复制 node_modules,也不冻结系统程序、Python 包环境和运行时计算的外部依赖;按名字调用其他插件属于宿主依赖,仍按调用时注册解析。安装换源或卸载后重装产生新的安装 ID。
131
+ Task acceptance pins cwd, environment, installation ID, source revision, and browser route. Environment is child-only and never persisted. Source and relative resources become content-hashed read-only snapshots. Static JS import/require must target Node built-ins or snapshot-relative files; external packages must be bundled. Escaping/missing dependencies reject before acceptance. Snapshots exclude node_modules and do not freeze system programs, Python packages, or dynamically resolved external dependencies. Named plugin calls remain host dependencies resolved at call time. Source changes or reinstall after removal create new installation IDs.
140
132
 
141
- 默认并发 4,排队上限 128;业务 input 上限 256 KiB,单次进度上限 64 KiB,历史最多 256 条且不超过 512 KiB。事件接口提供游标、缺口标记和最新任务快照。列表按页和字节数限制响应,返回 nextCursor;终态任务与结果保留 7 天,常驻期间每小时清理,并回收无任务引用的 revision。
133
+ Default concurrency is 4 with 128 queued tasks. Limits: input 256 KiB, individual progress 64 KiB, history 256 entries/512 KiB. Events include cursors, gap indicators, and latest task snapshots. Lists bound pages/bytes and return nextCursor. Terminal tasks/results remain seven days, cleaned hourly while resident along with unreferenced revisions.
142
134
 
143
- CLI/SDK/MCP 可以查询本机任务;工具页面限于自己插件的 toolbox/runtime 任务,公开入口创建的任务不向工具箱查询开放。工具箱的浏览器调用各自绑定原 Native Host 连接,页面关闭不影响连接;浏览器连接丢失后的浏览器调用明确中断,不切换到其他窗口。纯本机任务不依赖该连接。公开后台浏览器任务必须显式选择目标,名称在接受前解析为稳定 browserId,执行中禁止覆盖 browser/connector/session/transport/local。
135
+ CLI/SDK/MCP can query local tasks. Tool pages see only their own plugin's toolbox/runtime tasks, not public-entry tasks. Toolbox browser calls bind original Native Host connections and survive tool-page closure. Lost browser connections interrupt calls explicitly without switching windows; purely local work is independent. Public background browser tasks require explicit targets, resolved to stable browserId before acceptance. Execution cannot override browser/connector/session/transport/local.
144
136
 
145
- 显式 idempotencyKey 在安装与入口范围内去重,输入、revision、路由、环境或执行期限改变时报告冲突。工具箱自动合并同一 Native Host 的相同活动请求;任务完成后用户再次操作会创建新任务。等待超时与取消订阅只结束等待,取消执行须显式调用 cancel。
137
+ Explicit idempotencyKey deduplicates within installation/entry scope; changed input, revision, routing, environment, or deadline conflicts. Toolbox coalesces identical active requests on the same Native Host. Repeating an action after completion creates a new task. Wait timeout/unsubscribe stops observation only; execution cancellation requires cancel.
146
138
 
147
139
  ```typescript
148
140
  const tasks = new PluginTaskClient();
@@ -152,19 +144,19 @@ try {
152
144
  } finally { tasks.disconnect(); }
153
145
  ```
154
146
 
155
- 任务执行位置是调用入口所在的本机服务,任务 ID 带 hostId;异地 ID 明确拒绝,不广播查询。对外由插件提供业务命令及所需的进度、取消或恢复操作。CLI 和 MCP 通过插件业务入口调用,不公开通用任务、模型、状态、页面实例或宿主诊断工具。
147
+ Tasks execute on the entry point's local service, with hostId-bearing IDs. Foreign-host IDs reject without broadcast queries. Plugins expose business commands and relevant progress/cancel/recovery actions. CLI/MCP use those entries without generic task, model, state, page-instance, or host-diagnostic tools.
156
148
 
157
- 模型作为 `kind: model` 的持久任务,复用事件、取消、无损结果与保留期。命令默认并发 4,模型默认并发 2,分别最多排队 128 个,避免命令等待子模型时阻塞模型调度。`app.model(request)` / `context.model(request)` 创建关联当前任务的模型子任务,父任务取消或结束时先清理子任务;同步入口也把调用信号传给模型执行。模型默认期限 120 秒,执行器拥有独立 Provider 进程,取消后确认进程树停止才报告终态。
149
+ Models persist as `kind: model` tasks sharing events, cancellation, lossless results, and retention. Commands default to concurrency 4, models 2, each with 128 queued, so commands waiting on child models do not block model scheduling. `app.model(request)` / `context.model(request)` create child tasks; parent cancellation/end first cleans children. Synchronous entries propagate signals too. Model deadlines default to 120 seconds. Executors own independent Provider processes and confirm tree termination before terminal cancellation.
158
150
 
159
- `capabilities` 返回 Provider 实际支持的 mode、permissions、结构化输出、流式和取消能力。当前 Codex 只提供显式 `{ provider: 'codex', mode: 'agent', permissions: 'read-only' }`,每次使用临时 app-server 会话,禁用已配置的 MCP、Apps、搜索、插件和相关扩展功能,沙箱禁止写入和网络;人工请求直接失败。它仍有只读 Agent 的本机读取能力,不能承诺完全不调用工具,因此 `mode: 'generate'` 明确拒绝,绝不降级。交互侧边栏保持独立会话生命周期。
151
+ `capabilities` reports actually supported modes, permissions, structured output, streaming, and cancellation. Codex currently supports only explicit `{ provider: 'codex', mode: 'agent', permissions: 'read-only' }`. Each call uses a temporary app-server session with configured MCP, Apps, search, plugins, and related extensions disabled; sandboxes forbid writes/network and approval requests fail immediately. Read-only Agent file access remains, so tool-free execution is not promised. `mode: 'generate'` rejects explicitly without fallback. Interactive sidebar sessions remain independent.
160
152
 
161
- 请求最多 256 KiB;`outputSchema` 最多 64 KiB,通过 [Codex app-server](https://developers.openai.com/codex/app-server/) 的 `turn/start` 传递。宿主只接受成功 turn 的原始最终消息,并独立解析 JSON、校验 schema,再生成成功任务;Provider 的 willRetry 错误不提前结束执行,终态错误保留具体原因及稳定错误码。`model.delta` 是可缺失的诊断进度,不能拼接成最终结果。最终文本上限 32 MiB,结构化返回 `{ text, output?, provider, mode, model? }`,模型额外业务约束仍由插件校验。
153
+ Requests are limited to 256 KiB; `outputSchema` to 64 KiB, passed through [Codex app-server](https://developers.openai.com/codex/app-server/) `turn/start`. Hosts accept only successful turns' original final messages, parse JSON, then mark tasks successful. Providers enforce outputSchema; plugins validate final structure/business constraints. willRetry errors do not end execution early; terminal errors retain causes and stable codes. Optional `model.delta` diagnostics cannot be concatenated into final results. Final text is limited to 32 MiB, returning `{ text, output?, provider, mode, model? }`; plugins validate additional business constraints.
162
154
 
163
- SDK 使用 `PluginModelClient.capabilities/list/start/run`。工具箱使用 `host.model.capabilities/list/start` 和 `waitTask`。`PluginModelClient.run` 的 signal 会取消执行并等待清理,语义与仅停止观察的任务 `wait` 不同。
155
+ SDK uses `PluginModelClient.capabilities/list/start/run`; toolbox uses `host.model.capabilities/list/start` and `waitTask`. `PluginModelClient.run` signals cancel execution and await cleanup, unlike task `wait`, which stops observation only.
164
156
 
165
- ### 检查点与显式恢复
157
+ ### Checkpoints and explicit recovery
166
158
 
167
- 命令入口可声明 `taskRecovery` 并注册同名 `app.recoveryCommand()`,后者只在宿主创建的恢复 attempt 中执行,不通过公开命令、工具箱白名单或嵌套调用开放。
159
+ Command entries can declare `taskRecovery` and register matching `app.recoveryCommand()` handlers. These run only in host-created recovery attempts, never through public commands, toolbox allowlists, or nested invocation.
168
160
 
169
161
  ```javascript
170
162
  export const taskRecovery = { version: 1, commands: {
@@ -185,13 +177,13 @@ export default app => {
185
177
  };
186
178
  ```
187
179
 
188
- `context.checkpoint(data, expectedSequence)` 仅在 service 路由、未绑定页面实例的活动受管理公开命令中可用;子任务可记录检查点,显式恢复仍只接受合格根任务。负载最多 64 KiB,宿主按声明 schema 校验,分配递增检查点序号。数据、版本、宿主计算的 canonical schemaHash、任务快照和 checkpoint 事件写入同一个原子任务记录;并发过期写入明确冲突。检查点不是外部操作的完成证明,与插件 state 事务也不共享提交。外部提交与检查点之间的故障窗口由恢复入口查询操作状态或使用稳定幂等键处理。
180
+ `context.checkpoint(data, expectedSequence)` is available only in active managed public commands with service routing and no page-instance binding. Children may checkpoint, but explicit recovery accepts eligible roots only. Payloads are limited to 64 KiB. Hosts check JSON/CAS sequences and assign increasing checkpoint numbers; schemas describe format/version identity, while plugins validate data. Data, versions, canonical host schemaHash, task snapshots, and checkpoint events share one atomic record. Stale writes conflict explicitly. Checkpoints neither prove external completion nor share state-transaction commits. Recovery handlers close external-submit/checkpoint failure windows by querying operations or using stable idempotency keys.
189
181
 
190
- SDK 使用 `PluginTaskClient.checkpoint(taskId)` / `resume(taskId, { environment?, revision? })`。resume 只接受失败、取消或中断的根命令,创建新的 taskId 和执行代次,记录 `resumedFrom`,原任务保持终态;相同来源任务只有一个直接后继,重复请求返回已有 attempt,配置不同则冲突。继续恢复失败的新 attempt 时,对新 taskId 调用 resume。
182
+ SDK methods are `PluginTaskClient.checkpoint(taskId)` / `resume(taskId, { environment?, revision? })`. resume accepts only failed, cancelled, or interrupted root commands. It creates a new taskId and execution generation with `resumedFrom`, leaving the original terminal. A source task has at most one direct successor; duplicate requests return that attempt, while different configuration conflicts. To recover a failed new attempt, resume its new taskId.
191
183
 
192
- 恢复默认 `revision: 'original'`,沿用原安装、代码快照、输入、cwd、路由和执行期限。接受前重新验证安装归属与全部源码 hash,拒绝快照缺失或损坏,不回退到当前代码。原环境不落盘,SDK 默认显式提交调用进程的当前环境,也可传 environment;不使用服务的环境替代。`context.recovery` 包含 fromTaskId、fromRevision、原始 sourceCheckpoint 格式引用和已接受的 checkpoint。新 attempt 继承数据但自己的 checkpoint 序号从 0 开始;resumedFrom 保存原 revision/version/sequence。隐藏恢复入口接收原输入,不要求其满足新版本 CLI 的必填参数,由恢复 handler 处理历史输入。
184
+ Recovery defaults to `revision: 'original'`, retaining the original installation, source snapshot, input, cwd, routing, and execution time limit. Before acceptance, ownership and all source hashes are revalidated. Missing or corrupt snapshots are rejected without falling back to current code. The original environment is not persisted. By default the SDK explicitly submits the calling process's current environment, or callers can supply environment; the service environment is not substituted. `context.recovery` includes fromTaskId, fromRevision, the original sourceCheckpoint format reference, and accepted checkpoint. The new attempt inherits data but starts its checkpoint sequence at 0; resumedFrom preserves the original revision/version/sequence. Hidden recovery handlers receive original input without requiring compliance with the current CLI's required arguments and must handle historical input themselves.
193
185
 
194
- 显式 `resume(taskId, { revision: 'current' })`在首个请求进入时固定当前注册代码快照。同格式版本且 canonical schema 相同时直接接受;同版本修改 schema 拒绝,必须提升 checkpointVersion。跨格式版本只支持目标声明的单步迁移:
186
+ Explicit `resume(taskId, { revision: 'current' })` pins the registered source snapshot when the first request arrives. Matching format versions and canonical schemas are accepted directly. Changing a schema without increasing checkpointVersion is rejected. Cross-version recovery supports only a target-declared single-step migration:
195
187
 
196
188
  ```javascript
197
189
  export const taskRecovery = { version: 1, commands: {
@@ -207,35 +199,33 @@ export default app => {
207
199
  };
208
200
  ```
209
201
 
210
- migrateFrom 最多 16 个不同的旧版本(均低于目标版本),历史 schema 必须与检查点写入时记录的宿主 hash 及保留代码声明一致。即使源码没变,依赖环境等动态声明发生格式漂移也拒绝恢复。迁移和恢复进程调用隐藏 handler 前再次比较目标声明签名,普通 checkpoint 写入也校验执行端声明;Python 每次 run 的 ready ACK 前核对完整加载元数据。迁移返回新检查点的数据,宿主校验 JSON、64 KiB 限制和目标 schema,再将新数据及来源元信息原子写入唯一后继任务;旧任务和检查点不变。没有迁移声明、格式不匹配、转换失败或清理未确认均不接纳新任务。
211
-
212
- 同一源任务的并发恢复按 revision 选择与环境指纹合并,不同意图冲突;已提交后继的重试先核验请求指纹,不重复转换。迁移在独立受管理进程执行,10 秒期限,与声明解析共享最多 8 个进程预算,无等待队列,服务关闭会取消并等待清理。转换和原执行树均确认停止,且安装和双端代码快照再次核验后才接纳新任务。迁移没有宿主 API、模型、state、页面或嵌套插件调用权限;JS/Python 仍是可信本机代码,具备直接文件和网络访问能力,不构成 OS 沙箱。迁移必须是可重复的纯 JSON 转换:提交前崩溃可能再次执行,不承诺转换恰好执行一次。
202
+ migrateFrom allows up to 16 distinct versions below the target. Historical schemas must match checkpoint host hashes and retained declarations. Dynamic declaration drift rejects recovery even with unchanged source. Migration/recovery recheck target signatures before hidden handlers; normal checkpoint writes validate executing declarations; Python validates full metadata before each ready ACK. Migration returns new checkpoint data. Hosts validate JSON, the 64 KiB limit, and target identity, then atomically save data/provenance in the unique successor, leaving original tasks/checkpoints unchanged. Missing migrations, mismatched formats, conversion failures, and unconfirmed cleanup reject acceptance.
213
203
 
214
- 当前恢复限于 public 入口、由 service 持有稳定路由的根命令。工具箱/Native Host caller 路由、页面绑定、模型和子任务明确拒绝恢复;它们的生命周期不能从旧任务推断。接纳前递归核验原任务与所有子孙的监督进程、OS 创建身份及 POSIX 进程组,任何执行未停止或清理未确认都拒绝;未确认的所有权记录不因保留期到期而删除。Windows 服务崩溃后缺少完整进程树停止证明时同样拒绝。模型可由恢复入口显式重新请求,它属于新的执行。
204
+ Concurrent recovery of one source task coalesces by revision choice and environment fingerprint; different intents conflict. Retries of committed successors verify request fingerprints without rerunning conversion. Migration runs in an independent managed process with a 10-second limit, sharing an eight-process budget with declaration parsing and no wait queue. Service shutdown cancels it and waits for cleanup. Acceptance requires confirmed termination of both conversion and original execution trees, plus revalidation of installation and both source snapshots. Migration has no host APIs, models, state, pages, or nested plugin calls. JS/Python remains trusted native code with direct file/network access, not an OS sandbox. Migration must be repeatable pure JSON conversion: crashes before commit may rerun it, with no exactly-once guarantee.
215
205
 
216
- ### 页面运行时和事件
206
+ Recovery currently supports root commands with public entries and stable service-owned routes only. Toolbox/Native Host caller routes, page bindings, models, and child tasks explicitly reject recovery because their lifecycle cannot be inferred from an old task. Before acceptance, the host recursively checks supervising processes, OS creation identities, and POSIX groups for the task and all descendants. Running execution or unconfirmed cleanup rejects recovery; uncertain ownership records do not expire away. Windows also rejects recovery after service crashes without full process-tree termination proof. Recovery handlers can explicitly request models as new executions.
217
207
 
218
- 页面实例绑定插件 revision、浏览器来源、标签页、frame、文档和实例 ID。业务 frame 选择器用于 attach 时解析,实例随后固定目标;业务 WebView 切换产生旧实例失效和新实例建立。扩展使用原生 documentId;纯 CDP 使用默认执行上下文的 uniqueId 标识文档,隔离世界执行插件代码,通过受校验的 CDP binding 传递事件,支持跨域 iframe。旧版 Chromium 未提供 uniqueId 时使用当前连接生成的文档身份,重连后旧实例失效并由激活规则重建。外部 Provider 须声明 `page_runtime` 能力,未声明时明确失败。
208
+ ### Page runtimes and events
219
209
 
220
- `browser-core` 的 `PluginPageRuntimeHost` 共用实例生命周期、事件确认与重传、激活规则、租约和分块传输,扩展和 CDP 仅实现文档解析、脚本执行、端口与存储适配。CDP 会话存储按浏览器端点身份隔离,宿主重启保留会话身份,浏览器重启清除会话规则并保留持久规则;端点地址仅存储其摘要。CDP 沿用浏览器连接的权限范围,插件声明的 URL 匹配范围仍由公共宿主检查。
210
+ Page instances bind plugin revision, browser source, tab, frame, document, and instance ID. Business frame selectors resolve at attach time, then the target is fixed. Switching business WebViews invalidates old instances and creates new ones. Extension uses native documentId; pure CDP uses the default execution context's uniqueId for document identity, executes plugin code in isolated worlds, and passes events through validated CDP bindings, including cross-origin iframes. Chromium without uniqueId uses connection-local document identities; reconnecting invalidates old instances for activation rules to rebuild. External Providers must declare `page_runtime` or fail explicitly.
221
211
 
222
- CDP 会话层按浏览器端点身份保存 target 与虚拟 tabId 的映射,连接时先恢复映射,再开始页面发现和插件对账。页面存储按 storage key 独立落盘,租约更新只写租约记录;文档解析同时返回所属 target,执行操作只等待该 target。
212
+ `PluginPageRuntimeHost` in `browser-core` shares instance lifecycle, event acknowledgment/retransmission, activation rules, leases, and chunked transport. Extension and CDP implement only document resolution, script execution, ports, and storage adapters. CDP sessions are isolated by browser endpoint identity; host restarts preserve session identity, while browser restarts clear session rules but retain persistent rules. Only endpoint digests are stored. CDP inherits connection permissions, while the common host still checks declared plugin URL scopes.
223
213
 
224
- native-host 被清理或连接短暂中断时,文档实例在最后一次成功校验后的 30 秒租约内保留归属;无法确认文档的操作返回 `PLUGIN_PAGE_UNAVAILABLE`,不执行页面操作,也不把失败的对账响应当成实例缺失。重连后须校验原浏览器、文档和实例身份。已接收但尚未启动任务的事件可以继续排队;已发出的操作不自动重放。明确的导航、卸载、文档失效仍立即清理;长时间失联导致租约到期后清理旧实例,启用的激活规则可在宿主恢复时建立新实例。
214
+ The CDP session layer persists target-to-virtual-tabId mappings by browser endpoint identity and restores them before page discovery and plugin reconciliation. Page storage persists independently by storage key; lease updates write only lease records. Document resolution also returns its target, and execution waits only for that target.
225
215
 
226
- 构建后运行 `pnpm verify:plugin-pages:cdp`,用独立无头 Chrome、临时插件注册目录与执行服务验收文档隔离、后台事件任务、分块结果、SPA、跨域 iframe、导航重建和宿主重启;多标签页关闭后重启时还核验原手动实例、tabId、documentId 与后台事件执行。默认使用 macOS Chrome,可通过 `MEARL_CHROME_PATH` 指定其他平台的 Chrome/Chromium 可执行文件。
216
+ After native-host cleanup or transient disconnects, document instances retain ownership for a 30-second lease from the last successful validation. Unverifiable document operations return `PLUGIN_PAGE_UNAVAILABLE`, without performing page actions or treating failed reconciliation as absent instances. Reconnection must verify the original browser, document, and instance. Accepted events whose tasks have not started can stay queued; dispatched operations are not replayed. Explicit navigation, unloading, or invalidation cleans up immediately. Extended disconnects expire leases and old instances; enabled activation rules can create replacements after recovery.
227
217
 
228
- CDP 页面会话文件在同一连接标识切换浏览器身份、受控浏览器明确关闭或确认进程已退出时回收,持久激活规则保留。
229
- 缺少可验证浏览器归属的旧会话文件保留到完整卸载;`cdp-targets` 的标签 ID 递增计数跨浏览器生命周期保留,避免旧请求误操作新标签。
230
- 受控浏览器日志在启动前按 5 MiB 轮转,保留一份备份;无对应浏览器记录的日志保留 7 天,在宿主启动、浏览器查询或启动时清理。
218
+ CDP page-session files are reclaimed when one connection identifier changes browser identity, a controlled browser explicitly closes, or process exit is confirmed. Persistent activation rules remain.
219
+ Session files without verifiable browser ownership remain until full uninstall. `cdp-targets` tab-ID counters persist across browser lifecycles to prevent stale requests from targeting new tabs.
220
+ Controlled-browser logs rotate before launch at 5 MiB with one backup. Logs without browser records remain for seven days and are cleaned during host startup, browser queries, or launch.
231
221
 
232
- 宿主提供 `attach/get/list/send/detach`,独立于 toolbox 声明。工具页关闭只退订 UI;文档刷新按策略重建,SPA 导航通知实例,插件停用时解除事件并清理资源。出队、关键宿主调用和回写前都校验实例有效性。
222
+ The host provides `attach/get/list/send/detach` independently of toolbox declarations. Closing a tool page unsubscribes UI only. Document refresh rebuilds according to policy; SPA navigation notifies instances; plugin deactivation removes events and resources. Instance validity is checked on dequeue, before key host calls, and before writes.
233
223
 
234
- 事件绑定属于运行时声明,指定事件、负载结构、命令和串行、待处理合并或有限并发策略。宿主校验真实来源,控制负载和队列上限。事件按实例及连接代次编号:接收确认与业务完成分开,重发去重与业务重试分开。缓存缺口要求重同步;文档失效后废弃旧事件。不承诺跨故障恰好执行一次。
224
+ Runtime event declarations specify event, payload schema, command, and serial, pending-coalescing, or bounded-concurrency policy. Hosts validate real sources and bound payloads and queues. Events are numbered by instance and connection generation. Receipt acknowledgment is separate from business completion, and retransmission deduplication from business retry. Cache gaps require resynchronization; document invalidation discards old events. Exactly-once execution across failures is not guaranteed.
235
225
 
236
- 当前页面实例按插件安装与真实目标文档共享,CLI、工具箱和自动激活的并发 attach 等待同一次初始化;工具箱只能访问自己的插件。`pageRuntime` 独立于工具箱声明,JS 入口导出函数源码与 URL 范围,`app.runtimeCommand()` 注册后台初始化和事件处理器;这些命令不能被公开命令或工具箱命令直接调用。注册代码也在受管理子进程中读取,阻塞注册可终止。
226
+ Page instances are shared by plugin installation and real target document. Concurrent attach from CLI, toolbox, and automatic activation waits for one initialization. Toolbox can access only its own plugin. `pageRuntime` is independent of toolbox declarations; JS entries export function source and URL scope, and `app.runtimeCommand()` registers background initialization and event handlers. Public/toolbox commands cannot call these directly. Registration code is read in managed, terminable child processes.
237
227
 
238
- Python 入口也可导出同结构的 `pageRuntime` 字典,`code` 为网页端 JavaScript 函数字符串;`app.runtimeCommand()` 支持直接注册和装饰器。Python 的 `app.page()` 及实例绑定的 `context.page.send/get` 通过同步桥接调用同一页面服务。runtime-only 标记参与描述元数据校验与实际 Python handler 选择,公开命令和工具箱不能直接执行;普通 `app.command` 若同时用于页面事件,则继续保持公开身份。每次 Python run 的 ready ACK 之前核验完整 descriptor,包括页面声明和命令可见性。
228
+ Python entries may export the same `pageRuntime` dictionary, with `code` containing a browser JavaScript function string. `app.runtimeCommand()` supports direct registration and decorators. Python `app.page()` and instance-bound `context.page.send/get` synchronously bridge to the same page service. Runtime-only markers participate in descriptor validation and actual Python handler selection; public/toolbox commands cannot execute them directly. Ordinary `app.command` handlers also used for page events remain public. Every Python run checks the full descriptor, including page declarations and command visibility, before its ready ACK.
239
229
 
240
230
  ```javascript
241
231
  export const pageRuntime = {
@@ -256,113 +246,74 @@ export default app => {
256
246
  };
257
247
  ```
258
248
 
259
- 页面函数的 `host.emit(name, payload)` 返回接收确认;处理结果通过实例事件查询。`context.page.send/get` 始终绑定当前实例,运行时任务的普通页面动作和复合页面入口被拒绝。每次出队、宿主调用和提交结果都重新检查文档。运行时传输保留控制会话归属、暂停与释放检查,并行处理消息和回调;消息顺序由实例与事件策略负责,等待业务消息不会阻塞同文档的校验或取消。页面 `receive(message, { signal, requestId })` 提供每条消息的协作取消信号;任务取消传播到该文档对应请求,插件异步等待后应检查 signal。取消不强制终止页面 JS,已提交的外部操作仍需核对。后台默认每 10 秒并发续租,每实例校验超时 8 秒,浏览器租约 30 秒;续租只更新内存,独立定时器每 5 秒批量保存轻量租约,完整运行时代码与待确认事件只在对应状态变化时持久化。失效时取消排队和运行任务并释放页面资源。
260
-
261
- 事件负载最多 32 KiB,页面待确认队列 64 条;后台每事件默认待处理 32 条(最多 64),每实例活动事件最多 128,有限并发最多 8。历史最多 256 条及 384 KiB,游标缺口明确返回。Native Messaging 的请求与结果各按 192 KiB 分块并校验 SHA-256,单条页面业务消息/结果最多 24 MiB;浏览器与工具箱回复资源各有 32 MiB 总预算及 30 秒过期。
262
-
263
- SDK 使用 `PluginPageClient`。工具箱调用 `host.page.*`,或 `window.mearl.subscribe('page', { instanceId, cursor? }, listener)` 观察后台事件;`events` 接受 `cursor` 与 `waitMs`(0–25000),无新事件时可等待变化;工具箱订阅先读取快照,随后使用长轮询,退订只结束观察。内部 `page_runtime` 是文档适配协议,业务插件使用 `app.page()`。设计稿叠加已迁移,保存事件无需 Popup 转发。
264
-
265
- 激活通过 `app.page('activation', { enabled: true, matches, persistence: 'session' | 'persistent', frame, input })` 显式配置;实际范围是用户规则与声明范围的交集;自动激活实例在注入、SPA 导航及日常校验时复查该交集,离开范围即失效,手动挂载遵循插件声明范围。关闭规则清理实例,在途挂载由规则代次校验阻止。扩展 worker 重启后先与执行服务握手,核验 hostId、service epoch、安装、revision、browserSessionId、documentId 和 instanceId;同一所有者按服务端已接收位置续传未确认事件,确认位置只前进。连接 epoch 变化不改变业务事件序号。桥接暂时不可用时保留队列等待对账,租约过期后失效。
249
+ Page-function `host.emit(name, payload)` returns receipt acknowledgment; query instance events for processing results. `context.page.send/get` always binds the current instance. Ordinary and compound page actions from runtime tasks are rejected. Each dequeue, host call, and result commit rechecks the document. Runtime transport preserves control-session ownership, pause, and release checks, processing messages and callbacks in parallel. Instances and event policies own ordering; waiting for business messages does not block same-document validation or cancellation. Page `receive(message, { signal, requestId })` supplies cooperative per-message cancellation. Task cancellation propagates to that document request; plugins should check signal after asynchronous waits. Cancellation does not forcibly stop page JS, so external operations still need reconciliation. Background renewal defaults to concurrent checks every 10 seconds, with eight-second per-instance validation and 30-second browser leases. Renewal updates memory only; a separate timer saves lightweight leases every five seconds. Full code and pending events persist only on relevant state changes. Invalidation cancels queued/running tasks and releases page resources.
266
250
 
267
- 独立修改匹配范围时,可传 `enabled: true, matchPatch: { add?: string[], remove?: string[] }`,与 `matches` 互斥。宿主原子增删范围,避免并发调用覆盖其他范围;删除最后一个范围会关闭自动激活,保留手动挂载的页面实例。`persistence: 'persistent'` 在浏览器重启后保留,初始化 handler 可读取最新插件偏好,不必将易变配置复制到规则 input。
251
+ Event schemas describe formats; background handlers validate payloads, while hosts check bindings, document ownership, sizes, and scheduling. Payloads are limited to 32 KiB; page pending-ack queues to 64. Background pending defaults to 32 per event (maximum 64), active events to 128 per instance, concurrency to 8. History holds 256 entries/384 KiB with explicit cursor gaps. Native Messaging chunks requests/results at 192 KiB with SHA-256 checks. Page messages/results allow 24 MiB; browser/toolbox reply resources each share 32 MiB and expire after 30 seconds.
268
252
 
269
- 执行服务 epoch 变化、实例消失或接收位置回退时,旧实例和队列失效,不向新服务重放旧事件。已有激活规则可以创建新实例,手动实例需重新 attach;规则绑定安装 ID,插件卸载或换源后同名新安装不会继承授权。新注入在独立脚本世界内串行交接,等待旧 handler 完成 dispose,再建立新实例,避免旧覆盖层清理影响新 DOM。
253
+ SDK uses `PluginPageClient`; toolbox uses `host.page.*` or `window.mearl.subscribe('page', { instanceId, cursor? }, listener)`. `events` accepts `cursor` and `waitMs` (0–25000). Toolbox reads an initial snapshot then long-polls; unsubscribe stops observation only. Internal `page_runtime` adapts documents; plugins use `app.page()`. Design overlays save assets through page events.
270
254
 
271
- ### 状态和激活
255
+ Explicitly configure activation through `app.page('activation', { enabled: true, matches, persistence: 'session' | 'persistent', frame, input })`. Effective scope is the intersection of user rules and declarations. Automatic instances recheck it at injection, SPA navigation, and regular validation, invalidating when outside scope. Manual attachment follows declarations. Disabling rules cleans instances; rule generations block in-flight stale attaches. After extension worker restart, handshake with the execution service verifies hostId, service epoch, installation, revision, browserSessionId, documentId, and instanceId. The same owner resumes unacknowledged events from the service's received position, which only advances. Connection epochs do not change business event sequences. Temporary bridge outages retain queues for reconciliation until lease expiry.
272
256
 
273
- 插件导出 `stateStorage = { version: 1, collections: { ... } }`,每个集合声明 `scope`(installation/host/browser-session/instance)、`storage`(persistent/cache/memory)、`schemaVersion`、JSON Schema 和可选 `maxEntries/maxBytes/ttlMs`。浏览器会话与实例只接受 cache/memory;memory 只允许这两种作用域,并且只驻留插件执行宿主内存,按需分配,全宿主合计上限 128 MiB / 512 个集合命名空间,宿主重启清空;实例结束释放对应内存。浏览器会话在文档刷新时保留,新的浏览器会话使用独立空间。所有页面作用域都由真实活动页面解析,不能用业务字段伪造。实例结束清理专属缓存;其他缓存默认 TTL 一天,最多七天,读取、定时维护和配额检查会回收过期值。持久配置、缓存、任务记录和二进制资产分别管理。
257
+ To change matching scope independently, pass `enabled: true, matchPatch: { add?: string[], remove?: string[] }`, mutually exclusive with `matches`. Hosts atomically add/remove scopes to avoid concurrent overwrites. Removing the final scope disables automatic activation while retaining manually attached instances. `persistence: 'persistent'` survives browser restarts. Initialization handlers can read current plugin preferences instead of copying volatile settings into rule input.
274
258
 
275
- 同一命名空间已有状态时,memory 与磁盘存储之间切换须使用新的集合名或作用域;不支持跨存储介质迁移。
259
+ Service epoch changes, missing instances, or regressed receipt positions invalidate old instances and queues without replaying events into a new service. Existing activation rules can create new instances; manual instances require attach again. Rules bind installation IDs, so uninstalling or changing source does not transfer authorization to same-name replacements. Injection serializes handoff in an isolated script world, awaiting old handler dispose before creating a new instance so old overlays cannot remove new DOM.
276
260
 
277
- `get/list/transact` 在用户级任务服务串行处理,集合的值、CAS 版本、schema 版本、TTL 与最近 64 次幂等回执一起写入单个原子记录。每个 put/delete 必须携带读取获得的 opaque `expectedVersion`;集合版本单调递增,删除和过期不会重用版本。多键事务最多 128 项、512 KiB,单值最多 256 KiB,默认每集合 1024 项/8 MiB,磁盘状态每插件总量 64 MiB;memory 按前述全宿主上限管理。list 返回有界分页,调用方带 `expectedRevision` 检测翻页期间的变动。
261
+ ### State and activation
278
262
 
279
- 变更 schema 或存储类型需要提升 `schemaVersion`。迁移事务明确带 `migrateFromVersion` 与 `expectedRevision`,全部保留值通过新 schema 后才原子提交;旧 revision 禁止回写新 schema。同步 handler 也校验加载时的声明签名,避免代码更新期间误写。更换安装不会继承旧安装命名空间。
280
-
281
- 命令使用 `app.state(collection, operation, input)`,或通过自动绑定当前页面实例的 `context.state(...)` 调用。SDK `PluginStateClient` 与工具箱 `host.state.*` 使用同一服务。设计稿叠加的素材和设置使用 browser-session memory,素材通过有界分块读写。用户显式导出的图片不属于缓存。
282
-
283
- 插件声明可用 URL 范围与激活入口,用户规则决定是否启用、匹配范围和跨重启保留。激活维护页面实例,业务命令由显式调用或事件触发。扩展、浏览器和执行服务分别重启时,通过握手对账实例及任务,防止重复恢复。
284
-
285
- ### 实施进度和验收
286
-
287
- | 阶段 | 交付 | 状态 |
288
- | --- | --- | --- |
289
- | 执行基础 | 环境和调用身份、独立上下文、受管理执行器、后台命令接入、一次性 Agent 原始结果 | 已实现 |
290
- | 执行服务 | 用户级服务、租约、安装与 revision 快照、持久任务和大结果、SDK/CLI/MCP 任务入口 | 已实现 |
291
- | 页面能力 | 固定目标、运行实例、后台事件、激活规则及对应工具迁移 | 已实现(扩展与 CDP 共用宿主) |
292
- | 模型和状态 | Provider 能力声明、受管理模型任务、结构化结果、事务状态、现有工具迁移 | 已实现(Codex 只读 Agent) |
293
- | 故障恢复 | 原 revision 的检查点恢复、新 attempt、执行树核验、扩展/服务重启对账与激活重建 | 已实现(上述作用域) |
294
- | 子调用归属 | 独立子任务与 revision、专属服务、权限与文档约束、等待名额和取消树 | 已实现 |
295
- | 诊断和接口收口 | 有界元数据导出、SDK/CLI/MCP/工具箱/Python 对齐、错误语义及文档 | 已实现 |
296
-
297
- 工具箱后台入口统一为 host.task.*,迭代管理、标签管理和图片压缩已同步迁移。普通同步命令仍在调用进程执行,后台命令使用执行器。工具箱分块资源和业务状态不能依赖某次后台命令进程继续存活。
298
-
299
- 验收覆盖并发身份与服务初始化、上下文隔离、嵌套调用、入口限制、JS 阻塞终止、Python 上下文及取消、取消和完成竞态、挂起宿主调用、大文本和结构化结果、服务崩溃恢复、调用方断线、源码更新、事件缺口及结果校验。页面验收覆盖同文档并发初始化、连接代次与去重、事件合并、源码固定、导航取消、后台重启重发、激活停用竞态及双向资源完整性。模型与状态验收补充真实子进程中的大 Unicode 结果、结构化失败、独立队列、父子取消、Python 调用、CAS/TTL/迁移和页面失效写入竞态。恢复验收补充真实服务 SIGKILL、原 handler 不重放、并发 resume 去重、源码损坏、Python 恢复、子执行器未停、ACK 与对账交错、慢 dispose 和安装更换。真实 Chrome 已验证约 500 KB Unicode 文本双向分块、页面事件到实例状态的完整链路,以及执行服务 SIGKILL 后由激活规则重建新 epoch/实例并继续处理新事件。跨 revision 验收覆盖历史 schema 身份、显式单步迁移、版本固定、并发去重、输入兼容、宿主调用拦截和服务关闭清理。其他 Provider 和跨执行环境恢复保留在后续阶段。
300
-
301
- ### 真实端到端验收
302
-
303
- 根目录提供显式运行的验收脚本(不随单元测试访问真实模型):
304
-
305
- ```bash
306
- pnpm build
307
- pnpm verify:plugin-host:live \
308
- --browser <全局浏览器ID> \
309
- --model <当前账号支持的模型> \
310
- --codex /absolute/path/to/codex \
311
- --output /tmp/mearl-host-live-report.json
312
- ```
263
+ Schemas are format metadata; hosts do not evaluate JSON Schema keywords. Plugins validate state writes, event payloads, checkpoints, and migrations. Plugins export `stateStorage = { version: 1, collections: { ... } }`. Collections declare `scope` (installation/host/browser-session/instance), `storage` (persistent/cache/memory), `schemaVersion`, JSON Schema, and optional `maxEntries/maxBytes/ttlMs`. Browser-session/instance accept only cache/memory; memory supports only these scopes and lives in execution-host memory, allocated on demand with a host-wide 128 MiB/512-namespace limit. Restart clears it; instance end releases it. Browser-session state survives refresh but separates browser sessions. Real active pages determine scopes, never forged business fields. Instance end cleans dedicated caches; other caches default to one-day TTL, maximum seven days, reclaimed on reads, maintenance, and quotas. Settings, caches, tasks, and binary assets are separate.
313
264
 
314
- 运行前须已连接支持页面运行时的 Chrome 扩展,并且没有现存任务服务;仅在确认服务空闲时通过 SDK 显式调用 `tasks.stopHost()`。脚本自己创建服务并记录 PID/OS 身份,崩溃测试只终止该进程,接纳前检查其他插件的活动任务和页面实例。Native Host 若抢先自动拉起替代服务,验收观察其恢复结果,但不停止不属于本轮创建的进程。
265
+ If a namespace already has state, switching between memory and disk requires a new collection name or scope. Cross-medium migration is unsupported.
315
266
 
316
- `--codex` 显式指定与实际宿主一致的二进制,避免 PATH 中独立 CLI 与 App 内置运行时的版本、模型支持差异。需要已有文件认证,默认从当前 CODEX_HOME(或 ~/.codex)复制 auth.json 到受限临时目录,可用 `--codex-home` 指定认证来源;原配置和认证文件不修改。运行配置与工作目录均在 Skill 源码树之外,代码快照只包含测试入口和 SKILL.md。独立配置仅选择模型和推理强度,不继承用户 MCP/插件配置。
267
+ `get/list/transact` serialize in the user-level task service. Values, CAS versions, schema versions, TTLs, and the latest 64 idempotency receipts persist in one atomic record. Each put/delete requires the opaque `expectedVersion` obtained by reading. Collection versions increase monotonically and are never reused after deletion or expiry. Multi-key transactions allow 128 operations and 512 KiB; single values 256 KiB. Collections default to 1024 entries / 8 MiB, with disk state limited to 64 MiB per plugin. memory uses the host-wide limit above. list is bounded and paginated; callers use `expectedRevision` to detect changes between pages.
317
268
 
318
- 验收使用真实本地 Codex 和后台测试页,检查结构化任务、页面事件→模型→持久状态→真实 DOM、turn 开始后的取消、执行期限、未知模型失败、导航取消父子任务和服务 SIGKILL 后的新实例。期限、导航与崩溃场景通过代理在真实 turn/start 成功后暂缓交付通知,报告明确标记故障注入;不依赖模型生成速度,也不将代理当作模拟模型。代理默认完整转发 RPC,输出内容不写入诊断。
269
+ Changing schema/storage requires increasing `schemaVersion`. Migration transactions supply `migrateFromVersion` and `expectedRevision`. Plugins validate and transform retained values; hosts check versions/transaction constraints and atomically commit. Old revisions cannot write new schemas. Synchronous handlers verify load-time declaration signatures against update races. Replacement installations never inherit old namespaces.
319
270
 
320
- JSON 报告包含每项结果、耗时、任务/页面/执行代次证据、协议方法和失败原因。脚本分别清理任务、注册、页面、认证副本及本轮创建的服务;执行停止未确认时保留任务与代码记录,报告清理失败。Ctrl+C/SIGTERM 进入相同清理流程。默认单元测试验证代理的大文本转发、通知停滞和认证缺失时的早期清理,不发起真实模型请求。
271
+ Commands use `app.state(collection, operation, input)` or current-instance-bound `context.state(...)`. SDK `PluginStateClient` and toolbox `host.state.*` share the service. Design overlay assets/settings use browser-session memory with bounded chunked asset access. Explicitly exported user images are not cache.
321
272
 
322
- ### 默认环境验收
273
+ Plugins declare available URL scopes and activation entries. User rules control enablement, matching, and cross-restart persistence. Activation maintains page instances; explicit invocation or events trigger business commands. Extension, browser, and execution-service restarts reconcile instances and tasks through handshakes to prevent duplicate recovery.
323
274
 
324
- `pnpm verify:plugin-host:default --output /tmp/mearl-host-default.json` 使用调用环境原有的 Codex 运行时发现、配置、认证和默认模型,分别通过 SDK 与 PATH 中临时验收插件的 `proof` 业务命令 校验结构化结果、执行树停止和诊断。它不设置模型或二进制、不复制认证、不写用户配置;报告说明原环境是否已有 CODEX_HOME / MEARL_CODEX_PATH 覆盖。验收只清理自己创建的插件与任务,不停止共享服务。
275
+ Toolbox background commands use `host.task.*`. Ordinary synchronous commands run in calling processes; background commands use managed executors. Long-lived resources persist through state, files, or resource channels.
325
276
 
326
- 工具箱用户发起的页面 attach 在解析目标文档缺少站点权限时,复用宿主权限弹窗,批准后仅重试未派发的 resolve。页面心跳、已有实例消息与后台事件不弹窗,也不重放可能已产生效果的操作。扩展重载属于宿主操作,与当前网页的域权限独立。
277
+ When user-initiated toolbox attach lacks target-site permission, the host permission dialog is reused; approval retries only undispatched resolve. Heartbeats, instance messages, and background events never prompt or replay potentially effective operations. Extension reload is a host operation independent of the current page's domain grant.
327
278
 
328
- ### 跨插件调用
279
+ ### Cross-plugin calls
329
280
 
330
- 受管理任务的跨插件 `app.invoke(name, { command, input })` 建立独立子任务,固定子插件自己的 installation/revision,并提供自己的 state/model/page/checkpoint 归属。子调用只可执行公开命令,不继承工具箱或 runtime 隐藏命令权限;循环调用和超过 16 层的调用明确失败。父任务取消、到期或结束时取消并等待整个子执行树。子插件不能换浏览器路由;页面绑定调用继承原文档约束,页面实例需由子插件显式 attach 并遵循自己的 pageRuntime,不能借用父插件实例或控制其他文档。子任务可记录检查点,但显式 resume 仍只支持合格根任务。
281
+ Managed `app.invoke(name, { command, input })` creates an independent child task pinned to the child installation/revision with its own state/model/page/checkpoint ownership. Children can call only public commands, without hidden toolbox/runtime permissions. Cycles and depth over 16 fail explicitly. Parent cancellation, expiry, or completion cancels and awaits the child tree. Children cannot change browser routes. Page-bound calls inherit document constraints and must explicitly attach their own instances under their own pageRuntime, never borrowing parent instances or controlling other documents. Children can checkpoint, but explicit resume remains eligible-root-only.
331
282
 
332
- ### 统一诊断
283
+ ### Unified diagnostics
333
284
 
334
- `PluginTaskClient.diagnose()` 和工具箱 `host.task.diagnose` 读取同一元数据报告。筛选条件 taskId/plugin/instanceId 取交集,先按调用范围过滤,再展开父子与恢复关联;工具箱固定当前插件,不能借关联读取其他插件任务。诊断不启动模型、不发送浏览器请求,不改变页面绑定。
285
+ `PluginTaskClient.diagnose()` and toolbox `host.task.diagnose` read one metadata report. taskId/plugin/instanceId filters intersect; caller scope filters before expanding parent/child/recovery relationships. Toolbox stays restricted to its plugin, including relationships. Diagnostics start no models, send no browser requests, and change no page bindings.
335
286
 
336
- 报告关联 hostId/epoch、task/parent/resume、invocation、installation/revision 和 document/instance,模型记录运行时选择、基础配置来源、已观察的认证类型、请求/配置/实际改派的模型,以及 thread/turn 限制获接纳的事实。基础 config/read 不表示线程最终配置;缺少通知或历史记录时保留 unknown/incomplete,不推断账号、凭证或执行结果。可执行文件版本未取得时省略。
287
+ Reports link hostId/epoch, task/parent/resume, invocation, installation/revision, and document/instance. Model records include runtime choice, base configuration sources, observed authentication types, requested/configured/actually rerouted models, and acknowledged thread/turn limits. Base config/read does not establish final thread configuration. Missing notifications/history retain unknown/incomplete, without inferring accounts, credentials, or outcomes. Unavailable executable versions are omitted.
337
288
 
338
- 默认没有输入、提示词、进度正文、检查点数据、结果正文、完整 URL、环境和凭证。默认错误仅含 code、本地可执行路径只留文件名;显式 includeErrorMessages/includeLocalPaths 可加入有限错误消息和本地路径,始终不加入 error details/metadata/stack。CLI 导出为 0600。报告上限 256 KiB、128 个任务、64 个页面、每任务最近 8 条非进度事件;扫描上限为 2048 个目录项、512 份记录和 16 MiB 记录数据。coverage 分别报告扫描不足与输出省略,history.gap 表示原始保留历史缺口。
289
+ Defaults exclude input, prompts, progress/checkpoint/result bodies, full URLs, environment, and credentials. Errors include code only; executable paths retain basenames only. Explicit includeErrorMessages/includeLocalPaths permits bounded messages/local paths, never error details/metadata/stack. CLI exports use 0600 permissions. Limits: 256 KiB, 128 tasks, 64 pages, latest eight non-progress events per task; scans cap at 2048 directory entries, 512 records, and 16 MiB. coverage distinguishes incomplete scans from output omissions; history.gap reports retained-history gaps.
339
290
 
340
- 执行器状态区分未启动、当前观察存活、正向确认整树停止和未知;终态不自动等于停止。页面 service-snapshot 仅是服务当前记录,historical-binding 是执行时留存的文档身份,两者均不承诺浏览器此刻仍处于该文档。
291
+ Executor states distinguish unstarted, currently observed alive, positively confirmed whole-tree stopped, and unknown. Terminal task status does not imply stopped execution. Page service-snapshot is the service's current record; historical-binding is execution-time identity. Neither guarantees the browser remains at that document.
341
292
 
342
- ### 公开接口
293
+ ### Public interfaces
343
294
 
344
- | 能力 | SDK | CLI | MCP | 工具箱 |
295
+ | Capability | SDK | CLI | MCP | Toolbox |
345
296
  | --- | --- | --- | --- | --- |
346
- | 持久任务、结果、取消、检查点恢复 | PluginTaskClient | 插件按需提供业务命令 | plugin_invoke | host.task.start/get/list/events/result/cancel(检查点恢复由 SDK 发起) |
347
- | 诊断导出 | tasks.diagnose | — | — | host.task.diagnose |
348
- | 固定文档运行时与激活 | PluginPageClient | 插件业务命令 | plugin_invoke | host.page.* |
349
- | 事务状态 | PluginStateClient | 插件业务命令 | plugin_invoke | host.state.* |
350
- | 受管理模型 | PluginModelClient | 插件业务命令 | plugin_invoke | host.model.* |
297
+ | Persistent tasks, results, cancellation, checkpoint recovery | PluginTaskClient | Plugin-defined business commands | plugin_invoke | host.task.start/get/list/events/result/cancel (SDK initiates checkpoint recovery) |
298
+ | Diagnostic export | tasks.diagnose | — | — | host.task.diagnose |
299
+ | Fixed-document runtimes and activation | PluginPageClient | Plugin business commands | plugin_invoke | host.page.* |
300
+ | Transactional state | PluginStateClient | Plugin business commands | plugin_invoke | host.state.* |
301
+ | Managed models | PluginModelClient | Plugin business commands | plugin_invoke | host.model.* |
351
302
 
352
- 插件 JS/Python 使用命令上下文的 state/model/checkpoint 与 app.invoke 组合这些能力。普通同步命令在调用进程执行,受管理任务的跨插件调用使用独立子执行器;不应依赖命令进程承载页面长期资源。等待超时只结束观察,执行期限和 cancel 才终止执行树。调度有独立命令/模型名额;等待子任务的父命令让出名额,继续运行前重新获取,最多 32 个等待中的父执行器。活动父子关系由服务内存索引维护,任务收尾只取消并等待自己的活动子任务,不扫描保留历史;重启恢复仍核验持久化执行树。
303
+ JS/Python plugins compose these capabilities with context state/model/checkpoint and app.invoke. Synchronous commands run in calling processes; managed cross-plugin calls use separate child executors. Do not keep long-lived page resources in command processes. Wait timeouts stop observation; execution deadlines/cancel terminate trees. Commands/models have separate slots; parents waiting for children release slots and reacquire before continuing, with at most 32 waiting parents. In-memory active-child indexes let finalization cancel/wait only its children without scanning retained history; restart recovery still validates persisted trees.
353
304
 
354
- ## 构建与验证
305
+ ## Build and validation
355
306
 
356
307
  ```bash
357
- pnpm build # esbuild 打包到 dist/(内联 browser-core / daemon-core)
358
- pnpm typecheck # 仅类型检查
308
+ pnpm build # esbuild into dist/, inlining browser-core / daemon-core
309
+ pnpm typecheck # Type checking only
359
310
  ```
360
311
 
361
312
  ## License
362
313
 
363
- ISC
314
+ Apache-2.0
364
315
 
365
- 模型列表可通过 SDK `PluginModelClient.list()` 或工具箱 `host.model.list` 查询。模型项携带可用时的 `reasoningEfforts` 和 `defaultReasoningEffort`,界面按模型支持范围提供思考级别。宿主合并并发发现请求,已有模型目录立即返回,过期后后台刷新;刷新失败保留可用目录并短暂退避,不创建推理会话。状态批量读取使用 `get-many` 和 `keys`(SDK `getMany`、工具箱 `host.state.getMany`),一次读取同一 revision 的最多 128 个键,含缺失键的 CAS version,结果上限 1 MiB。
316
+ Query models through SDK `PluginModelClient.list()` or toolbox `host.model.list`. Entries include supported `reasoningEfforts` and `defaultReasoningEffort` where available; UI restricts choices accordingly. Hosts coalesce discovery, immediately return existing catalogs, and refresh stale data in background. Failures retain usable catalogs with short backoff and create no inference sessions. Batch state reads use `get-many` and `keys` (SDK `getMany`, toolbox `host.state.getMany`) for up to 128 keys at one revision, including missing keys' CAS versions, limited to 1 MiB.
366
317
 
367
318
 
368
- 插件宿主通过 `PluginHost` 统一组装 `tasks/state/pages/models` 并管理关闭顺序与诊断。任务调度器只管理任务执行、父子调用、取消和恢复,通过注入的服务函数访问状态与页面。模型执行仍作为任务使用独立调度额度,模型目录由宿主共享。页面实例的状态范围解析由宿主显式接线。`PluginHostConnection` 负责共享 RPC 通信;Native Host 按插件、交互渠道和来源分别缓存连接,避免共享连接改变权限申请行为。
319
+ `PluginHost` assembles `tasks/state/pages/models`, shutdown ordering, and diagnostics. Task schedulers own execution, parent/child calls, cancellation, and recovery, accessing state/pages through injected services. Models remain tasks with independent scheduling quotas and host-shared catalogs. Hosts explicitly wire page-instance state-scope resolution. `PluginHostConnection` shares RPC transport; Native Host caches connections separately by plugin, interaction channel, and source so sharing cannot change permission prompts.