codex-computer-use-mcp 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/ARCHITECTURE.md +134 -0
- package/CONTRIBUTING.md +16 -10
- package/MIGRATION.md +77 -0
- package/PROOF.md +114 -67
- package/README.md +94 -116
- package/SECURITY.md +43 -22
- package/dist/audit.d.ts +15 -14
- package/dist/audit.js +1 -1
- package/dist/audit.js.map +1 -1
- package/dist/config.d.ts +8 -8
- package/dist/config.js +8 -103
- package/dist/config.js.map +1 -1
- package/dist/direct-broker.d.ts +57 -0
- package/dist/direct-broker.js +638 -0
- package/dist/direct-broker.js.map +1 -0
- package/dist/direct-service.d.ts +31 -0
- package/dist/direct-service.js +345 -0
- package/dist/direct-service.js.map +1 -0
- package/dist/lock.d.ts +1 -0
- package/dist/lock.js +32 -3
- package/dist/lock.js.map +1 -1
- package/dist/mcp-server.js +46 -105
- package/dist/mcp-server.js.map +1 -1
- package/dist/system.d.ts +0 -1
- package/dist/system.js +12 -37
- package/dist/system.js.map +1 -1
- package/dist/tools.d.ts +78 -0
- package/dist/tools.js +192 -0
- package/dist/tools.js.map +1 -0
- package/integrations/pi/README.md +41 -27
- package/integrations/pi/index.ts +110 -123
- package/integrations/pi/mcp.json.example +3 -3
- package/npm-shrinkwrap.json +3278 -48
- package/package.json +40 -13
- package/dist/policy.d.ts +0 -42
- package/dist/policy.js +0 -428
- package/dist/policy.js.map +0 -1
- package/dist/prompt.d.ts +0 -62
- package/dist/prompt.js +0 -119
- package/dist/prompt.js.map +0 -1
- package/dist/runner.d.ts +0 -55
- package/dist/runner.js +0 -494
- package/dist/runner.js.map +0 -1
- package/dist/service.d.ts +0 -17
- package/dist/service.js +0 -382
- package/dist/service.js.map +0 -1
package/dist/prompt.js
DELETED
|
@@ -1,119 +0,0 @@
|
|
|
1
|
-
import { DICTIONARY_NEUTRAL_QUERY } from "./policy.js";
|
|
2
|
-
export const RESULT_SCHEMA = {
|
|
3
|
-
type: "object",
|
|
4
|
-
additionalProperties: false,
|
|
5
|
-
required: [
|
|
6
|
-
"status",
|
|
7
|
-
"app",
|
|
8
|
-
"mode",
|
|
9
|
-
"summary",
|
|
10
|
-
"cleaned",
|
|
11
|
-
"approvalRequired",
|
|
12
|
-
"usedCapabilities",
|
|
13
|
-
"apps",
|
|
14
|
-
"message",
|
|
15
|
-
],
|
|
16
|
-
properties: {
|
|
17
|
-
status: { type: "string", enum: ["ok", "approval_required", "blocked", "error"] },
|
|
18
|
-
app: { type: "string", maxLength: 500 },
|
|
19
|
-
mode: { type: "string", enum: ["list", "inspect", "act", "dictionary_lookup"] },
|
|
20
|
-
summary: { type: "string", maxLength: 1000 },
|
|
21
|
-
cleaned: { type: "boolean" },
|
|
22
|
-
approvalRequired: { type: "boolean" },
|
|
23
|
-
usedCapabilities: {
|
|
24
|
-
type: "array",
|
|
25
|
-
maxItems: 50,
|
|
26
|
-
items: {
|
|
27
|
-
type: "string",
|
|
28
|
-
enum: [
|
|
29
|
-
"list_apps",
|
|
30
|
-
"get_app_state",
|
|
31
|
-
"click",
|
|
32
|
-
"perform_secondary_action",
|
|
33
|
-
"set_value",
|
|
34
|
-
"select_text",
|
|
35
|
-
"scroll",
|
|
36
|
-
"drag",
|
|
37
|
-
"press_key",
|
|
38
|
-
"type_text",
|
|
39
|
-
],
|
|
40
|
-
},
|
|
41
|
-
},
|
|
42
|
-
apps: {
|
|
43
|
-
type: "array",
|
|
44
|
-
maxItems: 100,
|
|
45
|
-
items: {
|
|
46
|
-
type: "object",
|
|
47
|
-
additionalProperties: false,
|
|
48
|
-
required: ["name", "bundleId"],
|
|
49
|
-
properties: {
|
|
50
|
-
name: { type: "string", maxLength: 120 },
|
|
51
|
-
bundleId: { type: "string", maxLength: 160 },
|
|
52
|
-
},
|
|
53
|
-
},
|
|
54
|
-
},
|
|
55
|
-
message: { type: "string", maxLength: 256 },
|
|
56
|
-
},
|
|
57
|
-
};
|
|
58
|
-
function enabledToolNames(request) {
|
|
59
|
-
switch (request.mode) {
|
|
60
|
-
case "list":
|
|
61
|
-
return "computer-use/list_apps";
|
|
62
|
-
case "inspect":
|
|
63
|
-
return "computer-use/list_apps, computer-use/get_app_state";
|
|
64
|
-
case "act":
|
|
65
|
-
return [
|
|
66
|
-
"list_apps",
|
|
67
|
-
"get_app_state",
|
|
68
|
-
"click",
|
|
69
|
-
"perform_secondary_action",
|
|
70
|
-
"set_value",
|
|
71
|
-
"select_text",
|
|
72
|
-
"scroll",
|
|
73
|
-
"drag",
|
|
74
|
-
"press_key",
|
|
75
|
-
"type_text",
|
|
76
|
-
].map((tool) => `computer-use/${tool}`).join(", ");
|
|
77
|
-
case "dictionary_lookup":
|
|
78
|
-
return ["get_app_state", "set_value", "scroll"].map((tool) => `computer-use/${tool}`).join(", ");
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
function operationInstructions(request, targetApp = request.app) {
|
|
82
|
-
switch (request.mode) {
|
|
83
|
-
case "list":
|
|
84
|
-
return `Call list_apps exactly once. Return every discovered app as name + bundleId only. Do not include paths, PIDs, titles, windows, or app contents.`;
|
|
85
|
-
case "inspect":
|
|
86
|
-
return `Target only ${JSON.stringify(targetApp)}. Call get_app_state. Use list_apps only if the exact target is rejected and must be resolved. Do not mutate anything. Summarize the target's operational state without reproducing sensitive or unrelated text. Return apps=[].`;
|
|
87
|
-
case "act": {
|
|
88
|
-
const cleanup = request.cleanup
|
|
89
|
-
? request.cleanupInstructions ??
|
|
90
|
-
(request.permissionMode === "full-permissions"
|
|
91
|
-
? "Restore transient state created only for this task, without undoing the requested durable result."
|
|
92
|
-
: "Restore transient controls/content changed by this task to a neutral pre-task state without saving, sending, deleting, or affecting another app.")
|
|
93
|
-
: "Cleanup was disabled; leave the requested target-app result in place.";
|
|
94
|
-
const required = request.requiredCapabilities.length
|
|
95
|
-
? `The following capabilities must genuinely be exercised as part of the task and reported: ${request.requiredCapabilities.join(", ")}.`
|
|
96
|
-
: request.permissionMode === "full-permissions"
|
|
97
|
-
? "Use any of the complete official typed Computer Use methods needed for the task."
|
|
98
|
-
: "Choose the least disruptive Computer Use actions that complete the task.";
|
|
99
|
-
const authorization = request.permissionMode === "full-permissions"
|
|
100
|
-
? "The configured full-permissions mode broadly authorizes this wrapper to execute official-service actions without a wrapper per-operation confirmation."
|
|
101
|
-
: "The task has been confirmed in Pi safe mode.";
|
|
102
|
-
return `Target only ${JSON.stringify(targetApp)}. ${authorization} The requested task is: ${JSON.stringify(request.task)}. First call get_app_state, then perform the task using the typed Computer Use tools, then call get_app_state to verify the result. ${required} Cleanup requirement: ${cleanup} ${request.cleanup ? "After observing the task result, perform cleanup and call get_app_state again to verify the cleaned state." : "Do not invent cleanup."} Return apps=[].`;
|
|
103
|
-
}
|
|
104
|
-
case "dictionary_lookup":
|
|
105
|
-
return `Target only ${JSON.stringify(targetApp)}, which must resolve to the Apple-signed system Dictionary app. This is a narrowly authorized local dictionary lookup for ${JSON.stringify(request.query)}. Call get_app_state. Call set_value exactly once on Dictionary's search field with the exact approved query. Call get_app_state to read and summarize the definition; scroll only the definition area vertically by at most three pages if needed. Then call set_value exactly once on the same search field with the fixed neutral value ${JSON.stringify(DICTIONARY_NEUTRAL_QUERY)} and call get_app_state to verify that neutral state. Do not click, type, press keys, invoke secondary actions, drag, raise, activate, save, copy, share, open links, or operate any other control or app. Return apps=[].`;
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
export function buildPrompt(request, canonicalTargetApp) {
|
|
109
|
-
if (request.mode === "list") {
|
|
110
|
-
return `Call computer-use/list_apps exactly once. Do not call any other tool. Then return the required JSON object with status="ok", app="app inventory", mode="list", cleaned=true, approvalRequired=false, usedCapabilities=["list_apps"], apps containing every discovered name and bundleId only, and a minimal summary/message. Do not include paths, PIDs, titles, windows, app contents, screenshots, tool output, or reasoning.`;
|
|
111
|
-
}
|
|
112
|
-
const targetApp = canonicalTargetApp ?? request.app;
|
|
113
|
-
return `Use only these fully qualified MCP tools: ${enabledToolNames(request)}. Target exactly ${JSON.stringify(targetApp)} and no other app. Keep it in the background; do not raise, activate, or focus it. Make at most ${request.maxComputerUseCalls} calls.
|
|
114
|
-
|
|
115
|
-
${operationInstructions(request, targetApp)}
|
|
116
|
-
|
|
117
|
-
If an official app-approval elicitation is cancelled or unavailable, stop immediately and return status="approval_required", approvalRequired=true, cleaned=false, and only the methods that actually completed. Otherwise return status="ok" only after observing the requested result. In the final JSON, set app=${JSON.stringify(request.app)}. usedCapabilities must exactly list successfully completed Computer Use method names in order, including duplicates; apps must be empty. Return only the required JSON object with a minimal summary/message and no screenshots, paths, PIDs, raw accessibility tree, unrelated content, tool output, or reasoning.`;
|
|
118
|
-
}
|
|
119
|
-
//# sourceMappingURL=prompt.js.map
|
package/dist/prompt.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"prompt.js","sourceRoot":"","sources":["../src/prompt.ts"],"names":[],"mappings":"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"}
|
package/dist/runner.d.ts
DELETED
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import { type ReasoningEffort } from "./policy.ts";
|
|
2
|
-
export interface NativeResult {
|
|
3
|
-
status: "ok" | "approval_required" | "blocked" | "error";
|
|
4
|
-
app: string;
|
|
5
|
-
mode: "list" | "inspect" | "act" | "dictionary_lookup";
|
|
6
|
-
summary: string;
|
|
7
|
-
cleaned: boolean;
|
|
8
|
-
approvalRequired: boolean;
|
|
9
|
-
usedCapabilities: string[];
|
|
10
|
-
apps: Array<{
|
|
11
|
-
name: string;
|
|
12
|
-
bundleId: string;
|
|
13
|
-
}>;
|
|
14
|
-
message: string;
|
|
15
|
-
}
|
|
16
|
-
export interface Usage {
|
|
17
|
-
input: number;
|
|
18
|
-
cachedInput: number;
|
|
19
|
-
output: number;
|
|
20
|
-
}
|
|
21
|
-
export type FirstPartyInterruption = "app_approval" | "sensitive_action" | "os_permission" | "unknown_elicitation";
|
|
22
|
-
export interface RunnerResult {
|
|
23
|
-
exitCode: number;
|
|
24
|
-
result?: NativeResult;
|
|
25
|
-
usage: Usage;
|
|
26
|
-
computerUseMethods: string[];
|
|
27
|
-
approvalRequiredObserved: boolean;
|
|
28
|
-
firstPartyInterruption?: FirstPartyInterruption;
|
|
29
|
-
policyViolation?: string;
|
|
30
|
-
errorKind?: string;
|
|
31
|
-
errorSummary?: string;
|
|
32
|
-
codexVersion: string;
|
|
33
|
-
durationMs: number;
|
|
34
|
-
}
|
|
35
|
-
export interface RunnerOptions {
|
|
36
|
-
allowedTools: string[];
|
|
37
|
-
targetApp?: string;
|
|
38
|
-
targetAppAliases?: string[];
|
|
39
|
-
reasoningEffort?: ReasoningEffort;
|
|
40
|
-
maxToolCalls?: number;
|
|
41
|
-
validateCallArguments?: (tool: string, args: Record<string, unknown>) => string | undefined;
|
|
42
|
-
timeoutMs?: number;
|
|
43
|
-
signal?: AbortSignal;
|
|
44
|
-
codexPath?: string;
|
|
45
|
-
skipSignatureVerification?: boolean;
|
|
46
|
-
}
|
|
47
|
-
export declare class BrokerVerificationError extends Error {
|
|
48
|
-
constructor(message: string);
|
|
49
|
-
}
|
|
50
|
-
export declare function verifyOfficialBroker(codexPath?: string): string;
|
|
51
|
-
export declare function inspectExecEvent(event: any, allowedTools: readonly string[], targetApp?: string, targetAppAliases?: readonly string[]): {
|
|
52
|
-
violation?: string;
|
|
53
|
-
methods: string[];
|
|
54
|
-
};
|
|
55
|
-
export declare function runOfficialCodex(prompt: string, options: RunnerOptions): Promise<RunnerResult>;
|
package/dist/runner.js
DELETED
|
@@ -1,494 +0,0 @@
|
|
|
1
|
-
import { spawn, spawnSync } from "node:child_process";
|
|
2
|
-
import { mkdtemp, mkdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
3
|
-
import os from "node:os";
|
|
4
|
-
import path from "node:path";
|
|
5
|
-
import { MODEL, MODEL_REASONING } from "./policy.js";
|
|
6
|
-
import { RESULT_SCHEMA } from "./prompt.js";
|
|
7
|
-
const CODEX_PATH = "/Applications/ChatGPT.app/Contents/Resources/codex";
|
|
8
|
-
const COMPUTER_USE_PLUGIN_ROOT = "/Applications/ChatGPT.app/Contents/Resources/plugins/openai-bundled/plugins/computer-use";
|
|
9
|
-
const COMPUTER_USE_CLIENT_PATH = `${COMPUTER_USE_PLUGIN_ROOT}/Codex Computer Use.app/Contents/SharedSupport/SkyComputerUseClient.app/Contents/MacOS/SkyComputerUseClient`;
|
|
10
|
-
const OPENAI_TEAM_ID = "2DC432GLL2";
|
|
11
|
-
const DISABLED_PLUGINS = [
|
|
12
|
-
"github@openai-curated",
|
|
13
|
-
"google-drive@openai-curated",
|
|
14
|
-
"documents@openai-primary-runtime",
|
|
15
|
-
"spreadsheets@openai-primary-runtime",
|
|
16
|
-
"presentations@openai-primary-runtime",
|
|
17
|
-
"gmail@openai-curated",
|
|
18
|
-
"google-calendar@openai-curated",
|
|
19
|
-
"slack@openai-curated",
|
|
20
|
-
"linear@openai-curated",
|
|
21
|
-
"pdf@openai-primary-runtime",
|
|
22
|
-
"template-creator@openai-primary-runtime",
|
|
23
|
-
"visualize@openai-bundled",
|
|
24
|
-
"chrome@openai-bundled",
|
|
25
|
-
"browser@openai-bundled",
|
|
26
|
-
];
|
|
27
|
-
export class BrokerVerificationError extends Error {
|
|
28
|
-
constructor(message) {
|
|
29
|
-
super(message);
|
|
30
|
-
this.name = "BrokerVerificationError";
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
function verifySignedBinary(binaryPath) {
|
|
34
|
-
const verify = spawnSync("/usr/bin/codesign", ["--verify", "--strict", binaryPath], {
|
|
35
|
-
encoding: "utf8",
|
|
36
|
-
timeout: 10_000,
|
|
37
|
-
});
|
|
38
|
-
if (verify.status !== 0)
|
|
39
|
-
throw new BrokerVerificationError(`Signature verification failed for ${path.basename(binaryPath)}`);
|
|
40
|
-
const details = spawnSync("/usr/bin/codesign", ["-dv", "--verbose=2", binaryPath], {
|
|
41
|
-
encoding: "utf8",
|
|
42
|
-
timeout: 10_000,
|
|
43
|
-
});
|
|
44
|
-
const output = `${details.stdout ?? ""}\n${details.stderr ?? ""}`;
|
|
45
|
-
if (details.status !== 0 || !output.includes(`TeamIdentifier=${OPENAI_TEAM_ID}`)) {
|
|
46
|
-
throw new BrokerVerificationError(`${path.basename(binaryPath)} is not signed by the expected OpenAI team`);
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
export function verifyOfficialBroker(codexPath = CODEX_PATH) {
|
|
50
|
-
if (codexPath !== CODEX_PATH)
|
|
51
|
-
throw new BrokerVerificationError("Only the app-bundled Codex path is permitted");
|
|
52
|
-
verifySignedBinary(CODEX_PATH);
|
|
53
|
-
verifySignedBinary(COMPUTER_USE_CLIENT_PATH);
|
|
54
|
-
const version = spawnSync(CODEX_PATH, ["--version"], { encoding: "utf8", timeout: 5000 });
|
|
55
|
-
if (version.status !== 0 || !/^codex-cli\s+\d+\./.test((version.stdout ?? "").trim())) {
|
|
56
|
-
throw new BrokerVerificationError("Could not verify the app-bundled Codex version");
|
|
57
|
-
}
|
|
58
|
-
return (version.stdout ?? "").trim();
|
|
59
|
-
}
|
|
60
|
-
function tomlString(value) {
|
|
61
|
-
return JSON.stringify(value);
|
|
62
|
-
}
|
|
63
|
-
function buildArgs(schemaPath, outputPath, workDir, allowedTools, reasoningEffort) {
|
|
64
|
-
if (allowedTools.length === 0)
|
|
65
|
-
throw new Error("At least one Computer Use tool must be allowlisted");
|
|
66
|
-
const config = [
|
|
67
|
-
"features.shell_tool=false",
|
|
68
|
-
"features.unified_exec=false",
|
|
69
|
-
"features.multi_agent=false",
|
|
70
|
-
"features.memories=false",
|
|
71
|
-
"memories.use_memories=false",
|
|
72
|
-
"memories.generate_memories=false",
|
|
73
|
-
"features.shell_snapshot=false",
|
|
74
|
-
"features.remote_plugin=false",
|
|
75
|
-
"features.hooks=false",
|
|
76
|
-
"analytics.enabled=false",
|
|
77
|
-
'otel.exporter="none"',
|
|
78
|
-
"otel.log_user_prompt=false",
|
|
79
|
-
'web_search="disabled"',
|
|
80
|
-
'history.persistence="none"',
|
|
81
|
-
`model_reasoning_effort=${tomlString(reasoningEffort)}`,
|
|
82
|
-
'model_verbosity="low"',
|
|
83
|
-
"mcp_servers.computer-use.enabled=true",
|
|
84
|
-
`mcp_servers.computer-use.command=${tomlString(COMPUTER_USE_CLIENT_PATH)}`,
|
|
85
|
-
'mcp_servers.computer-use.args=["mcp"]',
|
|
86
|
-
`mcp_servers.computer-use.cwd=${tomlString(COMPUTER_USE_PLUGIN_ROOT)}`,
|
|
87
|
-
`mcp_servers.computer-use.enabled_tools=${JSON.stringify(allowedTools)}`,
|
|
88
|
-
"apps._default.enabled=false",
|
|
89
|
-
'plugins."computer-use@openai-bundled".enabled=true',
|
|
90
|
-
...DISABLED_PLUGINS.map((plugin) => `plugins.${JSON.stringify(plugin)}.enabled=false`),
|
|
91
|
-
];
|
|
92
|
-
const args = ["-a", "never", "-s", "read-only", "-m", MODEL];
|
|
93
|
-
for (const item of config)
|
|
94
|
-
args.push("-c", item);
|
|
95
|
-
args.push("exec", "--strict-config", "--ignore-user-config", "--ephemeral", "--skip-git-repo-check", "--ignore-rules", "--json", "--color", "never", "--output-schema", schemaPath, "--output-last-message", outputPath, "-C", workDir, "-");
|
|
96
|
-
return args;
|
|
97
|
-
}
|
|
98
|
-
function eventItem(event) {
|
|
99
|
-
return event?.item && typeof event.item === "object" ? event.item : undefined;
|
|
100
|
-
}
|
|
101
|
-
function classifyFirstPartyInterruption(item) {
|
|
102
|
-
if (!item || item.type !== "mcp_tool_call")
|
|
103
|
-
return undefined;
|
|
104
|
-
const failed = item.status === "failed" || item.isError === true || item.is_error === true || item.error != null;
|
|
105
|
-
if (!failed)
|
|
106
|
-
return undefined;
|
|
107
|
-
let text = "";
|
|
108
|
-
try {
|
|
109
|
-
text = JSON.stringify(item.result ?? item.output ?? item.error ?? "").slice(0, 100_000).toLowerCase();
|
|
110
|
-
}
|
|
111
|
-
catch {
|
|
112
|
-
return undefined;
|
|
113
|
-
}
|
|
114
|
-
if (/screen recording|accessibility permission|system privacy|privacy settings|\btcc\b/i.test(text))
|
|
115
|
-
return "os_permission";
|
|
116
|
-
if (/sensitive.{0,80}(?:action|confirmation|approval)|(?:confirm|approve).{0,80}sensitive|disruptive.{0,80}(?:action|confirmation)/i.test(text)) {
|
|
117
|
-
return "sensitive_action";
|
|
118
|
-
}
|
|
119
|
-
if (/(?:allow|approve).{0,80}(?:chatgpt|codex).{0,80}(?:use|access).{0,80}(?:app|application)|app.{0,80}(?:approval|access).{0,80}(?:required|denied|cancel)|not approved.{0,80}(?:app|application)/i.test(text)) {
|
|
120
|
-
return "app_approval";
|
|
121
|
-
}
|
|
122
|
-
if (/(?:elicitation|approval|permission).{0,80}(?:cancel|denied|required|not approved|unavailable)/i.test(text)) {
|
|
123
|
-
return "unknown_elicitation";
|
|
124
|
-
}
|
|
125
|
-
return undefined;
|
|
126
|
-
}
|
|
127
|
-
function parseToolArguments(item) {
|
|
128
|
-
const raw = item?.arguments ?? item?.args ?? item?.input;
|
|
129
|
-
if (raw && typeof raw === "object" && !Array.isArray(raw))
|
|
130
|
-
return raw;
|
|
131
|
-
if (typeof raw !== "string")
|
|
132
|
-
return undefined;
|
|
133
|
-
try {
|
|
134
|
-
const parsed = JSON.parse(raw);
|
|
135
|
-
return parsed && typeof parsed === "object" && !Array.isArray(parsed)
|
|
136
|
-
? parsed
|
|
137
|
-
: undefined;
|
|
138
|
-
}
|
|
139
|
-
catch {
|
|
140
|
-
return undefined;
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
export function inspectExecEvent(event, allowedTools, targetApp, targetAppAliases = []) {
|
|
144
|
-
const item = eventItem(event);
|
|
145
|
-
if (!item)
|
|
146
|
-
return { methods: [] };
|
|
147
|
-
const type = String(item.type ?? "");
|
|
148
|
-
if (["command_execution", "file_change", "web_search", "image_view", "browser_action"].includes(type)) {
|
|
149
|
-
return { violation: `Codex attempted forbidden tool item type: ${type}`, methods: [] };
|
|
150
|
-
}
|
|
151
|
-
if (type !== "mcp_tool_call")
|
|
152
|
-
return { methods: [] };
|
|
153
|
-
const server = String(item.server ?? item.server_name ?? item.mcp_server ?? "");
|
|
154
|
-
const tool = String(item.tool ?? item.tool_name ?? item.name ?? "");
|
|
155
|
-
if (server !== "computer-use" && server !== "computer_use") {
|
|
156
|
-
return { violation: `Codex attempted a non-computer-use MCP server: ${server || "unknown"}`, methods: [] };
|
|
157
|
-
}
|
|
158
|
-
if (!allowedTools.includes(tool)) {
|
|
159
|
-
return { violation: `Codex attempted a Computer Use tool outside the per-operation allowlist: ${tool || "unknown"}`, methods: [] };
|
|
160
|
-
}
|
|
161
|
-
if (tool !== "list_apps" && targetApp) {
|
|
162
|
-
const args = parseToolArguments(item);
|
|
163
|
-
const confirmedTargets = new Set([targetApp, ...targetAppAliases].map((value) => value.trim().toLowerCase()));
|
|
164
|
-
if (!args || typeof args.app !== "string" || !confirmedTargets.has(args.app.trim().toLowerCase())) {
|
|
165
|
-
return { violation: "Codex attempted Computer Use against an app outside the confirmed target lease", methods: [] };
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
return { methods: [tool] };
|
|
169
|
-
}
|
|
170
|
-
function normalizeUsage(event, usage) {
|
|
171
|
-
if (event?.type !== "turn.completed" || !event.usage)
|
|
172
|
-
return;
|
|
173
|
-
const source = event.usage;
|
|
174
|
-
usage.input = Number(source.input_tokens ?? source.input ?? usage.input) || 0;
|
|
175
|
-
usage.cachedInput = Number(source.cached_input_tokens ?? source.cached_input ?? source.cache_read ?? usage.cachedInput) || 0;
|
|
176
|
-
usage.output = Number(source.output_tokens ?? source.output ?? usage.output) || 0;
|
|
177
|
-
}
|
|
178
|
-
function safeErrorSummary(value) {
|
|
179
|
-
const first = value.split("\n").map((line) => line.trim()).find(Boolean);
|
|
180
|
-
if (!first)
|
|
181
|
-
return undefined;
|
|
182
|
-
return first
|
|
183
|
-
.replace(/\/Users\/[^/\s]+/g, "~")
|
|
184
|
-
.replace(/https?:\/\/\S+/g, "[url]")
|
|
185
|
-
.replace(/[A-Za-z0-9_-]{32,}/g, "[redacted]")
|
|
186
|
-
.slice(0, 256);
|
|
187
|
-
}
|
|
188
|
-
function buildWorkerEnv() {
|
|
189
|
-
const env = {
|
|
190
|
-
HOME: os.homedir(),
|
|
191
|
-
CODEX_HOME: path.join(os.homedir(), ".codex"),
|
|
192
|
-
PATH: process.env.PATH ?? "/usr/bin:/bin:/usr/sbin:/sbin",
|
|
193
|
-
TMPDIR: process.env.TMPDIR ?? os.tmpdir(),
|
|
194
|
-
NO_COLOR: "1",
|
|
195
|
-
CLICOLOR: "0",
|
|
196
|
-
};
|
|
197
|
-
for (const key of ["USER", "LOGNAME", "LANG", "LC_ALL", "LC_CTYPE", "SHELL", "TERM"]) {
|
|
198
|
-
if (process.env[key])
|
|
199
|
-
env[key] = process.env[key];
|
|
200
|
-
}
|
|
201
|
-
return env;
|
|
202
|
-
}
|
|
203
|
-
function classifyFailure(stderr, exitCode) {
|
|
204
|
-
const value = stderr.toLowerCase();
|
|
205
|
-
if (value.includes("rate limit") || value.includes("usage limit"))
|
|
206
|
-
return "rate_limit";
|
|
207
|
-
if (value.includes("requires a newer version"))
|
|
208
|
-
return "version_mismatch";
|
|
209
|
-
if (value.includes("not logged in") || value.includes("authentication"))
|
|
210
|
-
return "authentication";
|
|
211
|
-
if (value.includes("strict config") || value.includes("configuration"))
|
|
212
|
-
return "configuration";
|
|
213
|
-
if (exitCode === 124)
|
|
214
|
-
return "timeout";
|
|
215
|
-
return "broker_failed";
|
|
216
|
-
}
|
|
217
|
-
function processGroupExists(pid) {
|
|
218
|
-
try {
|
|
219
|
-
process.kill(-pid, 0);
|
|
220
|
-
return true;
|
|
221
|
-
}
|
|
222
|
-
catch (error) {
|
|
223
|
-
return error?.code === "EPERM";
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
async function terminateGroup(pid) {
|
|
227
|
-
if (!pid)
|
|
228
|
-
return;
|
|
229
|
-
try {
|
|
230
|
-
process.kill(-pid, "SIGTERM");
|
|
231
|
-
}
|
|
232
|
-
catch {
|
|
233
|
-
try {
|
|
234
|
-
process.kill(pid, "SIGTERM");
|
|
235
|
-
}
|
|
236
|
-
catch {
|
|
237
|
-
return;
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
for (let elapsed = 0; elapsed < 1000; elapsed += 50) {
|
|
241
|
-
if (!processGroupExists(pid))
|
|
242
|
-
return;
|
|
243
|
-
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
244
|
-
}
|
|
245
|
-
if (!processGroupExists(pid))
|
|
246
|
-
return;
|
|
247
|
-
try {
|
|
248
|
-
process.kill(-pid, "SIGKILL");
|
|
249
|
-
}
|
|
250
|
-
catch {
|
|
251
|
-
try {
|
|
252
|
-
process.kill(pid, "SIGKILL");
|
|
253
|
-
}
|
|
254
|
-
catch {
|
|
255
|
-
// already exited
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
function isNativeResult(value) {
|
|
260
|
-
return (value &&
|
|
261
|
-
typeof value === "object" &&
|
|
262
|
-
["ok", "approval_required", "blocked", "error"].includes(value.status) &&
|
|
263
|
-
typeof value.app === "string" &&
|
|
264
|
-
value.app.length <= 500 &&
|
|
265
|
-
["list", "inspect", "act", "dictionary_lookup"].includes(value.mode) &&
|
|
266
|
-
typeof value.summary === "string" &&
|
|
267
|
-
value.summary.length <= 1000 &&
|
|
268
|
-
typeof value.cleaned === "boolean" &&
|
|
269
|
-
typeof value.approvalRequired === "boolean" &&
|
|
270
|
-
Array.isArray(value.usedCapabilities) &&
|
|
271
|
-
value.usedCapabilities.length <= 50 &&
|
|
272
|
-
value.usedCapabilities.every((item) => typeof item === "string") &&
|
|
273
|
-
Array.isArray(value.apps) &&
|
|
274
|
-
value.apps.length <= 100 &&
|
|
275
|
-
value.apps.every((item) => item !== null &&
|
|
276
|
-
typeof item === "object" &&
|
|
277
|
-
typeof item.name === "string" &&
|
|
278
|
-
item.name.length <= 120 &&
|
|
279
|
-
typeof item.bundleId === "string" &&
|
|
280
|
-
item.bundleId.length <= 160) &&
|
|
281
|
-
typeof value.message === "string" &&
|
|
282
|
-
value.message.length <= 256);
|
|
283
|
-
}
|
|
284
|
-
export async function runOfficialCodex(prompt, options) {
|
|
285
|
-
const started = Date.now();
|
|
286
|
-
const codexPath = options.codexPath ?? CODEX_PATH;
|
|
287
|
-
const codexVersion = options.skipSignatureVerification ? "test-codex" : verifyOfficialBroker(codexPath);
|
|
288
|
-
const tempRoot = await mkdtemp(path.join(os.tmpdir(), "pi-native-app-worker."));
|
|
289
|
-
const schemaPath = path.join(tempRoot, "result-schema.json");
|
|
290
|
-
const outputPath = path.join(tempRoot, "last-message.json");
|
|
291
|
-
const usage = { input: 0, cachedInput: 0, output: 0 };
|
|
292
|
-
const methods = [];
|
|
293
|
-
let startedCallCount = 0;
|
|
294
|
-
let completedCallCount = 0;
|
|
295
|
-
let approvalRequiredObserved = false;
|
|
296
|
-
let firstPartyInterruption;
|
|
297
|
-
let policyViolation;
|
|
298
|
-
let stderr = "";
|
|
299
|
-
let observedErrorText = "";
|
|
300
|
-
let timedOut = false;
|
|
301
|
-
let aborted = false;
|
|
302
|
-
let proc;
|
|
303
|
-
let timeout;
|
|
304
|
-
let abortHandler;
|
|
305
|
-
let terminationPromise;
|
|
306
|
-
const requestTermination = () => {
|
|
307
|
-
terminationPromise ??= terminateGroup(proc?.pid);
|
|
308
|
-
return terminationPromise;
|
|
309
|
-
};
|
|
310
|
-
try {
|
|
311
|
-
await mkdir(path.join(tempRoot, "work"), { mode: 0o700 });
|
|
312
|
-
await writeFile(schemaPath, `${JSON.stringify(RESULT_SCHEMA)}\n`, { mode: 0o600 });
|
|
313
|
-
const args = buildArgs(schemaPath, outputPath, path.join(tempRoot, "work"), options.allowedTools, options.reasoningEffort ?? MODEL_REASONING);
|
|
314
|
-
proc = spawn(codexPath, args, {
|
|
315
|
-
cwd: path.join(tempRoot, "work"),
|
|
316
|
-
detached: true,
|
|
317
|
-
shell: false,
|
|
318
|
-
stdio: ["pipe", "pipe", "pipe"],
|
|
319
|
-
env: buildWorkerEnv(),
|
|
320
|
-
});
|
|
321
|
-
let stdoutBuffer = "";
|
|
322
|
-
const processEventLine = (line) => {
|
|
323
|
-
if (!line.trim() || policyViolation)
|
|
324
|
-
return;
|
|
325
|
-
let event;
|
|
326
|
-
try {
|
|
327
|
-
event = JSON.parse(line);
|
|
328
|
-
}
|
|
329
|
-
catch {
|
|
330
|
-
policyViolation = "Codex emitted malformed JSON events";
|
|
331
|
-
void requestTermination();
|
|
332
|
-
return;
|
|
333
|
-
}
|
|
334
|
-
normalizeUsage(event, usage);
|
|
335
|
-
if ((event.type === "error" || event.type === "turn.failed") && observedErrorText.length < 4096) {
|
|
336
|
-
const candidate = typeof event.message === "string" ? event.message : JSON.stringify(event.error ?? "");
|
|
337
|
-
observedErrorText += candidate.slice(0, 4096 - observedErrorText.length);
|
|
338
|
-
}
|
|
339
|
-
if (event.type !== "item.started" && event.type !== "item.completed")
|
|
340
|
-
return;
|
|
341
|
-
const rawArgs = event.item?.arguments ?? event.item?.args ?? event.item?.input;
|
|
342
|
-
const targetForInspection = event.type === "item.completed" || rawArgs !== undefined ? options.targetApp : undefined;
|
|
343
|
-
const inspection = inspectExecEvent(event, options.allowedTools, targetForInspection, options.targetAppAliases);
|
|
344
|
-
if (inspection.violation) {
|
|
345
|
-
policyViolation = inspection.violation;
|
|
346
|
-
void requestTermination();
|
|
347
|
-
return;
|
|
348
|
-
}
|
|
349
|
-
if (inspection.methods.length > 0) {
|
|
350
|
-
if (event.type === "item.started")
|
|
351
|
-
startedCallCount += inspection.methods.length;
|
|
352
|
-
else
|
|
353
|
-
completedCallCount += inspection.methods.length;
|
|
354
|
-
const attemptedCalls = Math.max(startedCallCount, completedCallCount);
|
|
355
|
-
if (attemptedCalls > (options.maxToolCalls ?? 100)) {
|
|
356
|
-
policyViolation = `Computer Use call budget exceeded in stream (${attemptedCalls}/${options.maxToolCalls ?? 100})`;
|
|
357
|
-
void requestTermination();
|
|
358
|
-
return;
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
if (event.type !== "item.completed")
|
|
362
|
-
return;
|
|
363
|
-
const interruption = classifyFirstPartyInterruption(event.item);
|
|
364
|
-
if (interruption) {
|
|
365
|
-
firstPartyInterruption ??= interruption;
|
|
366
|
-
if (interruption === "app_approval")
|
|
367
|
-
approvalRequiredObserved = true;
|
|
368
|
-
}
|
|
369
|
-
const status = String(event.item?.status ?? "").toLowerCase();
|
|
370
|
-
const successful = ["completed", "success", "succeeded", "ok"].includes(status) &&
|
|
371
|
-
event.item?.isError !== true &&
|
|
372
|
-
event.item?.is_error !== true &&
|
|
373
|
-
event.item?.error == null;
|
|
374
|
-
const failed = !successful;
|
|
375
|
-
if (!failed && options.validateCallArguments && inspection.methods.length > 0) {
|
|
376
|
-
const args = parseToolArguments(event.item);
|
|
377
|
-
const method = inspection.methods[0];
|
|
378
|
-
const argumentViolation = !args || !method
|
|
379
|
-
? "Could not validate successful Computer Use call arguments"
|
|
380
|
-
: options.validateCallArguments(method, args);
|
|
381
|
-
if (argumentViolation) {
|
|
382
|
-
policyViolation = argumentViolation;
|
|
383
|
-
void requestTermination();
|
|
384
|
-
return;
|
|
385
|
-
}
|
|
386
|
-
}
|
|
387
|
-
if (!failed)
|
|
388
|
-
methods.push(...inspection.methods);
|
|
389
|
-
};
|
|
390
|
-
proc.stdin?.on("error", (error) => {
|
|
391
|
-
if (error.code === "EPIPE" && (aborted || timedOut || policyViolation !== undefined))
|
|
392
|
-
return;
|
|
393
|
-
if (stderr.length < 16_384)
|
|
394
|
-
stderr += String(error.message).slice(0, 16_384 - stderr.length);
|
|
395
|
-
});
|
|
396
|
-
proc.stdout?.setEncoding("utf8");
|
|
397
|
-
proc.stdout?.on("data", (chunk) => {
|
|
398
|
-
stdoutBuffer += chunk;
|
|
399
|
-
if (stdoutBuffer.length > 8_000_000) {
|
|
400
|
-
policyViolation = "Codex JSON event line exceeded the 8MB safety bound";
|
|
401
|
-
void requestTermination();
|
|
402
|
-
return;
|
|
403
|
-
}
|
|
404
|
-
const lines = stdoutBuffer.split("\n");
|
|
405
|
-
stdoutBuffer = lines.pop() ?? "";
|
|
406
|
-
for (const line of lines)
|
|
407
|
-
processEventLine(line);
|
|
408
|
-
});
|
|
409
|
-
proc.stderr?.setEncoding("utf8");
|
|
410
|
-
proc.stderr?.on("data", (chunk) => {
|
|
411
|
-
if (stderr.length < 16_384)
|
|
412
|
-
stderr += chunk.slice(0, 16_384 - stderr.length);
|
|
413
|
-
});
|
|
414
|
-
const timeoutMs = options.timeoutMs ?? 240_000;
|
|
415
|
-
timeout = setTimeout(() => {
|
|
416
|
-
timedOut = true;
|
|
417
|
-
void requestTermination();
|
|
418
|
-
}, timeoutMs);
|
|
419
|
-
if (options.signal) {
|
|
420
|
-
abortHandler = () => {
|
|
421
|
-
aborted = true;
|
|
422
|
-
void requestTermination();
|
|
423
|
-
};
|
|
424
|
-
if (options.signal.aborted)
|
|
425
|
-
abortHandler();
|
|
426
|
-
else
|
|
427
|
-
options.signal.addEventListener("abort", abortHandler, { once: true });
|
|
428
|
-
}
|
|
429
|
-
if (aborted)
|
|
430
|
-
proc.stdin?.destroy();
|
|
431
|
-
else
|
|
432
|
-
proc.stdin?.end(prompt, "utf8");
|
|
433
|
-
const exitCode = await new Promise((resolve) => {
|
|
434
|
-
proc.once("error", () => resolve(1));
|
|
435
|
-
proc.once("close", (code) => resolve(code ?? 1));
|
|
436
|
-
});
|
|
437
|
-
if (stdoutBuffer.trim())
|
|
438
|
-
processEventLine(stdoutBuffer);
|
|
439
|
-
stdoutBuffer = "";
|
|
440
|
-
if (timeout)
|
|
441
|
-
clearTimeout(timeout);
|
|
442
|
-
if (abortHandler && options.signal)
|
|
443
|
-
options.signal.removeEventListener("abort", abortHandler);
|
|
444
|
-
if (terminationPromise)
|
|
445
|
-
await terminationPromise;
|
|
446
|
-
let result;
|
|
447
|
-
if (!policyViolation && !timedOut && exitCode === 0) {
|
|
448
|
-
try {
|
|
449
|
-
const parsed = JSON.parse(await readFile(outputPath, "utf8"));
|
|
450
|
-
if (isNativeResult(parsed))
|
|
451
|
-
result = parsed;
|
|
452
|
-
else
|
|
453
|
-
policyViolation = "Codex final response did not match the constrained result schema";
|
|
454
|
-
}
|
|
455
|
-
catch {
|
|
456
|
-
policyViolation = "Codex did not produce a parseable constrained result";
|
|
457
|
-
}
|
|
458
|
-
}
|
|
459
|
-
const failureText = `${stderr}\n${observedErrorText}`;
|
|
460
|
-
return {
|
|
461
|
-
exitCode: aborted ? 130 : timedOut ? 124 : exitCode,
|
|
462
|
-
result,
|
|
463
|
-
usage,
|
|
464
|
-
computerUseMethods: methods,
|
|
465
|
-
approvalRequiredObserved,
|
|
466
|
-
firstPartyInterruption,
|
|
467
|
-
policyViolation,
|
|
468
|
-
errorKind: policyViolation
|
|
469
|
-
? "policy_violation"
|
|
470
|
-
: aborted
|
|
471
|
-
? "cancelled"
|
|
472
|
-
: timedOut
|
|
473
|
-
? "timeout"
|
|
474
|
-
: exitCode === 0
|
|
475
|
-
? undefined
|
|
476
|
-
: classifyFailure(failureText, exitCode),
|
|
477
|
-
errorSummary: aborted ? "Computer Use request cancelled" : exitCode === 0 ? undefined : safeErrorSummary(failureText),
|
|
478
|
-
codexVersion,
|
|
479
|
-
durationMs: Date.now() - started,
|
|
480
|
-
};
|
|
481
|
-
}
|
|
482
|
-
finally {
|
|
483
|
-
if (timeout)
|
|
484
|
-
clearTimeout(timeout);
|
|
485
|
-
if (abortHandler && options.signal)
|
|
486
|
-
options.signal.removeEventListener("abort", abortHandler);
|
|
487
|
-
if (proc?.pid && proc.exitCode === null)
|
|
488
|
-
await requestTermination();
|
|
489
|
-
if (terminationPromise)
|
|
490
|
-
await terminationPromise;
|
|
491
|
-
await rm(tempRoot, { recursive: true, force: true });
|
|
492
|
-
}
|
|
493
|
-
}
|
|
494
|
-
//# sourceMappingURL=runner.js.map
|