import { type WorkbenchLayoutState, type WorkbenchPaneKind, type WorkbenchSessionRow, type WorkbenchTerminalLocation } from './agent-workbench-model.js'; import { type WorkbenchApi } from './agent-workbench-api.js'; import { type TerminalPaneControlMode } from './agent-workbench-terminal-control.js'; import type { WorkbenchCapabilities } from './agent-workbench-capabilities.js'; interface PaneCommonProps { session: WorkbenchSessionRow; api: WorkbenchApi; authenticated: boolean; /** P1-4:服务端投影的最小操作能力集。`authenticated` 继续决定观察级链路 * (同源 iframe vs viewToken、控制权状态拉取);写操作入口各看自己的布尔: * 终端「接管输入/释放输入」看 canControl,Preview「开启交互/立即锁定」看 * canInteract。触屏只读限制是叠加在这之上的,不是替换。 */ capabilities: WorkbenchCapabilities; now: number; } /** 终端 iframe 内部实时通道的一次读数。`unknown` 表示读不出来(跨域、时序、状态 * 节点还没渲染出来),一律不作数:宁可不提示,也不要在连接其实正常的浏览器上盖一层 * 假警告。 */ export type TerminalFrameStatus = 'connected' | 'disconnected' | 'unknown'; /** 生产实现。终端页和工作台同源(都挂在 /s/ 前置代理下),所以可以直接读它右下角 * 那个 #status 节点——'connected' / 'disconnected' 是终端页自己的 WebSocket 回调写进去 * 的,比任何外部探测都准。跨域或时序异常时读取会抛,按未知处理。 */ export declare function readTerminalFrameStatus(frame: HTMLIFrameElement | null): TerminalFrameStatus; /** 终端页自己算出来的写权限。`unknown` = 还没有依据(WS 没确认、跨域读不到)。 */ export type TerminalFrameWrite = 'writable' | 'readonly' | 'unknown'; /** * 生产实现:只读终端页的 `wsHasWrite`——**已经建立的那条 WebSocket** 实际拿到什么 * 权限(worker 在握手完成时用一条带外控制帧告诉页面,页面落成这个全局)。 * `null` / 读不到 = 还没有结论,一律 `unknown`。 * * 为什么不拿页面那次 HTTP GET 的 `hasToken` 兜底:两次是**各自独立**的鉴权。页面是 * HTTP 请求,iOS WebView 会带 Cookie;紧接着的 WebSocket 升级**不带** Cookie,前置 * 代理给平台所有者补 WRITE grant 这件事也就跟着落空。拿 hasToken 兜底等于在 WS 还 * 没连上(或永远连不上)时宣称「可以输入」,用户打的字被 worker 原地丢掉,屏幕上 * 什么都不发生。没有依据时说未知,是这里唯一诚实的答案。 */ export declare function readTerminalFrameWrite(frame: HTMLIFrameElement | null): TerminalFrameWrite; /** * 终端页 → 工作台的一次写权限回报(终端页在 WS 建立后 postMessage 上抛)。 * * 同源读全局已经够用,这条消息解决的是**时机**:不用等下一拍轮询,连上那一刻面板 * 就能把文案改对;而且万一将来这块 iframe 走到跨文档隔离的场景,同源读会直接抛, * 消息通道仍然成立。来源校验沿用外观桥那一套:只认这块 iframe 自己的 contentWindow * 发来的、结构完全对得上的消息。 * * `write: null` 是终端页每次**重连**时先发的一条「退回未知」——上一条连接的结论套 * 不到新连接上。返回 `null` 表示「这压根不是我们的消息」,两者不能混。 */ export declare function readTerminalWriteReport(data: unknown): TerminalFrameWrite | null; /** * 写请求(takeover / release)的硬超时。 * * 没有它时,一个**永远不回执**的 POST 会把这块面板整个焊死:`pending` 一直挂着 → * `busy` 恒为真 → 标题栏和行内按钮永久禁用;轮询读数一律被在途写丢弃,界面停在 * 「正在接管输入…」;连卸载补偿也等不到那个 promise。半开连接、被代理吞掉的回执 * 都是这个形状,而且它不会自己好。 * * 到点就按失败处理:进 `unknown`(**不是**只读——服务端很可能已经受理了)、遮罩 * 挡住盲输入、立刻发一次权威复核 GET 把状态收敛回来。 */ export declare const TERMINAL_WRITE_TIMEOUT_MS = 10000; /** 面板对外的一次控制权回执。形状与语义住在状态机文件里,这里只做转出,省得 * 调用方为了一个类型再多认一个模块。 */ export type { TerminalPaneControlMode }; export declare function TerminalPane(props: PaneCommonProps & { location: WorkbenchTerminalLocation | null; /** 这块面板是按哪个意图挂起来的,**两态**: * false 行内「终端」的只读打开 → 还攥着租约就还回去; * undefined 没有意图(分栏树里的面板、直接挂载的调用方)→ 一概不动控制权。 * 「没意见」必须和「明确要只读」分开:把缺省也当成只读意图,任何一处顺手挂起 * 的终端面板都会把用户正握着的写权限收走。 * 没有 `true`:接管**只**由标题栏的「接管输入」发起(产品决策:会话行不再提供 * 接管捷径),一键接管的意图连同它那条分支一起删掉了,别再加回来。 * 只读意图只在面板第一次拿到控制权读数时兑现一次,之后控制权只由标题栏的 * 「接管输入 / 释放输入」驱动。 */ autoTakeControl?: false; /** 控制权回执。面板是唯一知道自己真实模式的人——标题栏的接管/释放不经过外层, * 恒可写身份更是从头到尾没发过任何请求——外层的行内「终端」按钮要靠它判断 * 「再点一次 = 关掉还是降回只读」,断了回执就会把已接管的面板直接关掉。 */ onControlModeChange?(mode: TerminalPaneControlMode): void; /** 测试注入点:组件测试环境里没有真 iframe 的 internals,注入一个假的读数源即可。 * 生产默认走同源 contentWindow(`readTerminalFrameStatus`)。 */ readFrameStatus?: (frame: HTMLIFrameElement | null) => TerminalFrameStatus; /** 同上,注入「这块 iframe 自报能不能写」的读数源(生产走 * `readTerminalFrameWrite`,读终端页同源的 `wsHasWrite`)。 */ readFrameWrite?: (frame: HTMLIFrameElement | null) => TerminalFrameWrite; }): JSX.Element; export declare function WebPane(props: PaneCommonProps): JSX.Element; export declare function WorkbenchPaneRegion(props: PaneCommonProps & { layout: WorkbenchLayoutState; effectivePaneMode: 'focus' | 'split'; location: WorkbenchTerminalLocation | null; onRatioChange(ratio: number): void; }): JSX.Element; export declare function WorkbenchInfo(props: { session: WorkbenchSessionRow; }): JSX.Element; export declare function WorkbenchInfoDrawer(props: { session: WorkbenchSessionRow; open: boolean; onClose(): void; }): JSX.Element | null; export declare function paneLabel(kind: WorkbenchPaneKind): string; //# sourceMappingURL=agent-workbench-panes.d.ts.map