@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 +14 -0
- package/README.md +63 -3
- package/dist/anthropic.d.ts +18 -0
- package/dist/anthropic.js +27 -0
- package/dist/client.d.ts +20 -0
- package/dist/client.js +56 -1
- package/dist/gemini.d.ts +25 -1
- package/dist/gemini.js +27 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/openai.d.ts +29 -1
- package/dist/openai.js +26 -0
- package/dist/v2-generated.d.ts +1445 -0
- package/dist/v2-generated.js +1266 -0
- package/dist/v2.d.ts +279 -0
- package/dist/v2.js +154 -0
- package/package.json +4 -3
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
|
-
|
|
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`.
|
package/dist/anthropic.d.ts
CHANGED
|
@@ -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
|
|
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
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
|
+
}
|