feat(browser): keep sessions alive during desktop, takeover and agent use (AG-4)
camofox reaps a tab after TAB_INACTIVITY_MS (5 min) without API tool calls;
desktop-mode input goes straight to Selkies and screenshots don't count, so
tabs disappeared under the user during takeover.
- BrowserKeepalive (in-process): sessions held by an open desktop WebSocket
(ref-counted), a human takeover lease, or a running/queued agent run that
used browser.* get a keepalive every BROWSER_KEEPALIVE_MS (default 120s,
0 = off). Holds are re-checked each round; release/lease expiry/agent end/
session close stop it and camofox reaps as usual.
- Keepalive call: camofox 1.17 has no keepalive/touch endpoint and /stats does
not bump toolCalls; use GET /tabs/{id}/downloads without includeData/consume
(read-only metadata). Goes through BrowserService.run so restarts or reaped
tabs self-heal via BR-3 reconnect; no lastAutomationAt, no control needed.
- Runbook browser.md §12, deploy env table, compose env; tests against the
testkit mock camofox.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
919dc02200
commit
1fcfcac885
13 files changed
+523
-12
No files matched your search
@@ -811,6 +811,24 @@ export class CamofoxBrowserProvider implements BrowserProvider {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* AG-4 保活:camofox 1.17 没有 keepalive / touch 接口。`GET /tabs/{id}/stats` 只刷新会话的 lastAccess,
|
||||
* 不算标签页活动(标签页回收看 toolCalls 有没有变化);最轻的会让 toolCalls 加一的只读调用是
|
||||
* `GET /tabs/{id}/downloads`(不带 includeData / consume:只列下载元数据,不读页面、不清下载记录)。
|
||||
*/
|
||||
async keepAlive(ref: TabRef): Promise<void> {
|
||||
const s = this.state(ref.sessionId);
|
||||
await this.withProfile(s, async () => {
|
||||
const tab = this.tabOf(s, ref);
|
||||
const path = `/tabs/${encodeURIComponent(tab.upstreamTabId)}/downloads`;
|
||||
const res = await this.request("GET", path, {
|
||||
query: { userId: s.session.profileKey },
|
||||
timeoutMs: Math.min(this.cfg.timeoutMs ?? 30_000, 10_000),
|
||||
});
|
||||
await res.body?.cancel();
|
||||
});
|
||||
}
|
||||
|
||||
// ---------- 页面 ----------
|
||||
|
||||
async navigate(ref: TabRef, url: string, budget: NavigationBudget): Promise<NavigationResult> {
|
||||
|
||||
@@ -93,6 +93,14 @@ export interface BrowserProvider {
|
||||
*/
|
||||
reopenSession?(sessionId: string, req: ReopenSessionRequest): Promise<ReopenSessionResult>;
|
||||
|
||||
// ── AG-4 保活 ──
|
||||
/**
|
||||
* 对标签页做一次最轻的只读调用,让上游把它算作"有活动"(camofox 标签页空闲回收 TAB_INACTIVITY_MS 与会话空闲回收
|
||||
* SESSION_TIMEOUT_MS 都按 API 调用计;桌面模式里人的键鼠操作不经 API,不算活动)。不检查控制者、不改页面。
|
||||
* 标签页不在了报 not_found(调用方按 BR-3 自愈重连)。
|
||||
*/
|
||||
keepAlive?(ref: TabRef): Promise<void>;
|
||||
|
||||
// ── BR-4 浏览器 UA 与指纹统一、出口按站点 ──
|
||||
/** 固定浏览器身份的公开部分(UA、OS、版本);上游没有时返回 null */
|
||||
identity?(): Promise<BrowserIdentity | null>;
|
||||
|
||||
Reference in new issue
Block a user