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.
Files changed (193) hide show
  1. package/AGENTS.md +86 -79
  2. package/CONTRIBUTING.md +7 -2
  3. package/README.md +11 -2
  4. package/dist/actor-contract.d.ts +35 -1
  5. package/dist/actor-contract.js +38 -0
  6. package/dist/actor-contract.js.map +1 -1
  7. package/dist/adapter-extension.js +1 -0
  8. package/dist/adapter-extension.js.map +1 -1
  9. package/dist/automatic-analysis-config.d.ts +15 -5
  10. package/dist/automatic-analysis-config.js +25 -4
  11. package/dist/automatic-analysis-config.js.map +1 -1
  12. package/dist/automatic-study-analysis.js +4 -2
  13. package/dist/automatic-study-analysis.js.map +1 -1
  14. package/dist/browser-control-client.d.ts +14 -0
  15. package/dist/browser-control-client.js +134 -0
  16. package/dist/browser-control-client.js.map +1 -0
  17. package/dist/browser-control-dispatcher.d.ts +14 -0
  18. package/dist/browser-control-dispatcher.js +109 -0
  19. package/dist/browser-control-dispatcher.js.map +1 -0
  20. package/dist/browser-control-protocol.d.ts +371 -0
  21. package/dist/browser-control-protocol.js +155 -0
  22. package/dist/browser-control-protocol.js.map +1 -0
  23. package/dist/browser-control-transport.d.ts +24 -0
  24. package/dist/browser-control-transport.js +156 -0
  25. package/dist/browser-control-transport.js.map +1 -0
  26. package/dist/comms-lease-store.d.ts +1 -0
  27. package/dist/comms-lease-store.js +9 -3
  28. package/dist/comms-lease-store.js.map +1 -1
  29. package/dist/computer-use-actor.d.ts +2 -2
  30. package/dist/computer-use-actor.js +6 -1
  31. package/dist/computer-use-actor.js.map +1 -1
  32. package/dist/computer-use.d.ts +23 -1
  33. package/dist/computer-use.js +253 -70
  34. package/dist/computer-use.js.map +1 -1
  35. package/dist/cua-actor-lab.d.ts +24 -289
  36. package/dist/cua-actor-lab.js +203 -1983
  37. package/dist/cua-actor-lab.js.map +1 -1
  38. package/dist/cua-desktop-lane.d.ts +35 -0
  39. package/dist/cua-desktop-lane.js +13 -0
  40. package/dist/cua-desktop-lane.js.map +1 -0
  41. package/dist/cua-executor-error.d.ts +31 -0
  42. package/dist/cua-executor-error.js +48 -0
  43. package/dist/cua-executor-error.js.map +1 -0
  44. package/dist/cua-provider-error.d.ts +12 -0
  45. package/dist/cua-provider-error.js +28 -0
  46. package/dist/cua-provider-error.js.map +1 -0
  47. package/dist/desktop-session.d.ts +41 -0
  48. package/dist/desktop-session.js +46 -0
  49. package/dist/desktop-session.js.map +1 -0
  50. package/dist/doctor-lab.d.ts +8 -1
  51. package/dist/doctor-lab.js +40 -8
  52. package/dist/doctor-lab.js.map +1 -1
  53. package/dist/e2b-cua-desktop.d.ts +3 -0
  54. package/dist/e2b-cua-desktop.js +675 -0
  55. package/dist/e2b-cua-desktop.js.map +1 -0
  56. package/dist/e2b-cua-provisioning.d.ts +311 -0
  57. package/dist/e2b-cua-provisioning.js +1213 -0
  58. package/dist/e2b-cua-provisioning.js.map +1 -0
  59. package/dist/e2b-desktop-executor.d.ts +1 -24
  60. package/dist/e2b-desktop-executor.js +2 -127
  61. package/dist/e2b-desktop-executor.js.map +1 -1
  62. package/dist/e2b-desktop-session.d.ts +7 -0
  63. package/dist/e2b-desktop-session.js +29 -0
  64. package/dist/e2b-desktop-session.js.map +1 -0
  65. package/dist/e2b-terminal-lab.js +1 -0
  66. package/dist/e2b-terminal-lab.js.map +1 -1
  67. package/dist/frame-signature.d.ts +24 -0
  68. package/dist/frame-signature.js +128 -0
  69. package/dist/frame-signature.js.map +1 -0
  70. package/dist/guest-bootstrap.d.ts +43 -0
  71. package/dist/guest-bootstrap.js +240 -0
  72. package/dist/guest-bootstrap.js.map +1 -0
  73. package/dist/guest-browser-tools.d.ts +8 -0
  74. package/dist/guest-browser-tools.js +66 -0
  75. package/dist/guest-browser-tools.js.map +1 -0
  76. package/dist/guest-chromium-text.d.ts +27 -0
  77. package/dist/guest-chromium-text.js +281 -0
  78. package/dist/guest-chromium-text.js.map +1 -0
  79. package/dist/guest-desktop-executor.d.ts +22 -0
  80. package/dist/guest-desktop-executor.js +177 -0
  81. package/dist/guest-desktop-executor.js.map +1 -0
  82. package/dist/guest-desktop-native.d.ts +14 -0
  83. package/dist/guest-desktop-native.js +131 -0
  84. package/dist/guest-desktop-native.js.map +1 -0
  85. package/dist/guest-runtime-desktop.d.ts +35 -0
  86. package/dist/guest-runtime-desktop.js +231 -0
  87. package/dist/guest-runtime-desktop.js.map +1 -0
  88. package/dist/guest-runtime-main.d.ts +1 -0
  89. package/dist/guest-runtime-main.js +31 -0
  90. package/dist/guest-runtime-main.js.map +1 -0
  91. package/dist/guest-runtime-revision.d.ts +1 -0
  92. package/dist/guest-runtime-revision.js +3 -0
  93. package/dist/guest-runtime-revision.js.map +1 -0
  94. package/dist/guest-runtime.d.ts +25 -0
  95. package/dist/guest-runtime.js +96 -0
  96. package/dist/guest-runtime.js.map +1 -0
  97. package/dist/index.d.ts +1 -1
  98. package/dist/lab-config.js +10 -3
  99. package/dist/lab-config.js.map +1 -1
  100. package/dist/lab-engine.js +6 -0
  101. package/dist/lab-engine.js.map +1 -1
  102. package/dist/lab-summary.d.ts +5 -1
  103. package/dist/lab-summary.js +5 -0
  104. package/dist/lab-summary.js.map +1 -1
  105. package/dist/local-agent-cli.js +1 -1
  106. package/dist/local-agent-cli.js.map +1 -1
  107. package/dist/local-firecracker-desktop.d.ts +13 -0
  108. package/dist/local-firecracker-desktop.js +150 -0
  109. package/dist/local-firecracker-desktop.js.map +1 -0
  110. package/dist/local-firecracker-study.d.ts +9 -0
  111. package/dist/local-firecracker-study.js +93 -0
  112. package/dist/local-firecracker-study.js.map +1 -0
  113. package/dist/local-runtime-config.d.ts +6 -0
  114. package/dist/local-runtime-config.js +56 -0
  115. package/dist/local-runtime-config.js.map +1 -0
  116. package/dist/local-runtime-release.d.ts +3 -0
  117. package/dist/local-runtime-release.js +8 -0
  118. package/dist/local-runtime-release.js.map +1 -0
  119. package/dist/local-runtime.d.ts +25 -0
  120. package/dist/local-runtime.js +113 -0
  121. package/dist/local-runtime.js.map +1 -0
  122. package/dist/observer-app.html +4 -4
  123. package/dist/pricing.d.ts +22 -1
  124. package/dist/pricing.js +22 -0
  125. package/dist/pricing.js.map +1 -1
  126. package/dist/program.js +50 -9
  127. package/dist/program.js.map +1 -1
  128. package/dist/restricted-codex-analysis.d.ts +15 -0
  129. package/dist/restricted-codex-analysis.js +13 -0
  130. package/dist/restricted-codex-analysis.js.map +1 -0
  131. package/dist/restricted-codex-participant-policy.d.ts +39 -0
  132. package/dist/restricted-codex-participant-policy.js +69 -0
  133. package/dist/restricted-codex-participant-policy.js.map +1 -0
  134. package/dist/restricted-codex-participant-run.d.ts +20 -0
  135. package/dist/restricted-codex-participant-run.js +78 -0
  136. package/dist/restricted-codex-participant-run.js.map +1 -0
  137. package/dist/restricted-codex-participant.d.ts +14 -0
  138. package/dist/restricted-codex-participant.js +178 -0
  139. package/dist/restricted-codex-participant.js.map +1 -0
  140. package/dist/restricted-codex-policy.d.ts +56 -0
  141. package/dist/restricted-codex-policy.js +151 -0
  142. package/dist/restricted-codex-policy.js.map +1 -0
  143. package/dist/restricted-codex-session.d.ts +19 -0
  144. package/dist/restricted-codex-session.js +413 -0
  145. package/dist/restricted-codex-session.js.map +1 -0
  146. package/dist/restricted-codex-transport.d.ts +58 -0
  147. package/dist/restricted-codex-transport.js +233 -0
  148. package/dist/restricted-codex-transport.js.map +1 -0
  149. package/dist/run-detail.js +4 -2
  150. package/dist/run-detail.js.map +1 -1
  151. package/dist/run.d.ts +12 -5
  152. package/dist/run.js +17 -1
  153. package/dist/run.js.map +1 -1
  154. package/dist/shared-world-lab.js +2 -2
  155. package/dist/shared-world-lab.js.map +1 -1
  156. package/dist/study-analysis-codex-config.d.ts +11 -0
  157. package/dist/study-analysis-codex-config.js +34 -0
  158. package/dist/study-analysis-codex-config.js.map +1 -0
  159. package/dist/study-analysis-engine.d.ts +6 -3
  160. package/dist/study-analysis-engine.js +19 -10
  161. package/dist/study-analysis-engine.js.map +1 -1
  162. package/dist/study-analysis-job.d.ts +3 -2
  163. package/dist/study-analysis-job.js +1 -1
  164. package/dist/study-analysis-job.js.map +1 -1
  165. package/dist/study-analysis-provider.d.ts +4 -2
  166. package/dist/study-analysis-provider.js +1 -1
  167. package/dist/study-analysis-provider.js.map +1 -1
  168. package/dist/study-analysis-service.d.ts +3 -0
  169. package/dist/study-analysis-service.js +24 -6
  170. package/dist/study-analysis-service.js.map +1 -1
  171. package/dist/study-analysis-validation.d.ts +43 -19
  172. package/dist/study-analysis-validation.js +23 -10
  173. package/dist/study-analysis-validation.js.map +1 -1
  174. package/dist/study-analysis.d.ts +27 -2
  175. package/dist/study-costs.js +6 -0
  176. package/dist/study-costs.js.map +1 -1
  177. package/dist/tui-app.js +102 -102
  178. package/docs/architecture/browser-control.md +117 -0
  179. package/docs/architecture/desktop-sessions.md +80 -0
  180. package/docs/architecture/guest-desktop.md +87 -0
  181. package/docs/architecture/local-browser-runtime.md +100 -0
  182. package/docs/architecture/restricted-codex-analysis.md +91 -0
  183. package/docs/architecture/runtime-broker-core.md +30 -0
  184. package/docs/contracts/schemas.md +1 -1
  185. package/docs/contracts/study-analysis.md +44 -2
  186. package/docs/goals/current.md +25 -9
  187. package/docs/product/automatic-analysis.md +24 -3
  188. package/docs/product/open-source-install-experience.md +7 -0
  189. package/docs/ramp/README.md +30 -13
  190. package/docs/release/0.97.0-codex-account-analysis.md +45 -0
  191. package/docs/release/0.98.0-local-browser-studies.md +31 -0
  192. package/package.json +4 -2
  193. 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