codex-computer-use-mcp 0.3.1 → 0.3.3

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.
@@ -1,6 +1,6 @@
1
1
  # Pi integration
2
2
 
3
- The native Pi adapter is the primary 0.3 path. It registers ten namespaced typed tools so Pi itself chooses each official Computer Use method and argument. Pi 0.80.7 or newer exposes them progressively without changing the ten-tool contract.
3
+ The native Pi adapter is the primary 0.3 path. It registers and activates ten namespaced typed tools together so Pi itself chooses each official Computer Use method and argument.
4
4
 
5
5
  ## Source checkout acceptance
6
6
 
@@ -13,23 +13,20 @@ CODEX_COMPUTER_USE_HOME="$(mktemp -d)" \
13
13
 
14
14
  `-ne` prevents an installed 0.1 adapter from loading at the same time. This source workflow does not install or switch live Pi configuration.
15
15
 
16
- For normal installation, use the exact version 0.3.1 npm package:
16
+ For normal installation, use the exact version 0.3.3 npm package:
17
17
 
18
18
  ```bash
19
- pi install npm:codex-computer-use-mcp@0.3.1
19
+ pi install npm:codex-computer-use-mcp@0.3.3
20
20
  ```
21
21
 
22
22
  Use the source workflow only when you need to test an exact reviewed commit. Follow the rollback procedure in `MIGRATION.md` when replacing version 0.1.
23
23
 
24
24
  ## Registered surface
25
25
 
26
- Initially active:
26
+ All ten tools are registered and active from session start:
27
27
 
28
28
  - `computer_use_list_apps`
29
29
  - `computer_use_get_app_state`
30
-
31
- Registered and activated after a successful `get_app_state` call:
32
-
33
30
  - `computer_use_click`
34
31
  - `computer_use_perform_secondary_action`
35
32
  - `computer_use_set_value`
@@ -43,13 +40,15 @@ Command:
43
40
 
44
41
  - `/computer-use-status`
45
42
 
46
- The session-start narrowing preserves every active tool owned by Pi or another extension. Activation after inspection is purely additive: both inspection tools remain active, and the adapter does not remove or replace any tool in that call. The lazily activated tools rely on their official descriptions and omit `promptSnippet` and `promptGuidelines`, so native deferred schema loading does not rebuild the system prompt. Supported Anthropic and OpenAI models receive the definitions at the inspection result; other models receive Pi's normal active-tool fallback on the next request.
43
+ Session-start activation is purely additive and preserves every active tool owned by Pi or another extension. The interaction definitions rely on their official descriptions and omit extra prompt metadata.
44
+
45
+ A successful `get_app_state` retains its verified app-server runtime, thread, and signed Computer Use client. The following direct interaction uses that same client and official active-app lease, then closes the retained process tree with strict cleanup verification. A new inspection replaces and closes any earlier retained session; Pi `session_shutdown` also closes it.
47
46
 
48
47
  No-permissions is the only policy: all ten tools are registered and available through this lifecycle with no wrapper permission prompts, mode selector, or app/intent/action gate. The signed host runs with Codex Full access (`approvalPolicy: "never"`, `sandbox: "danger-full-access"`), so normal empty-schema Computer Use app approvals are accepted by Codex before they reach Pi. Pi renders any form, OpenAI-form, or URL elicitation app-server does emit. The user's `accept`, `decline`, or `cancel` response is returned unchanged; the adapter never fabricates one.
49
48
 
50
49
  ## Generic MCP gateway
51
50
 
52
- Merge `mcp.json.example` only for the exact 0.3.1 package or after building an exact reviewed source commit. `directTools: false` is intentional; it keeps this powerful generic MCP surface behind Pi's gateway.
51
+ Merge `mcp.json.example` only for the exact 0.3.3 package or after building an exact reviewed source commit. `directTools: false` is intentional; it keeps this powerful generic MCP surface behind Pi's gateway.
53
52
 
54
53
  For a source checkout:
55
54
 
@@ -4,9 +4,11 @@ import { getAgentDir, truncateHead, type ExtensionAPI } from "@earendil-works/pi
4
4
  import { Text } from "@earendil-works/pi-tui";
5
5
  import { Type, type TSchema } from "typebox";
6
6
  import { executeDirectTool, getDirectStatus } from "../../dist/direct-service.js";
7
- import type {
8
- DirectBrokerElicitationRequest,
9
- DirectBrokerElicitationResponse,
7
+ import {
8
+ createOfficialDirectToolSession,
9
+ type DirectBrokerElicitationRequest,
10
+ type DirectBrokerElicitationResponse,
11
+ type OfficialDirectToolSession,
10
12
  } from "../../dist/direct-broker.js";
11
13
  import { OFFICIAL_TOOL_METADATA, type DirectMethod } from "../../dist/tools.js";
12
14
 
@@ -82,15 +84,94 @@ export const INTERACTION_TOOL_NAMES = [
82
84
  "computer_use_type_text",
83
85
  ] as const;
84
86
 
87
+ export const COMPUTER_USE_TOOL_NAMES = [
88
+ ...INSPECTION_TOOL_NAMES,
89
+ ...INTERACTION_TOOL_NAMES,
90
+ ] as const;
91
+
85
92
  export function setInitialComputerUseTools(pi: Pick<ExtensionAPI, "getActiveTools" | "setActiveTools">): void {
86
- const interactionTools = new Set<string>(INTERACTION_TOOL_NAMES);
87
- const preserved = pi.getActiveTools().filter((name) => !interactionTools.has(name));
88
- pi.setActiveTools([...new Set([...preserved, ...INSPECTION_TOOL_NAMES])]);
93
+ pi.setActiveTools([...new Set([...pi.getActiveTools(), ...COMPUTER_USE_TOOL_NAMES])]);
94
+ }
95
+
96
+ interface SessionExecutorDependencies {
97
+ createSession?: typeof createOfficialDirectToolSession;
98
+ executeTool?: typeof executeDirectTool;
89
99
  }
90
100
 
91
- export function activateInteractionTools(pi: Pick<ExtensionAPI, "getActiveTools" | "setActiveTools">): void {
92
- const active = pi.getActiveTools();
93
- pi.setActiveTools([...new Set([...active, ...INTERACTION_TOOL_NAMES])]);
101
+ /** Keep the signed app-server/client alive across the required state -> action pair. */
102
+ export class PiDirectSessionExecutor {
103
+ private session: OfficialDirectToolSession | undefined;
104
+ private queue: Promise<void> = Promise.resolve();
105
+ private readonly dependencies: SessionExecutorDependencies;
106
+
107
+ constructor(dependencies: SessionExecutorDependencies = {}) {
108
+ this.dependencies = dependencies;
109
+ }
110
+
111
+ private async closeSession(): Promise<void> {
112
+ const session = this.session;
113
+ this.session = undefined;
114
+ await session?.close();
115
+ }
116
+
117
+ async execute(
118
+ method: DirectMethod,
119
+ params: Record<string, unknown>,
120
+ dependencies: Parameters<typeof executeDirectTool>[1],
121
+ ): Promise<Awaited<ReturnType<typeof executeDirectTool>>> {
122
+ const previous = this.queue;
123
+ let release!: () => void;
124
+ this.queue = new Promise<void>((resolve) => { release = resolve; });
125
+ await previous;
126
+ try {
127
+ const executeTool = this.dependencies.executeTool ?? executeDirectTool;
128
+ if (method === "list_apps") return await executeTool({ method, arguments: params }, dependencies);
129
+ if (method === "get_app_state") {
130
+ await this.closeSession();
131
+ const createSession = this.dependencies.createSession ?? createOfficialDirectToolSession;
132
+ try {
133
+ const response = await executeTool(
134
+ { method, arguments: params },
135
+ {
136
+ ...dependencies,
137
+ callTool: async (directMethod, args, options) => {
138
+ this.session = await createSession({ supportsOpenAiFormElicitation: true });
139
+ return this.session.call(directMethod, args, options);
140
+ },
141
+ },
142
+ );
143
+ if (response.isError) await this.closeSession();
144
+ return response;
145
+ } catch (error) {
146
+ await this.closeSession();
147
+ throw error;
148
+ }
149
+ }
150
+ const session = this.session;
151
+ try {
152
+ return await executeTool(
153
+ { method, arguments: params },
154
+ session ? {
155
+ ...dependencies,
156
+ callTool: async (directMethod, args, options) => {
157
+ const result = await session.call(directMethod, args, options);
158
+ await this.closeSession();
159
+ return { ...result, brokerCleanupVerified: true };
160
+ },
161
+ } : dependencies,
162
+ );
163
+ } finally {
164
+ await this.closeSession();
165
+ }
166
+ } finally {
167
+ release();
168
+ }
169
+ }
170
+
171
+ async close(): Promise<void> {
172
+ await this.queue;
173
+ await this.closeSession();
174
+ }
94
175
  }
95
176
 
96
177
  function toPiContent(content: Array<Record<string, unknown>>): Array<{ type: "text"; text: string } | { type: "image"; data: string; mimeType: string }> {
@@ -188,6 +269,7 @@ export async function handleOfficialElicitation(
188
269
 
189
270
  export default function directComputerUse(pi: ExtensionAPI) {
190
271
  const stateRoot = process.env.CODEX_COMPUTER_USE_HOME || path.join(getAgentDir(), "direct-computer-use");
272
+ const sessionExecutor = new PiDirectSessionExecutor();
191
273
 
192
274
  pi.registerCommand("computer-use-status", {
193
275
  description: "Show the direct-call architecture, durable no-permissions policy, signed broker, and private audit path",
@@ -214,8 +296,9 @@ export default function directComputerUse(pi: ExtensionAPI) {
214
296
  ...inspectionPromptMetadata,
215
297
  parameters: ToolParameters[method] as any,
216
298
  async execute(_toolCallId, params, signal, onUpdate, ctx) {
217
- const response = await executeDirectTool(
218
- { method, arguments: params as Record<string, unknown> },
299
+ const response = await sessionExecutor.execute(
300
+ method,
301
+ params as Record<string, unknown>,
219
302
  {
220
303
  stateRoot,
221
304
  signal,
@@ -232,7 +315,6 @@ export default function directComputerUse(pi: ExtensionAPI) {
232
315
  },
233
316
  );
234
317
  if (response.isError) throw new Error(errorText(response.content));
235
- if (method === "get_app_state") activateInteractionTools(pi);
236
318
  return { content: toPiContent(response.content), details: response.details };
237
319
  },
238
320
  renderCall(args, theme) {
@@ -244,4 +326,5 @@ export default function directComputerUse(pi: ExtensionAPI) {
244
326
  }
245
327
 
246
328
  pi.on("session_start", () => setInitialComputerUseTools(pi));
329
+ pi.on("session_shutdown", () => sessionExecutor.close());
247
330
  }
@@ -4,7 +4,7 @@
4
4
  "command": "npx",
5
5
  "args": [
6
6
  "-y",
7
- "codex-computer-use-mcp@0.3.1"
7
+ "codex-computer-use-mcp@0.3.3"
8
8
  ],
9
9
  "lifecycle": "lazy",
10
10
  "requestTimeoutMs": 180000,
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "codex-computer-use-mcp",
3
- "version": "0.3.1",
3
+ "version": "0.3.3",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "codex-computer-use-mcp",
9
- "version": "0.3.1",
9
+ "version": "0.3.3",
10
10
  "license": "MIT",
11
11
  "os": [
12
12
  "darwin"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codex-computer-use-mcp",
3
- "version": "0.3.1",
3
+ "version": "0.3.3",
4
4
  "description": "Expose the official macOS Computer Use tools directly to Pi and MCP clients without a nested model.",
5
5
  "author": "Thomas Mustier",
6
6
  "license": "MIT",
@@ -31,6 +31,7 @@
31
31
  "integrations/pi",
32
32
  "npm-shrinkwrap.json",
33
33
  "README.md",
34
+ "CHANGELOG.md",
34
35
  "PROOF.md",
35
36
  "ARCHITECTURE.md",
36
37
  "MIGRATION.md",