import { BROWSER_TOOL_NAMES, type KnowledgeRecord, type Observation, type RunState } from "../domain/types.ts"; import type { ActionInput, ActionResult, FillInput, FillResult } from "../session.ts"; import type { BrowserSession } from "../session.ts"; /** Methods the tool/command adapters need. Local BrowserSession and the desktop RPC proxy both satisfy this. */ export type SessionHandle = Pick< BrowserSession, | "inspect" | "act" | "runPlan" | "fill" | "askUser" | "takeover" | "resume" | "recordTool" | "proposeKnowledge" | "startRun" | "pauseRun" | "stopRun" | "status" | "browserToolNames" | "currentRunId" | "previousActiveTools" | "store" | "knowledge" | "worker" >; export interface RpcTransport { call(method: string, args: unknown[]): Promise; } export class RpcSessionHandle implements SessionHandle { currentRunId: string | null = null; previousActiveTools: string[] | null = null; constructor(private readonly rpc: RpcTransport) {} get store() { return { listStates: () => this.rpc.call("store.listStates", []), } as SessionHandle["store"]; } get knowledge() { return { search: (query: string) => this.rpc.call("knowledge.search", [query]), list: () => this.rpc.call("knowledge.list", []), setStatus: (id: string, status: "approved" | "rejected") => this.rpc.call("knowledge.setStatus", [id, status]), } as SessionHandle["knowledge"]; } get worker() { return { stop: () => this.rpc.call("worker.stop", []), } as SessionHandle["worker"]; } inspect(runId?: string, tabId?: string): Promise { return this.rpc.call("inspect", [runId, tabId]); } act(input: ActionInput): Promise { return this.rpc.call("act", [input]); } runPlan(plan: unknown, runId?: string): Promise { return this.rpc.call("runPlan", [plan, runId]); } fill(input: FillInput): Promise { return this.rpc.call("fill", [input]); } askUser(question: string, runId?: string, providedAnswer?: string): Promise { return this.rpc.call("askUser", [question, runId, providedAnswer]); } takeover(runId?: string, tabId?: string): Promise { return this.rpc.call("takeover", [runId, tabId]); } resume(runId?: string): Promise<{ state: RunState; observation: Observation }> { return this.rpc.call("resume", [runId]); } recordTool( toolName: string, params: Record, result: Record, isError = false, ): Promise { return this.rpc.call("recordTool", [toolName, params, result, isError]); } proposeKnowledge(input: { kind: KnowledgeRecord["kind"]; text: string; tags?: string[]; runId?: string; }): Promise { return this.rpc.call("proposeKnowledge", [input]); } async startRun(goal: string, startUrl?: string): Promise { const state = await this.rpc.call("startRun", [goal, startUrl]); this.currentRunId = state.runId; return state; } async pauseRun(runId?: string): Promise { return this.rpc.call("pauseRun", [runId]); } async stopRun(runId?: string, status?: RunState["status"]): Promise { const state = await this.rpc.call("stopRun", [runId, status]); if (this.currentRunId === state.runId) this.currentRunId = null; return state; } status(): Promise>> { return this.rpc.call("status", []); } browserToolNames(): string[] { return [...BROWSER_TOOL_NAMES]; } }