@allternit/computer-driver 0.1.0 → 0.2.0

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.
package/CHANGELOG.md ADDED
@@ -0,0 +1,14 @@
1
+ # Changelog
2
+
3
+ ## 0.2.0 — 2026-10-10
4
+
5
+ ### Added
6
+
7
+ - **Contract v2 (`allternit.computer.v2`) structured members.** Typed client methods for all ten driver-backed members — `read_ui`, `act`, `run_batch`, `verify`, `request_human`, `use_credential`, `run_subtask`, `run_parallel`, `run_skill`, `skills` — on the new `ComputerV2Driver` (exported from the package root). Input and result types are generated from `contracts/computer-toolset/allternit-computer-v2.json` (`src/v2-generated.ts`, emitted by `contracts/computer-toolset/generate.mjs`), so they cannot drift from the server contract.
8
+ - **`computer_v2` function tool next to the pixel tool in every adapter**, with the same steering guidance gizzi sends: `computerV2AnthropicTool` + `runAnthropicComputerV2` (anthropic subpath), `openaiComputerV2Tool` + `runOpenAIV2Call`, `geminiComputerV2Declaration` + `runGeminiV2Call`, plus the provider-neutral `computerV2Tool()`.
9
+ - **Approval flow helper.** `client.toolsetWithApproval(id, call, onApproval)` answers a 409 `approval_required` hold (approve + resend with the single-use `approval_grant`) for any member, pixel or structured.
10
+ - **Typed errors.** `ComputerV2Error` (a structured member answered `is_error`), `ComputerBusyError` (423 `computer_busy` / `computer_controlled_elsewhere`), `SandboxRequiredError` (409 `sandbox_required`) and `ComputerConflictError` (409 `computer_conflict`).
11
+
12
+ ### Notes
13
+
14
+ - Requires the Allternit Driver sidecar on the computer for the structured members (this-device today). The 17 pixel members are unchanged.
package/README.md CHANGED
@@ -8,6 +8,59 @@ const client = new AllternitComputers({ apiKey: process.env.ALLTERNIT_API_KEY })
8
8
  const computer = await client.create({ name: "agent-1" })
9
9
  ```
10
10
 
11
+ ## Structured UI driving (allternit.computer.v2)
12
+
13
+ Contract v2 adds ten driver-backed structured members — `read_ui`, `act`, `run_batch`, `verify`, `request_human`, `use_credential`, `run_subtask`, `run_parallel`, `run_skill`, `skills` — typed end to end. `ComputerV2Driver` calls them directly; every adapter also exposes them to the model as the `computer_v2` function tool. The structured members need the Allternit Driver on the computer (this-device today).
14
+
15
+ ```ts
16
+ import { ComputerV2Driver } from "@allternit/computer-driver"
17
+ const v2 = new ComputerV2Driver({ client, computerId: computer.id, onApproval: async (a) => askAPerson(a) })
18
+
19
+ // Read the UI as an element tree — no screenshots.
20
+ const ui = await v2.read_ui({ app: "Safari" })
21
+ const field = ui.elements?.find((e) => e.role === "textfield")
22
+ if (field) await v2.act({ id: field.id, op: "set_value", value: "hello@example.com", version: ui.version })
23
+
24
+ // Hand a bounded step sequence to the fast decision loop instead of clicking through it yourself.
25
+ const sub = await v2.run_subtask({
26
+ goal: "Fill the signup form and submit it",
27
+ inputs: [{ name: "email", value: "hello@example.com" }],
28
+ success: [{ role: "button", name: "Submit" }],
29
+ })
30
+ if (sub.status !== "done") console.log(sub.status, sub.next)
31
+ ```
32
+
33
+ ### Subtask safety statuses
34
+
35
+ `run_subtask` / `run_skill` / `run_parallel` results end with a `status`:
36
+
37
+ - `done` — the goal is met.
38
+ - `escalated` — handed back with a `reason` and the current `screen`; continue from there yourself.
39
+ - `needs_confirmation` — the next step (`held_step`) needs the person's confirmation; run it yourself with `act`/`run_batch` and the server asks them.
40
+ - `paused` — the safety monitor paused the subtask; stop and call `request_human`.
41
+ - `denied` — the step isn't allowed on this computer (app/domain lists or a credential binding); find another way.
42
+ - `use_api` — make the named API/MCP call yourself (`api.tool`); the screen is untouched.
43
+ - `failed` — ran out of steps, budget or a hard error.
44
+
45
+ ### The computer_v2 function tool in a model loop
46
+
47
+ Each adapter exposes the structured members with the same steering guidance gizzi sends:
48
+
49
+ ```ts
50
+ import { computerV2AnthropicTool, runAnthropicComputerV2 } from "@allternit/computer-driver/anthropic"
51
+ import { openaiComputerV2Tool, runOpenAIV2Call } from "@allternit/computer-driver"
52
+ import { geminiComputerV2Declaration, runGeminiV2Call } from "@allternit/computer-driver"
53
+
54
+ // Anthropic: put computerV2AnthropicTool() next to the toolset in tools, and answer
55
+ // tool_use blocks whose name is "computer_v2" with runAnthropicComputerV2({ client, computerId, onApproval }, name, input).
56
+ // OpenAI: put openaiComputerV2Tool() in tools and answer function_call items with
57
+ // runOpenAIV2Call({ client, computerId }, call.call_id, JSON.parse(call.arguments)).
58
+ // Gemini: put geminiComputerV2Declaration() in functionDeclarations and answer
59
+ // functionCall parts with runGeminiV2Call({ client, computerId }, part.functionCall.name, part.functionCall.args).
60
+ ```
61
+
62
+ `computerV2Tool()` returns the provider-neutral `{ name, description, schema }` if you wire tools yourself.
63
+
11
64
  ## Claude (`computer_toolset_20260801` / `browser_toolset_20260801`)
12
65
 
13
66
  Requires `@anthropic-ai/sdk` >= 0.132.0 (optional peer).
@@ -18,7 +71,7 @@ const toolset = new AllternitComputerToolset({
18
71
  client, computerId: computer.id,
19
72
  onApproval: async (approval) => askAPerson(approval), // server-held risky calls
20
73
  })
21
- // pass `toolset` to client.beta.messages.toolRunner({ tools: [toolset], ... })
74
+ // pass `toolset` to client.beta.messages.toolRunner({ tools: [toolset, computerV2AnthropicTool()], ... })
22
75
  ```
23
76
 
24
77
  Every member runs on the server through `POST /v1/computers/{id}/toolset`. When the server holds a call (409 `approval_required`), `onApproval` decides: `true` approves it and resends with the grant, `false` returns the held result to the model as an error. The SDK's own `confirm` option still runs first. Approving with an API key needs the project's `approval_mode` set to `api_key`.
@@ -40,6 +93,13 @@ const { functionResponse, inlineData } = await runGeminiCall({ client, computerI
40
93
 
41
94
  Gemini's 0–999 coordinates are sent as `coordinate_space: "normalized_1000"`; the server scales them.
42
95
 
43
- The OpenAI and Gemini adapters let `ApprovalRequiredError` propagate; catch it, call `client.approve()`, and resend the action.
96
+ The OpenAI and Gemini pixel adapters let `ApprovalRequiredError` propagate; catch it, call `client.approve()`, and resend the action.
97
+
98
+ ## Errors
44
99
 
45
- Errors: `ApprovalRequiredError` (409, carries `approval` and `result`) and `AllternitApiError` (everything else, with `status`, `code`, `type`).
100
+ - `ApprovalRequiredError` — 409 `approval_required`; carries `approval` (id, member, `approve_url`) and the held `result`. `client.toolsetWithApproval(id, call, onApproval)` approves and resends with the `approval_grant` for you.
101
+ - `ComputerV2Error` — a structured member answered `is_error: true`; carries the member name and raw `result`.
102
+ - `ComputerBusyError` — 423 `computer_busy` / `computer_controlled_elsewhere`.
103
+ - `SandboxRequiredError` — 409 `sandbox_required` (the call needs a sandbox computer).
104
+ - `ComputerConflictError` — 409 `computer_conflict` (another subtask or lease conflicts).
105
+ - `AllternitApiError` — everything else, with `status`, `code`, `type`.
@@ -23,3 +23,21 @@ export declare class AllternitBrowserToolset extends BetaAbstractBrowserToolset2
23
23
  constructor(o: DriverOptions & Omit<BetaBrowserToolsetOptions, "browserState"> & Partial<Pick<BetaBrowserToolsetOptions, "browserState">>);
24
24
  protected execute(ctx: BetaToolsetCallContext, name: string, input: unknown): Promise<BetaBrowserMemberResult>;
25
25
  }
26
+ /**
27
+ * The `computer_v2` function tool for Anthropic models: the ten structured
28
+ * members (read_ui … skills) next to the native pixel toolset. The Anthropic
29
+ * wire hook only rewrites tools carrying the v1 pixel marker, so this passes
30
+ * through untouched.
31
+ */
32
+ export declare function computerV2AnthropicTool(): {
33
+ name: string;
34
+ description: string;
35
+ input_schema: Record<string, unknown>;
36
+ };
37
+ /**
38
+ * Run one `computer_v2` function call (`{action, ...fields}`) and return the
39
+ * tool-result content blocks, like any toolset member. Throws ToolError on a
40
+ * failed action; without `onApproval`, a held call (409 approval_required)
41
+ * resolves its held result as an error, the same rule as the toolsets.
42
+ */
43
+ export declare function runAnthropicComputerV2(o: DriverOptions, name: string, input: unknown): Promise<any[]>;
package/dist/anthropic.js CHANGED
@@ -3,6 +3,7 @@
3
3
  // member on an Allternit hosted computer via POST /v1/computers/{id}/toolset.
4
4
  import { BetaAbstractBrowserToolset20260801, BetaAbstractComputerToolset20260801, ToolError, } from "@anthropic-ai/sdk/helpers/beta/toolsets";
5
5
  import { ApprovalRequiredError, resultImage, resultText, } from "./client.js";
6
+ import { computerV2Tool, runComputerV2Member } from "./v2.js";
6
7
  /**
7
8
  * Default SDK `confirm`: let the call through. The Allternit server is the gate: it applies the
8
9
  * project's member policy and holds risky calls with a 409, which `onApproval` answers. Pass your
@@ -128,3 +129,29 @@ export class AllternitBrowserToolset extends BetaAbstractBrowserToolset20260801
128
129
  }
129
130
  }
130
131
  }
132
+ // ------------------------------------------------------------------ computer_v2
133
+ /**
134
+ * The `computer_v2` function tool for Anthropic models: the ten structured
135
+ * members (read_ui … skills) next to the native pixel toolset. The Anthropic
136
+ * wire hook only rewrites tools carrying the v1 pixel marker, so this passes
137
+ * through untouched.
138
+ */
139
+ export function computerV2AnthropicTool() {
140
+ const t = computerV2Tool();
141
+ return { name: t.name, description: t.description, input_schema: t.schema };
142
+ }
143
+ /**
144
+ * Run one `computer_v2` function call (`{action, ...fields}`) and return the
145
+ * tool-result content blocks, like any toolset member. Throws ToolError on a
146
+ * failed action; without `onApproval`, a held call (409 approval_required)
147
+ * resolves its held result as an error, the same rule as the toolsets.
148
+ */
149
+ export async function runAnthropicComputerV2(o, name, input) {
150
+ const { action, ...fields } = (input ?? {});
151
+ if (!action)
152
+ throw new ToolError("The computer_v2 call needs an action.");
153
+ const res = await runComputerV2Member({ client: o.client, computerId: o.computerId, onApproval: o.onApproval }, action, fields);
154
+ if (res.is_error)
155
+ throw new ToolError(toAnthropicBlocks(res));
156
+ return toAnthropicBlocks(res);
157
+ }
package/dist/client.d.ts CHANGED
@@ -92,6 +92,18 @@ export declare class ApprovalRequiredError extends AllternitApiError {
92
92
  readonly result: ToolsetResult;
93
93
  constructor(err: Partial<ApiErrorBody>, approval: Approval, result: ToolsetResult, body: unknown);
94
94
  }
95
+ /** 423 computer_busy / computer_controlled_elsewhere: someone else holds the computer right now; retry shortly or request_human. */
96
+ export declare class ComputerBusyError extends AllternitApiError {
97
+ constructor(status: number, err: Partial<ApiErrorBody>, body: unknown);
98
+ }
99
+ /** 409 sandbox_required: the call only runs on a sandbox (cloud/bot) computer. */
100
+ export declare class SandboxRequiredError extends AllternitApiError {
101
+ constructor(status: number, err: Partial<ApiErrorBody>, body: unknown);
102
+ }
103
+ /** 409 computer_conflict: the call conflicts with another subtask or lease on the computer. */
104
+ export declare class ComputerConflictError extends AllternitApiError {
105
+ constructor(status: number, err: Partial<ApiErrorBody>, body: unknown);
106
+ }
95
107
  export interface ClientOptions {
96
108
  /** Project key (alt_live_… / alt_test_…). Defaults to ALLTERNIT_API_KEY. */
97
109
  apiKey?: string;
@@ -117,6 +129,14 @@ export declare class AllternitComputers {
117
129
  delete(id: string): Promise<Computer>;
118
130
  /** Run one toolset member. Action failures resolve with is_error:true; a held call throws ApprovalRequiredError. */
119
131
  toolset(id: string, call: ToolsetCall): Promise<ToolsetResult>;
132
+ /**
133
+ * Run one toolset member, answering a 409 approval_required hold. When the
134
+ * server holds the call and `onApproval` returns true, the approval is
135
+ * granted (POST /approvals/{id}) and the same call resent with the
136
+ * single-use `approval_grant`. When `onApproval` is missing or returns
137
+ * false, the held result resolves (is_error:true) so the model sees it.
138
+ */
139
+ toolsetWithApproval(id: string, call: ToolsetCall, onApproval?: (approval: Approval) => boolean | Promise<boolean>): Promise<ToolsetResult>;
120
140
  schema(id: string, toolset?: ToolsetName): Promise<Record<string, unknown>>;
121
141
  events(id: string, q?: {
122
142
  after?: string;
package/dist/client.js CHANGED
@@ -28,6 +28,41 @@ export class ApprovalRequiredError extends AllternitApiError {
28
28
  this.result = result;
29
29
  }
30
30
  }
31
+ /** 423 computer_busy / computer_controlled_elsewhere: someone else holds the computer right now; retry shortly or request_human. */
32
+ export class ComputerBusyError extends AllternitApiError {
33
+ constructor(status, err, body) {
34
+ super(status, err, body);
35
+ this.name = "ComputerBusyError";
36
+ }
37
+ }
38
+ /** 409 sandbox_required: the call only runs on a sandbox (cloud/bot) computer. */
39
+ export class SandboxRequiredError extends AllternitApiError {
40
+ constructor(status, err, body) {
41
+ super(status, err, body);
42
+ this.name = "SandboxRequiredError";
43
+ }
44
+ }
45
+ /** 409 computer_conflict: the call conflicts with another subtask or lease on the computer. */
46
+ export class ComputerConflictError extends AllternitApiError {
47
+ constructor(status, err, body) {
48
+ super(status, err, body);
49
+ this.name = "ComputerConflictError";
50
+ }
51
+ }
52
+ /** Maps a toolset error body onto the typed error for its code, if one exists. */
53
+ function typedApiError(status, err, body) {
54
+ switch (err.code) {
55
+ case "computer_busy":
56
+ case "computer_controlled_elsewhere":
57
+ return new ComputerBusyError(status, err, body);
58
+ case "sandbox_required":
59
+ return new SandboxRequiredError(status, err, body);
60
+ case "computer_conflict":
61
+ return new ComputerConflictError(status, err, body);
62
+ default:
63
+ return new AllternitApiError(status, err, body);
64
+ }
65
+ }
31
66
  export class AllternitComputers {
32
67
  baseUrl;
33
68
  #apiKey;
@@ -63,6 +98,26 @@ export class AllternitComputers {
63
98
  toolset(id, call) {
64
99
  return this.#req("POST", `/v1/computers/${enc(id)}/toolset`, call);
65
100
  }
101
+ /**
102
+ * Run one toolset member, answering a 409 approval_required hold. When the
103
+ * server holds the call and `onApproval` returns true, the approval is
104
+ * granted (POST /approvals/{id}) and the same call resent with the
105
+ * single-use `approval_grant`. When `onApproval` is missing or returns
106
+ * false, the held result resolves (is_error:true) so the model sees it.
107
+ */
108
+ async toolsetWithApproval(id, call, onApproval) {
109
+ try {
110
+ return await this.toolset(id, call);
111
+ }
112
+ catch (e) {
113
+ if (!(e instanceof ApprovalRequiredError))
114
+ throw e;
115
+ if (!onApproval || !(await onApproval(e.approval)))
116
+ return e.result;
117
+ await this.approve(id, e.approval.id);
118
+ return this.toolset(id, { ...call, approval_grant: e.approval.id });
119
+ }
120
+ }
66
121
  schema(id, toolset = "computer") {
67
122
  return this.#req("GET", `/v1/computers/${enc(id)}/toolset/schema${qs({ toolset })}`);
68
123
  }
@@ -97,7 +152,7 @@ export class AllternitComputers {
97
152
  if (res.status === 409 && err.code === "approval_required" && json?.approval) {
98
153
  throw new ApprovalRequiredError(err, json.approval, json.result ?? { is_error: true, content: [] }, json);
99
154
  }
100
- throw new AllternitApiError(res.status, err, json);
155
+ throw typedApiError(res.status, err, json);
101
156
  }
102
157
  }
103
158
  /** Text of a result, joined. */
package/dist/gemini.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { type AllternitComputers, type ToolsetCall, type ToolsetResult } from "./client.ts";
1
+ import { type AllternitComputers, type Approval, type ToolsetCall, type ToolsetResult } from "./client.ts";
2
2
  export interface GeminiFunctionCall {
3
3
  name: string;
4
4
  args?: Record<string, unknown>;
@@ -29,4 +29,28 @@ export interface GeminiStepResult {
29
29
  }
30
30
  /** Run one Gemini function call and return the parts Gemini expects back. */
31
31
  export declare function runGeminiCall(o: GeminiAdapterOptions, fc: GeminiFunctionCall): Promise<GeminiStepResult>;
32
+ /**
33
+ * The `computer_v2` function declaration for Gemini models: the ten structured
34
+ * members next to the `computer_use` declaration. Route matching
35
+ * `functionCall` parts through runGeminiV2Call.
36
+ */
37
+ export declare function geminiComputerV2Declaration(): {
38
+ name: string;
39
+ description: string;
40
+ parameters: Record<string, unknown>;
41
+ };
42
+ export interface GeminiV2AdapterOptions extends GeminiAdapterOptions {
43
+ /** Called when the server holds the call (409 approval_required). Without it the ApprovalRequiredError propagates, like the pixel adapter. */
44
+ onApproval?: (approval: Approval) => boolean | Promise<boolean>;
45
+ }
46
+ export interface GeminiV2StepResult {
47
+ /** A `functionResponse` part for the next turn. */
48
+ functionResponse: {
49
+ name: string;
50
+ response: Record<string, unknown>;
51
+ };
52
+ error?: ToolsetResult;
53
+ }
54
+ /** Run one `computer_v2` function call (`{action, ...fields}`) and return the function response part. */
55
+ export declare function runGeminiV2Call(o: GeminiV2AdapterOptions, name: string, input: unknown): Promise<GeminiV2StepResult>;
32
56
  export {};
package/dist/gemini.js CHANGED
@@ -1,7 +1,8 @@
1
1
  // Gemini computer_use adapter: runs one predefined function call on an Allternit
2
2
  // hosted computer. Gemini coordinates are 0–999 normalized, so every call is sent
3
3
  // with coordinate_space "normalized_1000" and the server scales to the screen.
4
- import { resultImage } from "./client.js";
4
+ import { resultImage, resultText } from "./client.js";
5
+ import { computerV2Tool, runComputerV2Member } from "./v2.js";
5
6
  const pt = (x, y) => [Math.round(Number(x)), Math.round(Number(y))];
6
7
  const key = (text) => ({ member: "key", input: { text } });
7
8
  const typed = (text) => ({ member: "type", input: { text } });
@@ -79,3 +80,28 @@ export async function runGeminiCall(o, fc) {
79
80
  ...(error ? { error } : {}),
80
81
  };
81
82
  }
83
+ // ------------------------------------------------------------------ computer_v2
84
+ /**
85
+ * The `computer_v2` function declaration for Gemini models: the ten structured
86
+ * members next to the `computer_use` declaration. Route matching
87
+ * `functionCall` parts through runGeminiV2Call.
88
+ */
89
+ export function geminiComputerV2Declaration() {
90
+ const t = computerV2Tool();
91
+ return { name: t.name, description: t.description, parameters: t.schema };
92
+ }
93
+ /** Run one `computer_v2` function call (`{action, ...fields}`) and return the function response part. */
94
+ export async function runGeminiV2Call(o, name, input) {
95
+ const { action, ...fields } = (input ?? {});
96
+ if (!action)
97
+ throw new Error("The computer_v2 call needs an action.");
98
+ const base = { client: o.client, computerId: o.computerId };
99
+ const res = o.onApproval
100
+ ? await runComputerV2Member({ ...base, onApproval: o.onApproval }, action, fields)
101
+ : await o.client.toolset(o.computerId, { toolset: "computer", member: action, input: fields });
102
+ const text = resultText(res);
103
+ const response = { result: text };
104
+ if (res.is_error)
105
+ response.error = text || "The action failed.";
106
+ return { functionResponse: { name, response }, ...(res.is_error ? { error: res } : {}) };
107
+ }
package/dist/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
1
  export * from "./client.ts";
2
+ export * from "./v2.ts";
2
3
  export * from "./openai.ts";
3
4
  export * from "./gemini.ts";
package/dist/index.js CHANGED
@@ -2,5 +2,6 @@
2
2
  // The Anthropic toolsets live on the "@allternit/computer-driver/anthropic" subpath
3
3
  // so this entry never loads the optional @anthropic-ai/sdk peer.
4
4
  export * from "./client.js";
5
+ export * from "./v2.js";
5
6
  export * from "./openai.js";
6
7
  export * from "./gemini.js";
package/dist/openai.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { type AllternitComputers, type ToolsetCall, type ToolsetResult } from "./client.ts";
1
+ import { type AllternitComputers, type Approval, type ToolsetCall, type ToolsetResult } from "./client.ts";
2
2
  type Pt = {
3
3
  x: number;
4
4
  y: number;
@@ -67,4 +67,32 @@ export interface OpenAIStepResult {
67
67
  }
68
68
  /** Run one OpenAI `computer_call` (its `call_id` and `action`) and return the output item. */
69
69
  export declare function runOpenAIAction(o: OpenAIAdapterOptions, callId: string, action: OpenAIComputerAction): Promise<OpenAIStepResult>;
70
+ /**
71
+ * The `computer_v2` function tool for the Responses API: the ten structured
72
+ * members next to the `computer_use_preview` tool. Register it in `tools`;
73
+ * route matching `function_call` items through runOpenAIV2Call.
74
+ */
75
+ export declare function openaiComputerV2Tool(): {
76
+ type: "function";
77
+ name: string;
78
+ description: string;
79
+ parameters: Record<string, unknown>;
80
+ strict: false;
81
+ };
82
+ export interface OpenAIV2AdapterOptions extends OpenAIAdapterOptions {
83
+ /** Called when the server holds the call (409 approval_required). Without it the ApprovalRequiredError propagates, like the pixel adapter. */
84
+ onApproval?: (approval: Approval) => boolean | Promise<boolean>;
85
+ }
86
+ export interface OpenAIV2StepResult {
87
+ /** Ready for the Responses API `input`: `{type:"function_call_output", call_id, output}`. */
88
+ output: {
89
+ type: "function_call_output";
90
+ call_id: string;
91
+ output: string;
92
+ };
93
+ /** The failed action result, if the action failed. */
94
+ error?: ToolsetResult;
95
+ }
96
+ /** Run one `computer_v2` function call (`{action, ...fields}`) and return the `function_call_output` item. */
97
+ export declare function runOpenAIV2Call(o: OpenAIV2AdapterOptions, callId: string, input: unknown): Promise<OpenAIV2StepResult>;
70
98
  export {};
package/dist/openai.js CHANGED
@@ -1,6 +1,7 @@
1
1
  // OpenAI computer-use adapter: runs one `computer_call` action on an Allternit
2
2
  // hosted computer and returns a `computer_call_output` with the next screenshot.
3
3
  import { resultImage } from "./client.js";
4
+ import { computerV2Tool, runComputerV2Member } from "./v2.js";
4
5
  const KEYS = {
5
6
  CTRL: "ctrl", CONTROL: "ctrl", ALT: "alt", OPTION: "alt", SHIFT: "shift", CMD: "super", META: "super", SUPER: "super",
6
7
  WIN: "super", ENTER: "Return", RETURN: "Return", ESC: "Escape", ESCAPE: "Escape", TAB: "Tab", SPACE: "space",
@@ -85,3 +86,28 @@ export async function runOpenAIAction(o, callId, action) {
85
86
  ...(error ? { error } : {}),
86
87
  };
87
88
  }
89
+ // ------------------------------------------------------------------ computer_v2
90
+ /**
91
+ * The `computer_v2` function tool for the Responses API: the ten structured
92
+ * members next to the `computer_use_preview` tool. Register it in `tools`;
93
+ * route matching `function_call` items through runOpenAIV2Call.
94
+ */
95
+ export function openaiComputerV2Tool() {
96
+ const t = computerV2Tool();
97
+ return { type: "function", name: t.name, description: t.description, parameters: t.schema, strict: false };
98
+ }
99
+ /** Run one `computer_v2` function call (`{action, ...fields}`) and return the `function_call_output` item. */
100
+ export async function runOpenAIV2Call(o, callId, input) {
101
+ const { action, ...fields } = (input ?? {});
102
+ if (!action)
103
+ throw new Error("The computer_v2 call needs an action.");
104
+ const base = { client: o.client, computerId: o.computerId };
105
+ const res = o.onApproval
106
+ ? await runComputerV2Member({ ...base, onApproval: o.onApproval }, action, fields)
107
+ : await o.client.toolset(o.computerId, { toolset: "computer", member: action, input: fields });
108
+ const text = res.content.flatMap((b) => (b.type === "text" ? [b.text] : [])).join("\n");
109
+ return {
110
+ output: { type: "function_call_output", call_id: callId, output: text || "Done." },
111
+ ...(res.is_error ? { error: res } : {}),
112
+ };
113
+ }