humanish 0.96.1 → 0.98.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/AGENTS.md +86 -79
- package/CONTRIBUTING.md +7 -2
- package/README.md +11 -2
- package/dist/actor-contract.d.ts +35 -1
- package/dist/actor-contract.js +38 -0
- package/dist/actor-contract.js.map +1 -1
- package/dist/adapter-extension.js +1 -0
- package/dist/adapter-extension.js.map +1 -1
- package/dist/automatic-analysis-config.d.ts +15 -5
- package/dist/automatic-analysis-config.js +25 -4
- package/dist/automatic-analysis-config.js.map +1 -1
- package/dist/automatic-study-analysis.js +4 -2
- package/dist/automatic-study-analysis.js.map +1 -1
- package/dist/browser-control-client.d.ts +14 -0
- package/dist/browser-control-client.js +134 -0
- package/dist/browser-control-client.js.map +1 -0
- package/dist/browser-control-dispatcher.d.ts +14 -0
- package/dist/browser-control-dispatcher.js +109 -0
- package/dist/browser-control-dispatcher.js.map +1 -0
- package/dist/browser-control-protocol.d.ts +371 -0
- package/dist/browser-control-protocol.js +155 -0
- package/dist/browser-control-protocol.js.map +1 -0
- package/dist/browser-control-transport.d.ts +24 -0
- package/dist/browser-control-transport.js +156 -0
- package/dist/browser-control-transport.js.map +1 -0
- package/dist/comms-lease-store.d.ts +1 -0
- package/dist/comms-lease-store.js +9 -3
- package/dist/comms-lease-store.js.map +1 -1
- package/dist/computer-use-actor.d.ts +2 -2
- package/dist/computer-use-actor.js +6 -1
- package/dist/computer-use-actor.js.map +1 -1
- package/dist/computer-use.d.ts +23 -1
- package/dist/computer-use.js +253 -70
- package/dist/computer-use.js.map +1 -1
- package/dist/cua-actor-lab.d.ts +24 -289
- package/dist/cua-actor-lab.js +203 -1983
- package/dist/cua-actor-lab.js.map +1 -1
- package/dist/cua-desktop-lane.d.ts +35 -0
- package/dist/cua-desktop-lane.js +13 -0
- package/dist/cua-desktop-lane.js.map +1 -0
- package/dist/cua-executor-error.d.ts +31 -0
- package/dist/cua-executor-error.js +48 -0
- package/dist/cua-executor-error.js.map +1 -0
- package/dist/cua-provider-error.d.ts +12 -0
- package/dist/cua-provider-error.js +28 -0
- package/dist/cua-provider-error.js.map +1 -0
- package/dist/desktop-session.d.ts +41 -0
- package/dist/desktop-session.js +46 -0
- package/dist/desktop-session.js.map +1 -0
- package/dist/doctor-lab.d.ts +8 -1
- package/dist/doctor-lab.js +40 -8
- package/dist/doctor-lab.js.map +1 -1
- package/dist/e2b-cua-desktop.d.ts +3 -0
- package/dist/e2b-cua-desktop.js +675 -0
- package/dist/e2b-cua-desktop.js.map +1 -0
- package/dist/e2b-cua-provisioning.d.ts +311 -0
- package/dist/e2b-cua-provisioning.js +1213 -0
- package/dist/e2b-cua-provisioning.js.map +1 -0
- package/dist/e2b-desktop-executor.d.ts +1 -24
- package/dist/e2b-desktop-executor.js +2 -127
- package/dist/e2b-desktop-executor.js.map +1 -1
- package/dist/e2b-desktop-session.d.ts +7 -0
- package/dist/e2b-desktop-session.js +29 -0
- package/dist/e2b-desktop-session.js.map +1 -0
- package/dist/e2b-terminal-lab.js +1 -0
- package/dist/e2b-terminal-lab.js.map +1 -1
- package/dist/frame-signature.d.ts +24 -0
- package/dist/frame-signature.js +128 -0
- package/dist/frame-signature.js.map +1 -0
- package/dist/guest-bootstrap.d.ts +43 -0
- package/dist/guest-bootstrap.js +240 -0
- package/dist/guest-bootstrap.js.map +1 -0
- package/dist/guest-browser-tools.d.ts +8 -0
- package/dist/guest-browser-tools.js +66 -0
- package/dist/guest-browser-tools.js.map +1 -0
- package/dist/guest-chromium-text.d.ts +27 -0
- package/dist/guest-chromium-text.js +281 -0
- package/dist/guest-chromium-text.js.map +1 -0
- package/dist/guest-desktop-executor.d.ts +22 -0
- package/dist/guest-desktop-executor.js +177 -0
- package/dist/guest-desktop-executor.js.map +1 -0
- package/dist/guest-desktop-native.d.ts +14 -0
- package/dist/guest-desktop-native.js +131 -0
- package/dist/guest-desktop-native.js.map +1 -0
- package/dist/guest-runtime-desktop.d.ts +35 -0
- package/dist/guest-runtime-desktop.js +231 -0
- package/dist/guest-runtime-desktop.js.map +1 -0
- package/dist/guest-runtime-main.d.ts +1 -0
- package/dist/guest-runtime-main.js +31 -0
- package/dist/guest-runtime-main.js.map +1 -0
- package/dist/guest-runtime-revision.d.ts +1 -0
- package/dist/guest-runtime-revision.js +3 -0
- package/dist/guest-runtime-revision.js.map +1 -0
- package/dist/guest-runtime.d.ts +25 -0
- package/dist/guest-runtime.js +96 -0
- package/dist/guest-runtime.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/lab-config.js +10 -3
- package/dist/lab-config.js.map +1 -1
- package/dist/lab-engine.js +6 -0
- package/dist/lab-engine.js.map +1 -1
- package/dist/lab-summary.d.ts +5 -1
- package/dist/lab-summary.js +5 -0
- package/dist/lab-summary.js.map +1 -1
- package/dist/local-agent-cli.js +1 -1
- package/dist/local-agent-cli.js.map +1 -1
- package/dist/local-firecracker-desktop.d.ts +13 -0
- package/dist/local-firecracker-desktop.js +150 -0
- package/dist/local-firecracker-desktop.js.map +1 -0
- package/dist/local-firecracker-study.d.ts +9 -0
- package/dist/local-firecracker-study.js +93 -0
- package/dist/local-firecracker-study.js.map +1 -0
- package/dist/local-runtime-config.d.ts +6 -0
- package/dist/local-runtime-config.js +56 -0
- package/dist/local-runtime-config.js.map +1 -0
- package/dist/local-runtime-release.d.ts +3 -0
- package/dist/local-runtime-release.js +8 -0
- package/dist/local-runtime-release.js.map +1 -0
- package/dist/local-runtime.d.ts +25 -0
- package/dist/local-runtime.js +113 -0
- package/dist/local-runtime.js.map +1 -0
- package/dist/observer-app.html +4 -4
- package/dist/pricing.d.ts +22 -1
- package/dist/pricing.js +22 -0
- package/dist/pricing.js.map +1 -1
- package/dist/program.js +50 -9
- package/dist/program.js.map +1 -1
- package/dist/restricted-codex-analysis.d.ts +15 -0
- package/dist/restricted-codex-analysis.js +13 -0
- package/dist/restricted-codex-analysis.js.map +1 -0
- package/dist/restricted-codex-participant-policy.d.ts +39 -0
- package/dist/restricted-codex-participant-policy.js +69 -0
- package/dist/restricted-codex-participant-policy.js.map +1 -0
- package/dist/restricted-codex-participant-run.d.ts +20 -0
- package/dist/restricted-codex-participant-run.js +78 -0
- package/dist/restricted-codex-participant-run.js.map +1 -0
- package/dist/restricted-codex-participant.d.ts +14 -0
- package/dist/restricted-codex-participant.js +178 -0
- package/dist/restricted-codex-participant.js.map +1 -0
- package/dist/restricted-codex-policy.d.ts +56 -0
- package/dist/restricted-codex-policy.js +151 -0
- package/dist/restricted-codex-policy.js.map +1 -0
- package/dist/restricted-codex-session.d.ts +19 -0
- package/dist/restricted-codex-session.js +413 -0
- package/dist/restricted-codex-session.js.map +1 -0
- package/dist/restricted-codex-transport.d.ts +58 -0
- package/dist/restricted-codex-transport.js +233 -0
- package/dist/restricted-codex-transport.js.map +1 -0
- package/dist/run-detail.js +4 -2
- package/dist/run-detail.js.map +1 -1
- package/dist/run.d.ts +12 -5
- package/dist/run.js +17 -1
- package/dist/run.js.map +1 -1
- package/dist/shared-world-lab.js +2 -2
- package/dist/shared-world-lab.js.map +1 -1
- package/dist/study-analysis-codex-config.d.ts +11 -0
- package/dist/study-analysis-codex-config.js +34 -0
- package/dist/study-analysis-codex-config.js.map +1 -0
- package/dist/study-analysis-engine.d.ts +6 -3
- package/dist/study-analysis-engine.js +19 -10
- package/dist/study-analysis-engine.js.map +1 -1
- package/dist/study-analysis-job.d.ts +3 -2
- package/dist/study-analysis-job.js +1 -1
- package/dist/study-analysis-job.js.map +1 -1
- package/dist/study-analysis-provider.d.ts +4 -2
- package/dist/study-analysis-provider.js +1 -1
- package/dist/study-analysis-provider.js.map +1 -1
- package/dist/study-analysis-service.d.ts +3 -0
- package/dist/study-analysis-service.js +24 -6
- package/dist/study-analysis-service.js.map +1 -1
- package/dist/study-analysis-validation.d.ts +43 -19
- package/dist/study-analysis-validation.js +23 -10
- package/dist/study-analysis-validation.js.map +1 -1
- package/dist/study-analysis.d.ts +27 -2
- package/dist/study-costs.js +6 -0
- package/dist/study-costs.js.map +1 -1
- package/dist/tui-app.js +102 -102
- package/docs/architecture/browser-control.md +117 -0
- package/docs/architecture/desktop-sessions.md +80 -0
- package/docs/architecture/guest-desktop.md +87 -0
- package/docs/architecture/local-browser-runtime.md +100 -0
- package/docs/architecture/restricted-codex-analysis.md +91 -0
- package/docs/architecture/runtime-broker-core.md +30 -0
- package/docs/contracts/schemas.md +1 -1
- package/docs/contracts/study-analysis.md +44 -2
- package/docs/goals/current.md +25 -9
- package/docs/product/automatic-analysis.md +24 -3
- package/docs/product/open-source-install-experience.md +7 -0
- package/docs/ramp/README.md +30 -13
- package/docs/release/0.97.0-codex-account-analysis.md +45 -0
- package/docs/release/0.98.0-local-browser-studies.md +31 -0
- package/package.json +4 -2
- package/skills/humanish/SKILL.md +39 -0
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/** Versioned, deliberately narrow profile qualified with an account-backed vision turn.
|
|
2
|
+
* The CLI still advertises code-mode tools: disabling their host is the enforcement
|
|
3
|
+
* boundary. Feature names alone are not proof that a tool has been removed. */
|
|
4
|
+
export const RESTRICTED_CODEX_ANALYSIS_IDENTITY = {
|
|
5
|
+
provider: "codex", authMode: "chatgpt", modelProvider: "openai", cliVersion: "0.154.0",
|
|
6
|
+
toolPolicy: "restricted-codex-v1", reasoningEffort: "low"
|
|
7
|
+
};
|
|
8
|
+
export const RESTRICTED_CODEX_ANALYSIS_MODELS = ["gpt-6-astra"];
|
|
9
|
+
export const CODEX_MAX_REQUEST_BYTES = 32 * 1024 * 1024;
|
|
10
|
+
export const CODEX_MAX_OUTPUT_BYTES = 2 * 1024 * 1024;
|
|
11
|
+
export const CODEX_MAX_STDOUT_BYTES = 8 * 1024 * 1024;
|
|
12
|
+
export const CODEX_MAX_EVENTS = 65_536;
|
|
13
|
+
export const CODEX_IMAGE = /^data:image\/(png|jpeg|webp);base64,([A-Za-z0-9+/]+={0,2})$/;
|
|
14
|
+
export const codexRecord = (value) => value !== null && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
15
|
+
const empty = (value) => value === null || value === undefined
|
|
16
|
+
|| (typeof value === "object" && Object.keys(value).length === 0);
|
|
17
|
+
const count = (value) => Number.isSafeInteger(value) && value >= 0 && value <= 1e12;
|
|
18
|
+
const disabledFeatures = ["apps", "plugins", "hooks", "shell_tool", "shell_snapshot", "view_image", "image_generation",
|
|
19
|
+
"skill_search", "skill_mcp_dependency_install", "multi_agent", "multi_agent_v2", "browser_use", "browser_use_external",
|
|
20
|
+
"computer_use", "memories", "external_agent_memory_import", "goals", "sleep_tool", "tool_suggest", "code_mode",
|
|
21
|
+
"code_mode_host", "code_mode_only", "default_mode_request_user_input", "unbounded_connection_retries",
|
|
22
|
+
"workspace_dependencies", "remote_plugin", "recommended_plugins", "in_app_local_automation"];
|
|
23
|
+
export function restrictedCodexConfig(model) {
|
|
24
|
+
const overrides = {
|
|
25
|
+
model, model_provider: "openai", model_reasoning_effort: "low", approval_policy: "never", sandbox_mode: "read-only",
|
|
26
|
+
forced_login_method: "chatgpt", project_doc_max_bytes: 0, web_search: "disabled", "history.persistence": "none",
|
|
27
|
+
"analytics.enabled": false, "features.skip_host_skill_discovery": true, "skills.bundled.enabled": false,
|
|
28
|
+
"agents.enabled": false, "agents.max_threads": 1, "agents.max_depth": 1
|
|
29
|
+
};
|
|
30
|
+
for (const name of disabledFeatures)
|
|
31
|
+
overrides[`features.${name}`] = false;
|
|
32
|
+
const toml = `model = ${JSON.stringify(model)}
|
|
33
|
+
model_provider = "openai"
|
|
34
|
+
model_reasoning_effort = "low"
|
|
35
|
+
approval_policy = "never"
|
|
36
|
+
sandbox_mode = "read-only"
|
|
37
|
+
forced_login_method = "chatgpt"
|
|
38
|
+
project_doc_max_bytes = 0
|
|
39
|
+
web_search = "disabled"
|
|
40
|
+
[history]
|
|
41
|
+
persistence = "none"
|
|
42
|
+
[analytics]
|
|
43
|
+
enabled = false
|
|
44
|
+
[features]
|
|
45
|
+
skip_host_skill_discovery = true
|
|
46
|
+
${disabledFeatures.map(name => `${name} = false`).join("\n")}
|
|
47
|
+
[skills.bundled]
|
|
48
|
+
enabled = false
|
|
49
|
+
[agents]
|
|
50
|
+
enabled = false
|
|
51
|
+
max_threads = 1
|
|
52
|
+
max_depth = 1
|
|
53
|
+
`;
|
|
54
|
+
return { toml, overrides };
|
|
55
|
+
}
|
|
56
|
+
/** Inspect before thread/start, which could otherwise start inherited integrations. */
|
|
57
|
+
export function admitsRestrictedCodexConfig(raw, configPath, model) {
|
|
58
|
+
const root = codexRecord(raw), config = codexRecord(root.config), features = codexRecord(config.features);
|
|
59
|
+
if (!Array.isArray(root.layers) || root.layers.length === 0)
|
|
60
|
+
return false;
|
|
61
|
+
let ownLayer = false;
|
|
62
|
+
for (const rawLayer of root.layers) {
|
|
63
|
+
const layer = codexRecord(rawLayer), name = codexRecord(layer.name);
|
|
64
|
+
if (name.type === "user" && name.file === configPath && name.profile === null
|
|
65
|
+
&& (layer.disabledReason === null || layer.disabledReason === undefined)) {
|
|
66
|
+
if (ownLayer)
|
|
67
|
+
return false;
|
|
68
|
+
ownLayer = true;
|
|
69
|
+
}
|
|
70
|
+
else if (name.type !== "system" || !empty(layer.config))
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
if (!ownLayer || config.model !== model || config.model_provider !== "openai" || config.model_reasoning_effort !== "low"
|
|
74
|
+
|| config.forced_login_method !== "chatgpt" || config.sandbox_mode !== "read-only" || config.approval_policy !== "never"
|
|
75
|
+
|| config.project_doc_max_bytes !== 0 || config.web_search !== "disabled" || features.skip_host_skill_discovery !== true
|
|
76
|
+
|| codexRecord(config.history).persistence !== "none" || codexRecord(config.analytics).enabled !== false
|
|
77
|
+
|| disabledFeatures.some(name => features[name] !== false))
|
|
78
|
+
return false;
|
|
79
|
+
const agents = codexRecord(config.agents), skills = codexRecord(config.skills);
|
|
80
|
+
if (agents.enabled !== false || agents.max_concurrent_threads_per_session !== 1 || agents.max_depth !== 1
|
|
81
|
+
|| codexRecord(skills.bundled).enabled !== false || Object.keys(skills).some(key => key !== "bundled"))
|
|
82
|
+
return false;
|
|
83
|
+
for (const key of ["mcp_servers", "plugins", "hooks", "notify", "instructions", "developer_instructions", "permissions",
|
|
84
|
+
"profiles", "projects", "model_providers", "experimental_compact_prompt_file", "experimental_model_instructions_file", "model_instructions_file",
|
|
85
|
+
"model_catalog_json", "openai_base_url", "orchestrator", "experimental_thread_store", "experimental_thread_store_endpoint",
|
|
86
|
+
"experimental_realtime_ws_base_url", "experimental_realtime_webrtc_call_base_url", "forced_chatgpt_workspace_id",
|
|
87
|
+
"log_dir", "sqlite_home", "otel", "marketplaces", "js_repl_node_path", "js_repl_node_module_dirs", "responses_api_metadata",
|
|
88
|
+
"compact_prompt", "profile", "default_permissions", "auto_review", "apps", "browser_use", "computer_use", "desktop", "memories", "realtime"])
|
|
89
|
+
if (!empty(config[key]))
|
|
90
|
+
return false;
|
|
91
|
+
if (Object.values(codexRecord(config.shell_environment_policy)).some(value => !empty(value)))
|
|
92
|
+
return false;
|
|
93
|
+
return config.cli_auth_credentials_store === "file" && config.chatgpt_base_url === "https://chatgpt.com/backend-api/";
|
|
94
|
+
}
|
|
95
|
+
export function admitsRestrictedCodexThread(raw, model, cwd) {
|
|
96
|
+
const value = codexRecord(raw), thread = codexRecord(value.thread), sandbox = codexRecord(value.sandbox);
|
|
97
|
+
return typeof thread.id === "string" && thread.id.length > 0 && thread.id.length <= 200
|
|
98
|
+
&& thread.ephemeral === true && thread.model === model && thread.modelProvider === "openai"
|
|
99
|
+
&& thread.reasoningEffort === "low" && thread.cliVersion === RESTRICTED_CODEX_ANALYSIS_IDENTITY.cliVersion
|
|
100
|
+
&& Array.isArray(thread.environments) && thread.environments.length === 0 && thread.path === null
|
|
101
|
+
&& value.model === model && value.modelProvider === "openai" && value.reasoningEffort === "low"
|
|
102
|
+
&& value.cwd === cwd && thread.cwd === cwd && value.approvalPolicy === "never"
|
|
103
|
+
&& sandbox.type === "readOnly" && sandbox.networkAccess === false
|
|
104
|
+
&& Array.isArray(value.instructionSources) && value.instructionSources.length === 0
|
|
105
|
+
&& Array.isArray(value.runtimeWorkspaceRoots) && value.runtimeWorkspaceRoots.length === 0;
|
|
106
|
+
}
|
|
107
|
+
export function restrictedCodexRequestError(request) {
|
|
108
|
+
if (request.signal?.aborted)
|
|
109
|
+
return "cancelled";
|
|
110
|
+
if (!RESTRICTED_CODEX_ANALYSIS_MODELS.some(model => model === request.model))
|
|
111
|
+
return "codex_model_unavailable";
|
|
112
|
+
if (request.maxOutputTokens !== null || !Number.isSafeInteger(request.timeoutMs) || request.timeoutMs < 1 || request.timeoutMs > 600_000
|
|
113
|
+
|| typeof request.instructions !== "string" || typeof request.evidence !== "string" || !Array.isArray(request.images)
|
|
114
|
+
|| request.images.length > 128 || !request.schema || typeof request.schema !== "object" || Array.isArray(request.schema))
|
|
115
|
+
return "invalid_request";
|
|
116
|
+
try {
|
|
117
|
+
if (new TextEncoder().encode(JSON.stringify({ instructions: request.instructions, evidence: request.evidence,
|
|
118
|
+
images: request.images, schema: request.schema })).byteLength > CODEX_MAX_REQUEST_BYTES)
|
|
119
|
+
return "invalid_request";
|
|
120
|
+
let imageBytes = 0;
|
|
121
|
+
for (const image of request.images) {
|
|
122
|
+
if (typeof image.evidenceId !== "string" || image.evidenceId.length === 0 || image.evidenceId.length > 300
|
|
123
|
+
|| typeof image.dataUrl !== "string")
|
|
124
|
+
return "invalid_request";
|
|
125
|
+
const match = CODEX_IMAGE.exec(image.dataUrl);
|
|
126
|
+
if (!match)
|
|
127
|
+
return "invalid_request";
|
|
128
|
+
const decoded = atob(match[2]);
|
|
129
|
+
imageBytes += decoded.length;
|
|
130
|
+
if (btoa(decoded) !== match[2] || imageBytes > 20 * 1024 * 1024)
|
|
131
|
+
return "invalid_request";
|
|
132
|
+
}
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
catch {
|
|
136
|
+
return "invalid_request";
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
export function restrictedCodexUsage(raw) {
|
|
140
|
+
const total = codexRecord(codexRecord(raw).total);
|
|
141
|
+
const input = total.inputTokens, output = total.outputTokens, cachedInput = total.cachedInputTokens, cacheWriteInput = total.cacheWriteInputTokens;
|
|
142
|
+
if (!count(input) || !count(output) || !count(cachedInput) || !count(cacheWriteInput) || cachedInput + cacheWriteInput > input)
|
|
143
|
+
return null;
|
|
144
|
+
return { input, output, cachedInput, cacheWriteInput };
|
|
145
|
+
}
|
|
146
|
+
export function restrictedCodexFailure(errorCode, dispatched = false, usage = null) {
|
|
147
|
+
return { status: errorCode === "cancelled" ? "cancelled" : errorCode === "timeout" ? "timed_out"
|
|
148
|
+
: errorCode === "refusal" ? "refused" : errorCode === "output_incomplete" ? "incomplete" : "failed",
|
|
149
|
+
output: null, usage, usageComplete: false, dispatched, errorCode };
|
|
150
|
+
}
|
|
151
|
+
//# sourceMappingURL=restricted-codex-policy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"restricted-codex-policy.js","sourceRoot":"","sources":["../src/restricted-codex-policy.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type RestrictedCodexRequest, type RestrictedCodexResult } from "./restricted-codex-policy.js";
|
|
2
|
+
import { type RestrictedCodexSpawn } from "./restricted-codex-transport.js";
|
|
3
|
+
/** Internal host dependencies. None of these options is accepted from a study artifact. */
|
|
4
|
+
export interface RestrictedCodexSessionOptions {
|
|
5
|
+
executable?: string;
|
|
6
|
+
authHome?: string;
|
|
7
|
+
env?: NodeJS.ProcessEnv;
|
|
8
|
+
tempRoot?: string;
|
|
9
|
+
platform?: NodeJS.Platform;
|
|
10
|
+
arch?: string;
|
|
11
|
+
spawnFn?: RestrictedCodexSpawn;
|
|
12
|
+
}
|
|
13
|
+
/** Each call owns a separate process, home and thread. An unresolved child blocks
|
|
14
|
+
* new work, but ordinary participant turns may run concurrently. */
|
|
15
|
+
export declare function runRestrictedCodexSession(request: RestrictedCodexRequest, options?: RestrictedCodexSessionOptions, readinessOnly?: boolean): Promise<RestrictedCodexResult>;
|
|
16
|
+
export declare function checkRestrictedCodexSessionReadiness(input?: {
|
|
17
|
+
signal?: AbortSignal;
|
|
18
|
+
timeoutMs?: number;
|
|
19
|
+
}, options?: RestrictedCodexSessionOptions): Promise<RestrictedCodexResult>;
|
|
@@ -0,0 +1,413 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { constants } from "node:fs";
|
|
3
|
+
import { access, chmod, lstat, mkdir, mkdtemp, open, readdir, realpath, rm, stat, symlink, unlink, writeFile } from "node:fs/promises";
|
|
4
|
+
import { homedir, tmpdir } from "node:os";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import { CODEX_IMAGE, CODEX_MAX_OUTPUT_BYTES, RESTRICTED_CODEX_ANALYSIS_IDENTITY, RESTRICTED_CODEX_ANALYSIS_MODELS, admitsRestrictedCodexConfig, admitsRestrictedCodexThread, codexRecord, restrictedCodexConfig, restrictedCodexFailure, restrictedCodexRequestError, restrictedCodexUsage } from "./restricted-codex-policy.js";
|
|
7
|
+
import { RestrictedCodexDeadline, RestrictedCodexStop, RestrictedCodexTransport, closeOwnedCodexProcess, ownCodexProcess } from "./restricted-codex-transport.js";
|
|
8
|
+
function childEnvironment(source, home, scratch) {
|
|
9
|
+
const result = { HOME: home, CODEX_HOME: home, TMPDIR: scratch };
|
|
10
|
+
for (const key of ["PATH", "LANG", "USER", "LOGNAME"])
|
|
11
|
+
if (typeof source[key] === "string")
|
|
12
|
+
result[key] = source[key];
|
|
13
|
+
return result;
|
|
14
|
+
}
|
|
15
|
+
async function isNativeExecutable(file) {
|
|
16
|
+
try {
|
|
17
|
+
await access(file, constants.X_OK);
|
|
18
|
+
const handle = await open(file, "r");
|
|
19
|
+
try {
|
|
20
|
+
const buffer = Buffer.alloc(4);
|
|
21
|
+
const read = await handle.read(buffer, 0, 4, 0);
|
|
22
|
+
return read.bytesRead === 4 && buffer.equals(Buffer.from([0x7f, 0x45, 0x4c, 0x46]));
|
|
23
|
+
}
|
|
24
|
+
finally {
|
|
25
|
+
await handle.close();
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/** Resolve PATH without running a shell. The npm launcher is resolved to its
|
|
33
|
+
* native optional package so cleanup owns the real app-server child. */
|
|
34
|
+
async function resolveExecutable(options, env) {
|
|
35
|
+
let selected = options.executable;
|
|
36
|
+
if (selected === undefined) {
|
|
37
|
+
for (const directory of (env.PATH ?? "").split(path.delimiter).filter(entry => path.isAbsolute(entry))) {
|
|
38
|
+
const candidate = path.join(directory, "codex");
|
|
39
|
+
try {
|
|
40
|
+
await access(candidate, constants.X_OK);
|
|
41
|
+
selected = candidate;
|
|
42
|
+
break;
|
|
43
|
+
}
|
|
44
|
+
catch { /* Continue PATH. */ }
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
if (selected === undefined || !path.isAbsolute(selected))
|
|
48
|
+
throw new RestrictedCodexStop("codex_unavailable");
|
|
49
|
+
let resolved;
|
|
50
|
+
try {
|
|
51
|
+
resolved = await realpath(selected);
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
throw new RestrictedCodexStop("codex_unavailable");
|
|
55
|
+
}
|
|
56
|
+
if (await isNativeExecutable(resolved))
|
|
57
|
+
return resolved;
|
|
58
|
+
if (path.basename(resolved) === "codex.js" && path.basename(path.dirname(resolved)) === "bin") {
|
|
59
|
+
const packageRoot = path.dirname(path.dirname(resolved));
|
|
60
|
+
const triple = "x86_64-unknown-linux-musl";
|
|
61
|
+
for (const candidate of [path.join(packageRoot, "node_modules", "@openai", "codex-linux-x64", "vendor", triple, "bin", "codex"),
|
|
62
|
+
path.join(packageRoot, "vendor", triple, "bin", "codex")]) {
|
|
63
|
+
if (await isNativeExecutable(candidate))
|
|
64
|
+
return realpath(candidate);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
throw new RestrictedCodexStop("codex_unavailable");
|
|
68
|
+
}
|
|
69
|
+
async function checkVersion(file, env, cwd, spawnFn, deadline) {
|
|
70
|
+
deadline.check();
|
|
71
|
+
const owned = ownCodexProcess(spawnFn(file, ["--version"], { cwd, env, detached: false, stdio: ["pipe", "pipe", "pipe"] }));
|
|
72
|
+
let text = "", bytes = 0, exitCode = null;
|
|
73
|
+
const timer = setTimeout(() => deadline.stop("timeout"), 15_000);
|
|
74
|
+
owned.child.on("error", () => deadline.stop("codex_unavailable"));
|
|
75
|
+
owned.child.stdin.on("error", () => deadline.stop("codex_unavailable"));
|
|
76
|
+
owned.child.stdout.on("data", (chunk) => {
|
|
77
|
+
bytes += chunk.length;
|
|
78
|
+
if (bytes > 4096)
|
|
79
|
+
deadline.stop("response_too_large");
|
|
80
|
+
else
|
|
81
|
+
text += chunk.toString("utf8");
|
|
82
|
+
});
|
|
83
|
+
owned.child.stderr.on("data", (chunk) => { bytes += chunk.length; if (bytes > 4096)
|
|
84
|
+
deadline.stop("response_too_large"); });
|
|
85
|
+
owned.child.on("exit", code => { exitCode = code; });
|
|
86
|
+
try {
|
|
87
|
+
await deadline.wait(owned.closed);
|
|
88
|
+
if (exitCode !== 0)
|
|
89
|
+
throw new RestrictedCodexStop("codex_unavailable");
|
|
90
|
+
if (text.trim() !== `codex-cli ${RESTRICTED_CODEX_ANALYSIS_IDENTITY.cliVersion}`)
|
|
91
|
+
throw new RestrictedCodexStop("codex_unsupported_version");
|
|
92
|
+
}
|
|
93
|
+
finally {
|
|
94
|
+
clearTimeout(timer);
|
|
95
|
+
if (!await closeOwnedCodexProcess(owned)) {
|
|
96
|
+
retainUnclosedChild(owned.closed);
|
|
97
|
+
throw new RestrictedCodexStop("codex_cleanup_failed");
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
const unclosedChildren = new Set();
|
|
102
|
+
function retainUnclosedChild(closed) {
|
|
103
|
+
unclosedChildren.add(closed);
|
|
104
|
+
void closed.then(() => { unclosedChildren.delete(closed); });
|
|
105
|
+
}
|
|
106
|
+
/** Each call owns a separate process, home and thread. An unresolved child blocks
|
|
107
|
+
* new work, but ordinary participant turns may run concurrently. */
|
|
108
|
+
export async function runRestrictedCodexSession(request, options = {}, readinessOnly = false) {
|
|
109
|
+
const error = restrictedCodexRequestError(request);
|
|
110
|
+
if (error)
|
|
111
|
+
return restrictedCodexFailure(error);
|
|
112
|
+
if (unclosedChildren.size)
|
|
113
|
+
return restrictedCodexFailure("codex_busy");
|
|
114
|
+
return executeRestrictedCodexSession(request, options, readinessOnly);
|
|
115
|
+
}
|
|
116
|
+
async function writeRecoveryMarker(work, sourceEnv, reason) {
|
|
117
|
+
const cache = sourceEnv.XDG_CACHE_HOME ?? path.join(sourceEnv.HOME ?? homedir(), ".cache");
|
|
118
|
+
if (!path.isAbsolute(cache))
|
|
119
|
+
return;
|
|
120
|
+
const markers = path.join(cache, "humanish", "codex-analysis-recovery");
|
|
121
|
+
await mkdir(markers, { recursive: true, mode: 0o700 });
|
|
122
|
+
await writeFile(path.join(markers, `${path.basename(work)}.json`), JSON.stringify({
|
|
123
|
+
schema: "humanish.codex-auth-recovery.v1", taskDirectoryName: path.basename(work),
|
|
124
|
+
homeDirectoryName: "home", authFileName: "auth.json", reason
|
|
125
|
+
}) + "\n", { mode: 0o600, flag: "wx" });
|
|
126
|
+
}
|
|
127
|
+
async function preserveUnexpectedAuth(work, sourceEnv) {
|
|
128
|
+
const home = path.join(work, "home");
|
|
129
|
+
await chmod(work, 0o700);
|
|
130
|
+
await chmod(home, 0o700);
|
|
131
|
+
const authPath = path.join(home, "auth.json"), auth = await lstat(authPath);
|
|
132
|
+
await chmod(authPath, auth.isDirectory() ? 0o700 : 0o600);
|
|
133
|
+
// Preserve only login state. Evidence, logs, databases and config are disposable.
|
|
134
|
+
for (const name of await readdir(home))
|
|
135
|
+
if (name !== "auth.json")
|
|
136
|
+
await rm(path.join(home, name), { recursive: true, force: true });
|
|
137
|
+
for (const name of await readdir(work))
|
|
138
|
+
if (name !== "home")
|
|
139
|
+
await rm(path.join(work, name), { recursive: true, force: true });
|
|
140
|
+
await writeRecoveryMarker(work, sourceEnv, "unexpected_auth_replacement");
|
|
141
|
+
}
|
|
142
|
+
function hasScopedIdentity(method, params, threadId, turnId) {
|
|
143
|
+
if (method === "turn/started" || method === "turn/completed") {
|
|
144
|
+
const id = codexRecord(params.turn).id;
|
|
145
|
+
return params.threadId === threadId && typeof id === "string" && id.length > 0 && id.length <= 200
|
|
146
|
+
&& (turnId === undefined || id === turnId);
|
|
147
|
+
}
|
|
148
|
+
if (method.startsWith("item/") || method.startsWith("rawResponse") || method === "thread/tokenUsage/updated")
|
|
149
|
+
return params.threadId === threadId && typeof params.turnId === "string" && params.turnId.length > 0
|
|
150
|
+
&& (turnId === undefined || params.turnId === turnId);
|
|
151
|
+
return (params.threadId === undefined || params.threadId === threadId) && (params.turnId === undefined || params.turnId === turnId);
|
|
152
|
+
}
|
|
153
|
+
/** Exactly one fresh thread/turn; private host auth, no participant state reuse. */
|
|
154
|
+
async function executeRestrictedCodexSession(request, options = {}, readinessOnly = false) {
|
|
155
|
+
const admissionError = restrictedCodexRequestError(request);
|
|
156
|
+
if (admissionError)
|
|
157
|
+
return restrictedCodexFailure(admissionError);
|
|
158
|
+
if ((options.platform ?? process.platform) !== "linux" || (options.arch ?? process.arch) !== "x64")
|
|
159
|
+
return restrictedCodexFailure("codex_unsupported_platform");
|
|
160
|
+
const deadline = new RestrictedCodexDeadline(request.timeoutMs, request.signal);
|
|
161
|
+
const sourceEnv = options.env ?? process.env;
|
|
162
|
+
const spawnFn = options.spawnFn ?? ((file, args, settings) => spawn(file, args, settings));
|
|
163
|
+
let work, authLink, transport;
|
|
164
|
+
let threadId, turnId, earlyTurnId;
|
|
165
|
+
let dispatched = false, completed = false, usage = null;
|
|
166
|
+
let generatedDeltaBytes = 0;
|
|
167
|
+
let outputItem;
|
|
168
|
+
let result = restrictedCodexFailure("codex_process_failed");
|
|
169
|
+
const early = [];
|
|
170
|
+
let resolveTurn;
|
|
171
|
+
const finished = new Promise(resolve => { resolveTurn = resolve; });
|
|
172
|
+
const handleTurnEvent = (method, params) => {
|
|
173
|
+
if (!hasScopedIdentity(method, params, threadId, turnId)) {
|
|
174
|
+
deadline.stop("codex_protocol_error");
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
const item = codexRecord(params.item);
|
|
178
|
+
if (method === "rawResponseItem/completed") {
|
|
179
|
+
if (!["message", "reasoning"].includes(String(item.type))) {
|
|
180
|
+
deadline.stop("codex_tool_call");
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
if (item.type === "message" && Array.isArray(item.content)
|
|
184
|
+
&& item.content.some(content => codexRecord(content).type === "refusal"))
|
|
185
|
+
deadline.stop("refusal");
|
|
186
|
+
}
|
|
187
|
+
if (method === "thread/tokenUsage/updated")
|
|
188
|
+
usage = restrictedCodexUsage(params.tokenUsage);
|
|
189
|
+
if (method === "item/started" || method === "item/completed") {
|
|
190
|
+
if (!["userMessage", "agentMessage", "reasoning"].includes(String(item.type))) {
|
|
191
|
+
deadline.stop("codex_tool_call");
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
if (item.type === "agentMessage") {
|
|
195
|
+
if (item.delivery === "async" || (Array.isArray(item.questions) && item.questions.length > 0)) {
|
|
196
|
+
deadline.stop("codex_tool_call");
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
if (method === "item/completed" && item.phase !== "commentary") {
|
|
200
|
+
if (typeof item.id !== "string" || typeof item.text !== "string" || Buffer.byteLength(item.text) > CODEX_MAX_OUTPUT_BYTES
|
|
201
|
+
|| (item.phase !== null && item.phase !== "final_answer") || (item.delivery !== null && item.delivery !== undefined)
|
|
202
|
+
|| (outputItem && (outputItem.id !== item.id || outputItem.text !== item.text))) {
|
|
203
|
+
deadline.stop("invalid_response");
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
outputItem = { id: item.id, text: item.text };
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
if (method === "turn/completed") {
|
|
211
|
+
const turn = codexRecord(params.turn);
|
|
212
|
+
if (turn.id !== turnId || completed) {
|
|
213
|
+
deadline.stop("codex_protocol_error");
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
completed = true;
|
|
217
|
+
if (turn.status === "interrupted") {
|
|
218
|
+
deadline.stop("cancelled");
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
if (turn.status !== "completed" || turn.error !== null || !outputItem) {
|
|
222
|
+
deadline.stop("invalid_response");
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
try {
|
|
226
|
+
resolveTurn({ status: "completed", output: JSON.parse(outputItem.text), usage,
|
|
227
|
+
usageComplete: usage !== null, dispatched: true, errorCode: null });
|
|
228
|
+
}
|
|
229
|
+
catch {
|
|
230
|
+
deadline.stop("invalid_response");
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
};
|
|
234
|
+
try {
|
|
235
|
+
deadline.check();
|
|
236
|
+
const file = await resolveExecutable(options, sourceEnv);
|
|
237
|
+
deadline.check();
|
|
238
|
+
work = await mkdtemp(path.join(options.tempRoot ?? tmpdir(), "humanish-codex-analysis-"));
|
|
239
|
+
await chmod(work, 0o700);
|
|
240
|
+
work = await realpath(work);
|
|
241
|
+
const home = path.join(work, "home"), cwd = path.join(work, "cwd"), scratch = path.join(work, "scratch");
|
|
242
|
+
for (const directory of [home, cwd, scratch])
|
|
243
|
+
await mkdir(directory, { mode: 0o700 });
|
|
244
|
+
const env = childEnvironment(sourceEnv, home, scratch);
|
|
245
|
+
await checkVersion(file, env, cwd, spawnFn, deadline);
|
|
246
|
+
const config = restrictedCodexConfig(request.model), configPath = path.join(home, "config.toml");
|
|
247
|
+
await writeFile(configPath, config.toml, { mode: 0o600, flag: "wx" });
|
|
248
|
+
const authHome = options.authHome ?? sourceEnv.CODEX_HOME ?? path.join(sourceEnv.HOME ?? homedir(), ".codex");
|
|
249
|
+
if (!path.isAbsolute(authHome))
|
|
250
|
+
throw new RestrictedCodexStop("codex_unsupported_auth");
|
|
251
|
+
let authFile;
|
|
252
|
+
try {
|
|
253
|
+
authFile = await realpath(path.join(authHome, "auth.json"));
|
|
254
|
+
if (!(await stat(authFile)).isFile())
|
|
255
|
+
throw new Error();
|
|
256
|
+
}
|
|
257
|
+
catch {
|
|
258
|
+
throw new RestrictedCodexStop("codex_login_required");
|
|
259
|
+
}
|
|
260
|
+
deadline.check();
|
|
261
|
+
authLink = path.join(home, "auth.json");
|
|
262
|
+
await symlink(authFile, authLink);
|
|
263
|
+
deadline.check();
|
|
264
|
+
const owned = ownCodexProcess(spawnFn(file, ["app-server", "--strict-config"], {
|
|
265
|
+
cwd, env, detached: false, stdio: ["pipe", "pipe", "pipe"]
|
|
266
|
+
}));
|
|
267
|
+
// Raw input notifications echo supplied image data. Bound that wire separately
|
|
268
|
+
// from the 2 MiB generated report, without dropping admitted evidence.
|
|
269
|
+
const frameLimit = Math.max(CODEX_MAX_OUTPUT_BYTES, Buffer.byteLength(JSON.stringify({ instructions: request.instructions,
|
|
270
|
+
evidence: request.evidence, images: request.images, schema: request.schema })) + 1024 * 1024);
|
|
271
|
+
transport = new RestrictedCodexTransport(owned, deadline, frameLimit);
|
|
272
|
+
transport.onNotification = (method, params) => {
|
|
273
|
+
if (!dispatched)
|
|
274
|
+
return;
|
|
275
|
+
if (!hasScopedIdentity(method, params, threadId, turnId ?? earlyTurnId)) {
|
|
276
|
+
deadline.stop("codex_protocol_error");
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
if (method === "item/agentMessage/delta") {
|
|
280
|
+
if (typeof params.delta !== "string") {
|
|
281
|
+
deadline.stop("codex_protocol_error");
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
generatedDeltaBytes += Buffer.byteLength(params.delta);
|
|
285
|
+
if (generatedDeltaBytes > CODEX_MAX_OUTPUT_BYTES) {
|
|
286
|
+
deadline.stop("response_too_large");
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
const item = codexRecord(params.item);
|
|
291
|
+
// Tool requests must fail even if the turn-start acknowledgment is lost.
|
|
292
|
+
if ((method === "rawResponseItem/completed" && !["message", "reasoning"].includes(String(item.type)))
|
|
293
|
+
|| (["item/started", "item/completed"].includes(method) && item.type === "agentMessage"
|
|
294
|
+
&& (item.delivery === "async" || (Array.isArray(item.questions) && item.questions.length > 0)))) {
|
|
295
|
+
deadline.stop("codex_tool_call");
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
298
|
+
if (method === "turn/started") {
|
|
299
|
+
const value = codexRecord(params.turn).id;
|
|
300
|
+
if (typeof value !== "string" || value.length === 0 || (earlyTurnId !== undefined && value !== earlyTurnId))
|
|
301
|
+
deadline.stop("codex_protocol_error");
|
|
302
|
+
else
|
|
303
|
+
earlyTurnId = value;
|
|
304
|
+
}
|
|
305
|
+
if (turnId === undefined)
|
|
306
|
+
early.push({ method, params });
|
|
307
|
+
else
|
|
308
|
+
handleTurnEvent(method, params);
|
|
309
|
+
};
|
|
310
|
+
const initialize = await transport.rpc("initialize", {
|
|
311
|
+
clientInfo: { name: "humanish_analysis", version: "1.0.0" }, capabilities: { experimentalApi: true }
|
|
312
|
+
});
|
|
313
|
+
if (typeof initialize.userAgent !== "string" || !initialize.userAgent.includes(`/0.154.0 `)
|
|
314
|
+
|| initialize.codexHome !== home || initialize.platformOs !== "linux" || initialize.platformFamily !== "unix")
|
|
315
|
+
throw new RestrictedCodexStop("codex_unsupported_version");
|
|
316
|
+
transport.notify("initialized", {});
|
|
317
|
+
const effective = await transport.rpc("config/read", { includeLayers: true, cwd });
|
|
318
|
+
if (!admitsRestrictedCodexConfig(effective, configPath, request.model))
|
|
319
|
+
throw new RestrictedCodexStop("codex_unsafe_configuration");
|
|
320
|
+
const account = await transport.rpc("account/read", { refreshToken: false });
|
|
321
|
+
if (account.account === null)
|
|
322
|
+
throw new RestrictedCodexStop("codex_login_required");
|
|
323
|
+
if (codexRecord(account.account).type !== "chatgpt" || account.requiresOpenaiAuth !== true)
|
|
324
|
+
throw new RestrictedCodexStop("codex_unsupported_auth");
|
|
325
|
+
const thread = await transport.rpc("thread/start", { cwd, ephemeral: true, experimentalRawEvents: true,
|
|
326
|
+
approvalPolicy: "never", sandbox: "read-only", model: request.model, modelProvider: "openai", allowProviderModelFallback: false,
|
|
327
|
+
environments: [], runtimeWorkspaceRoots: [], dynamicTools: [], baseInstructions: request.instructions, config: config.overrides });
|
|
328
|
+
if (!admitsRestrictedCodexThread(thread, request.model, cwd))
|
|
329
|
+
throw new RestrictedCodexStop("codex_unsafe_configuration");
|
|
330
|
+
threadId = String(codexRecord(thread.thread).id);
|
|
331
|
+
const mcp = await transport.rpc("mcpServerStatus/list", { limit: 100 });
|
|
332
|
+
if (!Array.isArray(mcp.data) || mcp.data.length !== 0 || mcp.nextCursor !== null)
|
|
333
|
+
throw new RestrictedCodexStop("codex_unsafe_configuration");
|
|
334
|
+
if (readinessOnly) {
|
|
335
|
+
result = { status: "completed", output: null, usage: null, usageComplete: false, dispatched: false, errorCode: null };
|
|
336
|
+
}
|
|
337
|
+
else {
|
|
338
|
+
const input = [{ type: "text", text: request.evidence, text_elements: [] }];
|
|
339
|
+
for (const [index, image] of request.images.entries()) {
|
|
340
|
+
deadline.check();
|
|
341
|
+
const match = CODEX_IMAGE.exec(image.dataUrl);
|
|
342
|
+
const imagePath = path.join(scratch, `evidence-${index}.${match[1] === "jpeg" ? "jpg" : match[1]}`);
|
|
343
|
+
await writeFile(imagePath, Buffer.from(match[2], "base64"), { mode: 0o600, flag: "wx" });
|
|
344
|
+
input.push({ type: "text", text: JSON.stringify({ captureEvidenceId: image.evidenceId }), text_elements: [] }, { type: "localImage", path: imagePath });
|
|
345
|
+
}
|
|
346
|
+
deadline.check();
|
|
347
|
+
// Even a lost acknowledgment may have dispatched the request. Never claim zero cost.
|
|
348
|
+
dispatched = true;
|
|
349
|
+
const turn = await transport.rpc("turn/start", { threadId, cwd, approvalPolicy: "never", sandboxPolicy: { type: "readOnly" },
|
|
350
|
+
environments: [], runtimeWorkspaceRoots: [], effort: "low", model: request.model, outputSchema: request.schema, input });
|
|
351
|
+
const returnedTurnId = codexRecord(turn.turn).id;
|
|
352
|
+
if (typeof returnedTurnId !== "string" || returnedTurnId.length === 0 || returnedTurnId.length > 200
|
|
353
|
+
|| (earlyTurnId !== undefined && earlyTurnId !== returnedTurnId))
|
|
354
|
+
throw new RestrictedCodexStop("codex_protocol_error");
|
|
355
|
+
turnId = returnedTurnId;
|
|
356
|
+
for (const event of early)
|
|
357
|
+
handleTurnEvent(event.method, event.params);
|
|
358
|
+
early.length = 0;
|
|
359
|
+
result = await deadline.wait(finished);
|
|
360
|
+
deadline.check();
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
catch (error) {
|
|
364
|
+
const code = error instanceof RestrictedCodexStop ? error.code : "codex_process_failed";
|
|
365
|
+
result = restrictedCodexFailure(code === "codex_cleanup_failed" ? code : deadline.code ?? code, dispatched, usage);
|
|
366
|
+
}
|
|
367
|
+
finally {
|
|
368
|
+
// Deadline remains authoritative until acceptance; teardown has its own small grace.
|
|
369
|
+
deadline.close();
|
|
370
|
+
let cleaned = result.errorCode !== "codex_cleanup_failed";
|
|
371
|
+
if (transport) {
|
|
372
|
+
const cleanupTurn = turnId ?? earlyTurnId;
|
|
373
|
+
cleaned = await transport.close(dispatched && !completed && threadId && cleanupTurn
|
|
374
|
+
? { threadId, turnId: cleanupTurn } : undefined).catch(() => false);
|
|
375
|
+
if (!cleaned)
|
|
376
|
+
retainUnclosedChild(transport.owned.closed);
|
|
377
|
+
}
|
|
378
|
+
let authReplaced = false;
|
|
379
|
+
if (authLink && cleaned) {
|
|
380
|
+
try {
|
|
381
|
+
const info = await lstat(authLink);
|
|
382
|
+
authReplaced = !info.isSymbolicLink();
|
|
383
|
+
if (!authReplaced)
|
|
384
|
+
await unlink(authLink);
|
|
385
|
+
}
|
|
386
|
+
catch (error) {
|
|
387
|
+
if (codexRecord(error).code !== "ENOENT")
|
|
388
|
+
cleaned = false;
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
// An unexpected replacement may contain rotated login state. Do not discard it
|
|
392
|
+
// or overwrite the host login. Leave this private control-plane directory for
|
|
393
|
+
// recovery and fail closed; no path or credential is included in the result.
|
|
394
|
+
if (authReplaced) {
|
|
395
|
+
cleaned = false;
|
|
396
|
+
if (work)
|
|
397
|
+
await preserveUnexpectedAuth(work, sourceEnv).catch(() => undefined);
|
|
398
|
+
}
|
|
399
|
+
if (work && cleaned && !authReplaced)
|
|
400
|
+
await rm(work, { recursive: true, force: true }).catch(() => { cleaned = false; });
|
|
401
|
+
if (work && !cleaned && !authReplaced)
|
|
402
|
+
await writeRecoveryMarker(work, sourceEnv, "process_cleanup_unconfirmed").catch(() => undefined);
|
|
403
|
+
if (!cleaned)
|
|
404
|
+
result = restrictedCodexFailure("codex_cleanup_failed", dispatched, usage);
|
|
405
|
+
}
|
|
406
|
+
return result;
|
|
407
|
+
}
|
|
408
|
+
export async function checkRestrictedCodexSessionReadiness(input = {}, options = {}) {
|
|
409
|
+
return runRestrictedCodexSession({ model: RESTRICTED_CODEX_ANALYSIS_MODELS[0], instructions: "Analyze only supplied study evidence.",
|
|
410
|
+
evidence: "", images: [], schema: { type: "object", additionalProperties: false, properties: {} }, maxOutputTokens: null,
|
|
411
|
+
timeoutMs: input.timeoutMs ?? 15_000, ...(input.signal === undefined ? {} : { signal: input.signal }) }, options, true);
|
|
412
|
+
}
|
|
413
|
+
//# sourceMappingURL=restricted-codex-session.js.map
|