humanish 0.0.1 → 0.15.1

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 (205) hide show
  1. package/AGENTS.md +77 -0
  2. package/CONTRIBUTING.md +39 -0
  3. package/LICENSE +21 -0
  4. package/README.md +437 -8
  5. package/SECURITY.md +34 -0
  6. package/dist/actor-contract.d.ts +107 -0
  7. package/dist/actor-contract.js +286 -0
  8. package/dist/actor-contract.js.map +1 -0
  9. package/dist/actor-registry.d.ts +73 -0
  10. package/dist/actor-registry.js +93 -0
  11. package/dist/actor-registry.js.map +1 -0
  12. package/dist/adapter-extension.d.ts +54 -0
  13. package/dist/adapter-extension.js +214 -0
  14. package/dist/adapter-extension.js.map +1 -0
  15. package/dist/argv.d.ts +1 -0
  16. package/dist/argv.js +8 -0
  17. package/dist/argv.js.map +1 -0
  18. package/dist/artifact-reference.d.ts +22 -0
  19. package/dist/artifact-reference.js +47 -0
  20. package/dist/artifact-reference.js.map +1 -0
  21. package/dist/browser-evidence-hygiene.d.ts +7 -0
  22. package/dist/browser-evidence-hygiene.js +40 -0
  23. package/dist/browser-evidence-hygiene.js.map +1 -0
  24. package/dist/claude-agent-sdk.d.ts +94 -0
  25. package/dist/claude-agent-sdk.js +363 -0
  26. package/dist/claude-agent-sdk.js.map +1 -0
  27. package/dist/cli.d.ts +2 -0
  28. package/dist/cli.js +5 -0
  29. package/dist/cli.js.map +1 -0
  30. package/dist/codex-app-server-ui.d.ts +39 -0
  31. package/dist/codex-app-server-ui.js +357 -0
  32. package/dist/codex-app-server-ui.js.map +1 -0
  33. package/dist/codex-app-server.d.ts +144 -0
  34. package/dist/codex-app-server.js +754 -0
  35. package/dist/codex-app-server.js.map +1 -0
  36. package/dist/command-failure.d.ts +29 -0
  37. package/dist/command-failure.js +54 -0
  38. package/dist/command-failure.js.map +1 -0
  39. package/dist/computer-use-actor.d.ts +52 -0
  40. package/dist/computer-use-actor.js +48 -0
  41. package/dist/computer-use-actor.js.map +1 -0
  42. package/dist/computer-use.d.ts +232 -0
  43. package/dist/computer-use.js +615 -0
  44. package/dist/computer-use.js.map +1 -0
  45. package/dist/concurrency.d.ts +1 -0
  46. package/dist/concurrency.js +27 -0
  47. package/dist/concurrency.js.map +1 -0
  48. package/dist/concurrent-shared-world-lab.d.ts +115 -0
  49. package/dist/concurrent-shared-world-lab.js +1094 -0
  50. package/dist/concurrent-shared-world-lab.js.map +1 -0
  51. package/dist/core/git-state.d.ts +33 -0
  52. package/dist/core/git-state.js +277 -0
  53. package/dist/core/git-state.js.map +1 -0
  54. package/dist/core/git-workspace.d.ts +31 -0
  55. package/dist/core/git-workspace.js +447 -0
  56. package/dist/core/git-workspace.js.map +1 -0
  57. package/dist/core/index.d.ts +4 -0
  58. package/dist/core/index.js +3 -0
  59. package/dist/core/index.js.map +1 -0
  60. package/dist/core/run-primitives.d.ts +66 -0
  61. package/dist/core/run-primitives.js +120 -0
  62. package/dist/core/run-primitives.js.map +1 -0
  63. package/dist/cua-actor-lab.d.ts +621 -0
  64. package/dist/cua-actor-lab.js +2845 -0
  65. package/dist/cua-actor-lab.js.map +1 -0
  66. package/dist/device-presets.d.ts +67 -0
  67. package/dist/device-presets.js +50 -0
  68. package/dist/device-presets.js.map +1 -0
  69. package/dist/e2b-desktop-executor.d.ts +101 -0
  70. package/dist/e2b-desktop-executor.js +309 -0
  71. package/dist/e2b-desktop-executor.js.map +1 -0
  72. package/dist/e2b-desktop-launch.d.ts +144 -0
  73. package/dist/e2b-desktop-launch.js +59 -0
  74. package/dist/e2b-desktop-launch.js.map +1 -0
  75. package/dist/e2b-detached.d.ts +53 -0
  76. package/dist/e2b-detached.js +130 -0
  77. package/dist/e2b-detached.js.map +1 -0
  78. package/dist/e2b-terminal-lab.d.ts +345 -0
  79. package/dist/e2b-terminal-lab.js +1532 -0
  80. package/dist/e2b-terminal-lab.js.map +1 -0
  81. package/dist/env-file.d.ts +14 -0
  82. package/dist/env-file.js +108 -0
  83. package/dist/env-file.js.map +1 -0
  84. package/dist/feedback.d.ts +51 -0
  85. package/dist/feedback.js +351 -0
  86. package/dist/feedback.js.map +1 -0
  87. package/dist/image-evidence.d.ts +2 -0
  88. package/dist/image-evidence.js +33 -0
  89. package/dist/image-evidence.js.map +1 -0
  90. package/dist/index.d.ts +63 -0
  91. package/dist/index.js +33 -0
  92. package/dist/index.js.map +1 -0
  93. package/dist/init-templates.d.ts +12 -0
  94. package/dist/init-templates.js +369 -0
  95. package/dist/init-templates.js.map +1 -0
  96. package/dist/init.d.ts +26 -0
  97. package/dist/init.js +429 -0
  98. package/dist/init.js.map +1 -0
  99. package/dist/lab-app-runner.d.ts +78 -0
  100. package/dist/lab-app-runner.js +403 -0
  101. package/dist/lab-app-runner.js.map +1 -0
  102. package/dist/lab-config.d.ts +583 -0
  103. package/dist/lab-config.js +1789 -0
  104. package/dist/lab-config.js.map +1 -0
  105. package/dist/lab-engine.d.ts +77 -0
  106. package/dist/lab-engine.js +216 -0
  107. package/dist/lab-engine.js.map +1 -0
  108. package/dist/lab-preflight.d.ts +67 -0
  109. package/dist/lab-preflight.js +385 -0
  110. package/dist/lab-preflight.js.map +1 -0
  111. package/dist/labs.d.ts +53 -0
  112. package/dist/labs.js +382 -0
  113. package/dist/labs.js.map +1 -0
  114. package/dist/observer-assets.d.ts +2 -0
  115. package/dist/observer-assets.js +2630 -0
  116. package/dist/observer-assets.js.map +1 -0
  117. package/dist/observer-data.d.ts +63 -0
  118. package/dist/observer-data.js +145 -0
  119. package/dist/observer-data.js.map +1 -0
  120. package/dist/observer-static.d.ts +39 -0
  121. package/dist/observer-static.js +306 -0
  122. package/dist/observer-static.js.map +1 -0
  123. package/dist/observer.d.ts +46 -0
  124. package/dist/observer.js +550 -0
  125. package/dist/observer.js.map +1 -0
  126. package/dist/openai-responses-cu.d.ts +113 -0
  127. package/dist/openai-responses-cu.js +545 -0
  128. package/dist/openai-responses-cu.js.map +1 -0
  129. package/dist/oss-lab.d.ts +52 -0
  130. package/dist/oss-lab.js +344 -0
  131. package/dist/oss-lab.js.map +1 -0
  132. package/dist/oss-meta-lab-scoring.d.ts +15 -0
  133. package/dist/oss-meta-lab-scoring.js +145 -0
  134. package/dist/oss-meta-lab-scoring.js.map +1 -0
  135. package/dist/oss-meta-lab.d.ts +277 -0
  136. package/dist/oss-meta-lab.js +4529 -0
  137. package/dist/oss-meta-lab.js.map +1 -0
  138. package/dist/oss-remote-telemetry.d.ts +77 -0
  139. package/dist/oss-remote-telemetry.js +403 -0
  140. package/dist/oss-remote-telemetry.js.map +1 -0
  141. package/dist/persona.d.ts +35 -0
  142. package/dist/persona.js +133 -0
  143. package/dist/persona.js.map +1 -0
  144. package/dist/pi-agent-core.d.ts +77 -0
  145. package/dist/pi-agent-core.js +203 -0
  146. package/dist/pi-agent-core.js.map +1 -0
  147. package/dist/program.d.ts +54 -0
  148. package/dist/program.js +2349 -0
  149. package/dist/program.js.map +1 -0
  150. package/dist/redaction.d.ts +90 -0
  151. package/dist/redaction.js +292 -0
  152. package/dist/redaction.js.map +1 -0
  153. package/dist/run-paths.d.ts +56 -0
  154. package/dist/run-paths.js +340 -0
  155. package/dist/run-paths.js.map +1 -0
  156. package/dist/run.d.ts +794 -0
  157. package/dist/run.js +4844 -0
  158. package/dist/run.js.map +1 -0
  159. package/dist/scripted-browser-actor.d.ts +297 -0
  160. package/dist/scripted-browser-actor.js +1399 -0
  161. package/dist/scripted-browser-actor.js.map +1 -0
  162. package/dist/scripted-browser-lab.d.ts +107 -0
  163. package/dist/scripted-browser-lab.js +788 -0
  164. package/dist/scripted-browser-lab.js.map +1 -0
  165. package/dist/selected-output-paths.d.ts +49 -0
  166. package/dist/selected-output-paths.js +354 -0
  167. package/dist/selected-output-paths.js.map +1 -0
  168. package/dist/shared-world-lab.d.ts +208 -0
  169. package/dist/shared-world-lab.js +1049 -0
  170. package/dist/shared-world-lab.js.map +1 -0
  171. package/dist/source-archive.d.ts +82 -0
  172. package/dist/source-archive.js +408 -0
  173. package/dist/source-archive.js.map +1 -0
  174. package/dist/stop-conditions.d.ts +26 -0
  175. package/dist/stop-conditions.js +66 -0
  176. package/dist/stop-conditions.js.map +1 -0
  177. package/dist/terminal-agent-actor.d.ts +36 -0
  178. package/dist/terminal-agent-actor.js +23 -0
  179. package/dist/terminal-agent-actor.js.map +1 -0
  180. package/docs/architecture/actor-contract.md +431 -0
  181. package/docs/architecture/github-feedback-loop.md +189 -0
  182. package/docs/architecture/local-codex-tui-actor.md +213 -0
  183. package/docs/architecture/observer.md +118 -0
  184. package/docs/architecture/oss-lab-poc.md +250 -0
  185. package/docs/architecture/project-layout.md +166 -0
  186. package/docs/architecture/state-driven-executor.md +203 -0
  187. package/docs/architecture/terminal-product-lane.md +148 -0
  188. package/docs/contracts/adapter-fixtures.md +80 -0
  189. package/docs/contracts/core.md +71 -0
  190. package/docs/contracts/feedback.md +145 -0
  191. package/docs/contracts/policy.md +311 -0
  192. package/docs/contracts/run-bundle.md +358 -0
  193. package/docs/contracts/schemas.md +984 -0
  194. package/docs/goals/current.md +428 -0
  195. package/docs/principles/invariants-and-defaults.md +135 -0
  196. package/docs/principles/self-driving-harness.md +129 -0
  197. package/docs/product/open-source-install-experience.md +237 -0
  198. package/docs/ramp/README.md +207 -0
  199. package/docs/release/open-source-readiness.md +208 -0
  200. package/docs/release/public-readiness-standard.md +205 -0
  201. package/docs/roadmap/world-class-open-source-v0.md +311 -0
  202. package/package.json +102 -8
  203. package/skills/humanish/SKILL.md +244 -0
  204. package/skills/humanish/agents/openai.yaml +7 -0
  205. package/index.js +0 -1
@@ -0,0 +1,4529 @@
1
+ import { execFile } from "node:child_process";
2
+ import { randomBytes } from "node:crypto";
3
+ import { lstat, mkdir, readFile, readdir, realpath, rm, stat, writeFile } from "node:fs/promises";
4
+ import path from "node:path";
5
+ import { runDesktopCommandOrThrow } from "./command-failure.js";
6
+ import { promisify } from "node:util";
7
+ import { fileURLToPath } from "node:url";
8
+ import { attachObserverRuntimeStreamUrls, renderObserver } from "./observer.js";
9
+ import { loadE2BDesktopModule } from "./e2b-desktop-launch.js";
10
+ import { DEFAULT_OSS_REPOS, normalizeOssRepoSlugs, repoSlug, validateOssRepoSlug } from "./oss-lab.js";
11
+ import { redactOssRemoteTelemetryText } from "./oss-remote-telemetry.js";
12
+ import { bindExistingRunArtifactPaths, prepareExclusiveHumanishStorageDirectory, prepareReusableHumanishStorageDirectory, validatePreparedRunArtifactPaths } from "./run-paths.js";
13
+ import { writeContainedOutputFile } from "./selected-output-paths.js";
14
+ import { buildRunSource, PUBLIC_TARGET_CWD, REVIEW_SCHEMA, RUN_BUNDLE_SCHEMA, runDryRun } from "./run.js";
15
+ import { scoreOssMetaMeaningfulUse } from "./oss-meta-lab-scoring.js";
16
+ export const OSS_META_LAB_SCHEMA = "humanish.oss-meta-lab-result.v1";
17
+ const execFileAsync = promisify(execFile);
18
+ const moduleRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
19
+ const liveRuntimeByResult = new WeakMap();
20
+ const OSS_META_LAB_PROVIDER_METADATA = {
21
+ mode: "oss-meta-lab",
22
+ tool: "humanish"
23
+ };
24
+ const OSS_META_LAB_REMOTE_ENV_NAMES = [
25
+ "HUMANISH_OSS_META_ACTOR_FIRST",
26
+ "HUMANISH_OSS_META_ACTOR_MODEL",
27
+ "HUMANISH_OSS_META_HOST_CODEX_ACTOR",
28
+ "HUMANISH_OSS_META_CODEX_APP_SERVER",
29
+ "HUMANISH_OSS_META_ACTOR_TIMEOUT_MS",
30
+ "HUMANISH_OSS_META_REQUIRE_ACTOR"
31
+ ];
32
+ const OSS_META_LAB_ACTOR_AUTH_PLACEHOLDER = "CODEX_API_KEY or CODEX_ACCESS_TOKEN";
33
+ const OSS_META_LAB_ACTOR_PREFLIGHT_PLACEHOLDER = "Codex actor API quota/auth preflight";
34
+ const OSS_META_LAB_HOST_ACTOR_PLACEHOLDER = "host Codex actor plan";
35
+ export function buildOssRepoAssignments(repos, count) {
36
+ return Array.from({ length: count }, (_, index) => {
37
+ const repo = repos[index % repos.length];
38
+ if (!repo) {
39
+ throw new Error("At least one OSS repo is required.");
40
+ }
41
+ return {
42
+ index: index + 1,
43
+ repo,
44
+ scenarioId: `oss-meta-${repoSlug(repo)}`,
45
+ simId: `oss-${String(index + 1).padStart(2, "0")}`,
46
+ streamId: `oss-${String(index + 1).padStart(2, "0")}-desktop`
47
+ };
48
+ });
49
+ }
50
+ export function collectOssMetaLabRemoteEnv(env) {
51
+ const result = {};
52
+ for (const name of OSS_META_LAB_REMOTE_ENV_NAMES) {
53
+ const value = env[name]?.trim();
54
+ if (value) {
55
+ result[name] = value;
56
+ }
57
+ }
58
+ return result;
59
+ }
60
+ function githubTokenFromEnv(env) {
61
+ return env.GH_TOKEN?.trim() || env.GITHUB_TOKEN?.trim() || env.GITHUB_PAT?.trim() || "";
62
+ }
63
+ async function createGitHubAskPassEnv(root, env) {
64
+ await mkdir(root, { recursive: true });
65
+ const askPassPath = path.join(root, `git-askpass-${randomBytes(4).toString("hex")}.sh`);
66
+ await writeFile(askPassPath, [
67
+ "#!/usr/bin/env bash",
68
+ "case \"$1\" in",
69
+ " *Username*) echo \"x-access-token\" ;;",
70
+ " *Password*) echo \"${HUMANISH_GITHUB_TOKEN_RUNTIME:-}\" ;;",
71
+ " *) echo \"\" ;;",
72
+ "esac",
73
+ ""
74
+ ].join("\n"), { encoding: "utf8", mode: 0o700 });
75
+ const token = githubTokenFromEnv(env);
76
+ return {
77
+ ...gitCredentialIsolatedEnv(env),
78
+ GIT_ASKPASS: askPassPath,
79
+ GIT_TERMINAL_PROMPT: "0",
80
+ ...(token ? { HUMANISH_GITHUB_TOKEN_RUNTIME: token } : {})
81
+ };
82
+ }
83
+ function gitEnvWithoutGitHubToken(env) {
84
+ return {
85
+ ...gitCredentialIsolatedEnv(env),
86
+ GIT_ASKPASS: "false",
87
+ SSH_ASKPASS: "false",
88
+ GIT_TERMINAL_PROMPT: "0"
89
+ };
90
+ }
91
+ function gitCredentialIsolatedEnv(env) {
92
+ const isolated = { ...env };
93
+ for (const key of Object.keys(isolated)) {
94
+ if (key.startsWith("GIT_CONFIG_")) {
95
+ delete isolated[key];
96
+ }
97
+ }
98
+ delete isolated.GH_TOKEN;
99
+ delete isolated.GITHUB_PAT;
100
+ delete isolated.GITHUB_TOKEN;
101
+ delete isolated.GIT_ASKPASS;
102
+ delete isolated.HUMANISH_GITHUB_TOKEN_RUNTIME;
103
+ delete isolated.SSH_ASKPASS;
104
+ return {
105
+ ...isolated,
106
+ GIT_CONFIG_GLOBAL: "/dev/null",
107
+ GIT_CONFIG_NOSYSTEM: "1",
108
+ GIT_TERMINAL_PROMPT: "0"
109
+ };
110
+ }
111
+ async function runGitRepoAccessProbe(execImpl, cwd, repoUrl, env, sourceEnv) {
112
+ await execImpl("git", ["-c", "credential.helper=", "ls-remote", "--exit-code", repoUrl, "HEAD"], {
113
+ cwd,
114
+ env,
115
+ maxBuffer: 256 * 1024,
116
+ timeout: readPositiveInt(sourceEnv.HUMANISH_OSS_META_REPO_PREFLIGHT_TIMEOUT_MS, 45_000)
117
+ });
118
+ }
119
+ export async function preflightOssMetaRepoAccess(args) {
120
+ const execImpl = args.execFileImpl ?? execFileAsync;
121
+ const tokenPresent = Boolean(githubTokenFromEnv(args.env));
122
+ const rootId = `repo-access-${randomBytes(4).toString("hex")}`;
123
+ const preparedRoot = await prepareExclusiveHumanishStorageDirectory(args.cwd, "tmp", rootId);
124
+ const root = await pinCleanupDirectory(preparedRoot);
125
+ try {
126
+ await mkdir(root.physicalPath, { recursive: true });
127
+ const tokenGitEnv = tokenPresent ? await createGitHubAskPassEnv(root.physicalPath, args.env) : undefined;
128
+ const anonymousGitEnv = gitEnvWithoutGitHubToken(args.env);
129
+ const results = [];
130
+ for (const assignment of args.assignments) {
131
+ const repoUrl = `https://github.com/${assignment.repo}.git`;
132
+ let anonymousError;
133
+ try {
134
+ await runGitRepoAccessProbe(execImpl, root.physicalPath, repoUrl, anonymousGitEnv, args.env);
135
+ results.push({
136
+ ok: true,
137
+ reason: tokenPresent
138
+ ? "GitHub repo clone access preflight passed with anonymous public clone access."
139
+ : "GitHub repo clone access preflight passed without token auth.",
140
+ repo: assignment.repo,
141
+ streamId: assignment.streamId,
142
+ tokenPresent
143
+ });
144
+ continue;
145
+ }
146
+ catch (error) {
147
+ anonymousError = error;
148
+ }
149
+ let tokenError;
150
+ if (tokenGitEnv) {
151
+ try {
152
+ await runGitRepoAccessProbe(execImpl, root.physicalPath, repoUrl, tokenGitEnv, args.env);
153
+ results.push({
154
+ ok: true,
155
+ reason: "GitHub repo clone access preflight passed with token auth after anonymous clone access failed.",
156
+ repo: assignment.repo,
157
+ streamId: assignment.streamId,
158
+ tokenPresent: true
159
+ });
160
+ continue;
161
+ }
162
+ catch (error) {
163
+ tokenError = error;
164
+ }
165
+ }
166
+ results.push({
167
+ ok: false,
168
+ reason: repoAccessFailureReason({
169
+ anonymousError,
170
+ redactRepoName: args.redactRepoNames === true,
171
+ repo: assignment.repo,
172
+ tokenError,
173
+ tokenPresent
174
+ }),
175
+ repo: assignment.repo,
176
+ streamId: assignment.streamId,
177
+ tokenPresent
178
+ });
179
+ }
180
+ return results;
181
+ }
182
+ finally {
183
+ if (await validatePinnedCleanupDirectory(root).catch(() => false)) {
184
+ await rm(root.physicalPath, { recursive: true, force: true }).catch(() => undefined);
185
+ }
186
+ }
187
+ }
188
+ async function pinCleanupDirectory(directoryInput) {
189
+ const physicalPath = await realpath(path.resolve(directoryInput));
190
+ const stats = await lstat(physicalPath, { bigint: true });
191
+ if (stats.isSymbolicLink() || !stats.isDirectory()) {
192
+ throw new Error("OSS meta-lab cleanup roots must be physical directories.");
193
+ }
194
+ return Object.freeze({ birthtimeNs: stats.birthtimeNs, dev: stats.dev, ino: stats.ino, physicalPath });
195
+ }
196
+ async function validatePinnedCleanupDirectory(directory) {
197
+ const stats = await lstat(directory.physicalPath, { bigint: true });
198
+ return !stats.isSymbolicLink()
199
+ && stats.isDirectory()
200
+ && stats.birthtimeNs === directory.birthtimeNs
201
+ && stats.dev === directory.dev
202
+ && stats.ino === directory.ino
203
+ && await realpath(directory.physicalPath) === directory.physicalPath;
204
+ }
205
+ export async function preflightOssMetaActorApiKey(args) {
206
+ const apiKey = args.env.CODEX_API_KEY?.trim() || args.env.OPENAI_API_KEY?.trim();
207
+ if (!apiKey) {
208
+ return {
209
+ ok: true,
210
+ reason: "No API-key actor auth present; preflight skipped."
211
+ };
212
+ }
213
+ const fetchImpl = args.fetchImpl ?? fetch;
214
+ const model = args.env.HUMANISH_OSS_META_ACTOR_PREFLIGHT_MODEL?.trim() || "gpt-4.1-mini";
215
+ try {
216
+ const response = await fetchImpl("https://api.openai.com/v1/responses", {
217
+ method: "POST",
218
+ headers: {
219
+ Authorization: `Bearer ${apiKey}`,
220
+ "Content-Type": "application/json"
221
+ },
222
+ body: JSON.stringify({
223
+ input: "Return a JSON object exactly like {\"status\":\"passed\"}. This is an actor auth preflight.",
224
+ max_output_tokens: 32,
225
+ model,
226
+ text: {
227
+ format: {
228
+ type: "json_object"
229
+ }
230
+ }
231
+ })
232
+ });
233
+ if (response.ok) {
234
+ return {
235
+ ok: true,
236
+ reason: `OpenAI actor API-key preflight passed for ${model}.`,
237
+ status: response.status
238
+ };
239
+ }
240
+ const body = await response.text().catch(() => "");
241
+ return {
242
+ ok: false,
243
+ reason: compactError(`OpenAI actor API-key preflight failed with HTTP ${response.status}: ${body}`),
244
+ status: response.status
245
+ };
246
+ }
247
+ catch (error) {
248
+ return {
249
+ ok: false,
250
+ reason: compactError(error)
251
+ };
252
+ }
253
+ }
254
+ function hostCodexActorRequested(env) {
255
+ return env.HUMANISH_OSS_META_HOST_CODEX_ACTOR === "1";
256
+ }
257
+ function codexAppServerModeRequested(env, explicit = false) {
258
+ return explicit || env.HUMANISH_OSS_META_CODEX_APP_SERVER === "1";
259
+ }
260
+ function remoteActorAuthRequested(env) {
261
+ return env.HUMANISH_OSS_META_ACTOR_FIRST === "1" || env.HUMANISH_OSS_META_REQUIRE_ACTOR === "1";
262
+ }
263
+ function actorRequired(env) {
264
+ return env.HUMANISH_OSS_META_REQUIRE_ACTOR === "1";
265
+ }
266
+ async function createHostActorPlans(_args) {
267
+ throw new Error("Host actor planning is disabled until live OSS meta-lab execution has an isolated credential boundary.");
268
+ }
269
+ function repoAccessFailureReason(args) {
270
+ const anonymous = sanitizeRepoAccessError(args.anonymousError, args.repo, args.redactRepoName);
271
+ const token = args.tokenError
272
+ ? ` Token auth also failed: ${sanitizeRepoAccessError(args.tokenError, args.repo, args.redactRepoName)}`
273
+ : "";
274
+ const authHint = args.tokenPresent
275
+ ? "A GitHub token was present, but `git ls-remote` could not read the repo with token or anonymous access. Check token repo access and scopes."
276
+ : "No GitHub token was present. Public repos should pass unauthenticated; private repos need GH_TOKEN, GITHUB_TOKEN, or GITHUB_PAT with read access.";
277
+ return `${authHint} Anonymous access failed: ${anonymous}${token}`.replace(/\s+/g, " ").trim().slice(0, 420);
278
+ }
279
+ function sanitizeRepoAccessError(error, repo, redactRepoName) {
280
+ let message = compactError(error)
281
+ .replace(/github_pat_[A-Za-z0-9_]{12,}/g, "[redacted-github-token]")
282
+ .replace(/\bHUMANISH_GITHUB_TOKEN_RUNTIME=[^\s]+/g, "HUMANISH_GITHUB_TOKEN_RUNTIME=[redacted-github-token]");
283
+ if (redactRepoName) {
284
+ message = message
285
+ .replaceAll(`https://github.com/${repo}.git`, "https://github.com/[redacted-authorized-repo].git")
286
+ .replaceAll(`github.com/${repo}.git`, "github.com/[redacted-authorized-repo].git")
287
+ .replaceAll(repo, "[redacted-authorized-repo]");
288
+ }
289
+ return message;
290
+ }
291
+ function blockedLiveDesktopsForRepoAccess(args) {
292
+ const checkedAt = new Date().toISOString();
293
+ const preflightByStream = new Map(args.preflight.map((result) => [result.streamId, result]));
294
+ const failedCount = args.preflight.filter((result) => !result.ok).length;
295
+ return args.assignments.map((assignment) => {
296
+ const repoLabel = args.redactRepoNames ? repoArtifactLabel(assignment) : assignment.repo;
297
+ const preflight = preflightByStream.get(assignment.streamId);
298
+ const reason = preflight?.ok
299
+ ? `GitHub repo clone access preflight passed for ${repoLabel}, but live launch was skipped because ${failedCount} assigned repo${failedCount === 1 ? "" : "s"} failed preflight.`
300
+ : preflight?.reason ?? `GitHub repo clone access preflight did not run for ${repoLabel}.`;
301
+ return {
302
+ completion: {
303
+ actorStatus: "blocked",
304
+ appReason: reason,
305
+ appStatus: "blocked",
306
+ checkedAt,
307
+ logTail: reason,
308
+ nestedObserverPresent: false,
309
+ nestedVerifyPassed: false,
310
+ reason,
311
+ status: "blocked",
312
+ visualReason: "No headed desktop was launched before repo clone access was proven.",
313
+ visualStatus: "not_started",
314
+ visualWindowCount: 0
315
+ },
316
+ repo: repoLabel,
317
+ simId: assignment.simId,
318
+ streamId: assignment.streamId
319
+ };
320
+ });
321
+ }
322
+ export function normalizeHostActorRecommendedProof(value) {
323
+ const proof = cleanHostActorText(value, "");
324
+ if (!/\bhumanish\s+run\b/.test(proof)) {
325
+ return "Start the target app on a loopback URL, then run `humanish run --app-url http://127.0.0.1:<port> --sims 2`.";
326
+ }
327
+ if (/\s--(?:browser|viewport|persona|scenario)\b/.test(proof)) {
328
+ return "Start the target app on a loopback URL, then run `humanish run --app-url http://127.0.0.1:<port> --sims 2`.";
329
+ }
330
+ return proof;
331
+ }
332
+ function cleanHostActorText(value, fallback) {
333
+ const text = String(typeof value === "string" ? value : fallback)
334
+ .replace(/sk-[A-Za-z0-9_-]{20,}/g, "[redacted-openai-key]")
335
+ .replace(/e2b_[A-Za-z0-9_-]{12,}/g, "[redacted-e2b-key]")
336
+ .replace(/gh[pousr]_[A-Za-z0-9_]{12,}/g, "[redacted-github-token]")
337
+ .replace(/github_pat_[A-Za-z0-9_]{12,}/g, "[redacted-github-token]")
338
+ .replace(/\s+/g, " ")
339
+ .trim()
340
+ .slice(0, 500);
341
+ return text || fallback;
342
+ }
343
+ export async function runOssMetaLab(options) {
344
+ const cwd = path.resolve(options.cwd);
345
+ const dryRun = options.dryRun === true;
346
+ const liveRequested = !dryRun;
347
+ const warnings = [];
348
+ const repos = normalizeOssRepoSlugs(options.repos);
349
+ const count = options.count ?? DEFAULT_OSS_REPOS.length;
350
+ if (!Number.isInteger(count) || count < 1) {
351
+ return {
352
+ schema: OSS_META_LAB_SCHEMA,
353
+ ok: false,
354
+ assignments: [],
355
+ cwd,
356
+ dryRun,
357
+ error: {
358
+ code: "HUMANISH_INVALID_OSS_COUNT",
359
+ message: "--count must be a positive integer."
360
+ },
361
+ liveRequested,
362
+ repos,
363
+ sandboxes: [],
364
+ warnings
365
+ };
366
+ }
367
+ const invalid = repos.find((repo) => !validateOssRepoSlug(repo));
368
+ if (invalid) {
369
+ return {
370
+ schema: OSS_META_LAB_SCHEMA,
371
+ ok: false,
372
+ assignments: [],
373
+ count,
374
+ cwd,
375
+ dryRun,
376
+ error: {
377
+ code: "HUMANISH_INVALID_OSS_REPO",
378
+ message: `Only GitHub owner/repo slugs are supported: ${invalid}`
379
+ },
380
+ liveRequested,
381
+ repos,
382
+ sandboxes: [],
383
+ warnings
384
+ };
385
+ }
386
+ const redactRepoNames = options.redactRepoNames ?? (liveRequested && (Boolean(githubTokenFromEnv(process.env)) || Boolean(options.repos?.length)));
387
+ const assignments = buildOssRepoAssignments(repos, count);
388
+ const publicAssignments = redactAssignments(assignments, redactRepoNames);
389
+ const publicRepos = redactRepoNames ? publicAssignments.map((assignment) => assignment.repo) : repos;
390
+ if (liveRequested) {
391
+ return {
392
+ schema: OSS_META_LAB_SCHEMA,
393
+ ok: false,
394
+ assignments: publicAssignments,
395
+ count,
396
+ cwd,
397
+ dryRun,
398
+ error: {
399
+ code: "HUMANISH_OSS_META_LIVE_ISOLATION_REQUIRED",
400
+ message: "Live OSS meta-lab execution is unavailable because repository-derived instructions require an isolated credential boundary. Use --dry-run."
401
+ },
402
+ liveRequested,
403
+ repos: publicRepos,
404
+ sandboxes: [],
405
+ warnings
406
+ };
407
+ }
408
+ const codexAppServerMode = codexAppServerModeRequested(process.env, options.codexAppServer === true);
409
+ const hostActorMode = liveRequested && hostCodexActorRequested(process.env);
410
+ const missingKeys = missingLiveKeys(process.env);
411
+ const runId = options.runId ?? makeMetaRunId();
412
+ const physicalCwd = await realpath(cwd);
413
+ const runResult = await runDryRun({
414
+ cwd: physicalCwd,
415
+ dryRun: true,
416
+ runId,
417
+ simCount: count
418
+ });
419
+ if (!runResult.ok || !runResult.runId) {
420
+ return {
421
+ schema: OSS_META_LAB_SCHEMA,
422
+ ok: false,
423
+ assignments: publicAssignments,
424
+ count,
425
+ cwd,
426
+ dryRun,
427
+ error: {
428
+ code: "HUMANISH_META_RUN_FAILED",
429
+ message: runResult.error?.message ?? "Failed to create OSS meta-lab run bundle."
430
+ },
431
+ liveRequested,
432
+ repos: publicRepos,
433
+ sandboxes: [],
434
+ warnings: [...warnings, ...runResult.warnings]
435
+ };
436
+ }
437
+ const preparedRunPaths = await bindExistingRunArtifactPaths(physicalCwd, runId);
438
+ const artifactRoot = preparedRunPaths;
439
+ const createdAt = new Date().toISOString();
440
+ const source = await buildRunSource({
441
+ capturedAt: createdAt,
442
+ cwd: physicalCwd,
443
+ humanishSource: "present",
444
+ packageName: "humanish"
445
+ });
446
+ const persistScreenshots = liveRequested;
447
+ let liveDesktops = [];
448
+ let substrateMissingKeys = [...missingKeys];
449
+ const initialBundle = buildMetaBundle({
450
+ assignments,
451
+ createdAt,
452
+ cwd,
453
+ dryRun,
454
+ liveDesktops,
455
+ liveRequested,
456
+ missingKeys: substrateMissingKeys,
457
+ redactRepoNames,
458
+ runId,
459
+ source
460
+ });
461
+ await writeMetaBundleArtifacts(artifactRoot, initialBundle);
462
+ let observer = await renderObserver(physicalCwd, runId, { open: false });
463
+ if (observer.ok && options.onObserverReady) {
464
+ await options.onObserverReady(observer);
465
+ await validatePreparedRunArtifactPaths(preparedRunPaths);
466
+ }
467
+ const shouldPreflightRepoAccess = liveRequested
468
+ && missingKeys.length === 0
469
+ && process.env.HUMANISH_OSS_META_SKIP_REPO_ACCESS_PREFLIGHT !== "1"
470
+ && (Boolean(githubTokenFromEnv(process.env)) || Boolean(options.repos?.length));
471
+ const repoAccessPreflight = shouldPreflightRepoAccess
472
+ ? await preflightOssMetaRepoAccess({
473
+ assignments,
474
+ cwd,
475
+ env: process.env,
476
+ redactRepoNames
477
+ })
478
+ : [];
479
+ const repoAccessPreflightBlocked = repoAccessPreflight.some((result) => !result.ok);
480
+ if (repoAccessPreflight.length > 0) {
481
+ const passed = repoAccessPreflight.filter((result) => result.ok).length;
482
+ warnings.push(`GitHub repo clone access preflight passed ${passed}/${repoAccessPreflight.length} assigned repo${repoAccessPreflight.length === 1 ? "" : "s"}.`);
483
+ for (const failed of repoAccessPreflight.filter((result) => !result.ok)) {
484
+ const assignment = assignments.find((candidate) => candidate.streamId === failed.streamId);
485
+ const repoLabel = assignment && redactRepoNames ? repoArtifactLabel(assignment) : failed.repo;
486
+ warnings.push(`${repoLabel}: ${failed.reason}`);
487
+ }
488
+ }
489
+ const hostActorPlanResults = hostActorMode && missingKeys.length === 0 && !repoAccessPreflightBlocked
490
+ ? await createHostActorPlans({
491
+ assignments,
492
+ cwd,
493
+ redactRepoNames,
494
+ runId
495
+ })
496
+ : [];
497
+ const hostActorPlansByStream = new Map(hostActorPlanResults.flatMap((result) => result.plan?.status === "passed" ? [[result.streamId, result]] : []));
498
+ if (hostActorPlanResults.length > 0) {
499
+ const passed = hostActorPlanResults.filter((result) => result.plan?.status === "passed").length;
500
+ warnings.push(`Host Codex actor authored ${passed}/${hostActorPlanResults.length} public-safe Humanish plan${hostActorPlanResults.length === 1 ? "" : "s"}.`);
501
+ for (const failed of hostActorPlanResults.filter((result) => result.plan?.status !== "passed")) {
502
+ warnings.push(`Host Codex actor plan failed for ${redactRepoNames ? "[redacted-authorized-repo]" : failed.repo}: ${failed.error ?? failed.plan?.summary ?? "unknown failure"}`);
503
+ }
504
+ }
505
+ const hostActorPlanBlocked = hostActorMode
506
+ && actorRequired(process.env)
507
+ && hostActorPlanResults.length > 0
508
+ && hostActorPlanResults.some((result) => result.plan?.status !== "passed");
509
+ const actorAuthPreflight = liveRequested
510
+ && missingKeys.length === 0
511
+ && !hostActorMode
512
+ && actorRequired(process.env)
513
+ && process.env.HUMANISH_OSS_META_SKIP_ACTOR_PREFLIGHT !== "1"
514
+ ? await preflightOssMetaActorApiKey({ env: process.env })
515
+ : undefined;
516
+ const actorAuthPreflightBlocked = actorAuthPreflight !== undefined && !actorAuthPreflight.ok;
517
+ substrateMissingKeys = [
518
+ ...missingKeys,
519
+ ...(hostActorPlanBlocked ? [OSS_META_LAB_HOST_ACTOR_PLACEHOLDER] : []),
520
+ ...(actorAuthPreflightBlocked ? [OSS_META_LAB_ACTOR_PREFLIGHT_PLACEHOLDER] : [])
521
+ ];
522
+ if (actorAuthPreflight) {
523
+ warnings.push(actorAuthPreflight.ok
524
+ ? actorAuthPreflight.reason
525
+ : `Remote Codex actor API-key preflight blocked live launch: ${actorAuthPreflight.reason}`);
526
+ }
527
+ let localPackage;
528
+ if (liveRequested && missingKeys.length === 0 && !repoAccessPreflightBlocked && !hostActorPlanBlocked && !actorAuthPreflightBlocked) {
529
+ try {
530
+ localPackage = await packLocalHumanishPackage(cwd, runId);
531
+ warnings.push(`Packed local humanish package for sandbox install (${localPackage.fileName}).`);
532
+ }
533
+ catch (error) {
534
+ warnings.push(`Local humanish package pack failed; sandbox bootstrap will try public npm fallback. ${compactError(error)}`);
535
+ }
536
+ }
537
+ if (liveRequested && missingKeys.length === 0 && !repoAccessPreflightBlocked && !hostActorPlanBlocked && !actorAuthPreflightBlocked) {
538
+ try {
539
+ liveDesktops = await launchLiveDesktops(assignments, {
540
+ codexAppServerMode,
541
+ cwd,
542
+ hostActorPlansByStream,
543
+ ...(localPackage ? { localPackage } : {}),
544
+ redactRepoNames
545
+ });
546
+ const completionSummary = await pollLiveDesktopCompletions(liveDesktops, {
547
+ ...(options.completionTimeoutMs === undefined
548
+ ? {}
549
+ : {
550
+ timeoutMs: options.completionTimeoutMs,
551
+ timeoutReason: "attached watch mode serves the Observer immediately after desktop streams are created"
552
+ })
553
+ });
554
+ warnings.push(...completionSummary.warnings);
555
+ }
556
+ catch (error) {
557
+ warnings.push(compactError(error));
558
+ liveDesktops = assignments.map((assignment) => {
559
+ const hostActorPlanResult = hostActorPlanResults.find((result) => result.streamId === assignment.streamId);
560
+ return {
561
+ error: compactError(error),
562
+ ...(hostActorPlanResult?.plan ? { hostActorPlan: hostActorPlanResult.plan } : {}),
563
+ ...(hostActorPlanResult?.artifactPath ? { hostActorPlanPath: hostActorPlanResult.artifactPath } : {}),
564
+ repo: redactRepoNames ? repoArtifactLabel(assignment) : assignment.repo,
565
+ simId: assignment.simId,
566
+ streamId: assignment.streamId
567
+ };
568
+ });
569
+ }
570
+ }
571
+ else if (repoAccessPreflightBlocked) {
572
+ liveDesktops = blockedLiveDesktopsForRepoAccess({
573
+ assignments,
574
+ preflight: repoAccessPreflight,
575
+ redactRepoNames
576
+ });
577
+ warnings.push("Live E2B launch skipped because GitHub repo clone access preflight failed.");
578
+ }
579
+ else if (hostActorPlanBlocked) {
580
+ warnings.push("Live E2B launch skipped because required host Codex actor plan evidence did not pass preflight.");
581
+ }
582
+ else if (actorAuthPreflightBlocked) {
583
+ warnings.push("Live E2B launch skipped because required Codex actor API-key quota/auth preflight failed.");
584
+ }
585
+ const liveDesktopCount = liveDesktops.filter((desktop) => desktop.url).length;
586
+ const failedLiveDesktopCount = liveDesktops.filter((desktop) => desktop.error).length;
587
+ const startedBootstrapCount = liveDesktops.filter((desktop) => desktop.bootstrap?.status === "started").length;
588
+ const terminalCompletionCount = liveDesktops.filter((desktop) => isTerminalCompletion(desktop.completion)).length;
589
+ const runningAppCount = liveDesktops.filter((desktop) => desktop.completion?.appStatus === "running").length;
590
+ const visibleDesktopCount = liveDesktops.filter((desktop) => desktop.completion?.visualStatus === "visible").length;
591
+ if (liveDesktops.length > 0) {
592
+ warnings.push(`Launched ${liveDesktopCount}/${liveDesktops.length} live E2B desktop stream${liveDesktops.length === 1 ? "" : "s"}.`);
593
+ if (startedBootstrapCount > 0) {
594
+ warnings.push(`Started ${startedBootstrapCount}/${liveDesktops.length} visible bootstrap terminal${liveDesktops.length === 1 ? "" : "s"} for target app startup, nested Humanish setup, and ${codexAppServerMode ? "Codex app-server client surface" : "Codex actor attempt"}.`);
595
+ if (terminalCompletionCount > 0) {
596
+ warnings.push(`Classified ${terminalCompletionCount}/${startedBootstrapCount} bootstrap terminal state${startedBootstrapCount === 1 ? "" : "s"} from remote public-safe evidence.`);
597
+ warnings.push(`Detected ${runningAppCount}/${terminalCompletionCount} target app HTTP-ready surface${terminalCompletionCount === 1 ? "" : "s"} from remote public-safe evidence.`);
598
+ warnings.push(`Detected ${visibleDesktopCount}/${terminalCompletionCount} headed desktop visual layout${terminalCompletionCount === 1 ? "" : "s"} from remote public-safe evidence.`);
599
+ }
600
+ }
601
+ else {
602
+ warnings.push(codexAppServerMode
603
+ ? "Codex app-server client surfacing and nested Humanish execution remain the next substrate slice behind these live desktops."
604
+ : "Codex TUI injection and nested Humanish execution remain the next substrate slice behind these live desktops.");
605
+ }
606
+ }
607
+ if (failedLiveDesktopCount > 0) {
608
+ warnings.push(`${failedLiveDesktopCount} E2B desktop launch${failedLiveDesktopCount === 1 ? "" : "es"} failed; see stream events in the Observer.`);
609
+ }
610
+ if (persistScreenshots) {
611
+ const screenshotSummary = await captureLiveDesktopScreenshots(artifactRoot, liveDesktops, { redactRepoNames });
612
+ warnings.push(...screenshotSummary.warnings);
613
+ }
614
+ const actorEvidenceSummary = await writeActorEvidenceArtifacts(artifactRoot, liveDesktops, {
615
+ assignments,
616
+ redactRepoNames
617
+ });
618
+ warnings.push(...actorEvidenceSummary.warnings);
619
+ const bundle = buildMetaBundle({
620
+ assignments,
621
+ createdAt,
622
+ cwd,
623
+ dryRun,
624
+ liveDesktops,
625
+ liveRequested,
626
+ missingKeys: substrateMissingKeys,
627
+ redactRepoNames,
628
+ runId,
629
+ source
630
+ });
631
+ await writeMetaBundleArtifacts(artifactRoot, bundle);
632
+ const finalObserver = await renderObserver(physicalCwd, runId, { open: options.open === true });
633
+ Object.assign(observer, finalObserver);
634
+ if (observer.ok) {
635
+ attachObserverRuntimeStreamUrls(observer, liveDesktops
636
+ .filter((desktop) => desktop.url)
637
+ .map((desktop) => ({
638
+ streamId: desktop.streamId,
639
+ url: desktop.url
640
+ })));
641
+ }
642
+ const outcome = classifyMetaLabOutcome({
643
+ dryRun,
644
+ liveDesktops,
645
+ liveRequested,
646
+ missingKeys: substrateMissingKeys
647
+ });
648
+ const result = {
649
+ schema: OSS_META_LAB_SCHEMA,
650
+ ok: observer.ok && outcome.ok,
651
+ assignments: publicAssignments,
652
+ count,
653
+ cwd,
654
+ dryRun,
655
+ ...(observer.ok && outcome.ok
656
+ ? {}
657
+ : {
658
+ error: {
659
+ code: "HUMANISH_META_RUN_FAILED",
660
+ message: observer.ok ? outcome.reason : observer.error?.message ?? "OSS meta-lab Observer failed."
661
+ }
662
+ }),
663
+ liveRequested,
664
+ observer,
665
+ repos: publicRepos,
666
+ runId,
667
+ sandboxes: liveDesktops.map((desktop) => formatLiveDesktopForResult(desktop, redactRepoNames)),
668
+ warnings: [...warnings, ...observer.warnings]
669
+ };
670
+ if (observer.ok && liveDesktops.some((desktop) => desktop.desktop && desktop.bootstrap?.status === "started")) {
671
+ liveRuntimeByResult.set(result, {
672
+ artifactRoot,
673
+ assignments,
674
+ createdAt,
675
+ cwd,
676
+ dryRun,
677
+ liveDesktops,
678
+ liveRequested,
679
+ missingKeys: substrateMissingKeys,
680
+ persistScreenshots,
681
+ physicalCwd,
682
+ redactRepoNames,
683
+ runId,
684
+ source,
685
+ startedAt: Date.now()
686
+ });
687
+ }
688
+ return result;
689
+ }
690
+ async function writeMetaBundleArtifacts(artifactRoot, bundle) {
691
+ const publicBundle = publicSafeOssMetaBundle(bundle);
692
+ await writeJson(artifactRoot, "run.json", publicBundle);
693
+ await writeJson(artifactRoot, "review.json", publicBundle.review);
694
+ await writeContainedOutputFile(artifactRoot, "review.md", renderMetaReviewMarkdown(publicBundle), "utf8");
695
+ await writeContainedOutputFile(artifactRoot, "events.ndjson", `${publicBundle.events.map((event) => JSON.stringify(event)).join("\n")}\n`, "utf8");
696
+ }
697
+ export function publicSafeOssMetaBundle(bundle) {
698
+ return {
699
+ ...bundle,
700
+ cwd: PUBLIC_TARGET_CWD,
701
+ streams: bundle.streams.map(publicSafeMetaStream)
702
+ };
703
+ }
704
+ function publicSafeMetaStream(stream) {
705
+ return {
706
+ ...stream,
707
+ artifacts: uniqueStreamArtifacts(stream.artifacts.filter((artifact) => isLocalEvidenceArtifactPath(artifact.path))),
708
+ ...(stream.terminal ? {
709
+ terminal: {
710
+ ...stream.terminal,
711
+ tail: sanitizeRemoteLog(stream.terminal.tail)
712
+ }
713
+ } : {}),
714
+ ...(stream.ui ? { ui: publicSafeMetaStreamUi(stream.ui) } : {})
715
+ };
716
+ }
717
+ function publicSafeMetaStreamUi(ui) {
718
+ const { nestedObserverPath: rawNestedObserverPath, nestedObserverUrl: _rawNestedObserverUrl, ...rest } = ui;
719
+ const nestedObserverPath = rawNestedObserverPath && isLocalEvidenceArtifactPath(rawNestedObserverPath)
720
+ ? rawNestedObserverPath
721
+ : undefined;
722
+ return {
723
+ ...rest,
724
+ ...(nestedObserverPath === undefined ? {} : { nestedObserverPath })
725
+ };
726
+ }
727
+ export function startOssMetaLabLiveRefresh(result, options = {}) {
728
+ const runtime = liveRuntimeByResult.get(result);
729
+ if (!runtime) {
730
+ return null;
731
+ }
732
+ const intervalMs = options.intervalMs ?? readPositiveInt(process.env.HUMANISH_OSS_META_WATCH_REFRESH_MS, 5_000);
733
+ const screenshotIntervalMs = options.screenshotIntervalMs ?? readPositiveInt(process.env.HUMANISH_OSS_META_SCREENSHOT_REFRESH_MS, 15_000);
734
+ const timeoutMs = options.timeoutMs ?? readNonNegativeInt(process.env.HUMANISH_OSS_META_COMPLETION_TIMEOUT_MS, 240_000);
735
+ const deadline = timeoutMs === 0 ? null : runtime.startedAt + timeoutMs;
736
+ let lastScreenshotAt = 0;
737
+ let timer = null;
738
+ let stopped = false;
739
+ let running = false;
740
+ const tick = async () => {
741
+ if (stopped || running) {
742
+ return;
743
+ }
744
+ running = true;
745
+ try {
746
+ const now = Date.now();
747
+ const timedOut = deadline !== null && now >= deadline;
748
+ const shouldCaptureScreenshot = runtime.persistScreenshots
749
+ && (timedOut || lastScreenshotAt === 0 || now - lastScreenshotAt >= screenshotIntervalMs);
750
+ await refreshOssMetaLabLiveRuntime(runtime, {
751
+ captureScreenshots: shouldCaptureScreenshot,
752
+ timedOut,
753
+ timeoutMs
754
+ });
755
+ result.sandboxes = runtime.liveDesktops.map((desktop) => formatLiveDesktopForResult(desktop, runtime.redactRepoNames));
756
+ const outcome = classifyMetaLabOutcome({
757
+ dryRun: runtime.dryRun,
758
+ liveDesktops: runtime.liveDesktops,
759
+ liveRequested: runtime.liveRequested,
760
+ missingKeys: runtime.missingKeys
761
+ });
762
+ result.ok = result.observer?.ok === true && outcome.ok;
763
+ if (result.ok) {
764
+ delete result.error;
765
+ }
766
+ else {
767
+ result.error = {
768
+ code: "HUMANISH_META_RUN_FAILED",
769
+ message: outcome.reason
770
+ };
771
+ }
772
+ if (shouldCaptureScreenshot) {
773
+ lastScreenshotAt = now;
774
+ }
775
+ if (runtime.liveDesktops.every((desktop) => isTerminalCompletion(desktop.completion))) {
776
+ if (timer) {
777
+ clearInterval(timer);
778
+ timer = null;
779
+ }
780
+ }
781
+ }
782
+ catch {
783
+ // Attached watch is best-effort telemetry. Keep serving the last verified
784
+ // bundle instead of crashing the user's observer process.
785
+ }
786
+ finally {
787
+ running = false;
788
+ }
789
+ };
790
+ void tick();
791
+ timer = setInterval(() => {
792
+ void tick();
793
+ }, intervalMs);
794
+ const stop = async () => {
795
+ stopped = true;
796
+ if (timer) {
797
+ clearInterval(timer);
798
+ timer = null;
799
+ }
800
+ while (running) {
801
+ await wait(100);
802
+ }
803
+ };
804
+ return {
805
+ async cleanup() {
806
+ await stop();
807
+ return cleanupOssMetaLabLiveDesktops(runtime.liveDesktops);
808
+ },
809
+ async stop() {
810
+ await stop();
811
+ }
812
+ };
813
+ }
814
+ function cleanupOssMetaLabLiveDesktops(liveDesktops, options = {}) {
815
+ const ids = [...new Set(liveDesktops.flatMap((entry) => entry.desktop?.sandboxId ? [entry.desktop.sandboxId] : []))];
816
+ return killOssMetaLabProviderSandboxIds(ids, {
817
+ ...(options.killSandbox === undefined ? {} : { killSandbox: options.killSandbox }),
818
+ ...(options.requestTimeoutMs === undefined ? {} : { requestTimeoutMs: options.requestTimeoutMs }),
819
+ skipped: liveDesktops.length - ids.length
820
+ });
821
+ }
822
+ export async function cleanupStaleOssMetaLabSandboxes(options = {}) {
823
+ return cleanupOssMetaLabSandboxesAndProviderMatches({ sandboxes: [] }, options);
824
+ }
825
+ export async function cleanupOssMetaLabSandboxesAndProviderMatches(result, options = {}) {
826
+ const requestTimeoutMs = options.requestTimeoutMs ?? readPositiveInt(process.env.HUMANISH_E2B_REQUEST_TIMEOUT_MS, 60_000);
827
+ const listed = await listOssMetaLabProviderSandboxIds({
828
+ ...(options.listSandboxes === undefined ? {} : { listSandboxes: options.listSandboxes }),
829
+ requestTimeoutMs
830
+ });
831
+ const cleanup = await killOssMetaLabProviderSandboxIds(listed.ids, {
832
+ ...(options.killSandbox === undefined ? {} : { killSandbox: options.killSandbox }),
833
+ redactIds: true,
834
+ requestTimeoutMs,
835
+ skipped: listed.skipped
836
+ });
837
+ const remaining = listed.errors.length > 0 || cleanup.errors.length > 0
838
+ ? undefined
839
+ : (await listOssMetaLabProviderSandboxIds({
840
+ ...(options.listSandboxes === undefined ? {} : { listSandboxes: options.listSandboxes }),
841
+ requestTimeoutMs
842
+ })).ids.length;
843
+ return {
844
+ killed: cleanup.killed,
845
+ matched: listed.ids.length,
846
+ ...(remaining === undefined ? {} : { remaining }),
847
+ skipped: result.sandboxes.length + cleanup.skipped,
848
+ errors: [
849
+ ...(result.sandboxes.length > 0
850
+ ? ["Stored OSS meta-lab sandbox IDs were not used; cleanup requires verified provider metadata."]
851
+ : []),
852
+ ...listed.errors,
853
+ ...cleanup.errors
854
+ ]
855
+ };
856
+ }
857
+ async function listOssMetaLabProviderSandboxIds(options) {
858
+ let listSandboxes = options.listSandboxes;
859
+ if (!listSandboxes) {
860
+ // humanish never enumerates an operator's E2B account by default (see
861
+ // docs/principles/invariants-and-defaults.md). Account-wide Sandbox.list discovery exists
862
+ // ONLY for a maintainer's explicit orphan sweep of a crashed
863
+ // prior process (`humanish lab cleanup oss`) and requires this opt-in, set deliberately by the
864
+ // maintainer running it against their own account -- never a default, never for a shared key.
865
+ if (process.env.HUMANISH_OSS_META_ALLOW_PROVIDER_LIST !== "1") {
866
+ return {
867
+ ids: [],
868
+ skipped: 0,
869
+ errors: ["Provider-wide Sandbox.list discovery is disabled by default (humanish never enumerates an E2B account); set HUMANISH_OSS_META_ALLOW_PROVIDER_LIST=1 to opt in for a maintainer-run orphan sweep."]
870
+ };
871
+ }
872
+ const e2bApiKey = process.env.E2B_API_KEY;
873
+ if (!e2bApiKey) {
874
+ return {
875
+ ids: [],
876
+ skipped: 0,
877
+ errors: ["E2B_API_KEY is not present; provider metadata cleanup readback skipped."]
878
+ };
879
+ }
880
+ const desktopModule = await loadE2BDesktopModule();
881
+ if (!desktopModule.Sandbox.list) {
882
+ return {
883
+ ids: [],
884
+ skipped: 0,
885
+ errors: ["Installed @e2b/desktop SDK does not expose Sandbox.list; provider metadata cleanup readback skipped."]
886
+ };
887
+ }
888
+ listSandboxes = async (request) => {
889
+ const paginator = desktopModule.Sandbox.list?.({
890
+ metadata: request.metadata,
891
+ requestTimeoutMs: request.requestTimeoutMs
892
+ });
893
+ const sandboxes = [];
894
+ if (!paginator) {
895
+ return sandboxes;
896
+ }
897
+ while (true) {
898
+ sandboxes.push(...await paginator.nextItems({ requestTimeoutMs: request.requestTimeoutMs }));
899
+ if (!paginator.hasNext) {
900
+ return sandboxes;
901
+ }
902
+ }
903
+ };
904
+ }
905
+ try {
906
+ const sandboxes = await listSandboxes({
907
+ metadata: { ...OSS_META_LAB_PROVIDER_METADATA },
908
+ requestTimeoutMs: options.requestTimeoutMs
909
+ });
910
+ let skipped = 0;
911
+ const ids = sandboxes.flatMap((sandbox) => {
912
+ const id = sandboxProviderId(sandbox);
913
+ if (!id || !isCleanupEligibleOssMetaLabSandbox(sandbox)) {
914
+ skipped += 1;
915
+ return [];
916
+ }
917
+ return [id];
918
+ });
919
+ return { ids: [...new Set(ids)], skipped, errors: [] };
920
+ }
921
+ catch (error) {
922
+ return {
923
+ ids: [],
924
+ skipped: 0,
925
+ errors: [`provider metadata cleanup readback failed: ${compactError(error)}`]
926
+ };
927
+ }
928
+ }
929
+ function sandboxProviderId(sandbox) {
930
+ for (const value of [sandbox.sandboxId, sandbox.sandboxID, sandbox.id]) {
931
+ if (typeof value === "string" && value.trim()) {
932
+ return value;
933
+ }
934
+ }
935
+ return null;
936
+ }
937
+ function isCleanupEligibleOssMetaLabSandbox(sandbox) {
938
+ const metadata = sandbox.metadata;
939
+ if (!metadata || metadata.tool !== OSS_META_LAB_PROVIDER_METADATA.tool || metadata.mode !== OSS_META_LAB_PROVIDER_METADATA.mode) {
940
+ return false;
941
+ }
942
+ const state = typeof sandbox.state === "string" ? sandbox.state.toLowerCase() : "";
943
+ return !["closed", "killed", "paused", "terminated"].includes(state);
944
+ }
945
+ export async function cleanupOssMetaLabSandboxes(result, _options = {}) {
946
+ return {
947
+ killed: 0,
948
+ skipped: result.sandboxes.length,
949
+ errors: result.sandboxes.some((sandbox) => sandbox.sandboxId)
950
+ ? ["Stored OSS meta-lab sandbox IDs cannot authorize provider mutation; use the explicit metadata-verified orphan sweep."]
951
+ : []
952
+ };
953
+ }
954
+ async function killOssMetaLabProviderSandboxIds(idsInput, options = {}) {
955
+ const ids = [...new Set(idsInput.filter((id) => id.trim()))];
956
+ const skipped = options.skipped ?? 0;
957
+ if (ids.length === 0) {
958
+ return { killed: 0, skipped, errors: [] };
959
+ }
960
+ const requestTimeoutMs = options.requestTimeoutMs ?? readPositiveInt(process.env.HUMANISH_E2B_REQUEST_TIMEOUT_MS, 60_000);
961
+ let killSandbox = options.killSandbox;
962
+ if (!killSandbox) {
963
+ const e2bApiKey = process.env.E2B_API_KEY;
964
+ if (!e2bApiKey) {
965
+ return {
966
+ killed: 0,
967
+ skipped: skipped + ids.length,
968
+ errors: ["E2B_API_KEY is not present; remote sandbox cleanup skipped."]
969
+ };
970
+ }
971
+ const desktopModule = await loadE2BDesktopModule();
972
+ if (!desktopModule.Sandbox.kill) {
973
+ return {
974
+ killed: 0,
975
+ skipped: skipped + ids.length,
976
+ errors: ["Installed @e2b/desktop SDK does not expose Sandbox.kill; remote sandbox cleanup skipped."]
977
+ };
978
+ }
979
+ killSandbox = async (sandboxId, timeout) => desktopModule.Sandbox.kill?.(sandboxId, { requestTimeoutMs: timeout });
980
+ }
981
+ let killed = 0;
982
+ const errors = [];
983
+ for (const id of ids) {
984
+ try {
985
+ await killSandbox(id, requestTimeoutMs);
986
+ killed += 1;
987
+ }
988
+ catch (error) {
989
+ const errorText = compactError(error);
990
+ errors.push(`${options.redactIds ? "[provider-runtime]" : id}: ${options.redactIds ? errorText.replaceAll(id, "[provider-runtime]") : errorText}`);
991
+ }
992
+ }
993
+ return { killed, skipped, errors };
994
+ }
995
+ export function buildOssMetaBundleFixture(args) {
996
+ return buildMetaBundle({
997
+ assignments: args.assignments,
998
+ createdAt: args.createdAt,
999
+ cwd: args.cwd,
1000
+ dryRun: args.dryRun,
1001
+ liveDesktops: args.lanes,
1002
+ liveRequested: args.liveRequested,
1003
+ missingKeys: args.missingKeys,
1004
+ redactRepoNames: args.redactRepoNames === true,
1005
+ runId: args.runId
1006
+ });
1007
+ }
1008
+ export function buildOssMetaBootstrapScriptFixture() {
1009
+ const [assignment] = buildOssRepoAssignments(["maciekt07/TodoApp"], 1);
1010
+ if (!assignment) {
1011
+ throw new Error("Missing OSS meta-lab fixture assignment.");
1012
+ }
1013
+ return buildRemoteBootstrapScript({
1014
+ assignment,
1015
+ appDir: "/home/user/maciekt07-todoapp",
1016
+ completionPath: "/home/user/.humanish-oss-lab/maciekt07-todoapp/completion.json",
1017
+ displayRepo: "maciekt07/TodoApp",
1018
+ logPath: "/home/user/.humanish-oss-lab/maciekt07-todoapp/bootstrap.log",
1019
+ nestedObserverPath: "/home/user/maciekt07-todoapp/.humanish/runs/nested-maciekt07-todoapp/observer/index.html",
1020
+ remoteHostActorPlanPath: "/home/user/.humanish-oss-lab/maciekt07-todoapp/host-actor-plan.json",
1021
+ stateDir: "/home/user/.humanish-oss-lab/maciekt07-todoapp",
1022
+ token: "maciekt07-todoapp"
1023
+ });
1024
+ }
1025
+ function syntheticOssMetaRunSource(createdAt) {
1026
+ return {
1027
+ packageName: "humanish",
1028
+ humanishSource: "present",
1029
+ git: {
1030
+ schema: "humanish.git-state.v1",
1031
+ status: "clean",
1032
+ capturedAt: createdAt,
1033
+ head: {
1034
+ shortSha: null,
1035
+ refState: "unknown"
1036
+ },
1037
+ changes: {
1038
+ staged: 0,
1039
+ unstaged: 0,
1040
+ untracked: 0,
1041
+ total: 0
1042
+ },
1043
+ note: "public-safe synthetic OSS meta-lab fixture"
1044
+ }
1045
+ };
1046
+ }
1047
+ function buildMetaBundle(args) {
1048
+ const simulations = [];
1049
+ const streams = [];
1050
+ const events = [
1051
+ {
1052
+ id: "event-000",
1053
+ at: args.createdAt,
1054
+ level: "info",
1055
+ type: "oss-meta.contract.created",
1056
+ message: "Created public-safe OSS meta-lab Observer-of-Observers contract."
1057
+ }
1058
+ ];
1059
+ for (const assignment of args.assignments) {
1060
+ const prompt = buildCodexBootstrapPrompt(assignment, args.redactRepoNames);
1061
+ const rawLiveDesktop = args.liveDesktops.find((desktop) => desktop.streamId === assignment.streamId);
1062
+ const liveDesktop = rawLiveDesktop && args.redactRepoNames
1063
+ ? redactLiveDesktopRepoMentions(rawLiveDesktop, assignment.repo)
1064
+ : rawLiveDesktop;
1065
+ const repoLabel = args.redactRepoNames ? repoArtifactLabel(assignment) : assignment.repo;
1066
+ const scenarioId = args.redactRepoNames ? `oss-meta-${repoLabel}` : assignment.scenarioId;
1067
+ const status = statusForMeta(args, liveDesktop);
1068
+ const completion = liveDesktop?.completion;
1069
+ const screenshot = liveDesktop?.screenshot;
1070
+ const terminalTail = terminalTailForMeta(prompt, liveDesktop);
1071
+ const liveStreamPresent = Boolean(liveDesktop?.url);
1072
+ const appServerMode = liveDesktop?.bootstrap?.codexMode === "app-server-client";
1073
+ simulations.push({
1074
+ id: assignment.simId,
1075
+ index: assignment.index,
1076
+ personaId: `codex-oss-operator-${String(assignment.index).padStart(2, "0")}`,
1077
+ scenarioId,
1078
+ status,
1079
+ streamKind: "browser",
1080
+ mode: "browser-sim",
1081
+ progress: progressForMeta(status, liveDesktop),
1082
+ currentStep: currentStepForMeta(args, assignment, liveDesktop),
1083
+ summary: completion
1084
+ ? `Headed E2B desktop lane assigned to ${repoLabel}; ${completion.reason}`
1085
+ : liveDesktop?.bootstrap?.status === "started"
1086
+ ? `Headed E2B desktop lane assigned to ${repoLabel}; bootstrap terminal launched to set up Humanish and open the nested Observer${appServerMode ? " plus Codex app-server client surface" : ""}.`
1087
+ : `Headed E2B desktop lane assigned to ${repoLabel}; remote bootstrap should set up Humanish and open a nested Observer inside that desktop.`,
1088
+ streamIds: [assignment.streamId],
1089
+ startedAt: args.createdAt,
1090
+ updatedAt: args.createdAt
1091
+ });
1092
+ const artifacts = uniqueStreamArtifacts([
1093
+ { label: "run bundle", path: "run.json", kind: "bundle" },
1094
+ { label: "review", path: "review.md", kind: "review" },
1095
+ { label: "events", path: "events.ndjson", kind: "events" },
1096
+ ...(completion?.appServerActorEvidence?.tracePath ? [{ label: "codex app-server trace", path: completion.appServerActorEvidence.tracePath, kind: "trace" }] : []),
1097
+ ...(completion?.appServerActorEvidence?.eventsPath ? [{ label: "codex app-server events", path: completion.appServerActorEvidence.eventsPath, kind: "events" }] : []),
1098
+ ...(completion?.appServerActorEvidence?.transcriptPath ? [{ label: "codex app-server transcript", path: completion.appServerActorEvidence.transcriptPath, kind: "log" }] : []),
1099
+ ...(liveDesktop?.actorEvidence?.actorLastMessageTailPath ? [{ label: "actor last-message tail", path: liveDesktop.actorEvidence.actorLastMessageTailPath, kind: "log" }] : []),
1100
+ ...(liveDesktop?.actorEvidence?.actorLogTailPath ? [{ label: "actor log tail", path: liveDesktop.actorEvidence.actorLogTailPath, kind: "log" }] : []),
1101
+ ...(liveDesktop?.actorEvidence?.appServerTracePath ? [{ label: "codex app-server trace", path: liveDesktop.actorEvidence.appServerTracePath, kind: "trace" }] : []),
1102
+ ...(liveDesktop?.actorEvidence?.appServerEventsPath ? [{ label: "codex app-server events", path: liveDesktop.actorEvidence.appServerEventsPath, kind: "events" }] : []),
1103
+ ...(liveDesktop?.actorEvidence?.appServerTranscriptPath ? [{ label: "codex app-server transcript", path: liveDesktop.actorEvidence.appServerTranscriptPath, kind: "log" }] : []),
1104
+ ...(liveDesktop?.actorEvidence?.nestedEvidencePath ? [{ label: "nested Humanish proof", path: liveDesktop.actorEvidence.nestedEvidencePath, kind: "trace" }] : []),
1105
+ ...(liveDesktop?.actorEvidence?.setupQualityPath ? [{ label: "setup quality", path: liveDesktop.actorEvidence.setupQualityPath, kind: "filesystem" }] : []),
1106
+ ...(liveDesktop?.hostActorPlanPath ? [{ label: "host Codex actor plan", path: liveDesktop.hostActorPlanPath, kind: "trace" }] : []),
1107
+ ...(screenshot ? [{ label: "desktop screenshot", path: screenshot.path, kind: "screenshot" }] : [])
1108
+ ]);
1109
+ const stream = {
1110
+ id: assignment.streamId,
1111
+ simId: assignment.simId,
1112
+ kind: "browser",
1113
+ label: `E2B desktop - ${repoLabel}`,
1114
+ status,
1115
+ transport: liveStreamPresent ? "sse" : screenshot ? "snapshot" : status === "contract_proof_only" ? "snapshot" : "sse",
1116
+ updatedAt: args.createdAt,
1117
+ embed: {
1118
+ kind: screenshot ? "screenshot" : "placeholder",
1119
+ ...(screenshot ? { url: screenshot.observerUrl } : {}),
1120
+ title: `E2B desktop ${assignment.index}`
1121
+ },
1122
+ viewport: {
1123
+ width: 1440,
1124
+ height: 960,
1125
+ deviceScaleFactor: 1
1126
+ },
1127
+ terminal: {
1128
+ title: `${appServerMode ? "Codex app-server" : "Codex"} bootstrap - ${repoLabel}`,
1129
+ format: "plain",
1130
+ stdin: liveDesktop?.bootstrap ? "sent" : "planned",
1131
+ tail: terminalTail
1132
+ },
1133
+ ui: {
1134
+ route: completion?.appUrl ?? `e2b://desktop/${repoLabel}`,
1135
+ intent: appServerMode
1136
+ ? "Watch the headed desktop where the bootstrap clones the repo, starts the target app, sets up Humanish, opens the nested Observer, and opens a Codex app-server client surface."
1137
+ : "Watch the headed desktop where the bootstrap clones the repo, starts the target app, sets up Humanish, opens the nested Observer, and attempts a Codex actor.",
1138
+ ...(completion?.actorStatus ? { actorStatus: completion.actorStatus } : {}),
1139
+ ...(completion?.appStatus ? { appStatus: completion.appStatus } : {}),
1140
+ ...(completion?.appUrl ? { appUrl: completion.appUrl } : {}),
1141
+ ...(liveDesktop?.actorEvidence?.nestedEvidencePath ? { nestedObserverPath: liveDesktop.actorEvidence.nestedEvidencePath } : {}),
1142
+ ...(screenshot ? { screenshotUrl: screenshot.observerUrl } : {}),
1143
+ ...(completion?.visualStatus ? { visualStatus: completion.visualStatus } : {}),
1144
+ state: completion
1145
+ ? [
1146
+ completion.reason,
1147
+ completion.appStatus ? `app=${completion.appStatus}` : "",
1148
+ completion.actorStatus ? `actor=${completion.actorStatus}` : "",
1149
+ completion.visualStatus ? `visual=${completion.visualStatus}` : ""
1150
+ ].filter(Boolean).join(" | ")
1151
+ : liveDesktop?.bootstrap?.status === "started"
1152
+ ? "bootstrap terminal launched; target app and nested Observer setup running"
1153
+ : liveStreamPresent ? "live E2B desktop stream present; stream URL is runtime-only" : args.dryRun ? "contract desktop" : "headed E2B desktop"
1154
+ },
1155
+ ...(completion ? { completion: completionForStream(completion, appServerMode) } : {}),
1156
+ artifacts
1157
+ };
1158
+ if (appServerMode) {
1159
+ stream.codex = codexMetadataForMetaStream(liveDesktop, completion);
1160
+ }
1161
+ streams.push(stream);
1162
+ events.push({
1163
+ id: `event-${String(assignment.index).padStart(3, "0")}-assigned`,
1164
+ at: args.createdAt,
1165
+ level: "info",
1166
+ type: "oss-meta.repo.assigned",
1167
+ message: `Assigned ${repoLabel} to Codex desktop lane ${assignment.index}.`,
1168
+ simId: assignment.simId,
1169
+ streamId: assignment.streamId
1170
+ }, {
1171
+ id: `event-${String(assignment.index).padStart(3, "0")}-prompt`,
1172
+ at: args.createdAt,
1173
+ level: "info",
1174
+ type: "oss-meta.codex.prompt.ready",
1175
+ message: appServerMode
1176
+ ? "Codex app-server client hook is available in the stream logs tab."
1177
+ : "Codex bootstrap prompt is available in the stream logs tab.",
1178
+ simId: assignment.simId,
1179
+ streamId: assignment.streamId
1180
+ });
1181
+ if (liveStreamPresent && liveDesktop) {
1182
+ events.push({
1183
+ id: `event-${String(assignment.index).padStart(3, "0")}-stream`,
1184
+ at: args.createdAt,
1185
+ level: "info",
1186
+ type: "oss-meta.e2b.stream.started",
1187
+ message: `Live E2B desktop stream started for ${repoLabel}; auth URL is runtime-only and not persisted in run artifacts.`,
1188
+ simId: assignment.simId,
1189
+ streamId: assignment.streamId
1190
+ });
1191
+ if (liveDesktop.bootstrap?.status === "started") {
1192
+ events.push({
1193
+ id: `event-${String(assignment.index).padStart(3, "0")}-bootstrap-started`,
1194
+ at: args.createdAt,
1195
+ level: "info",
1196
+ type: "oss-meta.bootstrap.started",
1197
+ message: `Visible bootstrap terminal launched for ${repoLabel}.`,
1198
+ simId: assignment.simId,
1199
+ streamId: assignment.streamId
1200
+ });
1201
+ if (completion) {
1202
+ events.push({
1203
+ id: `event-${String(assignment.index).padStart(3, "0")}-bootstrap-${completion.status}`,
1204
+ at: completion.checkedAt,
1205
+ level: eventLevelForCompletion(completion.status),
1206
+ type: `oss-meta.bootstrap.${completion.status}`,
1207
+ message: `${repoLabel}: ${completion.reason}`,
1208
+ simId: assignment.simId,
1209
+ streamId: assignment.streamId
1210
+ });
1211
+ if (completion.nestedStepTraceSummary) {
1212
+ events.push({
1213
+ id: `event-${String(assignment.index).padStart(3, "0")}-nested-step-trace`,
1214
+ at: completion.checkedAt,
1215
+ level: completion.nestedStepTraceSummary.status === "passed" ? "info" : "warn",
1216
+ type: "oss-meta.nested.step_trace.summary",
1217
+ message: `${repoLabel}: Nested browser trace summary captured ${completion.nestedStepTraceSummary.counts.passedSteps}/${completion.nestedStepTraceSummary.counts.totalSteps} steps across ${completion.nestedStepTraceSummary.counts.surfaces} surface(s).`,
1218
+ simId: assignment.simId,
1219
+ streamId: assignment.streamId
1220
+ });
1221
+ }
1222
+ }
1223
+ }
1224
+ else if (liveDesktop.bootstrap?.status === "failed") {
1225
+ events.push({
1226
+ id: `event-${String(assignment.index).padStart(3, "0")}-bootstrap-failed`,
1227
+ at: args.createdAt,
1228
+ level: "error",
1229
+ type: "oss-meta.bootstrap.failed",
1230
+ message: `Bootstrap launcher failed for ${repoLabel}.`,
1231
+ simId: assignment.simId,
1232
+ streamId: assignment.streamId
1233
+ });
1234
+ }
1235
+ }
1236
+ else if (completion) {
1237
+ events.push({
1238
+ id: `event-${String(assignment.index).padStart(3, "0")}-completion-${completion.status}`,
1239
+ at: completion.checkedAt,
1240
+ level: eventLevelForCompletion(completion.status),
1241
+ type: `oss-meta.bootstrap.${completion.status}`,
1242
+ message: `${repoLabel}: ${completion.reason}`,
1243
+ simId: assignment.simId,
1244
+ streamId: assignment.streamId
1245
+ });
1246
+ if (completion.nestedStepTraceSummary) {
1247
+ events.push({
1248
+ id: `event-${String(assignment.index).padStart(3, "0")}-nested-step-trace`,
1249
+ at: completion.checkedAt,
1250
+ level: completion.nestedStepTraceSummary.status === "passed" ? "info" : "warn",
1251
+ type: "oss-meta.nested.step_trace.summary",
1252
+ message: `${repoLabel}: Nested browser trace summary captured ${completion.nestedStepTraceSummary.counts.passedSteps}/${completion.nestedStepTraceSummary.counts.totalSteps} steps across ${completion.nestedStepTraceSummary.counts.surfaces} surface(s).`,
1253
+ simId: assignment.simId,
1254
+ streamId: assignment.streamId
1255
+ });
1256
+ }
1257
+ }
1258
+ else if (liveDesktop?.error) {
1259
+ events.push({
1260
+ id: `event-${String(assignment.index).padStart(3, "0")}-stream-error`,
1261
+ at: args.createdAt,
1262
+ level: "error",
1263
+ type: "oss-meta.e2b.stream.failed",
1264
+ message: `E2B desktop stream failed for ${repoLabel}: ${liveDesktop.error}`,
1265
+ simId: assignment.simId,
1266
+ streamId: assignment.streamId
1267
+ });
1268
+ }
1269
+ }
1270
+ if (args.liveRequested && args.missingKeys.length > 0) {
1271
+ events.push({
1272
+ id: "event-live-keys-blocked",
1273
+ at: args.createdAt,
1274
+ level: "warn",
1275
+ type: "oss-meta.live.keys_missing",
1276
+ message: `Live launch is blocked until ${args.missingKeys.join(", ")} are present.`
1277
+ });
1278
+ }
1279
+ if (args.liveRequested && args.liveDesktops.length === 0) {
1280
+ events.push({
1281
+ id: "event-live-substrate-planned",
1282
+ at: args.createdAt,
1283
+ level: "warn",
1284
+ type: "oss-meta.live.substrate_planned",
1285
+ message: args.missingKeys.length > 0
1286
+ ? "E2B desktop launch is waiting on required environment variables."
1287
+ : "Codex TUI injection and nested Humanish execution are planned behind this Observer contract."
1288
+ });
1289
+ }
1290
+ if (args.liveDesktops.some((desktop) => desktop.url)) {
1291
+ events.push({
1292
+ id: "event-live-substrate-started",
1293
+ at: args.createdAt,
1294
+ level: "info",
1295
+ type: "oss-meta.live.substrate_started",
1296
+ message: args.liveDesktops.some((desktop) => desktop.bootstrap?.status === "started")
1297
+ ? "E2B desktop streams are connected and bootstrap terminals are launched."
1298
+ : "E2B desktop streams are connected; Codex TUI injection is still pending."
1299
+ });
1300
+ }
1301
+ const review = createMetaReview(args);
1302
+ const feedbackCandidates = buildMetaFeedbackCandidates({
1303
+ assignments: args.assignments,
1304
+ liveDesktops: args.liveDesktops,
1305
+ redactRepoNames: args.redactRepoNames,
1306
+ runId: args.runId
1307
+ });
1308
+ return {
1309
+ schema: RUN_BUNDLE_SCHEMA,
1310
+ runId: args.runId,
1311
+ mode: args.dryRun ? "dry-run" : "live",
1312
+ simCount: args.assignments.length,
1313
+ createdAt: args.createdAt,
1314
+ cwd: args.cwd,
1315
+ artifactRoot: path.join(".humanish", "runs", args.runId),
1316
+ source: args.source ?? syntheticOssMetaRunSource(args.createdAt),
1317
+ persona: {
1318
+ id: "oss-meta-codex-tui-operators",
1319
+ name: "Codex TUI OSS Setup Operators",
1320
+ source: "lab:oss:meta",
1321
+ sourceDigest: "public-safe"
1322
+ },
1323
+ scenario: {
1324
+ id: "oss-meta-observer-of-observers",
1325
+ title: "OSS Observer-of-Observers Meta-Lab",
1326
+ goal: "Launch headed E2B desktops where Codex agents clone authorized GitHub repos, set up Humanish, run nested Humanish proof commands, attempt Codex TUI, and keep each nested Observer visible.",
1327
+ source: "lab:oss:meta",
1328
+ sourceDigest: "public-safe"
1329
+ },
1330
+ lifecycle: [
1331
+ {
1332
+ at: args.createdAt,
1333
+ event: "oss-meta.run.created",
1334
+ message: `Created OSS meta-lab run with ${args.assignments.length} headed desktop lane${args.assignments.length === 1 ? "" : "s"}.`
1335
+ },
1336
+ {
1337
+ at: args.createdAt,
1338
+ event: "oss-meta.repos.assigned",
1339
+ message: `Assigned repos: ${args.assignments.map((assignment) => args.redactRepoNames ? repoArtifactLabel(assignment) : assignment.repo).join(", ")}.`
1340
+ },
1341
+ {
1342
+ at: args.createdAt,
1343
+ event: "oss-meta.observer.ready",
1344
+ message: "Top-level Observer is ready to watch nested Humanish Observers."
1345
+ }
1346
+ ],
1347
+ simulations,
1348
+ streams,
1349
+ events,
1350
+ redaction: {
1351
+ status: "passed",
1352
+ notes: "OSS meta-lab artifacts contain GitHub slugs and redacted/synthetic bootstrap evidence only."
1353
+ },
1354
+ artifacts: {
1355
+ run: "run.json",
1356
+ reviewJson: "review.json",
1357
+ reviewMarkdown: "review.md",
1358
+ observerData: "observer/observer-data.json",
1359
+ events: "events.ndjson"
1360
+ },
1361
+ review,
1362
+ feedbackCandidates
1363
+ };
1364
+ }
1365
+ function uniqueStreamArtifacts(artifacts) {
1366
+ const seen = new Set();
1367
+ const unique = [];
1368
+ for (const artifact of artifacts) {
1369
+ const key = `${artifact.kind}:${artifact.path}`;
1370
+ if (seen.has(key))
1371
+ continue;
1372
+ seen.add(key);
1373
+ unique.push(artifact);
1374
+ }
1375
+ return unique;
1376
+ }
1377
+ function codexMetadataForMetaStream(liveDesktop, completion) {
1378
+ const trace = completion?.appServerActorEvidence?.traceJson;
1379
+ const traceRecord = isRecord(trace) ? trace : undefined;
1380
+ const counts = isRecord(traceRecord?.counts) ? traceRecord.counts : undefined;
1381
+ const eventCount = typeof counts?.envelopes === "number" ? counts.envelopes : undefined;
1382
+ return {
1383
+ provider: "codex-app-server",
1384
+ state: codexStateForMeta(completion),
1385
+ contract: "Codex app-server JSON-RPC actor telemetry projected from redacted event envelopes, trace summary, transcript tail, and filesystem setup evidence.",
1386
+ ...(eventCount === undefined ? {} : { eventCount }),
1387
+ ...(liveDesktop?.actorEvidence?.appServerTracePath ? { tracePath: liveDesktop.actorEvidence.appServerTracePath } : {}),
1388
+ ...(typeof traceRecord?.threadId === "string" && traceRecord.threadId.trim() ? { threadId: traceRecord.threadId } : {}),
1389
+ ...(typeof traceRecord?.turnId === "string" && traceRecord.turnId.trim() ? { turnId: traceRecord.turnId } : {}),
1390
+ ...(typeof traceRecord?.sessionId === "string" && traceRecord.sessionId.trim() ? { sessionId: traceRecord.sessionId } : {}),
1391
+ ...(typeof traceRecord?.model === "string" && traceRecord.model.trim() ? { model: traceRecord.model } : {})
1392
+ };
1393
+ }
1394
+ function codexStateForMeta(completion) {
1395
+ if (!completion)
1396
+ return "connecting";
1397
+ if (completion.actorStatus === "passed")
1398
+ return "completed";
1399
+ if (completion.actorStatus === "running")
1400
+ return "running";
1401
+ if (completion.actorStatus === "timed_out" || completion.status === "timed_out")
1402
+ return "timed_out";
1403
+ if (completion.actorStatus === "blocked" || completion.status === "blocked")
1404
+ return "blocked";
1405
+ if (completion.actorStatus === "failed" || completion.status === "failed")
1406
+ return "failed";
1407
+ return completion.status === "running" ? "running" : "watching";
1408
+ }
1409
+ function statusForMeta(args, liveDesktop) {
1410
+ if (args.dryRun)
1411
+ return "contract_proof_only";
1412
+ if (liveDesktop?.completion?.status === "passed" && liveDesktop.completion.appStatus !== "running")
1413
+ return "blocked";
1414
+ if (liveDesktop?.completion?.status === "passed" && liveDesktop.completion.visualStatus !== "visible")
1415
+ return "blocked";
1416
+ if (liveDesktop?.completion?.status === "passed"
1417
+ && liveDesktop.bootstrap?.codexMode === "app-server-client"
1418
+ && liveDesktop.completion.actorStatus !== "passed")
1419
+ return "blocked";
1420
+ if (liveDesktop?.completion?.status === "passed")
1421
+ return "passed";
1422
+ if (liveDesktop?.completion?.status === "failed")
1423
+ return "failed";
1424
+ if (liveDesktop?.completion?.status === "blocked")
1425
+ return "blocked";
1426
+ if (liveDesktop?.completion?.status === "timed_out")
1427
+ return "timed_out";
1428
+ if (liveDesktop?.bootstrap?.status === "failed")
1429
+ return "failed";
1430
+ if (liveDesktop?.url)
1431
+ return "running";
1432
+ if (liveDesktop?.error)
1433
+ return "failed";
1434
+ if (args.missingKeys.length > 0)
1435
+ return "blocked";
1436
+ return "preparing";
1437
+ }
1438
+ function progressForMeta(status, liveDesktop) {
1439
+ if (status === "contract_proof_only")
1440
+ return 100;
1441
+ if (status === "passed")
1442
+ return 100;
1443
+ if (status === "timed_out")
1444
+ return 100;
1445
+ if (status === "failed" && liveDesktop?.completion)
1446
+ return 100;
1447
+ if (status === "blocked")
1448
+ return 18;
1449
+ if (status === "failed")
1450
+ return 8;
1451
+ if (liveDesktop?.completion?.appStatus === "running")
1452
+ return 86;
1453
+ if (liveDesktop?.bootstrap?.status === "started")
1454
+ return 74;
1455
+ if (liveDesktop?.url)
1456
+ return 58;
1457
+ return 34;
1458
+ }
1459
+ function currentStepForMeta(args, assignment, liveDesktop) {
1460
+ const repoLabel = args.redactRepoNames ? repoArtifactLabel(assignment) : assignment.repo;
1461
+ if (args.dryRun) {
1462
+ return `Contract ready for ${repoLabel}; no E2B desktop launched.`;
1463
+ }
1464
+ if (liveDesktop?.completion) {
1465
+ return liveDesktop.completion.reason;
1466
+ }
1467
+ if (liveDesktop?.bootstrap?.status === "started") {
1468
+ return liveDesktop.bootstrap.codexMode === "app-server-client"
1469
+ ? `Bootstrap terminal launched for ${repoLabel}; Codex app-server actor, Humanish setup, target app, and nested Observer run inside the desktop.`
1470
+ : `Bootstrap terminal launched for ${repoLabel}; Codex TUI attempt, Humanish setup, and nested Observer run inside the desktop.`;
1471
+ }
1472
+ if (liveDesktop?.bootstrap?.status === "failed") {
1473
+ return `Bootstrap launcher failed for ${repoLabel}.`;
1474
+ }
1475
+ if (liveDesktop?.url) {
1476
+ return `Live E2B desktop connected for ${repoLabel}; Codex TUI injection pending.`;
1477
+ }
1478
+ if (liveDesktop?.error) {
1479
+ return `E2B desktop launch failed for ${repoLabel}.`;
1480
+ }
1481
+ if (args.missingKeys.length > 0) {
1482
+ return `Waiting for ${args.missingKeys.join(", ")} before launching ${repoLabel}.`;
1483
+ }
1484
+ return `Ready to launch E2B desktop and run Codex actor setup for ${repoLabel}.`;
1485
+ }
1486
+ function createMetaReview(args) {
1487
+ const started = args.liveDesktops.filter((desktop) => desktop.bootstrap?.status === "started");
1488
+ const terminalCompletions = args.liveDesktops.filter((desktop) => isTerminalCompletion(desktop.completion));
1489
+ const appRunning = args.liveDesktops.filter((desktop) => desktop.completion?.appStatus === "running");
1490
+ const visualVisible = args.liveDesktops.filter((desktop) => desktop.completion?.visualStatus === "visible");
1491
+ const nestedLiveProof = args.liveDesktops.some((desktop) => desktop.completion?.nestedVerifyPassed === true
1492
+ && /\bhumanish run live\b/.test(desktop.completion.logTail ?? ""));
1493
+ const outcome = classifyMetaLabOutcome(args);
1494
+ const gaps = [
1495
+ nestedLiveProof && appRunning.length > 0 && visualVisible.length > 0
1496
+ ? "Target app browser surfaces, nested Observer windows, and nested Humanish live app-url proof are visible inside headed desktops."
1497
+ : appRunning.length > 0 && visualVisible.length > 0
1498
+ ? "Target app browser surfaces and nested Observer windows are visible inside headed desktops; nested Humanish live proof is still missing."
1499
+ : appRunning.length > 0
1500
+ ? "Target app surfaces responded over HTTP, but headed desktop browser-window visibility was not detected for every lane."
1501
+ : started.length > 0 && terminalCompletions.length === started.length
1502
+ ? "OSS lane terminal states are classified from public-safe remote bootstrap evidence, but target app HTTP readiness was not detected."
1503
+ : args.liveDesktops.some((desktop) => desktop.bootstrap?.status === "started")
1504
+ ? "Visible E2B bootstrap terminals are launched and run nested Humanish setup plus target app startup; completion is watched in the desktop stream until remote evidence is polled back."
1505
+ : "Nested Humanish Observer evidence is represented as a lane contract until Codex TUI injection and nested Humanish execution land.",
1506
+ nestedLiveProof
1507
+ ? "Nested Humanish proof reached live app-url mode with desktop/mobile browser persona evidence; richer app-specific journey manifests remain the next adapter slice."
1508
+ : "Nested Humanish proof did not reach live app-url mode; target app startup or browser evidence is still missing.",
1509
+ "The top-level run does not clone, modify, commit, push, or file issues in target repos.",
1510
+ "Public runs may record GitHub owner/repo slugs; token-backed maintainer/private runs redact repo labels in durable artifacts by default."
1511
+ ];
1512
+ if (args.liveRequested && args.missingKeys.length > 0) {
1513
+ gaps.unshift(`Live launch is blocked until ${args.missingKeys.join(", ")} are available in environment.`);
1514
+ }
1515
+ if (args.liveDesktops.some((desktop) => desktop.url) && !args.liveDesktops.some((desktop) => desktop.bootstrap?.status === "started")) {
1516
+ gaps.unshift("Live E2B desktop streams are connected, but Codex TUI injection and nested Humanish execution are not yet automated.");
1517
+ }
1518
+ return {
1519
+ schema: REVIEW_SCHEMA,
1520
+ verdict: outcome.verdict,
1521
+ summary: outcome.verdict === "fail" || outcome.verdict === "timed_out" || outcome.verdict === "blocked"
1522
+ ? outcome.reason
1523
+ : args.dryRun
1524
+ ? "OSS meta-lab dry-run rendered the Observer-of-Observers contract without provider spend."
1525
+ : terminalCompletions.length > 0
1526
+ ? `OSS meta-lab launched live E2B desktop streams, classified ${terminalCompletions.length}/${started.length || terminalCompletions.length} bootstrap terminal state${terminalCompletions.length === 1 ? "" : "s"} from public-safe remote evidence, detected ${appRunning.length}/${terminalCompletions.length} target app HTTP-ready surface${terminalCompletions.length === 1 ? "" : "s"}, and detected ${visualVisible.length}/${terminalCompletions.length} headed desktop visual layout${terminalCompletions.length === 1 ? "" : "s"}.`
1527
+ : args.liveDesktops.some((desktop) => desktop.bootstrap?.status === "started")
1528
+ ? "OSS meta-lab launched live E2B desktop streams, injected visible bootstrap terminals, and started target app plus nested Humanish setup inside each desktop."
1529
+ : args.liveDesktops.some((desktop) => desktop.url)
1530
+ ? "OSS meta-lab launched live E2B desktop streams and rendered them in the top-level Observer."
1531
+ : "OSS meta-lab rendered the live headed-desktop control surface and marked the missing substrate truth in-lane.",
1532
+ gaps
1533
+ };
1534
+ }
1535
+ function classifyMetaLabOutcome(args) {
1536
+ if (args.dryRun) {
1537
+ return {
1538
+ ok: true,
1539
+ reason: "OSS meta-lab dry-run rendered the Observer-of-Observers contract without provider spend.",
1540
+ verdict: "contract_proof_only"
1541
+ };
1542
+ }
1543
+ if (args.liveRequested && args.missingKeys.length > 0) {
1544
+ return {
1545
+ ok: true,
1546
+ reason: `Live launch is blocked until ${args.missingKeys.join(", ")} are available in environment.`,
1547
+ verdict: "blocked"
1548
+ };
1549
+ }
1550
+ const launchFailures = args.liveDesktops.filter((desktop) => desktop.error || desktop.bootstrap?.status === "failed");
1551
+ if (launchFailures.length > 0) {
1552
+ return {
1553
+ ok: false,
1554
+ reason: `OSS meta-lab failed ${launchFailures.length}/${args.liveDesktops.length} live desktop or bootstrap launch${launchFailures.length === 1 ? "" : "es"}.`,
1555
+ verdict: "fail"
1556
+ };
1557
+ }
1558
+ const failedCompletions = args.liveDesktops.filter((desktop) => desktop.completion?.status === "failed");
1559
+ if (failedCompletions.length > 0) {
1560
+ return {
1561
+ ok: false,
1562
+ reason: `OSS meta-lab classified ${failedCompletions.length}/${args.liveDesktops.length} bootstrap terminal state${failedCompletions.length === 1 ? "" : "s"} as failed from public-safe remote evidence.`,
1563
+ verdict: "fail"
1564
+ };
1565
+ }
1566
+ const timedOutCompletions = args.liveDesktops.filter((desktop) => desktop.completion?.status === "timed_out");
1567
+ if (timedOutCompletions.length > 0) {
1568
+ return {
1569
+ ok: false,
1570
+ reason: `OSS meta-lab timed out waiting for ${timedOutCompletions.length}/${args.liveDesktops.length} bootstrap completion marker${timedOutCompletions.length === 1 ? "" : "s"}.`,
1571
+ verdict: "timed_out"
1572
+ };
1573
+ }
1574
+ const blockedCompletions = args.liveDesktops.filter((desktop) => desktop.completion?.status === "blocked");
1575
+ if (blockedCompletions.length > 0) {
1576
+ return {
1577
+ ok: false,
1578
+ reason: `OSS meta-lab classified ${blockedCompletions.length}/${args.liveDesktops.length} bootstrap terminal state${blockedCompletions.length === 1 ? "" : "s"} as blocked from public-safe remote evidence.`,
1579
+ verdict: "blocked"
1580
+ };
1581
+ }
1582
+ const completedWithMissingApp = args.liveDesktops.filter((desktop) => desktop.completion?.status === "passed"
1583
+ && desktop.completion.appStatus !== "running");
1584
+ if (completedWithMissingApp.length > 0) {
1585
+ return {
1586
+ ok: false,
1587
+ reason: `OSS meta-lab completed nested Humanish setup but did not detect ${completedWithMissingApp.length}/${args.liveDesktops.length} target app HTTP-ready surface${completedWithMissingApp.length === 1 ? "" : "s"}.`,
1588
+ verdict: "blocked"
1589
+ };
1590
+ }
1591
+ const completedWithMissingVisual = args.liveDesktops.filter((desktop) => desktop.completion?.status === "passed"
1592
+ && desktop.completion.visualStatus !== "visible");
1593
+ if (completedWithMissingVisual.length > 0) {
1594
+ return {
1595
+ ok: false,
1596
+ reason: `OSS meta-lab completed nested Humanish setup but did not detect ${completedWithMissingVisual.length}/${args.liveDesktops.length} headed desktop visual layout${completedWithMissingVisual.length === 1 ? "" : "s"}.`,
1597
+ verdict: "blocked"
1598
+ };
1599
+ }
1600
+ const completedWithMissingAppServerActor = args.liveDesktops.filter((desktop) => desktop.bootstrap?.codexMode === "app-server-client"
1601
+ && desktop.completion?.status === "passed"
1602
+ && desktop.completion.actorStatus !== "passed");
1603
+ if (completedWithMissingAppServerActor.length > 0) {
1604
+ return {
1605
+ ok: false,
1606
+ reason: `OSS meta-lab completed nested Humanish setup but did not capture passed Codex app-server actor evidence for ${completedWithMissingAppServerActor.length}/${args.liveDesktops.length} headed desktop lane${completedWithMissingAppServerActor.length === 1 ? "" : "s"}.`,
1607
+ verdict: "blocked"
1608
+ };
1609
+ }
1610
+ if (args.liveDesktops.length > 0 && args.liveDesktops.every((desktop) => desktop.completion?.status === "passed")) {
1611
+ return {
1612
+ ok: true,
1613
+ reason: `OSS meta-lab passed ${args.liveDesktops.length}/${args.liveDesktops.length} bootstrap terminal states with target app HTTP readiness and headed desktop visual layout detected from public-safe remote evidence.`,
1614
+ verdict: "pass"
1615
+ };
1616
+ }
1617
+ return {
1618
+ ok: true,
1619
+ reason: "OSS meta-lab rendered the live headed-desktop control surface and marked the missing substrate truth in-lane.",
1620
+ verdict: "contract_proof_only"
1621
+ };
1622
+ }
1623
+ function terminalTailForMeta(prompt, liveDesktop) {
1624
+ if (!liveDesktop?.completion) {
1625
+ return liveDesktop?.bootstrap?.tail ?? prompt;
1626
+ }
1627
+ const lines = [
1628
+ `Remote bootstrap ${liveDesktop.completion.status}: ${liveDesktop.completion.reason}`,
1629
+ `checked_at: ${liveDesktop.completion.checkedAt}`,
1630
+ ...(liveDesktop.completion.appStatus === undefined ? [] : [`app_status: ${liveDesktop.completion.appStatus}`]),
1631
+ ...(liveDesktop.completion.appUrl === undefined ? [] : [`app_url: ${liveDesktop.completion.appUrl}`]),
1632
+ ...(liveDesktop.completion.appReason === undefined ? [] : [`app_reason: ${liveDesktop.completion.appReason}`]),
1633
+ ...(liveDesktop.completion.actorStatus === undefined ? [] : [`actor_status: ${liveDesktop.completion.actorStatus}`]),
1634
+ ...(liveDesktop.completion.exitCode === undefined ? [] : [`exit_code: ${liveDesktop.completion.exitCode}`]),
1635
+ ...(liveDesktop.completion.nestedVerifyPassed === undefined ? [] : [`nested_verify_passed: ${liveDesktop.completion.nestedVerifyPassed ? "true" : "false"}`]),
1636
+ ...(liveDesktop.completion.nestedObserverPresent === undefined ? [] : [`nested_observer_present: ${liveDesktop.completion.nestedObserverPresent ? "true" : "false"}`]),
1637
+ ...(liveDesktop.completion.visualStatus === undefined ? [] : [`visual_status: ${liveDesktop.completion.visualStatus}`]),
1638
+ ...(liveDesktop.completion.visualWindowCount === undefined ? [] : [`visual_window_count: ${liveDesktop.completion.visualWindowCount}`]),
1639
+ ...(liveDesktop.completion.visualReason === undefined ? [] : [`visual_reason: ${liveDesktop.completion.visualReason}`]),
1640
+ ...(liveDesktop.completion.setupQuality === undefined ? [] : [
1641
+ `setup_quality: ${liveDesktop.completion.setupQuality.status}`,
1642
+ `setup_summary: ${liveDesktop.completion.setupQuality.summary}`,
1643
+ ...(liveDesktop.completion.setupQuality.studyQuality === undefined ? [] : [
1644
+ `study_quality: ${liveDesktop.completion.setupQuality.studyQuality.rating}`,
1645
+ `study_summary: ${liveDesktop.completion.setupQuality.studyQuality.summary}`
1646
+ ])
1647
+ ]),
1648
+ "",
1649
+ "public-safe actor last message tail:",
1650
+ liveDesktop.completion.actorLastMessageTail?.trim() || "(no actor last-message captured)",
1651
+ "",
1652
+ "public-safe actor log tail:",
1653
+ liveDesktop.completion.actorLogTail?.trim() || "(no actor log tail captured)",
1654
+ "",
1655
+ "public-safe bootstrap log tail:",
1656
+ liveDesktop.completion.logTail?.trim() || "(no log tail captured)"
1657
+ ];
1658
+ return lines.join("\n").trim();
1659
+ }
1660
+ function completionForStream(completion, appServerMode = false) {
1661
+ const effectiveStatus = appServerMode && completion.status === "passed" && completion.actorStatus !== "passed"
1662
+ ? "blocked"
1663
+ : completion.status;
1664
+ const effectiveReason = effectiveStatus === "blocked" && completion.status === "passed" && completion.actorStatus !== "passed"
1665
+ ? "Codex app-server mode requires passed app-server actor evidence; actor evidence did not reach passed."
1666
+ : completion.reason;
1667
+ const meaningfulUse = scoreOssMetaMeaningfulUse({
1668
+ ...(completion.actorLastMessageTail === undefined ? {} : { actorLastMessageTail: completion.actorLastMessageTail }),
1669
+ ...(completion.actorLogTail === undefined ? {} : { actorLogTail: completion.actorLogTail }),
1670
+ actorRequired: appServerMode,
1671
+ ...(completion.actorStatus === undefined ? {} : { actorStatus: completion.actorStatus }),
1672
+ ...(completion.appStatus === undefined ? {} : { appStatus: completion.appStatus }),
1673
+ ...(completion.appUrl === undefined ? {} : { appUrl: completion.appUrl }),
1674
+ ...(completion.nestedObserverPresent === undefined ? {} : { nestedObserverPresent: completion.nestedObserverPresent }),
1675
+ ...(completion.nestedVerifyPassed === undefined ? {} : { nestedVerifyPassed: completion.nestedVerifyPassed }),
1676
+ ...(completion.setupQuality === undefined ? {} : { setupQuality: completion.setupQuality }),
1677
+ status: effectiveStatus,
1678
+ ...(completion.visualStatus === undefined ? {} : { visualStatus: completion.visualStatus })
1679
+ });
1680
+ return {
1681
+ ...(completion.actorLogPath === undefined ? {} : { actorLogPath: completion.actorLogPath }),
1682
+ ...(completion.actorLogTail === undefined ? {} : { actorLogTail: completion.actorLogTail }),
1683
+ ...(completion.actorLastMessageTail === undefined ? {} : { actorLastMessageTail: completion.actorLastMessageTail }),
1684
+ ...(completion.actorPid === undefined ? {} : { actorPid: completion.actorPid }),
1685
+ ...(completion.actorStatus === undefined ? {} : { actorStatus: completion.actorStatus }),
1686
+ ...(completion.appLogPath === undefined ? {} : { appLogPath: completion.appLogPath }),
1687
+ ...(completion.appPid === undefined ? {} : { appPid: completion.appPid }),
1688
+ ...(completion.appReason === undefined ? {} : { appReason: completion.appReason }),
1689
+ ...(completion.appStatus === undefined ? {} : { appStatus: completion.appStatus }),
1690
+ ...(completion.appUrl === undefined ? {} : { appUrl: completion.appUrl }),
1691
+ checkedAt: completion.checkedAt,
1692
+ ...(completion.exitCode === undefined ? {} : { exitCode: completion.exitCode }),
1693
+ ...(completion.logTail === undefined ? {} : { logTail: completion.logTail }),
1694
+ ...(completion.nestedObserverPresent === undefined ? {} : { nestedObserverPresent: completion.nestedObserverPresent }),
1695
+ ...(completion.nestedVerifyPassed === undefined ? {} : { nestedVerifyPassed: completion.nestedVerifyPassed }),
1696
+ reason: effectiveReason,
1697
+ status: effectiveStatus,
1698
+ meaningfulUse,
1699
+ ...(completion.visualReason === undefined ? {} : { visualReason: completion.visualReason }),
1700
+ ...(completion.visualStatus === undefined ? {} : { visualStatus: completion.visualStatus }),
1701
+ ...(completion.visualWindowCount === undefined ? {} : { visualWindowCount: completion.visualWindowCount })
1702
+ };
1703
+ }
1704
+ function buildMetaFeedbackCandidates(args) {
1705
+ const candidates = [];
1706
+ for (const assignment of args.assignments) {
1707
+ const desktop = args.liveDesktops.find((lane) => lane.streamId === assignment.streamId);
1708
+ if (!desktop) {
1709
+ continue;
1710
+ }
1711
+ const repoLabel = args.redactRepoNames ? repoArtifactLabel(assignment) : assignment.repo;
1712
+ const scenarioId = args.redactRepoNames ? `oss-meta-${repoLabel}` : assignment.scenarioId;
1713
+ const baseEvidence = feedbackEvidenceForDesktop(desktop);
1714
+ const setupQualityPath = desktop.actorEvidence?.setupQualityPath;
1715
+ const failedSetupChecks = desktop.completion?.setupQuality?.checks.filter((check) => !check.ok) ?? [];
1716
+ const studyQuality = desktop.completion?.setupQuality?.studyQuality;
1717
+ if (setupQualityPath && failedSetupChecks.length > 0) {
1718
+ candidates.push({
1719
+ schema: "humanish.feedback-candidate.v1",
1720
+ id: `setup-quality-${safeArtifactToken(assignment.streamId)}`,
1721
+ run_id: args.runId,
1722
+ stream_id: assignment.streamId,
1723
+ adapter_id: "oss-meta-lab",
1724
+ scenario_id: scenarioId,
1725
+ persona_id: `codex-oss-operator-${String(assignment.index).padStart(2, "0")}`,
1726
+ actor: "codex-tui",
1727
+ substrate: "e2b-desktop",
1728
+ failure_owner: "actor",
1729
+ summary: `Generated Humanish setup for ${repoLabel} needs review`,
1730
+ expected: "The setup actor should create committed Humanish source files, useful personas/scenarios, a package script, and a .humanish/ runtime ignore without preserving private state.",
1731
+ actual: failedSetupChecks.map((check) => `${check.label}: ${check.detail}`).join(" "),
1732
+ evidence: [
1733
+ {
1734
+ path: setupQualityPath,
1735
+ kind: "filesystem",
1736
+ note: "Setup-quality snapshot with tree, checks, package scripts, and allowlisted previews."
1737
+ },
1738
+ ...baseEvidence
1739
+ ],
1740
+ redaction: {
1741
+ status: "passed",
1742
+ notes: "Feedback candidate references local public-safe run artifacts only."
1743
+ },
1744
+ idempotency_key: `humanish:${args.runId}:${assignment.streamId}:setup-quality`,
1745
+ proposed_next_state: "setup-quality-review",
1746
+ acceptance_proof: [
1747
+ `pnpm humanish -- verify --run ${args.runId} --json`,
1748
+ `pnpm humanish -- watch --run ${args.runId} --no-open`
1749
+ ]
1750
+ });
1751
+ }
1752
+ const actorText = `${desktop.completion?.actorLastMessageTail ?? ""}\n${desktop.completion?.actorLogTail ?? ""}`;
1753
+ if (hasAppUrlProofBlocker(actorText)) {
1754
+ candidates.push({
1755
+ schema: "humanish.feedback-candidate.v1",
1756
+ id: `published-cli-app-url-${safeArtifactToken(assignment.streamId)}`,
1757
+ run_id: args.runId,
1758
+ stream_id: assignment.streamId,
1759
+ adapter_id: "oss-meta-lab",
1760
+ scenario_id: scenarioId,
1761
+ persona_id: `codex-oss-operator-${String(assignment.index).padStart(2, "0")}`,
1762
+ actor: "codex-tui",
1763
+ substrate: "e2b-desktop",
1764
+ failure_owner: "harness",
1765
+ summary: "Published Humanish install path blocked app-url proof",
1766
+ expected: "A fresh npm-installed Humanish CLI should support the app-url live proof path documented for agents.",
1767
+ actual: "The actor evidence reports that the installed CLI did not accept or expose the app-url proof option.",
1768
+ evidence: baseEvidence,
1769
+ redaction: {
1770
+ status: "passed",
1771
+ notes: "Actor evidence was redacted before persistence."
1772
+ },
1773
+ idempotency_key: `humanish:${args.runId}:${assignment.streamId}:published-cli-app-url`,
1774
+ proposed_next_state: "adapter-hardening",
1775
+ acceptance_proof: [
1776
+ "npm view humanish version",
1777
+ "npx --yes --package humanish humanish run --help | grep -- --app-url",
1778
+ `pnpm humanish -- verify --run ${args.runId} --json`
1779
+ ]
1780
+ });
1781
+ }
1782
+ if (setupQualityPath && studyQuality && (studyQuality.rating === "none" || studyQuality.rating === "ceremonial")) {
1783
+ candidates.push({
1784
+ schema: "humanish.feedback-candidate.v1",
1785
+ id: `study-quality-${safeArtifactToken(assignment.streamId)}`,
1786
+ run_id: args.runId,
1787
+ stream_id: assignment.streamId,
1788
+ adapter_id: "oss-meta-lab",
1789
+ scenario_id: scenarioId,
1790
+ persona_id: `codex-oss-operator-${String(assignment.index).padStart(2, "0")}`,
1791
+ actor: "codex-tui",
1792
+ substrate: "e2b-desktop",
1793
+ failure_owner: "actor",
1794
+ summary: `Generated Humanish setup for ${repoLabel} was ${studyQuality.rating}`,
1795
+ expected: "The setup actor should turn Humanish init into an app-aware user-study plan with customized coverage, personas, scenarios, app-url proof, and public-safe feedback.",
1796
+ actual: studyQuality.summary,
1797
+ evidence: [
1798
+ {
1799
+ path: setupQualityPath,
1800
+ kind: "filesystem",
1801
+ note: "Setup-quality snapshot includes study-quality checks and public-safe structural signals."
1802
+ },
1803
+ ...baseEvidence
1804
+ ],
1805
+ redaction: {
1806
+ status: "passed",
1807
+ notes: "Study-quality feedback candidate references local public-safe artifacts only."
1808
+ },
1809
+ idempotency_key: `humanish:${args.runId}:${assignment.streamId}:study-quality`,
1810
+ proposed_next_state: "study-quality-review",
1811
+ acceptance_proof: [
1812
+ `pnpm humanish -- verify --run ${args.runId} --json`,
1813
+ `pnpm humanish -- watch --run ${args.runId} --no-open`,
1814
+ "Study-quality rating is useful or high_leverage, or the remaining ceremonial state is explicitly explained."
1815
+ ]
1816
+ });
1817
+ }
1818
+ }
1819
+ return candidates.slice(0, 20);
1820
+ }
1821
+ function feedbackEvidenceForDesktop(desktop) {
1822
+ const evidence = [];
1823
+ if (desktop.actorEvidence?.actorLastMessageTailPath) {
1824
+ evidence.push({
1825
+ path: desktop.actorEvidence.actorLastMessageTailPath,
1826
+ kind: "log",
1827
+ note: "Public-safe actor last-message tail."
1828
+ });
1829
+ }
1830
+ if (desktop.actorEvidence?.actorLogTailPath) {
1831
+ evidence.push({
1832
+ path: desktop.actorEvidence.actorLogTailPath,
1833
+ kind: "log",
1834
+ note: "Public-safe actor log tail."
1835
+ });
1836
+ }
1837
+ if (desktop.screenshot?.path) {
1838
+ evidence.push({
1839
+ path: desktop.screenshot.path,
1840
+ kind: "screenshot",
1841
+ note: "Headed desktop screenshot fallback."
1842
+ });
1843
+ }
1844
+ if (desktop.hostActorPlanPath) {
1845
+ evidence.push({
1846
+ path: desktop.hostActorPlanPath,
1847
+ kind: "trace",
1848
+ note: "Host-authored public-safe actor plan."
1849
+ });
1850
+ }
1851
+ return evidence;
1852
+ }
1853
+ function eventLevelForCompletion(status) {
1854
+ if (status === "passed")
1855
+ return "info";
1856
+ if (status === "running")
1857
+ return "info";
1858
+ if (status === "blocked" || status === "timed_out")
1859
+ return "warn";
1860
+ return "error";
1861
+ }
1862
+ function isTerminalCompletion(completion) {
1863
+ return completion !== undefined && completion.status !== "running";
1864
+ }
1865
+ function buildCodexBootstrapPrompt(assignment, redactRepoName = false) {
1866
+ const repoLabel = redactRepoName ? "[redacted-authorized-repo]" : `https://github.com/${assignment.repo}.git`;
1867
+ return [
1868
+ `# Humanish OSS Meta-Lab Actor ${assignment.index}`,
1869
+ "",
1870
+ "You are running inside a disposable headed E2B desktop with a visible terminal and browser.",
1871
+ "Public-safety hard rails: use only authorized repo contents; never print keys; never commit, push, file issues, or preserve private artifacts.",
1872
+ "",
1873
+ `Target repo: ${repoLabel}`,
1874
+ "",
1875
+ "Mission:",
1876
+ "1. Clone the target repo into a clean disposable workspace.",
1877
+ "2. Inspect the package manager, dev scripts, README, and app shape.",
1878
+ "3. Get the repo into a local runnable dev mode if feasible.",
1879
+ "4. Discover the Humanish skill path and try installing it with `npx skills add danielgwilson/humanish --skill humanish`.",
1880
+ "5. Install Humanish as a dev dependency with the package manager the repo already uses. The package is `humanish`; the binary is `humanish`.",
1881
+ "6. Run `npx --no-install humanish init --yes` or the package-manager equivalent. Do not run bare `npx humanish` unless you have confirmed it resolves the local `humanish` binary.",
1882
+ "7. Replace starter coverage files with an app-aware `humanish/coverage-map.md` and `humanish/coverage-matrix.md` that name real screens, roles, states, happy paths, and at least one sad/friction path discovered from the repo/app.",
1883
+ "8. Author at least two public-safe, app-specific Humanish personas and two desktop/mobile browser scenarios. Avoid generic `first-run-smoke` only; each scenario should name a target journey, route/state, expected evidence, and a failure/friction check.",
1884
+ "9. Run `npx --no-install humanish run --help` and verify `--app-url` is available. If the local binary is missing, install `humanish`; one-shot fallback is `npx --yes --package humanish@latest humanish run --help`.",
1885
+ "10. If the app is running locally, run `npx --no-install humanish run --app-url <loopback-url> --sims 2`; do not use `humanish watch --sims` as app behavior proof.",
1886
+ "11. After `run --app-url`, render or open the nested Humanish Observer with `npx --no-install humanish watch --run latest --detach --no-open --json` and keep it visible.",
1887
+ "12. Final summary must be public-safe and include: personas/scenarios created, product journeys covered, one observed friction/improvement or `none observed`, and evidence paths. Do not stop at install/init proof.",
1888
+ "13. Record public-safe blockers and evidence paths only.",
1889
+ "",
1890
+ "Expected nested outcome: the top-level Humanish Observer shows this desktop, and this desktop shows its own nested Humanish Observer."
1891
+ ].join("\n");
1892
+ }
1893
+ function renderMetaReviewMarkdown(bundle) {
1894
+ return `# Humanish OSS Meta-Lab Review
1895
+
1896
+ Run: ${bundle.runId}
1897
+
1898
+ Verdict: ${bundle.review.verdict}
1899
+
1900
+ ${bundle.review.summary}
1901
+
1902
+ ## Public-Safety
1903
+
1904
+ - Redaction: ${bundle.redaction.status}
1905
+ - Notes: ${bundle.redaction.notes}
1906
+
1907
+ ## Assigned Repos
1908
+
1909
+ ${bundle.streams.map((stream) => `- ${stream.label}: ${stream.simId}`).join("\n")}
1910
+
1911
+ ## Gaps
1912
+
1913
+ ${bundle.review.gaps.map((gap) => `- ${gap}`).join("\n")}
1914
+ `;
1915
+ }
1916
+ async function launchLiveDesktops(assignments, options = {}) {
1917
+ const e2bApiKey = process.env.E2B_API_KEY;
1918
+ if (!e2bApiKey) {
1919
+ return [];
1920
+ }
1921
+ const desktopModule = await loadE2BDesktopModule();
1922
+ const timeoutMs = readPositiveInt(process.env.HUMANISH_E2B_TIMEOUT_MS, 60 * 60 * 1000);
1923
+ const requestTimeoutMs = readPositiveInt(process.env.HUMANISH_E2B_REQUEST_TIMEOUT_MS, 60_000);
1924
+ return Promise.all(assignments.map(async (assignment) => {
1925
+ const repoLabel = options.redactRepoNames ? repoArtifactLabel(assignment) : assignment.repo;
1926
+ const hostActorPlanResult = options.hostActorPlansByStream?.get(assignment.streamId);
1927
+ // Retain the provider object in process memory so attached cleanup can use a trusted handle.
1928
+ // Durable result IDs are evidence only and never authorize provider mutation.
1929
+ let desktop;
1930
+ try {
1931
+ desktop = await desktopModule.Sandbox.create({
1932
+ apiKey: e2bApiKey,
1933
+ requestTimeoutMs,
1934
+ timeoutMs,
1935
+ metadata: {
1936
+ ...OSS_META_LAB_PROVIDER_METADATA,
1937
+ repo: repoLabel,
1938
+ simId: assignment.simId
1939
+ },
1940
+ envs: {
1941
+ ...collectOssMetaLabRemoteEnv(process.env),
1942
+ ...(options.codexAppServerMode ? { HUMANISH_OSS_META_CODEX_APP_SERVER: "1" } : {})
1943
+ },
1944
+ resolution: [1440, 960],
1945
+ dpi: 96,
1946
+ lifecycle: {
1947
+ onTimeout: "kill"
1948
+ }
1949
+ });
1950
+ const bootstrap = await startOssBootstrap(desktop, assignment, options.localPackage, requestTimeoutMs, {
1951
+ ...(options.codexAppServerMode === undefined ? {} : { codexAppServerMode: options.codexAppServerMode }),
1952
+ ...(hostActorPlanResult === undefined ? {} : { hostActorPlanResult }),
1953
+ repoLabel,
1954
+ token: options.redactRepoNames ? repoLabel : repoSlug(assignment.repo)
1955
+ });
1956
+ await desktop.wait(750).catch(() => undefined);
1957
+ await desktop.stream.start({ requireAuth: true });
1958
+ const authKey = desktop.stream.getAuthKey();
1959
+ const url = desktop.stream.getUrl({
1960
+ authKey,
1961
+ autoConnect: true,
1962
+ viewOnly: true,
1963
+ resize: "scale"
1964
+ });
1965
+ return {
1966
+ bootstrap,
1967
+ desktop,
1968
+ ...(hostActorPlanResult?.plan ? { hostActorPlan: hostActorPlanResult.plan } : {}),
1969
+ ...(hostActorPlanResult?.artifactPath ? { hostActorPlanPath: hostActorPlanResult.artifactPath } : {}),
1970
+ repo: repoLabel,
1971
+ sandboxId: desktop.sandboxId,
1972
+ simId: assignment.simId,
1973
+ streamId: assignment.streamId,
1974
+ url
1975
+ };
1976
+ }
1977
+ catch (error) {
1978
+ return {
1979
+ error: compactError(error),
1980
+ ...(desktop ? { desktop } : {}),
1981
+ ...(hostActorPlanResult?.plan ? { hostActorPlan: hostActorPlanResult.plan } : {}),
1982
+ ...(hostActorPlanResult?.artifactPath ? { hostActorPlanPath: hostActorPlanResult.artifactPath } : {}),
1983
+ repo: repoLabel,
1984
+ // Keep the id as public-safe lifecycle evidence only. Cleanup authority comes from the
1985
+ // in-memory provider object above or a separately verified provider-metadata sweep.
1986
+ ...(desktop?.sandboxId ? { sandboxId: desktop.sandboxId } : {}),
1987
+ simId: assignment.simId,
1988
+ streamId: assignment.streamId
1989
+ };
1990
+ }
1991
+ }));
1992
+ }
1993
+ async function pollLiveDesktopCompletions(liveDesktops, options = {}) {
1994
+ const pollable = liveDesktops.filter((desktop) => desktop.desktop
1995
+ && desktop.bootstrap?.status === "started"
1996
+ && desktop.bootstrap.completionPath);
1997
+ if (pollable.length === 0) {
1998
+ return { warnings: [] };
1999
+ }
2000
+ const timeoutMs = options.timeoutMs ?? readNonNegativeInt(process.env.HUMANISH_OSS_META_COMPLETION_TIMEOUT_MS, 240_000);
2001
+ const intervalMs = readPositiveInt(process.env.HUMANISH_OSS_META_COMPLETION_INTERVAL_MS, 5_000);
2002
+ const requestTimeoutMs = readPositiveInt(process.env.HUMANISH_E2B_REQUEST_TIMEOUT_MS, 60_000);
2003
+ const warnings = [];
2004
+ if (timeoutMs === 0) {
2005
+ warnings.push(`Initial OSS meta-lab completion wait skipped because ${options.timeoutReason ?? "HUMANISH_OSS_META_COMPLETION_TIMEOUT_MS=0"}; attached watch continues polling while the Observer is open.`);
2006
+ return { warnings };
2007
+ }
2008
+ const deadline = Date.now() + timeoutMs;
2009
+ while (Date.now() <= deadline) {
2010
+ await Promise.all(pollable.map(async (desktop) => {
2011
+ if (isTerminalCompletion(desktop.completion) || !desktop.desktop || !desktop.bootstrap?.completionPath) {
2012
+ return;
2013
+ }
2014
+ const completion = await readRemoteCompletion(desktop.desktop, desktop.bootstrap, requestTimeoutMs);
2015
+ if (completion) {
2016
+ desktop.completion = completion;
2017
+ }
2018
+ }));
2019
+ if (pollable.every((desktop) => isTerminalCompletion(desktop.completion))) {
2020
+ return { warnings };
2021
+ }
2022
+ await wait(Math.min(intervalMs, Math.max(0, deadline - Date.now())));
2023
+ }
2024
+ await Promise.all(pollable.map(async (desktop) => {
2025
+ if (isTerminalCompletion(desktop.completion) || !desktop.desktop || !desktop.bootstrap) {
2026
+ return;
2027
+ }
2028
+ desktop.completion = {
2029
+ checkedAt: new Date().toISOString(),
2030
+ logTail: await readRemoteLogTail(desktop.desktop, desktop.bootstrap, requestTimeoutMs),
2031
+ reason: `Timed out waiting ${timeoutMs}ms for remote bootstrap completion marker.`,
2032
+ status: "timed_out"
2033
+ };
2034
+ }));
2035
+ warnings.push(`Timed out waiting for ${pollable.filter((desktop) => desktop.completion?.status === "timed_out").length}/${pollable.length} OSS meta-lab bootstrap completion marker${pollable.length === 1 ? "" : "s"}.`);
2036
+ return { warnings };
2037
+ }
2038
+ async function refreshOssMetaLabLiveRuntime(runtime, options) {
2039
+ await refreshLiveDesktopProgress(runtime.liveDesktops, {
2040
+ timedOut: options.timedOut,
2041
+ timeoutMs: options.timeoutMs
2042
+ });
2043
+ if (options.captureScreenshots) {
2044
+ await captureLiveDesktopScreenshots(runtime.artifactRoot, runtime.liveDesktops);
2045
+ }
2046
+ await writeActorEvidenceArtifacts(runtime.artifactRoot, runtime.liveDesktops, {
2047
+ assignments: runtime.assignments,
2048
+ redactRepoNames: runtime.redactRepoNames
2049
+ });
2050
+ const bundle = buildMetaBundle({
2051
+ assignments: runtime.assignments,
2052
+ createdAt: runtime.createdAt,
2053
+ cwd: runtime.cwd,
2054
+ dryRun: runtime.dryRun,
2055
+ liveDesktops: runtime.liveDesktops,
2056
+ liveRequested: runtime.liveRequested,
2057
+ missingKeys: runtime.missingKeys,
2058
+ redactRepoNames: runtime.redactRepoNames,
2059
+ runId: runtime.runId,
2060
+ source: runtime.source
2061
+ });
2062
+ await writeMetaBundleArtifacts(runtime.artifactRoot, bundle);
2063
+ await renderObserver(runtime.physicalCwd, runtime.runId, { open: false });
2064
+ }
2065
+ async function refreshLiveDesktopProgress(liveDesktops, options) {
2066
+ const requestTimeoutMs = readPositiveInt(process.env.HUMANISH_E2B_REQUEST_TIMEOUT_MS, 60_000);
2067
+ await Promise.all(liveDesktops.map(async (desktop) => {
2068
+ if (!desktop.desktop || desktop.bootstrap?.status !== "started") {
2069
+ return;
2070
+ }
2071
+ if (isTerminalCompletion(desktop.completion)) {
2072
+ return;
2073
+ }
2074
+ const completion = await readRemoteCompletion(desktop.desktop, desktop.bootstrap, requestTimeoutMs);
2075
+ if (completion) {
2076
+ desktop.completion = completion;
2077
+ return;
2078
+ }
2079
+ const logTail = await readRemoteLogTail(desktop.desktop, desktop.bootstrap, requestTimeoutMs);
2080
+ desktop.completion = {
2081
+ checkedAt: new Date().toISOString(),
2082
+ logTail,
2083
+ reason: options.timedOut
2084
+ ? `Timed out waiting ${options.timeoutMs}ms for remote bootstrap completion marker.`
2085
+ : logTail
2086
+ ? "Remote bootstrap is running; latest public-safe log tail is available."
2087
+ : "Remote bootstrap is running; waiting for first public-safe log output.",
2088
+ status: options.timedOut ? "timed_out" : "running"
2089
+ };
2090
+ }));
2091
+ }
2092
+ async function readRemoteCompletion(desktop, bootstrap, requestTimeoutMs) {
2093
+ if (!bootstrap.completionPath) {
2094
+ return null;
2095
+ }
2096
+ const command = `if [ -f ${shellQuote(bootstrap.completionPath)} ]; then cat ${shellQuote(bootstrap.completionPath)}; else exit 3; fi`;
2097
+ const result = await desktop.commands.run(`bash -lc ${shellQuote(command)}`, {
2098
+ requestTimeoutMs,
2099
+ timeoutMs: 30_000
2100
+ }).catch(() => null);
2101
+ if (!result || (result.exitCode && result.exitCode !== 0) || !result.stdout) {
2102
+ return null;
2103
+ }
2104
+ return parseRemoteCompletion(result.stdout);
2105
+ }
2106
+ async function readRemoteLogTail(desktop, bootstrap, requestTimeoutMs) {
2107
+ if (!bootstrap.logPath) {
2108
+ return "";
2109
+ }
2110
+ const command = `tail -n 80 ${shellQuote(bootstrap.logPath)} 2>/dev/null || true`;
2111
+ const result = await desktop.commands.run(`bash -lc ${shellQuote(command)}`, {
2112
+ requestTimeoutMs,
2113
+ timeoutMs: 30_000
2114
+ }).catch(() => null);
2115
+ return sanitizeRemoteLog(result?.stdout ?? "");
2116
+ }
2117
+ async function captureLiveDesktopScreenshots(artifactRoot, liveDesktops, options = { redactRepoNames: false }) {
2118
+ const candidates = liveDesktops.filter((desktop) => desktop.desktop && desktop.url);
2119
+ if (candidates.length === 0) {
2120
+ return { warnings: [] };
2121
+ }
2122
+ const warnings = [];
2123
+ await Promise.all(candidates.map(async (desktop) => {
2124
+ if (!desktop.desktop) {
2125
+ return;
2126
+ }
2127
+ try {
2128
+ await arrangeLiveDesktopForScreenshot(desktop.desktop, desktop.bootstrap?.terminalTitle, readPositiveInt(process.env.HUMANISH_E2B_REQUEST_TIMEOUT_MS, 60_000));
2129
+ await desktop.desktop.wait(readPositiveInt(process.env.HUMANISH_OSS_META_SCREENSHOT_SETTLE_MS, 2_500)).catch(() => undefined);
2130
+ const bytes = await desktop.desktop.screenshot("bytes");
2131
+ const fileName = `${safeArtifactToken(desktop.streamId)}.png`;
2132
+ await writeContainedOutputFile(artifactRoot, path.join("screenshots", fileName), Buffer.from(bytes));
2133
+ desktop.screenshot = {
2134
+ capturedAt: new Date().toISOString(),
2135
+ observerUrl: `../screenshots/${fileName}`,
2136
+ path: path.join("screenshots", fileName)
2137
+ };
2138
+ }
2139
+ catch (error) {
2140
+ const laneLabel = options.redactRepoNames ? desktop.streamId : desktop.repo;
2141
+ warnings.push(`Screenshot capture failed for ${laneLabel}: ${compactError(error)}`);
2142
+ }
2143
+ }));
2144
+ const capturedCount = liveDesktops.filter((desktop) => desktop.screenshot).length;
2145
+ if (capturedCount > 0) {
2146
+ warnings.push(options.redactRepoNames
2147
+ ? `Captured ${capturedCount}/${candidates.length} local-only redacted E2B desktop screenshot fallback${candidates.length === 1 ? "" : "s"}; do not publish private screenshots.`
2148
+ : `Captured ${capturedCount}/${candidates.length} E2B desktop screenshot fallback${candidates.length === 1 ? "" : "s"}.`);
2149
+ }
2150
+ return { warnings };
2151
+ }
2152
+ async function writeActorEvidenceArtifacts(artifactRoot, liveDesktops, options) {
2153
+ const candidates = liveDesktops.filter((desktop) => desktop.completion?.actorLastMessageTail
2154
+ || desktop.completion?.actorLogTail
2155
+ || desktop.completion?.appServerActorEvidence
2156
+ || desktop.completion?.nestedObserverPresent !== undefined
2157
+ || desktop.completion?.nestedStepTraceSummary !== undefined
2158
+ || desktop.completion?.nestedVerifyPassed !== undefined
2159
+ || desktop.completion?.setupQuality);
2160
+ if (candidates.length === 0) {
2161
+ return { warnings: [] };
2162
+ }
2163
+ let written = 0;
2164
+ for (const desktop of candidates) {
2165
+ const assignment = options.assignments.find((candidate) => candidate.streamId === desktop.streamId);
2166
+ const repoForRedaction = options.redactRepoNames ? assignment?.repo : undefined;
2167
+ const baseName = safeArtifactToken(desktop.streamId);
2168
+ const actorEvidence = {};
2169
+ if (desktop.completion?.actorLastMessageTail) {
2170
+ const relativePath = path.join("actor-evidence", `${baseName}-actor-last-message-tail.txt`);
2171
+ await writeContainedOutputFile(artifactRoot, relativePath, renderPublicSafeActorEvidenceText("actor-last-message", desktop.streamId, desktop.completion.actorLastMessageTail, {
2172
+ providerRuntimeId: options.redactRepoNames ? desktop.sandboxId : undefined,
2173
+ repo: repoForRedaction
2174
+ }), "utf8");
2175
+ actorEvidence.actorLastMessageTailPath = relativePath;
2176
+ written += 1;
2177
+ }
2178
+ if (desktop.completion?.actorLogTail) {
2179
+ const relativePath = path.join("actor-evidence", `${baseName}-actor-log-tail.txt`);
2180
+ await writeContainedOutputFile(artifactRoot, relativePath, renderPublicSafeActorEvidenceText("actor-log", desktop.streamId, desktop.completion.actorLogTail, {
2181
+ providerRuntimeId: options.redactRepoNames ? desktop.sandboxId : undefined,
2182
+ repo: repoForRedaction
2183
+ }), "utf8");
2184
+ actorEvidence.actorLogTailPath = relativePath;
2185
+ written += 1;
2186
+ }
2187
+ if (desktop.completion?.setupQuality) {
2188
+ const relativePath = path.join("setup-quality", `${baseName}-setup-quality.json`);
2189
+ const snapshot = options.redactRepoNames
2190
+ ? redactSetupQualityRepoMentions(suppressSetupQualityPreviews(desktop.completion.setupQuality), repoForRedaction)
2191
+ : desktop.completion.setupQuality;
2192
+ await writeJson(artifactRoot, relativePath, snapshot);
2193
+ actorEvidence.setupQualityPath = relativePath;
2194
+ written += 1;
2195
+ }
2196
+ if (desktop.completion && (desktop.completion.nestedObserverPresent !== undefined
2197
+ || desktop.completion.nestedStepTraceSummary !== undefined
2198
+ || desktop.completion.nestedVerifyPassed !== undefined)) {
2199
+ const relativePath = path.join("nested-evidence", `${baseName}-nested-proof.json`);
2200
+ await writeJson(artifactRoot, relativePath, {
2201
+ schema: "humanish.oss-meta-nested-proof.v1",
2202
+ streamId: desktop.streamId,
2203
+ redaction: {
2204
+ status: "passed",
2205
+ notes: "Nested proof summary contains booleans and redacted local artifact pointers only; remote sandbox paths are intentionally omitted."
2206
+ },
2207
+ status: desktop.completion.status,
2208
+ reason: desktop.completion.reason,
2209
+ checkedAt: desktop.completion.checkedAt,
2210
+ nestedObserverPresent: desktop.completion.nestedObserverPresent === true,
2211
+ nestedVerifyPassed: desktop.completion.nestedVerifyPassed === true,
2212
+ ...(desktop.completion.nestedStepTraceSummary ? { stepTraceSummary: desktop.completion.nestedStepTraceSummary } : {}),
2213
+ appStatus: desktop.completion.appStatus ?? "unknown",
2214
+ actorStatus: desktop.completion.actorStatus ?? "unknown"
2215
+ });
2216
+ actorEvidence.nestedEvidencePath = relativePath;
2217
+ written += 1;
2218
+ }
2219
+ if (desktop.completion?.appServerActorEvidence) {
2220
+ const evidence = desktop.completion.appServerActorEvidence;
2221
+ const tracePath = path.join("codex-app-server", `${baseName}-summary.json`);
2222
+ const eventsPath = path.join("codex-app-server", `${baseName}-events.ndjson`);
2223
+ const transcriptPath = path.join("codex-app-server", `${baseName}-transcript.txt`);
2224
+ await writeJson(artifactRoot, tracePath, isRecord(evidence.traceJson)
2225
+ ? evidence.traceJson
2226
+ : {
2227
+ schema: "humanish.codex-app-server-trace.missing.v1",
2228
+ redaction: { status: "passed" },
2229
+ status: evidence.status ?? "unknown",
2230
+ reason: evidence.reason ?? "Remote app-server trace JSON was not captured.",
2231
+ traceText: evidence.traceText ?? ""
2232
+ });
2233
+ await writeContainedOutputFile(artifactRoot, eventsPath, evidence.eventsText ?? "No app-server event envelope tail captured.\n", "utf8");
2234
+ await writeContainedOutputFile(artifactRoot, transcriptPath, evidence.transcriptText ?? "No app-server transcript tail captured.\n", "utf8");
2235
+ evidence.tracePath = tracePath;
2236
+ evidence.eventsPath = eventsPath;
2237
+ evidence.transcriptPath = transcriptPath;
2238
+ actorEvidence.appServerTracePath = tracePath;
2239
+ actorEvidence.appServerEventsPath = eventsPath;
2240
+ actorEvidence.appServerTranscriptPath = transcriptPath;
2241
+ written += 3;
2242
+ }
2243
+ desktop.actorEvidence = actorEvidence;
2244
+ }
2245
+ return {
2246
+ warnings: [
2247
+ `Persisted ${written} public-safe local actor evidence artifact${written === 1 ? "" : "s"}.`
2248
+ ]
2249
+ };
2250
+ }
2251
+ function suppressSetupQualityPreviews(snapshot) {
2252
+ return {
2253
+ ...snapshot,
2254
+ summary: sanitizeSetupQualityText(snapshot.summary),
2255
+ checks: snapshot.checks.map((check) => ({
2256
+ ...check,
2257
+ label: sanitizeSetupQualityText(check.label),
2258
+ detail: sanitizeSetupQualityText(check.detail)
2259
+ })),
2260
+ ...(snapshot.studyQuality ? { studyQuality: sanitizeStudyQualitySnapshot(snapshot.studyQuality) } : {}),
2261
+ previews: [],
2262
+ redaction: {
2263
+ status: "passed",
2264
+ rawPreviews: "suppressed",
2265
+ notes: "Raw file previews are suppressed for token-backed/private OSS meta-lab runs."
2266
+ }
2267
+ };
2268
+ }
2269
+ function renderPublicSafeActorEvidenceText(kind, streamId, text, redaction) {
2270
+ const sanitized = sanitizeRemoteLog(redactPrivateActorEvidence(text, redaction));
2271
+ return [
2272
+ `schema: humanish.oss-meta-actor-evidence.v1`,
2273
+ `kind: ${kind}`,
2274
+ `stream: ${streamId}`,
2275
+ `redaction: passed`,
2276
+ "",
2277
+ sanitized || "(no actor evidence captured)"
2278
+ ].join("\n").trimEnd() + "\n";
2279
+ }
2280
+ function redactLiveDesktopRepoMentions(desktop, repo) {
2281
+ return {
2282
+ ...desktop,
2283
+ ...(desktop.bootstrap
2284
+ ? {
2285
+ bootstrap: {
2286
+ ...desktop.bootstrap,
2287
+ tail: redactPrivateRuntimeMentions(desktop.bootstrap.tail, { providerRuntimeId: desktop.sandboxId, repo })
2288
+ }
2289
+ }
2290
+ : {}),
2291
+ ...(desktop.completion
2292
+ ? { completion: redactCompletionRepoMentions(desktop.completion, repo, desktop.sandboxId) }
2293
+ : {})
2294
+ };
2295
+ }
2296
+ function redactCompletionRepoMentions(completion, repo, providerRuntimeId) {
2297
+ const redaction = { providerRuntimeId, repo };
2298
+ return {
2299
+ ...completion,
2300
+ ...(completion.actorLogTail === undefined ? {} : { actorLogTail: redactPrivateActorEvidence(completion.actorLogTail, redaction) }),
2301
+ ...(completion.actorLastMessageTail === undefined ? {} : { actorLastMessageTail: redactPrivateActorEvidence(completion.actorLastMessageTail, redaction) }),
2302
+ ...(completion.appServerActorEvidence === undefined ? {} : { appServerActorEvidence: redactAppServerActorEvidence(completion.appServerActorEvidence, redaction) }),
2303
+ ...(completion.appReason === undefined ? {} : { appReason: redactPrivateRuntimeMentions(completion.appReason, redaction) }),
2304
+ ...(completion.logTail === undefined ? {} : { logTail: redactPrivateRuntimeMentions(completion.logTail, redaction) }),
2305
+ ...(completion.nestedStepTraceSummary === undefined ? {} : { nestedStepTraceSummary: redactNestedStepTraceSummary(completion.nestedStepTraceSummary, redaction) }),
2306
+ reason: redactPrivateRuntimeMentions(completion.reason, redaction),
2307
+ ...(completion.setupQuality === undefined ? {} : { setupQuality: redactSetupQualityRepoMentions(completion.setupQuality, repo) }),
2308
+ ...(completion.visualReason === undefined ? {} : { visualReason: redactPrivateRuntimeMentions(completion.visualReason, redaction) })
2309
+ };
2310
+ }
2311
+ function redactNestedStepTraceSummary(summary, redaction) {
2312
+ return normalizeNestedStepTraceSummary(redactJsonValue(summary, redaction)) ?? summary;
2313
+ }
2314
+ function redactAppServerActorEvidence(evidence, redaction) {
2315
+ return {
2316
+ ...evidence,
2317
+ ...(evidence.eventsText === undefined ? {} : { eventsText: redactPrivateActorEvidence(evidence.eventsText, redaction) }),
2318
+ ...(evidence.reason === undefined ? {} : { reason: redactPrivateRuntimeMentions(evidence.reason, redaction) }),
2319
+ ...(evidence.status === undefined ? {} : { status: redactPrivateRuntimeMentions(evidence.status, redaction) }),
2320
+ ...(evidence.traceJson === undefined ? {} : { traceJson: redactJsonValue(evidence.traceJson, redaction) }),
2321
+ ...(evidence.traceText === undefined ? {} : { traceText: redactPrivateActorEvidence(evidence.traceText, redaction) }),
2322
+ ...(evidence.transcriptText === undefined ? {} : { transcriptText: redactPrivateActorEvidence(evidence.transcriptText, redaction) })
2323
+ };
2324
+ }
2325
+ function redactJsonValue(value, redaction) {
2326
+ try {
2327
+ return JSON.parse(redactPrivateActorEvidence(JSON.stringify(value), redaction));
2328
+ }
2329
+ catch {
2330
+ return value;
2331
+ }
2332
+ }
2333
+ function redactPrivateActorEvidence(text, redaction) {
2334
+ const redacted = redactPrivateRuntimeMentions(text, redaction);
2335
+ return redaction?.repo ? stripSourceDiffBlocks(redacted) : redacted;
2336
+ }
2337
+ function stripSourceDiffBlocks(text) {
2338
+ const lines = text.split(/\r?\n/);
2339
+ const output = [];
2340
+ let inDiff = false;
2341
+ let emittedMarker = false;
2342
+ for (const line of lines) {
2343
+ if (/^diff --git\b/.test(line)) {
2344
+ if (!emittedMarker) {
2345
+ output.push("[redacted-source-diff]");
2346
+ emittedMarker = true;
2347
+ }
2348
+ inDiff = true;
2349
+ continue;
2350
+ }
2351
+ if (inDiff) {
2352
+ if (/^(tokens used|Installed\b|Humanish\b|Created personas:|Created browser scenarios:|Product journeys covered:|Observed friction|Evidence paths:|Verification:|If you want\b)/.test(line)) {
2353
+ inDiff = false;
2354
+ }
2355
+ else {
2356
+ continue;
2357
+ }
2358
+ }
2359
+ output.push(line);
2360
+ }
2361
+ return output.join("\n");
2362
+ }
2363
+ function redactSetupQualityRepoMentions(snapshot, repo) {
2364
+ if (!repo) {
2365
+ return snapshot;
2366
+ }
2367
+ return JSON.parse(JSON.stringify(snapshot, (_key, value) => typeof value === "string" ? redactRepoMentions(value, repo) : value));
2368
+ }
2369
+ function redactRepoMentions(text, repo) {
2370
+ const [owner, name] = repo.split("/");
2371
+ if (!owner || !name) {
2372
+ return text;
2373
+ }
2374
+ const replacement = "[redacted-authorized-repo]";
2375
+ let redacted = text
2376
+ .replace(new RegExp(`https://github\\.com/${escapeRegExp(owner)}/${escapeRegExp(name)}(?:\\.git)?`, "gi"), replacement)
2377
+ .replace(new RegExp(`git@github\\.com:${escapeRegExp(owner)}/${escapeRegExp(name)}(?:\\.git)?`, "gi"), replacement)
2378
+ .replace(new RegExp(`\\b${escapeRegExp(owner)}/${escapeRegExp(name)}\\b`, "gi"), replacement);
2379
+ if (name.length >= 4 && !isCommonRepoBasename(name)) {
2380
+ redacted = redacted.replace(new RegExp(`\\b${escapeRegExp(name)}\\b`, "gi"), replacement);
2381
+ }
2382
+ return redacted;
2383
+ }
2384
+ function redactPrivateRuntimeMentions(text, redaction) {
2385
+ let redacted = text;
2386
+ if (redaction?.repo) {
2387
+ redacted = redactRepoMentions(redacted, redaction.repo);
2388
+ }
2389
+ if (redaction?.providerRuntimeId) {
2390
+ redacted = redacted.replace(new RegExp(escapeRegExp(redaction.providerRuntimeId), "g"), "[redacted-provider-runtime-id]");
2391
+ }
2392
+ return redacted;
2393
+ }
2394
+ function isCommonRepoBasename(value) {
2395
+ return new Set(["app", "web", "api", "cli", "repo", "site", "docs", "main", "next", "demo", "test"]).has(value.toLowerCase());
2396
+ }
2397
+ function escapeRegExp(value) {
2398
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
2399
+ }
2400
+ function hasAppUrlProofBlocker(text) {
2401
+ const normalized = normalizeActorEvidenceForPattern(text);
2402
+ const blocker = "(?:unknown option|unsupported|not available|does\\s+\\W*not\\W*(?:support|expose|accept)|did\\s+\\W*not\\W*(?:support|expose|accept)|doesnt\\s+(?:support|expose|accept)|didnt\\s+(?:support|expose|accept))";
2403
+ const appUrl = "(?:--app-url|run\\s+--app-url|app-url\\s+proof)";
2404
+ return new RegExp(`${blocker}[\\s\\S]{0,220}${appUrl}|${appUrl}[\\s\\S]{0,220}${blocker}`, "i").test(normalized);
2405
+ }
2406
+ function normalizeActorEvidenceForPattern(text) {
2407
+ return text
2408
+ .replace(/[*_`~[\](){}<>]/g, " ")
2409
+ .replace(/\s+/g, " ")
2410
+ .trim();
2411
+ }
2412
+ async function arrangeLiveDesktopForScreenshot(desktop, terminalTitle, requestTimeoutMs) {
2413
+ const command = buildRemoteScreenshotArrangeCommand(terminalTitle);
2414
+ await desktop.commands.run(`bash -lc ${shellQuote(command)}`, {
2415
+ requestTimeoutMs,
2416
+ timeoutMs: 15_000
2417
+ }).catch(() => undefined);
2418
+ }
2419
+ function parseRemoteCompletion(payload) {
2420
+ try {
2421
+ const parsed = JSON.parse(payload);
2422
+ const status = normalizeCompletionStatus(parsed.status);
2423
+ if (!status) {
2424
+ return null;
2425
+ }
2426
+ const nestedVerifyPassed = parsed.nestedVerifyStatus === "passed"
2427
+ ? true
2428
+ : parsed.nestedVerifyStatus === "failed" ? false : undefined;
2429
+ const appStatus = normalizeAppStatus(parsed.appStatus);
2430
+ const actorStatus = normalizeActorStatus(parsed.actorStatus);
2431
+ const visualStatus = normalizeVisualStatus(parsed.visualStatus);
2432
+ const appServerActorEvidence = normalizeAppServerActorEvidence(parsed.appServerActorEvidence);
2433
+ const nestedStepTraceSummary = normalizeNestedStepTraceSummary(parsed.nestedStepTraceSummary);
2434
+ return {
2435
+ ...(typeof parsed.actorLogPath === "string" && parsed.actorLogPath.trim() ? { actorLogPath: sanitizeRemoteLog(parsed.actorLogPath) } : {}),
2436
+ ...(typeof parsed.actorLogTail === "string" && parsed.actorLogTail.trim() ? { actorLogTail: sanitizeRemoteLog(parsed.actorLogTail) } : {}),
2437
+ ...(typeof parsed.actorLastMessageTail === "string" && parsed.actorLastMessageTail.trim() ? { actorLastMessageTail: sanitizeRemoteLog(parsed.actorLastMessageTail) } : {}),
2438
+ ...(typeof parsed.actorPid === "number" && Number.isFinite(parsed.actorPid) ? { actorPid: parsed.actorPid } : {}),
2439
+ ...(actorStatus ? { actorStatus } : {}),
2440
+ ...(appServerActorEvidence ? { appServerActorEvidence } : {}),
2441
+ ...(typeof parsed.appLogPath === "string" && parsed.appLogPath.trim() ? { appLogPath: sanitizeRemoteLog(parsed.appLogPath) } : {}),
2442
+ ...(typeof parsed.appPid === "number" && Number.isFinite(parsed.appPid) ? { appPid: parsed.appPid } : {}),
2443
+ ...(typeof parsed.appReason === "string" && parsed.appReason.trim() ? { appReason: sanitizeRemoteLog(parsed.appReason).replace(/\s+/g, " ").slice(0, 240) } : {}),
2444
+ ...(appStatus ? { appStatus } : {}),
2445
+ ...(typeof parsed.appUrl === "string" && parsed.appUrl.trim() ? { appUrl: sanitizeRemoteLog(parsed.appUrl).replace(/\s+/g, " ").slice(0, 240) } : {}),
2446
+ checkedAt: typeof parsed.completedAt === "string" ? parsed.completedAt : new Date().toISOString(),
2447
+ ...(typeof parsed.exitCode === "number" ? { exitCode: parsed.exitCode } : {}),
2448
+ ...(typeof parsed.logTail === "string" ? { logTail: sanitizeRemoteLog(parsed.logTail) } : {}),
2449
+ ...(typeof parsed.nestedObserverPresent === "boolean" ? { nestedObserverPresent: parsed.nestedObserverPresent } : {}),
2450
+ ...(nestedStepTraceSummary ? { nestedStepTraceSummary } : {}),
2451
+ ...(nestedVerifyPassed === undefined ? {} : { nestedVerifyPassed }),
2452
+ reason: typeof parsed.reason === "string" && parsed.reason.trim()
2453
+ ? sanitizeRemoteLog(parsed.reason).replace(/\s+/g, " ").slice(0, 240)
2454
+ : defaultReasonForCompletion(status),
2455
+ ...(isRunSetupQualitySnapshot(parsed.setupQuality) ? { setupQuality: sanitizeSetupQualitySnapshot(parsed.setupQuality) } : {}),
2456
+ status,
2457
+ ...(typeof parsed.visualReason === "string" && parsed.visualReason.trim() ? { visualReason: sanitizeRemoteLog(parsed.visualReason).replace(/\s+/g, " ").slice(0, 240) } : {}),
2458
+ ...(visualStatus ? { visualStatus } : {}),
2459
+ ...(typeof parsed.visualWindowCount === "number" && Number.isFinite(parsed.visualWindowCount) ? { visualWindowCount: parsed.visualWindowCount } : {})
2460
+ };
2461
+ }
2462
+ catch {
2463
+ return null;
2464
+ }
2465
+ }
2466
+ function normalizeAppStatus(value) {
2467
+ return value === "not_started"
2468
+ || value === "running"
2469
+ || value === "blocked"
2470
+ || value === "failed"
2471
+ || value === "missing"
2472
+ || value === "unknown"
2473
+ ? value
2474
+ : null;
2475
+ }
2476
+ function normalizeVisualStatus(value) {
2477
+ return value === "not_started"
2478
+ || value === "visible"
2479
+ || value === "blocked"
2480
+ || value === "unknown"
2481
+ ? value
2482
+ : null;
2483
+ }
2484
+ function normalizeActorStatus(value) {
2485
+ return value === "not_started"
2486
+ || value === "running"
2487
+ || value === "passed"
2488
+ || value === "failed"
2489
+ || value === "blocked"
2490
+ || value === "timed_out"
2491
+ || value === "suspended"
2492
+ || value === "unknown"
2493
+ ? value
2494
+ : null;
2495
+ }
2496
+ function normalizeNestedStepTraceSummary(value) {
2497
+ if (!isRecord(value) || !Array.isArray(value.surfaces)) {
2498
+ return null;
2499
+ }
2500
+ const surfaces = value.surfaces
2501
+ .slice(0, 8)
2502
+ .filter((surface) => isRecord(surface))
2503
+ .map((surface) => {
2504
+ const steps = Array.isArray(surface.steps)
2505
+ ? surface.steps
2506
+ .slice(0, 20)
2507
+ .filter((step) => isRecord(step))
2508
+ .map((step) => {
2509
+ const status = normalizeNestedStepStatus(step.status);
2510
+ const assertionStatuses = Array.isArray(step.assertionStatuses)
2511
+ ? step.assertionStatuses
2512
+ .map((statusValue) => publicSafeSummaryToken(statusValue, "assertion"))
2513
+ .filter(Boolean)
2514
+ .slice(0, 12)
2515
+ : undefined;
2516
+ return {
2517
+ action: publicSafeSummaryToken(step.action, "action"),
2518
+ ...(assertionStatuses && assertionStatuses.length > 0 ? { assertionStatuses } : {}),
2519
+ id: publicSafeSummaryToken(step.id, "step"),
2520
+ ...(typeof step.label === "string" && step.label.trim() ? { label: sanitizeNestedTraceText(step.label, 140) } : {}),
2521
+ reason: sanitizeNestedTraceText(step.reason, 240),
2522
+ status
2523
+ };
2524
+ })
2525
+ : [];
2526
+ return {
2527
+ id: publicSafeSummaryToken(surface.id, "surface"),
2528
+ ...(typeof surface.label === "string" && surface.label.trim() ? { label: sanitizeNestedTraceText(surface.label, 140) } : {}),
2529
+ ok: surface.ok === true,
2530
+ reason: sanitizeNestedTraceText(surface.reason, 240),
2531
+ steps
2532
+ };
2533
+ })
2534
+ .filter((surface) => surface.steps.length > 0);
2535
+ if (surfaces.length === 0) {
2536
+ return null;
2537
+ }
2538
+ const totalSteps = surfaces.reduce((total, surface) => total + surface.steps.length, 0);
2539
+ const passedSteps = surfaces.reduce((total, surface) => total + surface.steps.filter((step) => step.status === "passed").length, 0);
2540
+ const blockedSteps = surfaces.reduce((total, surface) => total + surface.steps.filter((step) => step.status === "blocked").length, 0);
2541
+ const scenario = isRecord(value.scenario)
2542
+ ? {
2543
+ id: publicSafeSummaryToken(value.scenario.id, "scenario"),
2544
+ ...(typeof value.scenario.source === "string" && isSafeRepoRelativePath(value.scenario.source) ? { source: sanitizeSetupQualityPath(value.scenario.source) } : {}),
2545
+ ...(typeof value.scenario.sourceDigest === "string" && value.scenario.sourceDigest.trim() ? { sourceDigest: publicSafeSummaryToken(value.scenario.sourceDigest, "digest") } : {}),
2546
+ ...(typeof value.scenario.stepCount === "number" && Number.isFinite(value.scenario.stepCount) ? { stepCount: Math.max(0, Math.round(value.scenario.stepCount)) } : {}),
2547
+ ...(typeof value.scenario.title === "string" && value.scenario.title.trim() ? { title: sanitizeNestedTraceText(value.scenario.title, 140) } : {})
2548
+ }
2549
+ : undefined;
2550
+ return {
2551
+ schema: "humanish.oss-meta-nested-step-trace-summary.v1",
2552
+ redaction: {
2553
+ status: "passed",
2554
+ notes: "Nested browser trace summary stores counts and redacted step metadata only; URLs, auth streams, remote paths, screenshots, and raw DOM text are omitted."
2555
+ },
2556
+ counts: {
2557
+ blockedSteps,
2558
+ passedSteps,
2559
+ surfaces: surfaces.length,
2560
+ totalSteps,
2561
+ traces: surfaces.length
2562
+ },
2563
+ ...(scenario ? { scenario } : {}),
2564
+ status: blockedSteps > 0 ? "blocked" : passedSteps === totalSteps ? "passed" : "unknown",
2565
+ surfaces
2566
+ };
2567
+ }
2568
+ function normalizeNestedStepStatus(value) {
2569
+ return value === "passed" || value === "blocked" ? value : "unknown";
2570
+ }
2571
+ function publicSafeSummaryToken(value, fallback) {
2572
+ const token = sanitizeSetupQualityText(value)
2573
+ .toLowerCase()
2574
+ .replace(/[^a-z0-9_.-]+/g, "-")
2575
+ .replace(/^-+|-+$/g, "")
2576
+ .slice(0, 80);
2577
+ return token || fallback;
2578
+ }
2579
+ function sanitizeNestedTraceText(value, maxLength) {
2580
+ return sanitizeSetupQualityText(value)
2581
+ .replace(/https?:\/\/\S+/gi, "[redacted-url]")
2582
+ .replace(/(?:^|\s)\/(?:home|tmp|var|Users)\/\S+/g, " [redacted-path]")
2583
+ .replace(/\s+/g, " ")
2584
+ .trim()
2585
+ .slice(0, maxLength);
2586
+ }
2587
+ function normalizeAppServerActorEvidence(value) {
2588
+ if (!isRecord(value)) {
2589
+ return null;
2590
+ }
2591
+ const tracePath = safeRelativeArtifactPath(typeof value.tracePath === "string" ? value.tracePath : "");
2592
+ const eventsPath = safeRelativeArtifactPath(typeof value.eventsPath === "string" ? value.eventsPath : "");
2593
+ const transcriptPath = safeRelativeArtifactPath(typeof value.transcriptPath === "string" ? value.transcriptPath : "");
2594
+ if (!tracePath || !eventsPath || !transcriptPath) {
2595
+ return null;
2596
+ }
2597
+ return {
2598
+ eventsPath,
2599
+ ...(typeof value.eventsText === "string" && value.eventsText.trim() ? { eventsText: sanitizeRemoteLog(value.eventsText) } : {}),
2600
+ ...(typeof value.reason === "string" && value.reason.trim() ? { reason: sanitizeRemoteLog(value.reason).replace(/\s+/g, " ").slice(0, 240) } : {}),
2601
+ ...(typeof value.status === "string" && value.status.trim() ? { status: sanitizeRemoteLog(value.status).replace(/\s+/g, " ").slice(0, 80) } : {}),
2602
+ ...(isRecord(value.traceJson) ? { traceJson: value.traceJson } : {}),
2603
+ tracePath,
2604
+ ...(typeof value.traceText === "string" && value.traceText.trim() ? { traceText: sanitizeRemoteLog(value.traceText) } : {}),
2605
+ transcriptPath,
2606
+ ...(typeof value.transcriptText === "string" && value.transcriptText.trim() ? { transcriptText: sanitizeRemoteLog(value.transcriptText) } : {})
2607
+ };
2608
+ }
2609
+ function safeRelativeArtifactPath(value) {
2610
+ const normalized = value.replace(/\\/g, "/").replace(/^\.\/+/, "");
2611
+ if (!normalized || normalized.startsWith("/") || normalized.startsWith("../") || normalized.includes("://") || normalized.split("/").includes("..")) {
2612
+ return "";
2613
+ }
2614
+ return normalized;
2615
+ }
2616
+ function normalizeCompletionStatus(value) {
2617
+ return value === "running"
2618
+ || value === "passed"
2619
+ || value === "failed"
2620
+ || value === "blocked"
2621
+ || value === "timed_out"
2622
+ ? value
2623
+ : null;
2624
+ }
2625
+ function defaultReasonForCompletion(status) {
2626
+ switch (status) {
2627
+ case "passed":
2628
+ return "Remote bootstrap completed successfully.";
2629
+ case "failed":
2630
+ return "Remote bootstrap failed.";
2631
+ case "blocked":
2632
+ return "Remote bootstrap is blocked.";
2633
+ case "timed_out":
2634
+ return "Remote bootstrap completion timed out.";
2635
+ case "running":
2636
+ return "Remote bootstrap is still running.";
2637
+ }
2638
+ }
2639
+ function isRunSetupQualitySnapshot(value) {
2640
+ if (!isRecord(value) || value.schema !== "humanish.setup-quality.v1") {
2641
+ return false;
2642
+ }
2643
+ return Array.isArray(value.checks)
2644
+ && Array.isArray(value.tree)
2645
+ && Array.isArray(value.previews)
2646
+ && isRecord(value.humanish)
2647
+ && isRecord(value.packageScripts)
2648
+ && typeof value.generatedAt === "string"
2649
+ && typeof value.summary === "string"
2650
+ && (value.status === "passed" || value.status === "needs_review" || value.status === "blocked");
2651
+ }
2652
+ function sanitizeSetupQualitySnapshot(snapshot) {
2653
+ const safeTree = snapshot.tree
2654
+ .filter((entry) => isSafeRepoRelativePath(entry.path) && (entry.type === "file" || entry.type === "directory"))
2655
+ .slice(0, 240)
2656
+ .map((entry) => ({
2657
+ path: sanitizeSetupQualityPath(entry.path),
2658
+ type: entry.type,
2659
+ ...(typeof entry.sizeBytes === "number" && Number.isFinite(entry.sizeBytes) ? { sizeBytes: Math.max(0, Math.round(entry.sizeBytes)) } : {})
2660
+ }));
2661
+ const safePreviews = snapshot.previews
2662
+ .filter((preview) => isSafeRepoRelativePath(preview.path))
2663
+ .slice(0, 20)
2664
+ .map((preview) => ({
2665
+ path: sanitizeSetupQualityPath(preview.path),
2666
+ language: sanitizePreviewLanguage(preview.language),
2667
+ truncated: preview.truncated === true,
2668
+ text: sanitizeSetupQualityText(preview.text).slice(0, 8_000)
2669
+ }));
2670
+ const safeScripts = {};
2671
+ for (const [key, value] of Object.entries(snapshot.packageScripts)) {
2672
+ const safeKey = sanitizeSetupQualityText(key).replace(/[^a-zA-Z0-9:_-]/g, "").slice(0, 80);
2673
+ if (!safeKey || typeof value !== "string") {
2674
+ continue;
2675
+ }
2676
+ safeScripts[safeKey] = sanitizeSetupQualityText(value).slice(0, 300);
2677
+ }
2678
+ return {
2679
+ schema: "humanish.setup-quality.v1",
2680
+ generatedAt: sanitizeSetupQualityText(snapshot.generatedAt).slice(0, 80) || new Date().toISOString(),
2681
+ redaction: {
2682
+ status: "passed",
2683
+ rawPreviews: snapshot.redaction?.rawPreviews === "suppressed" ? "suppressed" : "included",
2684
+ notes: sanitizeSetupQualityText(snapshot.redaction?.notes ?? "Remote setup snapshot was redacted before persistence.").slice(0, 240)
2685
+ },
2686
+ summary: sanitizeSetupQualityText(snapshot.summary).slice(0, 320),
2687
+ status: snapshot.status,
2688
+ checks: snapshot.checks
2689
+ .filter((check) => isRecord(check) && typeof check.id === "string" && typeof check.label === "string" && typeof check.detail === "string")
2690
+ .slice(0, 40)
2691
+ .map((check) => ({
2692
+ id: sanitizeSetupQualityText(check.id).replace(/[^a-zA-Z0-9:_-]/g, "").slice(0, 80) || "check",
2693
+ label: sanitizeSetupQualityText(check.label).slice(0, 140),
2694
+ ok: check.ok === true,
2695
+ detail: sanitizeSetupQualityText(check.detail).slice(0, 320)
2696
+ })),
2697
+ tree: safeTree,
2698
+ previews: safePreviews,
2699
+ ...(snapshot.studyQuality ? { studyQuality: sanitizeStudyQualitySnapshot(snapshot.studyQuality) } : {}),
2700
+ packageScripts: safeScripts,
2701
+ humanish: {
2702
+ configPresent: snapshot.humanish.configPresent === true,
2703
+ personaCount: typeof snapshot.humanish.personaCount === "number" && Number.isFinite(snapshot.humanish.personaCount) ? Math.max(0, Math.round(snapshot.humanish.personaCount)) : 0,
2704
+ scenarioCount: typeof snapshot.humanish.scenarioCount === "number" && Number.isFinite(snapshot.humanish.scenarioCount) ? Math.max(0, Math.round(snapshot.humanish.scenarioCount)) : 0,
2705
+ packageScriptPresent: snapshot.humanish.packageScriptPresent === true,
2706
+ gitignoreContainsRuntimeIgnore: snapshot.humanish.gitignoreContainsRuntimeIgnore === true
2707
+ }
2708
+ };
2709
+ }
2710
+ function sanitizeStudyQualitySnapshot(studyQuality) {
2711
+ const rating = studyQuality.rating === "none"
2712
+ || studyQuality.rating === "ceremonial"
2713
+ || studyQuality.rating === "useful"
2714
+ || studyQuality.rating === "high_leverage"
2715
+ ? studyQuality.rating
2716
+ : "none";
2717
+ return {
2718
+ schema: "humanish.study-quality.v1",
2719
+ rating,
2720
+ summary: sanitizeSetupQualityText(studyQuality.summary).slice(0, 320),
2721
+ checks: Array.isArray(studyQuality.checks)
2722
+ ? studyQuality.checks
2723
+ .filter((check) => isRecord(check) && typeof check.id === "string" && typeof check.label === "string" && typeof check.detail === "string")
2724
+ .slice(0, 20)
2725
+ .map((check) => ({
2726
+ id: sanitizeSetupQualityText(check.id).replace(/[^a-zA-Z0-9:_-]/g, "").slice(0, 80) || "study-check",
2727
+ label: sanitizeSetupQualityText(check.label).slice(0, 140),
2728
+ ok: check.ok === true,
2729
+ detail: sanitizeSetupQualityText(check.detail).slice(0, 320)
2730
+ }))
2731
+ : [],
2732
+ signals: {
2733
+ appUrlProofBlocked: studyQuality.signals?.appUrlProofBlocked === true,
2734
+ appUrlProofMentioned: studyQuality.signals?.appUrlProofMentioned === true,
2735
+ actorInsightCaptured: studyQuality.signals?.actorInsightCaptured === true,
2736
+ coverageCustomized: studyQuality.signals?.coverageCustomized === true,
2737
+ personaCustomized: studyQuality.signals?.personaCustomized === true,
2738
+ scenarioCustomized: studyQuality.signals?.scenarioCustomized === true
2739
+ }
2740
+ };
2741
+ }
2742
+ function sanitizePreviewLanguage(value) {
2743
+ return value === "json" || value === "yaml" || value === "typescript" || value === "markdown" || value === "text"
2744
+ ? value
2745
+ : "text";
2746
+ }
2747
+ function sanitizeSetupQualityPath(value) {
2748
+ return value.replace(/\\/g, "/").replace(/^\.\/+/, "").slice(0, 240);
2749
+ }
2750
+ function sanitizeSetupQualityText(value) {
2751
+ return redactOssRemoteTelemetryText(String(value ?? ""))
2752
+ .replace(/\0/g, "")
2753
+ .replace(/https?:\/\/[^/\s]*e2b[^)\s]+/gi, "[redacted-e2b-url]")
2754
+ .trim();
2755
+ }
2756
+ function isSafeRepoRelativePath(value) {
2757
+ if (typeof value !== "string" || !value.trim()) {
2758
+ return false;
2759
+ }
2760
+ const normalized = value.replace(/\\/g, "/").replace(/^\.\/+/, "");
2761
+ return !path.isAbsolute(normalized)
2762
+ && !normalized.includes("://")
2763
+ && !normalized.startsWith("../")
2764
+ && !normalized.split("/").includes("..")
2765
+ && !normalized.split("/").some((segment) => /^(?:\.env(?:\..*)?|\.npmrc|\.git|node_modules|dist|build|\.next)$/.test(segment));
2766
+ }
2767
+ function isLocalEvidenceArtifactPath(value) {
2768
+ return isSafeRepoRelativePath(value);
2769
+ }
2770
+ function isRecord(value) {
2771
+ return typeof value === "object" && value !== null && !Array.isArray(value);
2772
+ }
2773
+ function sanitizeRemoteLog(value) {
2774
+ return redactOssRemoteTelemetryText(value)
2775
+ .split(/\r?\n/)
2776
+ .slice(-80)
2777
+ .join("\n")
2778
+ .trim();
2779
+ }
2780
+ async function startOssBootstrap(desktop, assignment, localPackage, requestTimeoutMs, display = {
2781
+ repoLabel: assignment.repo,
2782
+ token: repoSlug(assignment.repo)
2783
+ }) {
2784
+ const token = display.token;
2785
+ const appDir = `/home/user/${token}`;
2786
+ const stateDir = `/home/user/.humanish-oss-lab/${token}`;
2787
+ const remotePackagePath = `/tmp/${localPackage?.fileName ?? "humanish.tgz"}`;
2788
+ const remoteHostActorPlanPath = `${stateDir}/host-actor-plan.json`;
2789
+ const bootstrapPath = `${stateDir}/bootstrap.sh`;
2790
+ const launcherPath = `${stateDir}/launch-terminal.sh`;
2791
+ const logPath = `${stateDir}/bootstrap.log`;
2792
+ const completionPath = `${stateDir}/completion.json`;
2793
+ const nestedObserverPath = `${appDir}/.humanish/runs/nested-${token}/observer/index.html`;
2794
+ const title = `Humanish ${assignment.index} ${display.repoLabel}`;
2795
+ const codexMode = codexAppServerModeRequested(process.env, display.codexAppServerMode === true) ? "app-server-client" : "tui-attempted";
2796
+ const baseTail = [
2797
+ `repo: ${display.repoLabel}`,
2798
+ `project: ${appDir}`,
2799
+ `sandbox: ${desktop.sandboxId}`,
2800
+ `remote package: ${localPackage ? remotePackagePath : "npm:humanish fallback"}`,
2801
+ `bootstrap: ${bootstrapPath}`,
2802
+ `completion: ${completionPath}`,
2803
+ `log: ${logPath}`,
2804
+ `nested observer: ${nestedObserverPath}`
2805
+ ].join("\n");
2806
+ try {
2807
+ await runDesktopCommand(desktop, `mkdir -p ${shellQuote(stateDir)}`, {
2808
+ requestTimeoutMs,
2809
+ timeoutMs: 30_000
2810
+ });
2811
+ if (localPackage) {
2812
+ const packageBytes = await readFile(localPackage.path);
2813
+ await desktop.files.write(remotePackagePath, toArrayBuffer(packageBytes), {
2814
+ requestTimeoutMs,
2815
+ useOctetStream: true
2816
+ });
2817
+ }
2818
+ if (display.hostActorPlanResult?.plan) {
2819
+ await desktop.files.write(remoteHostActorPlanPath, `${JSON.stringify(display.hostActorPlanResult.plan, null, 2)}\n`, {
2820
+ requestTimeoutMs
2821
+ });
2822
+ }
2823
+ const bootstrapScript = buildRemoteBootstrapScript({
2824
+ assignment,
2825
+ appDir,
2826
+ completionPath,
2827
+ displayRepo: display.repoLabel,
2828
+ logPath,
2829
+ nestedObserverPath,
2830
+ ...(display.hostActorPlanResult?.plan ? { remoteHostActorPlanPath } : {}),
2831
+ stateDir,
2832
+ token,
2833
+ ...(localPackage ? { remotePackagePath } : {})
2834
+ });
2835
+ const launcherScript = buildRemoteLauncherScript({
2836
+ bootstrapPath,
2837
+ launcherPath,
2838
+ logPath,
2839
+ title
2840
+ });
2841
+ await desktop.files.write(bootstrapPath, bootstrapScript, { requestTimeoutMs });
2842
+ await desktop.files.write(launcherPath, launcherScript, { requestTimeoutMs });
2843
+ await runDesktopCommand(desktop, `chmod +x ${shellQuote(bootstrapPath)} ${shellQuote(launcherPath)}`, {
2844
+ requestTimeoutMs,
2845
+ timeoutMs: 30_000
2846
+ });
2847
+ await runDesktopCommand(desktop, `bash ${shellQuote(launcherPath)}`, {
2848
+ requestTimeoutMs,
2849
+ timeoutMs: 30_000
2850
+ });
2851
+ await desktop.wait(1200).catch(() => undefined);
2852
+ await runDesktopCommand(desktop, buildRemoteFocusCommand(title), {
2853
+ requestTimeoutMs,
2854
+ timeoutMs: 10_000
2855
+ }).catch(() => undefined);
2856
+ return {
2857
+ codexMode,
2858
+ completionPath,
2859
+ launcherPath,
2860
+ logPath,
2861
+ humanishPackageUploaded: Boolean(localPackage),
2862
+ nestedObserverPath,
2863
+ status: "started",
2864
+ tail: [
2865
+ "Visible E2B bootstrap terminal launched.",
2866
+ codexMode === "app-server-client"
2867
+ ? "The terminal clones the authorized repo, installs this local humanish package tarball when available, runs nested Humanish proof commands, opens the nested Observer, and opens the Codex app-server client surface in Chrome when configured."
2868
+ : "The terminal clones the authorized repo, installs this local humanish package tarball when available, runs nested Humanish proof commands, attempts Codex TUI, then opens the nested Observer in Chrome.",
2869
+ baseTail
2870
+ ].join("\n"),
2871
+ terminalTitle: title
2872
+ };
2873
+ }
2874
+ catch (error) {
2875
+ return {
2876
+ codexMode,
2877
+ completionPath,
2878
+ launcherPath,
2879
+ logPath,
2880
+ humanishPackageUploaded: Boolean(localPackage),
2881
+ nestedObserverPath,
2882
+ status: "failed",
2883
+ tail: [
2884
+ "Bootstrap launcher failed before the remote terminal could start.",
2885
+ baseTail,
2886
+ `error: ${compactError(error)}`
2887
+ ].join("\n"),
2888
+ terminalTitle: title
2889
+ };
2890
+ }
2891
+ }
2892
+ function buildRemoteBootstrapScript(args) {
2893
+ const repoUrl = `https://github.com/${args.assignment.repo}.git`;
2894
+ const runId = `nested-${args.token}`;
2895
+ return `#!/usr/bin/env bash
2896
+ set -Eeuo pipefail
2897
+ export TERM=xterm-256color
2898
+ export HUMANISH_PUBLIC_SAFE=1
2899
+ HUMANISH_PRIVATE_CODEX_API_KEY="\${HUMANISH_CODEX_API_KEY:-}"
2900
+ HUMANISH_PRIVATE_CODEX_ACCESS_TOKEN="\${HUMANISH_CODEX_ACCESS_TOKEN:-}"
2901
+ HUMANISH_PRIVATE_CODEX_APP_SERVER_URL="\${HUMANISH_CODEX_APP_SERVER_URL:-}"
2902
+ HUMANISH_PRIVATE_GITHUB_TOKEN="\${HUMANISH_GITHUB_TOKEN:-}"
2903
+ unset HUMANISH_CODEX_API_KEY HUMANISH_CODEX_ACCESS_TOKEN HUMANISH_CODEX_APP_SERVER_URL HUMANISH_GITHUB_TOKEN
2904
+ unset OPENAI_API_KEY CODEX_API_KEY CODEX_ACCESS_TOKEN E2B_API_KEY GH_TOKEN GITHUB_TOKEN
2905
+ STATE_DIR=${shellQuote(args.stateDir)}
2906
+ ROOT_DIR="$STATE_DIR"
2907
+ APP_DIR=${shellQuote(args.appDir)}
2908
+ LOG_PATH=${shellQuote(args.logPath)}
2909
+ COMPLETION_PATH=${shellQuote(args.completionPath)}
2910
+ NESTED_OBSERVER=${shellQuote(args.nestedObserverPath)}
2911
+ REMOTE_PACKAGE=${args.remotePackagePath ? shellQuote(args.remotePackagePath) : "''"}
2912
+ HOST_ACTOR_PLAN=${args.remoteHostActorPlanPath ? shellQuote(args.remoteHostActorPlanPath) : "''"}
2913
+ APP_LOG_PATH="$ROOT_DIR/app.log"
2914
+ ACTOR_LOG_PATH="$ROOT_DIR/actor.log"
2915
+ ACTOR_LAST_MESSAGE_PATH="$ROOT_DIR/actor-last-message.txt"
2916
+ CODEX_APP_SERVER_ROOT_PATH="$ROOT_DIR/codex-app-server"
2917
+ CODEX_APP_SERVER_STATE_PATH="$CODEX_APP_SERVER_ROOT_PATH/state.json"
2918
+ CODEX_APP_SERVER_LOG_PATH="$CODEX_APP_SERVER_ROOT_PATH/ui.log"
2919
+ CODEX_APP_SERVER_PROMPT_PATH="$ROOT_DIR/codex-app-server-prompt.txt"
2920
+ CODEX_APP_SERVER_PORT="\${HUMANISH_OSS_META_CODEX_APP_SERVER_PORT:-45137}"
2921
+ CODEX_APP_SERVER_UI_URL=""
2922
+ STREAM_ID=${shellQuote(args.assignment.streamId)}
2923
+ TERMINAL_TITLE=${shellQuote(`Humanish ${args.assignment.index} ${args.displayRepo}`)}
2924
+ mkdir -p "$ROOT_DIR"
2925
+ touch "$LOG_PATH"
2926
+ exec > >(tee -a "$LOG_PATH") 2>&1
2927
+ NESTED_VERIFY_STATUS=not_run
2928
+ APP_STATUS=not_started
2929
+ APP_REASON="Target app startup has not started."
2930
+ APP_URL=""
2931
+ APP_PID=""
2932
+ ACTOR_STATUS=not_started
2933
+ ACTOR_PID=""
2934
+ VISUAL_STATUS=not_started
2935
+ VISUAL_REASON="Browser windows have not been arranged."
2936
+ VISUAL_WINDOW_COUNT=0
2937
+ CODEX_APP_SERVER_CLIENT_OPENED=0
2938
+
2939
+ write_completion() {
2940
+ local status="$1"
2941
+ local reason="$2"
2942
+ local exit_code="$3"
2943
+ local nested_observer_present=false
2944
+ if [[ -f "$NESTED_OBSERVER" ]]; then
2945
+ nested_observer_present=true
2946
+ fi
2947
+
2948
+ node - "$COMPLETION_PATH" "$LOG_PATH" "$APP_DIR" "$status" "$reason" "$exit_code" "$nested_observer_present" "$NESTED_OBSERVER" "$NESTED_VERIFY_STATUS" "$APP_STATUS" "$APP_REASON" "$APP_URL" "$APP_PID" "$APP_LOG_PATH" "$ACTOR_STATUS" "$ACTOR_PID" "$ACTOR_LOG_PATH" "$ACTOR_LAST_MESSAGE_PATH" "$VISUAL_STATUS" "$VISUAL_REASON" "$VISUAL_WINDOW_COUNT" "$STREAM_ID" "$CODEX_APP_SERVER_STATE_PATH" "$CODEX_APP_SERVER_ROOT_PATH" <<'NODE' || true
2949
+ const fs = require("node:fs");
2950
+ const path = require("node:path");
2951
+ const [
2952
+ completionPath,
2953
+ logPath,
2954
+ appDir,
2955
+ status,
2956
+ reason,
2957
+ exitCode,
2958
+ nestedObserverPresent,
2959
+ nestedObserverPath,
2960
+ nestedVerifyStatus,
2961
+ appStatus,
2962
+ appReason,
2963
+ appUrl,
2964
+ appPid,
2965
+ appLogPath,
2966
+ actorStatus,
2967
+ actorPid,
2968
+ actorLogPath,
2969
+ actorLastMessagePath,
2970
+ visualStatus,
2971
+ visualReason,
2972
+ visualWindowCount,
2973
+ streamId,
2974
+ codexAppServerStatePath,
2975
+ codexAppServerRootPath
2976
+ ] = process.argv.slice(2);
2977
+ const tailFile = (filePath, lines = 80) => fs.existsSync(filePath)
2978
+ ? fs.readFileSync(filePath, "utf8").split(/\\r?\\n/).slice(-lines).join("\\n")
2979
+ : "";
2980
+ const redactText = (value) => String(value || "")
2981
+ .replace(/sk-(?:proj-)?[A-Za-z0-9_-]{20,}/g, "[redacted-openai-key]")
2982
+ .replace(/e2b_[A-Za-z0-9_-]{12,}/g, "[redacted-e2b-key]")
2983
+ .replace(/(?:gh[pousr]_[A-Za-z0-9_]{12,}|github_pat_[A-Za-z0-9_]{12,})/g, "[redacted-github-token]")
2984
+ .replace(/\\bBearer\\s+[A-Za-z0-9._~+/=-]{12,}\\b/gi, "Bearer [redacted-token]")
2985
+ .replace(/https?:\\/\\/[^/\\s]*e2b[^)\\s]+/gi, "[redacted-e2b-url]");
2986
+ const redactedTail = redactText(tailFile(logPath, 80));
2987
+ const actorLogTail = redactText(tailFile(actorLogPath, 80));
2988
+ const actorLastMessageTail = redactText(tailFile(actorLastMessagePath, 40));
2989
+ const numberOrNull = (value) => /^\\d+$/.test(String(value || "")) ? Number(value) : null;
2990
+ const cleanText = (value) => redactText(value)
2991
+ .replace(/\\s+/g, " ")
2992
+ .trim()
2993
+ .slice(0, 240);
2994
+ const safeRel = (value) => {
2995
+ const normalized = String(value || "").replace(/\\\\/g, "/").replace(/^\\.\\/+/, "");
2996
+ if (!normalized || normalized.startsWith("../") || normalized.includes("://") || normalized.split("/").includes("..")) return "";
2997
+ return normalized;
2998
+ };
2999
+ const languageFor = (rel) => {
3000
+ if (rel.endsWith(".json")) return "json";
3001
+ if (rel.endsWith(".yaml") || rel.endsWith(".yml")) return "yaml";
3002
+ if (rel.endsWith(".ts") || rel.endsWith(".tsx")) return "typescript";
3003
+ if (rel.endsWith(".md") || rel.endsWith(".markdown")) return "markdown";
3004
+ return "text";
3005
+ };
3006
+ const shouldPreview = (rel) => rel === "package.json"
3007
+ || rel === ".gitignore"
3008
+ || rel === "humanish/config.ts"
3009
+ || rel === "humanish/coverage-map.md"
3010
+ || rel === "humanish/coverage-matrix.md"
3011
+ || rel.startsWith("humanish/personas/")
3012
+ || rel.startsWith("humanish/scenarios/");
3013
+ const ignoredSegments = new Set([".git", "node_modules", ".humanish", "dist", "build", ".next", "coverage", ".turbo", ".cache"]);
3014
+ const readTextLimited = (filePath, maxBytes = 12000) => {
3015
+ try {
3016
+ const buffer = fs.readFileSync(filePath);
3017
+ return redactText(buffer.slice(0, maxBytes).toString("utf8")).replace(/\\0/g, "");
3018
+ } catch {
3019
+ return "";
3020
+ }
3021
+ };
3022
+ const countFilesUnder = (root, prefix) => {
3023
+ try {
3024
+ return fs.readdirSync(path.join(root, prefix), { withFileTypes: true }).filter((entry) => entry.isFile()).length;
3025
+ } catch {
3026
+ return 0;
3027
+ }
3028
+ };
3029
+ const buildSetupQuality = (root) => {
3030
+ const tree = [];
3031
+ const previews = [];
3032
+ const walk = (dir, depth) => {
3033
+ if (depth > 4 || tree.length >= 240) return;
3034
+ let entries = [];
3035
+ try { entries = fs.readdirSync(dir, { withFileTypes: true }); } catch { return; }
3036
+ entries.sort((a, b) => a.name.localeCompare(b.name));
3037
+ for (const entry of entries) {
3038
+ if (tree.length >= 240) break;
3039
+ if (ignoredSegments.has(entry.name)) continue;
3040
+ const absolute = path.join(dir, entry.name);
3041
+ const rel = safeRel(path.relative(root, absolute));
3042
+ if (!rel) continue;
3043
+ if (entry.isDirectory()) {
3044
+ tree.push({ path: rel, type: "directory" });
3045
+ walk(absolute, depth + 1);
3046
+ continue;
3047
+ }
3048
+ if (!entry.isFile()) continue;
3049
+ const stats = fs.statSync(absolute);
3050
+ tree.push({ path: rel, type: "file", sizeBytes: stats.size });
3051
+ if (shouldPreview(rel) && previews.length < 20) {
3052
+ const text = readTextLimited(absolute);
3053
+ previews.push({ path: rel, language: languageFor(rel), truncated: stats.size > 12000 || text.length >= 8000, text: text.slice(0, 8000) });
3054
+ }
3055
+ }
3056
+ };
3057
+ const packageJsonPath = path.join(root, "package.json");
3058
+ let packageScripts = {};
3059
+ if (fs.existsSync(packageJsonPath)) {
3060
+ try {
3061
+ const parsedPackage = JSON.parse(readTextLimited(packageJsonPath, 20000));
3062
+ if (parsedPackage && typeof parsedPackage.scripts === "object" && parsedPackage.scripts) {
3063
+ packageScripts = Object.fromEntries(Object.entries(parsedPackage.scripts).filter(([, value]) => typeof value === "string"));
3064
+ }
3065
+ } catch {}
3066
+ }
3067
+ walk(root, 0);
3068
+ const readRel = (rel) => readTextLimited(path.join(root, rel), 20000);
3069
+ const listFilesUnder = (prefix) => {
3070
+ const absoluteRoot = path.join(root, prefix);
3071
+ const files = [];
3072
+ const visit = (dir, depth) => {
3073
+ if (depth > 2 || files.length >= 40) return;
3074
+ let entries = [];
3075
+ try { entries = fs.readdirSync(dir, { withFileTypes: true }); } catch { return; }
3076
+ entries.sort((a, b) => a.name.localeCompare(b.name));
3077
+ for (const entry of entries) {
3078
+ const absolute = path.join(dir, entry.name);
3079
+ const rel = safeRel(path.relative(root, absolute));
3080
+ if (!rel) continue;
3081
+ if (entry.isDirectory()) visit(absolute, depth + 1);
3082
+ else if (entry.isFile()) files.push(rel);
3083
+ }
3084
+ };
3085
+ visit(absoluteRoot, 0);
3086
+ return files;
3087
+ };
3088
+ const coverageMapText = readRel("humanish/coverage-map.md");
3089
+ const coverageMatrixText = readRel("humanish/coverage-matrix.md");
3090
+ const personaFiles = listFilesUnder(path.join("humanish", "personas"));
3091
+ const scenarioFiles = listFilesUnder(path.join("humanish", "scenarios"));
3092
+ const personaText = personaFiles.map((rel) => readRel(rel)).join("\\n");
3093
+ const scenarioText = scenarioFiles.map((rel) => readRel(rel)).join("\\n");
3094
+ const configText = readRel("humanish/config.ts");
3095
+ const defaultPersonaFiles = new Set(["humanish/personas/synthetic-new-user.yaml", "humanish/personas/skeptical-power-user.yaml"]);
3096
+ const defaultScenarioFiles = new Set(["humanish/scenarios/first-run-smoke.yaml", "humanish/scenarios/onboarding-regression.yaml"]);
3097
+ const personaEntries = personaFiles.map((rel) => ({ rel, text: readRel(rel) }));
3098
+ const scenarioEntries = scenarioFiles.map((rel) => ({ rel, text: readRel(rel) }));
3099
+ const coverageCustomized = (
3100
+ Boolean(coverageMapText.trim())
3101
+ && !/Current starter coverage is intentionally minimal/i.test(coverageMapText)
3102
+ && /\\b(screen|route|role|state|path|flow|journey|surface|happy|sad|friction)\\b/i.test(coverageMapText)
3103
+ ) || (
3104
+ Boolean(coverageMatrixText.trim())
3105
+ && !/\\bstarter\\b/i.test(coverageMatrixText)
3106
+ && /\\b(screen|route|role|state|path|flow|journey|surface|happy|sad|friction)\\b/i.test(coverageMatrixText)
3107
+ );
3108
+ const personaCustomizedCount = personaEntries.filter(({ rel, text }) => !defaultPersonaFiles.has(rel)
3109
+ || (Boolean(text.trim())
3110
+ && !/privacy-safe first-time user evaluating the app|prefers clear affordances over novelty|starter persona/i.test(text)
3111
+ && /\\b(role|workflow|creator|operator|admin|guest|player|customer|seller|designer|developer|founder|marketer|power user|novice|skeptical|accessibility|persona|job-to-be-done)\\b/i.test(text))).length;
3112
+ const scenarioCustomizedCount = scenarioEntries.filter(({ rel, text }) => !defaultScenarioFiles.has(rel)
3113
+ || (Boolean(text.trim())
3114
+ && !/Reach the first meaningful product state|Exercise onboarding friction|starter scenario/i.test(text)
3115
+ && /\\b(route|screen|journey|state|happy|sad|friction|error|empty|mobile|desktop|evidence|expectation|selector|upload|preview|pricing|auth|settings|checkout|workflow)\\b/i.test(text))).length;
3116
+ const personaCustomized = personaCustomizedCount >= Math.min(2, personaFiles.length || 2);
3117
+ const scenarioCustomized = scenarioCustomizedCount >= Math.min(2, scenarioFiles.length || 2);
3118
+ const actorEvidenceText = [actorLogTail, actorLastMessageTail, redactedTail, scenarioText, configText].join("\\n");
3119
+ const actorEvidenceNormalized = actorEvidenceText.replace(/[*_\`~[\\](){}<>]/g, " ").replace(/\\s+/g, " ").trim();
3120
+ const appUrlProofBlocked = /(?:unknown option|unsupported|not available|does\\s+\\W*not\\W*(?:support|expose|accept)|did\\s+\\W*not\\W*(?:support|expose|accept)|doesnt\\s+(?:support|expose|accept)|didnt\\s+(?:support|expose|accept))[\\s\\S]{0,220}(?:--app-url|run\\s+--app-url|app-url\\s+proof)|(?:--app-url|run\\s+--app-url|app-url\\s+proof)[\\s\\S]{0,220}(?:unknown option|unsupported|not available|does\\s+\\W*not\\W*(?:support|expose|accept)|did\\s+\\W*not\\W*(?:support|expose|accept)|doesnt\\s+(?:support|expose|accept)|didnt\\s+(?:support|expose|accept))/i.test(actorEvidenceNormalized);
3121
+ const appUrlProofMentioned = !appUrlProofBlocked && (
3122
+ nestedVerifyStatus === "passed"
3123
+ || /(?:humanish\\s+run[\\s\\S]{0,160}--app-url|--app-url[\\s\\S]{0,160}humanish\\s+run)/i.test(actorEvidenceText)
3124
+ );
3125
+ const actorInsightCaptured = /\\b(observed|found|friction|improvement|issue|gap|confusing|blocked|recommend|none observed|feedback|useful improvement|next harness upgrade)\\b/i.test(actorEvidenceNormalized);
3126
+ const gitignore = readTextLimited(path.join(root, ".gitignore"), 20000);
3127
+ const configPresent = fs.existsSync(path.join(root, "humanish", "config.ts"));
3128
+ const personaCount = countFilesUnder(root, path.join("humanish", "personas"));
3129
+ const scenarioCount = countFilesUnder(root, path.join("humanish", "scenarios"));
3130
+ const packageScriptPresent = Object.entries(packageScripts).some(([key, value]) => key.includes("humanish") || String(value).includes("humanish"));
3131
+ const gitignoreContainsRuntimeIgnore = /(^|\\n)\\s*\\.humanish\\/?\\s*(\\n|$)/.test(gitignore);
3132
+ const checks = [
3133
+ { id: "humanish-config", label: "Humanish config", ok: configPresent, detail: configPresent ? "humanish/config.ts exists." : "humanish/config.ts was not created." },
3134
+ { id: "personas", label: "Personas", ok: personaCount > 0, detail: personaCount + " persona file(s) detected." },
3135
+ { id: "scenarios", label: "Scenarios", ok: scenarioCount > 0, detail: scenarioCount + " scenario file(s) detected." },
3136
+ { id: "package-script", label: "Package script", ok: packageScriptPresent, detail: packageScriptPresent ? "package.json exposes a Humanish script." : "package.json does not expose a Humanish script." },
3137
+ { id: "runtime-ignore", label: "Runtime ignore", ok: gitignoreContainsRuntimeIgnore, detail: gitignoreContainsRuntimeIgnore ? ".gitignore ignores .humanish/." : ".gitignore does not ignore .humanish/." }
3138
+ ];
3139
+ const studyChecks = [
3140
+ { id: "coverage-customized", label: "Coverage customized", ok: coverageCustomized, detail: coverageCustomized ? "Coverage map or matrix appears app-aware." : "Coverage map/matrix still appears starter-level or absent." },
3141
+ { id: "personas-customized", label: "Personas customized", ok: personaCustomized, detail: personaCustomized ? "Personas appear app-specific or non-starter." : "Personas appear generic starter-level." },
3142
+ { id: "scenarios-customized", label: "Scenarios customized", ok: scenarioCustomized, detail: scenarioCustomized ? "Scenarios appear app-specific or non-starter." : "Scenarios appear generic starter-level." },
3143
+ { id: "app-url-proof", label: "App-url proof", ok: appUrlProofMentioned, detail: appUrlProofMentioned ? "Actor/setup evidence references app-url proof." : appUrlProofBlocked ? "Actor evidence reports that app-url proof was blocked." : "No app-url proof signal detected." },
3144
+ { id: "actor-insight", label: "Actor insight", ok: actorInsightCaptured, detail: actorInsightCaptured ? "Actor evidence mentions feedback, friction, coverage, or observed product behavior." : "Actor evidence does not capture product feedback or coverage insight." }
3145
+ ];
3146
+ const studySignalCount = studyChecks.filter((check) => check.ok).length;
3147
+ const usefulStudy = coverageCustomized && personaCustomized && scenarioCustomized;
3148
+ const concreteStudyInsight = actorInsightCaptured || appUrlProofBlocked;
3149
+ const studyRating = !configPresent || personaCount === 0 || scenarioCount === 0
3150
+ ? "none"
3151
+ : usefulStudy && concreteStudyInsight && (appUrlProofMentioned || appUrlProofBlocked)
3152
+ ? "high_leverage"
3153
+ : usefulStudy
3154
+ ? "useful"
3155
+ : "ceremonial";
3156
+ const failed = checks.filter((check) => !check.ok);
3157
+ return {
3158
+ schema: "humanish.setup-quality.v1",
3159
+ generatedAt: new Date().toISOString(),
3160
+ redaction: {
3161
+ status: "passed",
3162
+ rawPreviews: "included",
3163
+ notes: "Only allowlisted setup files are previewed; generated state, browser profiles, secrets, .git, node_modules, and .humanish are excluded."
3164
+ },
3165
+ summary: failed.length === 0 ? "Humanish setup evidence looks complete." : failed.length + " setup-quality gap(s) need review.",
3166
+ status: failed.length === 0 ? "passed" : "needs_review",
3167
+ checks,
3168
+ tree,
3169
+ previews,
3170
+ studyQuality: {
3171
+ schema: "humanish.study-quality.v1",
3172
+ rating: studyRating,
3173
+ summary: "Study-quality rating " + studyRating + " from " + studySignalCount + "/5 app-specific leverage signals" + (appUrlProofBlocked ? "; app-url proof path was blocked by actor evidence." : "."),
3174
+ checks: studyChecks,
3175
+ signals: {
3176
+ appUrlProofBlocked,
3177
+ appUrlProofMentioned,
3178
+ actorInsightCaptured,
3179
+ coverageCustomized,
3180
+ personaCustomized,
3181
+ scenarioCustomized
3182
+ }
3183
+ },
3184
+ packageScripts,
3185
+ humanish: { configPresent, personaCount, scenarioCount, packageScriptPresent, gitignoreContainsRuntimeIgnore }
3186
+ };
3187
+ };
3188
+ const setupQuality = buildSetupQuality(appDir);
3189
+ const safeToken = (value) => String(value || "stream").replace(/[^a-zA-Z0-9_.-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 120) || "stream";
3190
+ const appServerActorEvidence = (() => {
3191
+ if (!fs.existsSync(codexAppServerStatePath)) return null;
3192
+ try {
3193
+ const state = JSON.parse(fs.readFileSync(codexAppServerStatePath, "utf8"));
3194
+ const result = state && typeof state === "object" ? state.result || {} : {};
3195
+ const readArtifact = (relativePath, maxBytes = 120000) => {
3196
+ if (!relativePath || String(relativePath).includes("..") || String(relativePath).includes("://")) return "";
3197
+ return readTextLimited(path.join(codexAppServerRootPath, String(relativePath)), maxBytes);
3198
+ };
3199
+ const readJsonArtifact = (relativePath) => {
3200
+ if (!relativePath || String(relativePath).includes("..") || String(relativePath).includes("://")) return null;
3201
+ try {
3202
+ return JSON.parse(fs.readFileSync(path.join(codexAppServerRootPath, String(relativePath)), "utf8"));
3203
+ } catch {
3204
+ return null;
3205
+ }
3206
+ };
3207
+ const compactList = (items, mapper, limit = 8) => Array.isArray(items)
3208
+ ? items.slice(-limit).map(mapper).filter(Boolean)
3209
+ : undefined;
3210
+ const projectTraceJson = (value) => {
3211
+ if (!value || typeof value !== "object") return null;
3212
+ const messages = compactList(value.messages, (message) => message && typeof message === "object" ? {
3213
+ ...(message.itemId ? { itemId: cleanText(message.itemId) } : {}),
3214
+ ...(message.text ? { text: cleanText(message.text).slice(0, 4000) } : {})
3215
+ } : null, 10);
3216
+ const commands = compactList(value.commands, (command) => command && typeof command === "object" ? {
3217
+ ...(command.itemId ? { itemId: cleanText(command.itemId) } : {}),
3218
+ ...(command.command ? { command: cleanText(command.command).slice(0, 2000) } : {}),
3219
+ ...(command.cwd ? { cwd: cleanText(command.cwd).slice(0, 500) } : {}),
3220
+ ...(command.status ? { status: cleanText(command.status).slice(0, 80) } : {}),
3221
+ ...(Number.isFinite(command.exitCode) ? { exitCode: command.exitCode } : {}),
3222
+ ...(command.outputTail ? { outputTail: cleanText(command.outputTail).slice(-4000) } : {})
3223
+ } : null, 12);
3224
+ const fileChanges = compactList(value.fileChanges, (change) => change && typeof change === "object" ? {
3225
+ ...(change.itemId ? { itemId: cleanText(change.itemId) } : {}),
3226
+ ...(change.status ? { status: cleanText(change.status).slice(0, 80) } : {}),
3227
+ ...(Number.isFinite(change.changeCount) ? { changeCount: change.changeCount } : {}),
3228
+ ...(change.outputTail ? { outputTail: cleanText(change.outputTail).slice(-2000) } : {})
3229
+ } : null, 8);
3230
+ return {
3231
+ schema: "humanish.codex-app-server-trace.projected.v1",
3232
+ sourceSchema: cleanText(value.schema || ""),
3233
+ redaction: value.redaction && typeof value.redaction === "object" ? value.redaction : { status: "passed" },
3234
+ status: cleanText(value.status || result.status || state.status || ""),
3235
+ reason: cleanText(value.reason || result.reason || state.reason || ""),
3236
+ ...(value.counts && typeof value.counts === "object" ? { counts: value.counts } : {}),
3237
+ ...(messages && messages.length ? { messages } : {}),
3238
+ ...(commands && commands.length ? { commands } : {}),
3239
+ ...(fileChanges && fileChanges.length ? { fileChanges } : {}),
3240
+ ...(value.tokenUsage && typeof value.tokenUsage === "object" ? { tokenUsage: value.tokenUsage } : {}),
3241
+ ...(value.threadId ? { threadId: cleanText(value.threadId) } : {}),
3242
+ ...(value.turnId ? { turnId: cleanText(value.turnId) } : {}),
3243
+ ...(value.sessionId ? { sessionId: cleanText(value.sessionId) } : {}),
3244
+ ...(value.completedAt ? { completedAt: cleanText(value.completedAt) } : {}),
3245
+ ...(Number.isFinite(value.durationMs) ? { durationMs: value.durationMs } : {})
3246
+ };
3247
+ };
3248
+ const rawTraceJson = readJsonArtifact(result.tracePath);
3249
+ const traceJson = projectTraceJson(rawTraceJson);
3250
+ const traceText = traceJson ? "" : readArtifact(result.tracePath, 160000);
3251
+ const token = safeToken(streamId);
3252
+ return {
3253
+ eventsPath: path.join("codex-app-server", token + "-events.ndjson"),
3254
+ eventsText: readArtifact(result.eventsPath, 120000),
3255
+ reason: cleanText(state.reason || result.reason || ""),
3256
+ status: cleanText(state.status || result.status || ""),
3257
+ tracePath: path.join("codex-app-server", token + "-summary.json"),
3258
+ ...(traceJson ? { traceJson } : { traceText }),
3259
+ transcriptPath: path.join("codex-app-server", token + "-transcript.txt"),
3260
+ transcriptText: readArtifact(result.transcriptPath, 80000)
3261
+ };
3262
+ } catch {
3263
+ return null;
3264
+ }
3265
+ })();
3266
+ const nestedStepTraceSummary = (() => {
3267
+ try {
3268
+ const runRoot = path.dirname(path.dirname(String(nestedObserverPath || "")));
3269
+ const tracesDir = path.join(runRoot, "traces");
3270
+ if (!fs.existsSync(tracesDir)) return null;
3271
+ const traceNames = fs.readdirSync(tracesDir)
3272
+ .filter((name) => name.endsWith(".json"))
3273
+ .sort()
3274
+ .slice(0, 8);
3275
+ if (!traceNames.length) return null;
3276
+ const surfaceSummaries = [];
3277
+ let scenario = null;
3278
+ for (const name of traceNames) {
3279
+ let trace = null;
3280
+ try {
3281
+ trace = JSON.parse(fs.readFileSync(path.join(tracesDir, name), "utf8"));
3282
+ } catch {
3283
+ continue;
3284
+ }
3285
+ if (!trace || typeof trace !== "object" || !Array.isArray(trace.steps)) continue;
3286
+ if (!scenario && trace.scenario && typeof trace.scenario === "object") {
3287
+ scenario = {
3288
+ id: cleanText(trace.scenario.id || "scenario"),
3289
+ ...(trace.scenario.source ? { source: safeRel(trace.scenario.source) } : {}),
3290
+ ...(trace.scenario.sourceDigest ? { sourceDigest: cleanText(trace.scenario.sourceDigest) } : {}),
3291
+ ...(Number.isFinite(trace.scenario.stepCount) ? { stepCount: trace.scenario.stepCount } : {}),
3292
+ ...(trace.scenario.title ? { title: cleanText(trace.scenario.title) } : {})
3293
+ };
3294
+ }
3295
+ const steps = trace.steps.slice(0, 20).map((step) => {
3296
+ const assertions = Array.isArray(step.assertions)
3297
+ ? step.assertions.slice(0, 12).map((assertion) => cleanText((assertion.id || "assertion") + ":" + (assertion.status || "unknown")))
3298
+ : [];
3299
+ return {
3300
+ action: cleanText(step.action || "action"),
3301
+ ...(assertions.length ? { assertionStatuses: assertions } : {}),
3302
+ id: cleanText(step.id || "step"),
3303
+ ...(step.label ? { label: cleanText(step.label) } : {}),
3304
+ reason: cleanText(step.reason || ""),
3305
+ status: step.status === "passed" || step.status === "blocked" ? step.status : "unknown"
3306
+ };
3307
+ });
3308
+ if (!steps.length) continue;
3309
+ surfaceSummaries.push({
3310
+ id: cleanText(trace.surface && trace.surface.id ? trace.surface.id : path.basename(name, ".json")),
3311
+ ...(trace.surface && trace.surface.label ? { label: cleanText(trace.surface.label) } : {}),
3312
+ ok: trace.ok === true,
3313
+ reason: cleanText(trace.reason || ""),
3314
+ steps
3315
+ });
3316
+ }
3317
+ if (!surfaceSummaries.length) return null;
3318
+ const totalSteps = surfaceSummaries.reduce((total, surface) => total + surface.steps.length, 0);
3319
+ const passedSteps = surfaceSummaries.reduce((total, surface) => total + surface.steps.filter((step) => step.status === "passed").length, 0);
3320
+ const blockedSteps = surfaceSummaries.reduce((total, surface) => total + surface.steps.filter((step) => step.status === "blocked").length, 0);
3321
+ return {
3322
+ schema: "humanish.oss-meta-nested-step-trace-summary.v1",
3323
+ redaction: {
3324
+ status: "passed",
3325
+ notes: "Projected inside disposable sandbox from nested Humanish trace JSON; URLs, screenshots, and remote paths are omitted."
3326
+ },
3327
+ counts: {
3328
+ blockedSteps,
3329
+ passedSteps,
3330
+ surfaces: surfaceSummaries.length,
3331
+ totalSteps,
3332
+ traces: traceNames.length
3333
+ },
3334
+ ...(scenario ? { scenario } : {}),
3335
+ status: blockedSteps > 0 ? "blocked" : passedSteps === totalSteps ? "passed" : "unknown",
3336
+ surfaces: surfaceSummaries
3337
+ };
3338
+ } catch {
3339
+ return null;
3340
+ }
3341
+ })();
3342
+ fs.writeFileSync(completionPath, JSON.stringify({
3343
+ schema: "humanish.oss-meta-bootstrap-completion.v1",
3344
+ status,
3345
+ reason,
3346
+ exitCode: Number(exitCode),
3347
+ appStatus,
3348
+ appReason: cleanText(appReason),
3349
+ appUrl: cleanText(appUrl),
3350
+ appPid: numberOrNull(appPid),
3351
+ appLogPath: cleanText(appLogPath),
3352
+ actorStatus,
3353
+ actorPid: numberOrNull(actorPid),
3354
+ actorLogPath: cleanText(actorLogPath),
3355
+ actorLogTail,
3356
+ actorLastMessageTail,
3357
+ ...(appServerActorEvidence ? { appServerActorEvidence } : {}),
3358
+ nestedObserverPresent: nestedObserverPresent === "true",
3359
+ ...(nestedStepTraceSummary ? { nestedStepTraceSummary } : {}),
3360
+ nestedVerifyStatus,
3361
+ setupQuality,
3362
+ visualStatus,
3363
+ visualReason: cleanText(visualReason),
3364
+ visualWindowCount: numberOrNull(visualWindowCount),
3365
+ logTail: redactedTail,
3366
+ completedAt: new Date().toISOString()
3367
+ }, null, 2) + "\\n");
3368
+ NODE
3369
+ }
3370
+
3371
+ finish() {
3372
+ local exit_code="$?"
3373
+ trap - EXIT
3374
+ if [[ "$exit_code" -ne 0 ]]; then
3375
+ write_completion "failed" "Bootstrap exited before nested Humanish proof completed." "$exit_code"
3376
+ elif [[ "$NESTED_VERIFY_STATUS" != "passed" ]]; then
3377
+ write_completion "failed" "Nested Humanish verification did not pass." "$exit_code"
3378
+ elif [[ "$APP_STATUS" != "running" ]]; then
3379
+ write_completion "blocked" "Nested Humanish proof completed, but the target app surface was not proven running." "$exit_code"
3380
+ elif [[ "$VISUAL_STATUS" != "visible" ]]; then
3381
+ write_completion "blocked" "Nested Humanish proof completed, but headed desktop visual layout was not proven visible." "$exit_code"
3382
+ elif [[ "\${HUMANISH_OSS_META_CODEX_APP_SERVER:-0}" == "1" && "$ACTOR_STATUS" != "passed" ]]; then
3383
+ write_completion "blocked" "Codex app-server mode requires passed Codex app-server actor evidence." "$exit_code"
3384
+ elif [[ "\${HUMANISH_OSS_META_REQUIRE_ACTOR:-0}" == "1" && "$ACTOR_STATUS" != "passed" ]]; then
3385
+ write_completion "blocked" "Required Codex actor evidence did not reach a passed terminal status." "$exit_code"
3386
+ else
3387
+ write_completion "passed" "Target app surface, nested Humanish proof, and nested Observer were checked." "$exit_code"
3388
+ fi
3389
+ exit "$exit_code"
3390
+ }
3391
+ trap finish EXIT
3392
+
3393
+ echo "== humanish oss meta-lab bootstrap =="
3394
+ echo "repo=${args.displayRepo}"
3395
+ echo "public_safe=1"
3396
+ echo "provider_secrets=isolated"
3397
+ echo "github_token=$([[ -n "$HUMANISH_PRIVATE_GITHUB_TOKEN" ]] && echo available-for-clone || echo absent)"
3398
+ echo "codex_actor_auth=$([[ -n "$HUMANISH_PRIVATE_CODEX_API_KEY$HUMANISH_PRIVATE_CODEX_ACCESS_TOKEN" ]] && echo available-for-actor || echo absent)"
3399
+ echo "codex_app_server_mode=\${HUMANISH_OSS_META_CODEX_APP_SERVER:-0}"
3400
+ echo "codex_app_server_client=$([[ -n "$HUMANISH_PRIVATE_CODEX_APP_SERVER_URL" ]] && echo available || echo placeholder)"
3401
+ echo "host_actor_plan=$([[ -f "$HOST_ACTOR_PLAN" ]] && echo available || echo absent)"
3402
+ echo
3403
+
3404
+ need() {
3405
+ if ! command -v "$1" >/dev/null 2>&1; then
3406
+ echo "missing command: $1"
3407
+ return 1
3408
+ fi
3409
+ }
3410
+
3411
+ ensure_node() {
3412
+ local major=0
3413
+ if command -v node >/dev/null 2>&1; then
3414
+ major="$(node -e 'console.log(Number(process.versions.node.split(".")[0]))' 2>/dev/null || echo 0)"
3415
+ fi
3416
+
3417
+ if command -v node >/dev/null 2>&1 && command -v npm >/dev/null 2>&1 && [[ "$major" -ge 20 ]]; then
3418
+ return 0
3419
+ fi
3420
+
3421
+ echo "node_or_npm=missing_or_too_old"
3422
+ if command -v apt-get >/dev/null 2>&1 && command -v sudo >/dev/null 2>&1; then
3423
+ echo "installing nodejs/npm via nodesource"
3424
+ sudo -n apt-get update
3425
+ sudo -n apt-get install -y ca-certificates curl gnupg
3426
+ curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -
3427
+ sudo -n apt-get install -y nodejs
3428
+ fi
3429
+ }
3430
+
3431
+ need git
3432
+ ensure_node
3433
+ need node
3434
+ need npm
3435
+
3436
+ wait_for_http() {
3437
+ local url="$1"
3438
+ local timeout_ms="$2"
3439
+ local interval_ms="$3"
3440
+ node - "$url" "$timeout_ms" "$interval_ms" <<'NODE'
3441
+ const [url, timeoutArg, intervalArg] = process.argv.slice(2);
3442
+ const timeoutMs = Number(timeoutArg);
3443
+ const intervalMs = Number(intervalArg);
3444
+ const startedAt = Date.now();
3445
+
3446
+ async function probe() {
3447
+ const controller = new AbortController();
3448
+ const timer = setTimeout(() => controller.abort(), Math.min(intervalMs, 5_000));
3449
+ try {
3450
+ const response = await fetch(url, { signal: controller.signal });
3451
+ return response.status < 500;
3452
+ } catch {
3453
+ return false;
3454
+ } finally {
3455
+ clearTimeout(timer);
3456
+ }
3457
+ }
3458
+
3459
+ while (Date.now() - startedAt <= timeoutMs) {
3460
+ if (await probe()) process.exit(0);
3461
+ await new Promise((resolve) => setTimeout(resolve, intervalMs));
3462
+ }
3463
+ process.exit(1);
3464
+ NODE
3465
+ }
3466
+
3467
+ wait_for_any_http() {
3468
+ local timeout_ms="$1"
3469
+ local interval_ms="$2"
3470
+ shift 2
3471
+ node - "$timeout_ms" "$interval_ms" "$@" <<'NODE'
3472
+ const [timeoutArg, intervalArg, ...urls] = process.argv.slice(2);
3473
+ const timeoutMs = Number(timeoutArg);
3474
+ const intervalMs = Number(intervalArg);
3475
+ const startedAt = Date.now();
3476
+
3477
+ async function probe(url) {
3478
+ const controller = new AbortController();
3479
+ const timer = setTimeout(() => controller.abort(), Math.min(intervalMs, 5_000));
3480
+ try {
3481
+ const response = await fetch(url, { signal: controller.signal });
3482
+ return response.status < 500;
3483
+ } catch {
3484
+ return false;
3485
+ } finally {
3486
+ clearTimeout(timer);
3487
+ }
3488
+ }
3489
+
3490
+ while (Date.now() - startedAt <= timeoutMs) {
3491
+ for (const url of urls) {
3492
+ if (await probe(url)) {
3493
+ console.log(url);
3494
+ process.exit(0);
3495
+ }
3496
+ }
3497
+ await new Promise((resolve) => setTimeout(resolve, intervalMs));
3498
+ }
3499
+ process.exit(1);
3500
+ NODE
3501
+ }
3502
+
3503
+ open_browser_url() {
3504
+ local url="$1"
3505
+ local profile="$2"
3506
+ local x="$3"
3507
+ local y="$4"
3508
+ local width="$5"
3509
+ local height="$6"
3510
+ if command -v google-chrome >/dev/null 2>&1; then
3511
+ nohup google-chrome --new-window --no-first-run --no-default-browser-check --disable-default-apps --user-data-dir="$ROOT_DIR/chrome-$profile" --window-position="$x,$y" --window-size="$width,$height" "$url" >/dev/null 2>&1 &
3512
+ elif command -v firefox >/dev/null 2>&1; then
3513
+ nohup firefox --width "$width" --height "$height" "$url" >/dev/null 2>&1 &
3514
+ else
3515
+ echo "browser_open=skipped profile=$profile url=$url"
3516
+ fi
3517
+ }
3518
+
3519
+ open_nested_observer() {
3520
+ local label="$1"
3521
+ echo
3522
+ echo "== $label =="
3523
+ if [[ -f "$NESTED_OBSERVER" ]]; then
3524
+ open_browser_url "file://$NESTED_OBSERVER" nested-observer 760 0 680 940
3525
+ else
3526
+ echo "Nested observer missing: $NESTED_OBSERVER"
3527
+ fi
3528
+ }
3529
+
3530
+ open_codex_app_server_client() {
3531
+ if [[ "\${HUMANISH_OSS_META_CODEX_APP_SERVER:-0}" != "1" ]]; then
3532
+ return 0
3533
+ fi
3534
+ if [[ "$CODEX_APP_SERVER_CLIENT_OPENED" == "1" ]]; then
3535
+ return 0
3536
+ fi
3537
+
3538
+ echo
3539
+ echo "== codex app-server client surface =="
3540
+ local client_url="\${CODEX_APP_SERVER_UI_URL:-$HUMANISH_PRIVATE_CODEX_APP_SERVER_URL}"
3541
+ if [[ -z "$client_url" ]]; then
3542
+ ACTOR_STATUS=blocked
3543
+ echo "codex_app_server_client=blocked reason=no real app-server UI URL available"
3544
+ return 1
3545
+ fi
3546
+ echo "codex_app_server_client=provided"
3547
+ open_browser_url "$client_url" codex-app-server 430 520 330 420
3548
+ CODEX_APP_SERVER_CLIENT_OPENED=1
3549
+ }
3550
+
3551
+ arrange_lab_windows() {
3552
+ echo
3553
+ echo "== visual desktop layout =="
3554
+ VISUAL_STATUS=unknown
3555
+ VISUAL_REASON="Window manager proof was not collected."
3556
+ VISUAL_WINDOW_COUNT=0
3557
+ local expected_chrome_windows=2
3558
+ if [[ "\${HUMANISH_OSS_META_CODEX_APP_SERVER:-0}" == "1" ]]; then
3559
+ expected_chrome_windows=3
3560
+ fi
3561
+ if ! command -v xdotool >/dev/null 2>&1; then
3562
+ VISUAL_STATUS=unknown
3563
+ VISUAL_REASON="xdotool is unavailable; cannot prove headed browser window visibility."
3564
+ echo "visual_status=$VISUAL_STATUS reason=$VISUAL_REASON"
3565
+ return 0
3566
+ fi
3567
+
3568
+ local chrome_windows=()
3569
+ local observer_window=""
3570
+ for attempt in $(seq 1 40); do
3571
+ mapfile -t chrome_windows < <(xdotool search --onlyvisible --class google-chrome 2>/dev/null || true)
3572
+ observer_window="$(xdotool search --onlyvisible --name "Humanish Observer" 2>/dev/null | tail -n 1 || true)"
3573
+ VISUAL_WINDOW_COUNT="\${#chrome_windows[@]}"
3574
+ if [[ "$VISUAL_WINDOW_COUNT" -ge "$expected_chrome_windows" && -n "$observer_window" ]]; then
3575
+ break
3576
+ fi
3577
+ sleep 0.25
3578
+ done
3579
+
3580
+ if [[ -n "$observer_window" ]]; then
3581
+ xdotool windowsize "$observer_window" 680 933 >/dev/null 2>&1 || true
3582
+ xdotool windowmove "$observer_window" 760 27 >/dev/null 2>&1 || true
3583
+ fi
3584
+
3585
+ local slot=0
3586
+ local win
3587
+ for win in "\${chrome_windows[@]}"; do
3588
+ if [[ -n "$observer_window" && "$win" == "$observer_window" ]]; then
3589
+ continue
3590
+ fi
3591
+ if [[ "$slot" -eq 0 ]]; then
3592
+ xdotool windowsize "$win" 760 493 >/dev/null 2>&1 || true
3593
+ xdotool windowmove "$win" 0 27 >/dev/null 2>&1 || true
3594
+ elif [[ "$slot" -eq 1 ]]; then
3595
+ xdotool windowsize "$win" 430 420 >/dev/null 2>&1 || true
3596
+ xdotool windowmove "$win" 0 520 >/dev/null 2>&1 || true
3597
+ else
3598
+ xdotool windowsize "$win" 330 420 >/dev/null 2>&1 || true
3599
+ xdotool windowmove "$win" 430 520 >/dev/null 2>&1 || true
3600
+ fi
3601
+ slot=$((slot + 1))
3602
+ done
3603
+
3604
+ local terminal_window
3605
+ terminal_window="$(xdotool search --onlyvisible --name "$TERMINAL_TITLE" 2>/dev/null | head -n 1 || true)"
3606
+ if [[ -n "$terminal_window" ]]; then
3607
+ xdotool windowlower "$terminal_window" >/dev/null 2>&1 || xdotool windowminimize "$terminal_window" >/dev/null 2>&1 || true
3608
+ fi
3609
+ if [[ -n "$observer_window" ]]; then
3610
+ xdotool windowactivate "$observer_window" >/dev/null 2>&1 || true
3611
+ fi
3612
+
3613
+ mapfile -t chrome_windows < <(xdotool search --onlyvisible --class google-chrome 2>/dev/null || true)
3614
+ VISUAL_WINDOW_COUNT="\${#chrome_windows[@]}"
3615
+ if [[ "$VISUAL_WINDOW_COUNT" -ge "$expected_chrome_windows" && -n "$observer_window" ]]; then
3616
+ VISUAL_STATUS=visible
3617
+ if [[ "\${HUMANISH_OSS_META_CODEX_APP_SERVER:-0}" == "1" ]]; then
3618
+ VISUAL_REASON="Detected $VISUAL_WINDOW_COUNT visible Chrome windows including target app, nested Observer, and Codex app-server client surface."
3619
+ else
3620
+ VISUAL_REASON="Detected $VISUAL_WINDOW_COUNT visible Chrome windows including nested Observer; app and Observer windows arranged for screenshot."
3621
+ fi
3622
+ else
3623
+ VISUAL_STATUS=blocked
3624
+ if [[ "\${HUMANISH_OSS_META_CODEX_APP_SERVER:-0}" == "1" ]]; then
3625
+ VISUAL_REASON="Expected target app, nested Observer, and Codex app-server client browser windows, detected $VISUAL_WINDOW_COUNT visible Chrome window(s)."
3626
+ else
3627
+ VISUAL_REASON="Expected app and nested Observer browser windows, detected $VISUAL_WINDOW_COUNT visible Chrome window(s)."
3628
+ fi
3629
+ fi
3630
+ echo "visual_status=$VISUAL_STATUS window_count=$VISUAL_WINDOW_COUNT reason=$VISUAL_REASON"
3631
+ }
3632
+
3633
+ detect_app_plan() {
3634
+ node <<'NODE'
3635
+ const fs = require("node:fs");
3636
+ const shell = (value) => "'" + String(value).replace(/'/g, "'\\"'\\"'") + "'";
3637
+ const emit = (key, value) => console.log(key + "=" + shell(value));
3638
+
3639
+ if (!fs.existsSync("package.json")) {
3640
+ console.log("APP_PLAN_OK='0'");
3641
+ emit("APP_PM", "npm");
3642
+ emit("APP_PLAN_REASON", "package.json not found");
3643
+ process.exit(0);
3644
+ }
3645
+
3646
+ let pkg;
3647
+ try {
3648
+ pkg = JSON.parse(fs.readFileSync("package.json", "utf8"));
3649
+ } catch {
3650
+ console.log("APP_PLAN_OK='0'");
3651
+ emit("APP_PM", "npm");
3652
+ emit("APP_PLAN_REASON", "package.json could not be parsed");
3653
+ process.exit(0);
3654
+ }
3655
+
3656
+ const scripts = pkg.scripts && typeof pkg.scripts === "object" ? pkg.scripts : {};
3657
+ const deps = { ...(pkg.dependencies || {}), ...(pkg.devDependencies || {}) };
3658
+ const lockfiles = fs.readdirSync(".").filter((name) => /^(pnpm-lock\\.yaml|yarn\\.lock|bun\\.lockb?|package-lock\\.json|npm-shrinkwrap\\.json)$/.test(name));
3659
+ const packageManagerHint = typeof pkg.packageManager === "string" ? pkg.packageManager : "";
3660
+ let pm = "npm";
3661
+ if (packageManagerHint.startsWith("pnpm@") || lockfiles.includes("pnpm-lock.yaml")) pm = "pnpm";
3662
+ else if (packageManagerHint.startsWith("yarn@") || lockfiles.includes("yarn.lock")) pm = "yarn";
3663
+ else if (packageManagerHint.startsWith("bun@") || lockfiles.includes("bun.lock") || lockfiles.includes("bun.lockb")) pm = "bun";
3664
+
3665
+ const names = ["dev", "start", "serve", "preview"];
3666
+ const entries = names
3667
+ .map((name) => ({ name, command: typeof scripts[name] === "string" ? scripts[name].trim() : "" }))
3668
+ .filter((entry) => entry.command && /^[A-Za-z0-9:_-]+$/.test(entry.name));
3669
+
3670
+ if (!entries.length) {
3671
+ console.log("APP_PLAN_OK='0'");
3672
+ emit("APP_PM", pm);
3673
+ emit("APP_PLAN_REASON", "no dev/start/serve/preview script found");
3674
+ process.exit(0);
3675
+ }
3676
+
3677
+ const pickByCommand = (pattern) => entries.find((entry) => pattern.test(entry.command));
3678
+ let picked = null;
3679
+ let framework = "generic";
3680
+ if ((picked = pickByCommand(/\\bnext\\s+(dev|start)\\b/)) || deps.next) framework = "next";
3681
+ if (!picked && (picked = pickByCommand(/\\bvite(\\s|$)/))) framework = "vite";
3682
+ if (!picked && deps.vite) { picked = entries.find((entry) => entry.name === "dev") || entries[0]; framework = "vite"; }
3683
+ if (!picked && deps.next) picked = entries.find((entry) => entry.name === "dev" || entry.name === "start") || entries[0];
3684
+ if (!picked && (picked = pickByCommand(/\\bastro\\s+dev\\b/))) framework = "vite";
3685
+ if (!picked) picked = entries.find((entry) => entry.name === "dev") || entries.find((entry) => entry.name === "start") || entries[0];
3686
+
3687
+ const command = picked.command;
3688
+ const portFromCommand = /(?:^|\\s)(?:--port|-p)\\s+([0-9]{2,5})(?:\\s|$)/.exec(command)?.[1]
3689
+ || /(?:^|\\s)PORT=([0-9]{2,5})(?:\\s|$)/.exec(command)?.[1];
3690
+ const defaultPort = framework === "vite" ? "5173" : "3000";
3691
+
3692
+ console.log("APP_PLAN_OK='1'");
3693
+ emit("APP_PM", pm);
3694
+ emit("APP_SCRIPT", picked.name);
3695
+ emit("APP_FRAMEWORK", framework);
3696
+ emit("APP_PORT", portFromCommand || defaultPort);
3697
+ emit("APP_PLAN_REASON", "selected " + picked.name + " script for " + framework + " app startup");
3698
+ NODE
3699
+ }
3700
+
3701
+ ensure_package_manager() {
3702
+ local pm="$1"
3703
+ mkdir -p "$ROOT_DIR/npm-global"
3704
+ export NPM_CONFIG_PREFIX="$ROOT_DIR/npm-global"
3705
+ export PATH="$NPM_CONFIG_PREFIX/bin:$PATH"
3706
+ if command -v "$pm" >/dev/null 2>&1; then
3707
+ return 0
3708
+ fi
3709
+ if command -v corepack >/dev/null 2>&1; then
3710
+ corepack enable >/dev/null 2>&1 || true
3711
+ if [[ "$pm" == "pnpm" ]]; then
3712
+ corepack prepare pnpm@latest --activate >/dev/null 2>&1 || true
3713
+ elif [[ "$pm" == "yarn" ]]; then
3714
+ corepack prepare yarn@stable --activate >/dev/null 2>&1 || true
3715
+ fi
3716
+ fi
3717
+ if command -v "$pm" >/dev/null 2>&1; then
3718
+ return 0
3719
+ fi
3720
+ case "$pm" in
3721
+ pnpm) npm i -g pnpm --no-audit --no-fund --prefix "$NPM_CONFIG_PREFIX" ;;
3722
+ yarn) npm i -g yarn --no-audit --no-fund --prefix "$NPM_CONFIG_PREFIX" ;;
3723
+ bun) npm i -g bun --no-audit --no-fund --prefix "$NPM_CONFIG_PREFIX" ;;
3724
+ npm) command -v npm >/dev/null 2>&1 ;;
3725
+ *) return 1 ;;
3726
+ esac
3727
+ }
3728
+
3729
+ install_project_dependencies() {
3730
+ local pm="$1"
3731
+ if [[ "$pm" == "pnpm" ]]; then
3732
+ pnpm config set verify-deps-before-run false --location project >/dev/null 2>&1 || true
3733
+ pnpm config set dangerously-allow-all-builds true --location project >/dev/null 2>&1 || true
3734
+ fi
3735
+ if [[ -d node_modules ]]; then
3736
+ echo "dependencies=present"
3737
+ return 0
3738
+ fi
3739
+ echo "dependencies=installing manager=$pm"
3740
+ case "$pm" in
3741
+ pnpm)
3742
+ echo "pnpm_build_scripts=allowed disposable_e2b_lab=1"
3743
+ if [[ -f pnpm-lock.yaml ]]; then pnpm install --frozen-lockfile --dangerously-allow-all-builds || pnpm install --dangerously-allow-all-builds; else pnpm install --dangerously-allow-all-builds; fi
3744
+ ;;
3745
+ yarn)
3746
+ if [[ -f yarn.lock ]]; then yarn install --frozen-lockfile --ignore-scripts || yarn install --ignore-scripts; else yarn install --ignore-scripts; fi
3747
+ ;;
3748
+ bun)
3749
+ bun install
3750
+ ;;
3751
+ npm)
3752
+ if [[ -f package-lock.json || -f npm-shrinkwrap.json ]]; then npm ci --ignore-scripts --no-audit --no-fund || npm install --ignore-scripts --no-audit --no-fund; else npm install --ignore-scripts --no-audit --no-fund; fi
3753
+ ;;
3754
+ *) return 1 ;;
3755
+ esac
3756
+ }
3757
+
3758
+ install_humanish_cli() {
3759
+ echo
3760
+ echo "== installing humanish =="
3761
+ local plan_output
3762
+ plan_output="$(detect_app_plan)"
3763
+ eval "$plan_output"
3764
+ local pm="\${APP_PM:-npm}"
3765
+ if ! ensure_package_manager "$pm"; then
3766
+ echo "humanish_install=blocked package_manager=$pm"
3767
+ return 1
3768
+ fi
3769
+ local spec="humanish"
3770
+ if [[ -n "$REMOTE_PACKAGE" && -f "$REMOTE_PACKAGE" ]]; then
3771
+ spec="$REMOTE_PACKAGE"
3772
+ fi
3773
+
3774
+ case "$pm" in
3775
+ pnpm)
3776
+ if pnpm root -w >/dev/null 2>&1; then
3777
+ PNPM_CONFIG_VERIFY_DEPS_BEFORE_RUN=false npm_config_verify_deps_before_run=false pnpm add --save-dev --workspace-root "$spec" --ignore-scripts
3778
+ else
3779
+ PNPM_CONFIG_VERIFY_DEPS_BEFORE_RUN=false npm_config_verify_deps_before_run=false pnpm add --save-dev "$spec" --ignore-scripts
3780
+ fi
3781
+ ;;
3782
+ yarn)
3783
+ YARN_ENABLE_SCRIPTS=0 yarn add -D "$spec"
3784
+ ;;
3785
+ bun)
3786
+ bun add -d "$spec"
3787
+ ;;
3788
+ npm)
3789
+ npm i -D "$spec" --ignore-scripts --no-audit --no-fund
3790
+ ;;
3791
+ *)
3792
+ return 1
3793
+ ;;
3794
+ esac
3795
+ }
3796
+
3797
+ apply_host_actor_plan() {
3798
+ if [[ "\${HUMANISH_OSS_META_HOST_CODEX_ACTOR:-0}" != "1" ]]; then
3799
+ return 0
3800
+ fi
3801
+
3802
+ echo
3803
+ echo "== host codex actor plan =="
3804
+ if [[ ! -f "$HOST_ACTOR_PLAN" ]]; then
3805
+ ACTOR_STATUS=blocked
3806
+ echo "host_actor_plan=missing"
3807
+ return 1
3808
+ fi
3809
+
3810
+ node - "$HOST_ACTOR_PLAN" <<'NODE'
3811
+ const fs = require("node:fs");
3812
+ const path = require("node:path");
3813
+ const [planPath] = process.argv.slice(2);
3814
+ const plan = JSON.parse(fs.readFileSync(planPath, "utf8"));
3815
+ if (plan.schema !== "humanish.oss-host-actor-plan.v1" || plan.status !== "passed") {
3816
+ throw new Error("host actor plan is not passed");
3817
+ }
3818
+ const clean = (value, fallback) => String(value || fallback)
3819
+ .replace(/sk-[A-Za-z0-9_-]{20,}/g, "[redacted-openai-key]")
3820
+ .replace(/e2b_[A-Za-z0-9_-]{12,}/g, "[redacted-e2b-key]")
3821
+ .replace(/gh[pousr]_[A-Za-z0-9_]{12,}/g, "[redacted-github-token]")
3822
+ .replace(/github_pat_[A-Za-z0-9_]{12,}/g, "[redacted-github-token]")
3823
+ .replace(/\\s+/g, " ")
3824
+ .trim()
3825
+ .slice(0, 300);
3826
+ const token = (value, fallback) => clean(value, fallback).toLowerCase().replace(/[^a-z0-9_-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 80) || fallback;
3827
+ const yamlScalar = (value) => clean(value, "").replace(/"/g, "\\\\\"");
3828
+ const yamlList = (values) => (Array.isArray(values) && values.length ? values : ["public_safe"]).map((value) => " - " + yamlScalar(value)).join("\\n");
3829
+ fs.mkdirSync("humanish/personas", { recursive: true });
3830
+ fs.mkdirSync("humanish/scenarios", { recursive: true });
3831
+ const personas = Array.isArray(plan.personas) ? plan.personas.slice(0, 2) : [];
3832
+ const scenarios = Array.isArray(plan.scenarios) ? plan.scenarios.slice(0, 2) : [];
3833
+ if (!personas.length || !scenarios.length) {
3834
+ throw new Error("host actor plan lacks personas or scenarios");
3835
+ }
3836
+ for (const persona of personas) {
3837
+ const id = token(persona.id, "host-codex-persona");
3838
+ fs.writeFileSync(path.join("humanish/personas", id + ".yaml"), [
3839
+ "schema: humanish.persona.v1",
3840
+ "id: " + id,
3841
+ "name: " + yamlScalar(persona.name || "Host Codex Persona"),
3842
+ "summary: " + yamlScalar(persona.intent || "Public-safe host Codex actor persona."),
3843
+ "traits:",
3844
+ yamlList(persona.traits),
3845
+ "constraints:",
3846
+ " - Do not use real personal data.",
3847
+ " - Do not use production accounts.",
3848
+ " - Treat all credentials as env var names only.",
3849
+ " - Source: local Codex host actor plan applied inside disposable E2B."
3850
+ ].join("\\n") + "\\n");
3851
+ }
3852
+ for (const scenario of scenarios) {
3853
+ const id = token(scenario.id, "host-codex-scenario");
3854
+ const personaId = token(personas[0].id, "host-codex-persona");
3855
+ fs.writeFileSync(path.join("humanish/scenarios", id + ".yaml"), [
3856
+ "schema: humanish.scenario.v1",
3857
+ "id: " + id,
3858
+ "title: " + yamlScalar(scenario.title || "Host Codex Scenario"),
3859
+ "persona: " + personaId,
3860
+ "goal: " + yamlScalar(scenario.goal || "Exercise the app through a public-safe browser flow."),
3861
+ "mode: browser",
3862
+ "steps:",
3863
+ ...(Array.isArray(scenario.steps) && scenario.steps.length ? scenario.steps : ["Open the app", "Reach a meaningful state"]).slice(0, 8).map((step, index) => [
3864
+ " - name: Step " + String(index + 1),
3865
+ " expectation: " + yamlScalar(step)
3866
+ ].join("\\n"))
3867
+ ].join("\\n") + "\\n");
3868
+ }
3869
+ console.log("host_actor_plan=applied personas=" + personas.length + " scenarios=" + scenarios.length);
3870
+ console.log("host_actor_recommended_proof=" + clean(plan.recommendedProof, "humanish run --app-url <loopback-url> --sims 2"));
3871
+ NODE
3872
+ local apply_exit=$?
3873
+ if [[ "$apply_exit" -eq 0 ]]; then
3874
+ ACTOR_STATUS=blocked
3875
+ echo "actor_status=$ACTOR_STATUS source=host-codex-plan reason=remote-actor-not-run"
3876
+ else
3877
+ ACTOR_STATUS=failed
3878
+ echo "actor_status=$ACTOR_STATUS source=host-codex-plan exit=$apply_exit"
3879
+ fi
3880
+ return "$apply_exit"
3881
+ }
3882
+
3883
+ start_codex_app_server_actor_attempt() {
3884
+ echo "Codex app-server mode requested."
3885
+ mkdir -p "$CODEX_APP_SERVER_ROOT_PATH"
3886
+ cat > "$CODEX_APP_SERVER_PROMPT_PATH" <<'PROMPT'
3887
+ You are a Humanish meta-lab actor running through Codex app-server in a disposable authorized repo clone.
3888
+
3889
+ Goal: make Humanish useful for this app, then run at least one meaningful public-safe user-test proof.
3890
+
3891
+ Work requirements:
3892
+ - Inspect package.json, README, routes/components where lightweight, and the running app URL from APP_URL.
3893
+ - Improve Humanish setup beyond ceremonial init: app-aware coverage map/matrix, at least two app-specific synthetic personas, and at least two meaningful browser scenarios covering desktop/mobile or happy/friction states.
3894
+ - Do not leave only starter persona/scenario filenames, ids, or names. Write app-specific persona and scenario files with app-specific ids, names, goals, selectors, and expectations.
3895
+ - Use committed humanish/ source files for personas/scenarios. Keep runtime output under ignored .humanish/.
3896
+ - Run at least one meaningful Humanish proof against the running app URL when available: npx --no-install humanish run --app-url "$APP_URL" --sims 2.
3897
+ - Render or refresh the nested Observer with npx --no-install humanish watch --run latest --detach --no-open when feasible.
3898
+ - Finish with concise public-safe feedback: what user journey was tested, what evidence path proves it, and one useful product or Humanish harness improvement.
3899
+
3900
+ Safety:
3901
+ - Do not print secrets, environment values, raw private source, or private screenshots.
3902
+ - Do not commit, push, file issues, or mutate remotes.
3903
+ - Do not run provider-spend-heavy commands beyond the bounded Humanish/Codex proof.
3904
+ PROMPT
3905
+
3906
+ local timeout_ms="\${HUMANISH_OSS_META_ACTOR_TIMEOUT_MS:-480000}"
3907
+ local command
3908
+ command="APP_URL=$(printf '%q' "$APP_URL") HUMANISH_PRIVATE_CODEX_API_KEY=$(printf '%q' "$HUMANISH_PRIVATE_CODEX_API_KEY") HUMANISH_PRIVATE_CODEX_ACCESS_TOKEN=$(printf '%q' "$HUMANISH_PRIVATE_CODEX_ACCESS_TOKEN") npx --no-install humanish codex app-server --cwd $(printf '%q' "$APP_DIR") --run-root $(printf '%q' "$CODEX_APP_SERVER_ROOT_PATH") --state-file $(printf '%q' "$CODEX_APP_SERVER_STATE_PATH") --prompt-file $(printf '%q' "$CODEX_APP_SERVER_PROMPT_PATH") --timeout-ms $(printf '%q' "$timeout_ms") --port $(printf '%q' "$CODEX_APP_SERVER_PORT") --sandbox danger-full-access --actor-command 'npx -y @openai/codex@latest app-server --listen stdio://' --keep-open"
3909
+ nohup bash -lc "$command" > "$CODEX_APP_SERVER_LOG_PATH" 2>&1 &
3910
+ ACTOR_PID=$!
3911
+ ACTOR_STATUS=running
3912
+ CODEX_APP_SERVER_UI_URL="http://127.0.0.1:$CODEX_APP_SERVER_PORT/"
3913
+ echo "actor_status=$ACTOR_STATUS source=codex-app-server pid=$ACTOR_PID log=$CODEX_APP_SERVER_LOG_PATH state=$CODEX_APP_SERVER_STATE_PATH"
3914
+ echo "codex_app_server_trace=codex-app-server/summary.json"
3915
+ echo "codex_app_server_events=codex-app-server/events.ndjson"
3916
+ echo "codex_app_server_transcript=codex-app-server/transcript.txt"
3917
+ wait_for_http "$CODEX_APP_SERVER_UI_URL" 60000 500 || true
3918
+ open_codex_app_server_client || true
3919
+ }
3920
+
3921
+ write_app_specific_browser_scenario() {
3922
+ if [[ "$APP_STATUS" != "running" || -z "$APP_URL" ]]; then
3923
+ return 0
3924
+ fi
3925
+
3926
+ node - "$APP_DIR" "$APP_URL" <<'NODE' || true
3927
+ const fs = require("node:fs");
3928
+ const path = require("node:path");
3929
+ const [appDir, appUrl] = process.argv.slice(2);
3930
+ const clean = (value, fallback = "") => String(value || fallback)
3931
+ .replace(/sk-(?:proj-)?[A-Za-z0-9_-]{20,}/g, "[redacted-openai-key]")
3932
+ .replace(/e2b_[A-Za-z0-9_-]{12,}/g, "[redacted-e2b-key]")
3933
+ .replace(/(?:gh[pousr]_[A-Za-z0-9_]{12,}|github_pat_[A-Za-z0-9_]{12,})/g, "[redacted-github-token]")
3934
+ .replace(/https?:\\/\\/[^\\s]+/g, "[redacted-url]")
3935
+ .replace(/\\s+/g, " ")
3936
+ .trim()
3937
+ .slice(0, 160);
3938
+ const yamlScalar = (value) => clean(value, "").replace(/'/g, "''");
3939
+ const token = (value, fallback) => clean(value, fallback).toLowerCase().replace(/[^a-z0-9_-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 80) || fallback;
3940
+ const visibleTextFromHtml = (html) => clean(html
3941
+ .replace(/<script[\\s\\S]*?<\\/script>/gi, " ")
3942
+ .replace(/<style[\\s\\S]*?<\\/style>/gi, " ")
3943
+ .replace(/<[^>]+>/g, " "), "");
3944
+ const titleFromHtml = (html) => {
3945
+ const match = html.match(/<title[^>]*>([\\s\\S]*?)<\\/title>/i);
3946
+ return clean(match ? match[1] : "", "");
3947
+ };
3948
+ (async () => {
3949
+ try {
3950
+ const response = await fetch(appUrl);
3951
+ const html = await response.text();
3952
+ const title = titleFromHtml(html);
3953
+ const bodyText = visibleTextFromHtml(html);
3954
+ const expectedText = clean(bodyText.split(/[.!?\\n]/).find((part) => clean(part, "").length >= 4) || title, "");
3955
+ const scenarioId = token(title || "app-surface-browser", "app-surface-browser");
3956
+ const scenarioPath = path.join(appDir, "humanish", "scenarios", "app-surface-browser.yaml");
3957
+ fs.mkdirSync(path.dirname(scenarioPath), { recursive: true });
3958
+ const lines = [
3959
+ "schema: humanish.scenario.v1",
3960
+ "id: " + scenarioId,
3961
+ "title: App surface browser proof",
3962
+ "persona: synthetic-new-user",
3963
+ "goal: Prove the running app exposes a public-safe browser surface for synthetic users.",
3964
+ "mode: browser",
3965
+ "browser:",
3966
+ " startPath: /",
3967
+ " steps:",
3968
+ " - id: app-surface-load",
3969
+ " label: Load the running app surface",
3970
+ " action: goto",
3971
+ " path: /",
3972
+ " expect:",
3973
+ " selectorVisible: body",
3974
+ ...(expectedText ? [
3975
+ " text: '" + yamlScalar(expectedText) + "'"
3976
+ ] : []),
3977
+ " - id: app-surface-ready",
3978
+ " label: Confirm the browser can observe the app body",
3979
+ " action: waitForSelector",
3980
+ " selector: body"
3981
+ ];
3982
+ fs.writeFileSync(scenarioPath, lines.join("\\n") + "\\n");
3983
+ console.log("browser_scenario=authored path=humanish/scenarios/app-surface-browser.yaml expected_text=" + (expectedText ? "present" : "absent"));
3984
+ } catch (error) {
3985
+ console.log("browser_scenario=skipped reason=" + clean(error && error.message ? error.message : error, "unknown"));
3986
+ }
3987
+ })();
3988
+ NODE
3989
+ }
3990
+
3991
+ start_target_app_surface() {
3992
+ echo
3993
+ echo "== target app surface =="
3994
+ local plan_output
3995
+ plan_output="$(detect_app_plan)"
3996
+ eval "$plan_output"
3997
+ if [[ "\${APP_PLAN_OK:-0}" != "1" ]]; then
3998
+ APP_STATUS=missing
3999
+ APP_REASON="\${APP_PLAN_REASON:-no runnable app script detected}"
4000
+ echo "app_status=$APP_STATUS reason=$APP_REASON"
4001
+ return 0
4002
+ fi
4003
+
4004
+ if ! ensure_package_manager "$APP_PM"; then
4005
+ APP_STATUS=blocked
4006
+ APP_REASON="package manager $APP_PM is unavailable"
4007
+ echo "app_status=$APP_STATUS reason=$APP_REASON"
4008
+ return 0
4009
+ fi
4010
+
4011
+ if ! install_project_dependencies "$APP_PM"; then
4012
+ APP_STATUS=blocked
4013
+ APP_REASON="dependency install failed before app startup"
4014
+ echo "app_status=$APP_STATUS reason=$APP_REASON"
4015
+ return 0
4016
+ fi
4017
+
4018
+ local app_url_candidates=("http://localhost:$APP_PORT" "http://127.0.0.1:$APP_PORT" "http://[::1]:$APP_PORT")
4019
+ APP_URL="\${app_url_candidates[0]}"
4020
+ local start_command
4021
+ local command_prefix="HOST=0.0.0.0 PORT=$APP_PORT"
4022
+ local script_arg_separator="--"
4023
+ if [[ "$APP_PM" == "pnpm" ]]; then
4024
+ command_prefix="PNPM_CONFIG_VERIFY_DEPS_BEFORE_RUN=false npm_config_verify_deps_before_run=false $command_prefix"
4025
+ script_arg_separator=""
4026
+ fi
4027
+ case "$APP_FRAMEWORK" in
4028
+ next)
4029
+ start_command="$command_prefix $APP_PM run $APP_SCRIPT $script_arg_separator --hostname 0.0.0.0 --port $APP_PORT"
4030
+ ;;
4031
+ vite)
4032
+ start_command="$command_prefix $APP_PM run $APP_SCRIPT $script_arg_separator --host 0.0.0.0 --port $APP_PORT"
4033
+ ;;
4034
+ *)
4035
+ start_command="$command_prefix $APP_PM run $APP_SCRIPT"
4036
+ ;;
4037
+ esac
4038
+
4039
+ echo "app_plan=$APP_PLAN_REASON"
4040
+ echo "app_url_candidates=\${app_url_candidates[*]}"
4041
+ echo "app_command=$start_command"
4042
+ nohup bash -lc "$start_command" > "$APP_LOG_PATH" 2>&1 &
4043
+ APP_PID=$!
4044
+
4045
+ local ready_url=""
4046
+ if ready_url="$(wait_for_any_http 120000 1000 "\${app_url_candidates[@]}")"; then
4047
+ APP_URL="$ready_url"
4048
+ APP_STATUS=running
4049
+ APP_REASON="target app responded at $APP_URL"
4050
+ open_browser_url "$APP_URL" app-desktop 0 0 760 520
4051
+ open_browser_url "$APP_URL" app-compact 0 520 430 420
4052
+ write_app_specific_browser_scenario || true
4053
+ else
4054
+ APP_STATUS=blocked
4055
+ APP_REASON="target app did not become HTTP-ready at $APP_URL"
4056
+ tail -n 40 "$APP_LOG_PATH" || true
4057
+ fi
4058
+ echo "app_status=$APP_STATUS reason=$APP_REASON pid=$APP_PID"
4059
+ }
4060
+
4061
+ start_actor_attempt() {
4062
+ echo
4063
+ echo "== codex actor attempt =="
4064
+ if [[ "\${HUMANISH_OSS_META_CODEX_APP_SERVER:-0}" == "1" ]]; then
4065
+ start_codex_app_server_actor_attempt
4066
+ return 0
4067
+ fi
4068
+
4069
+ local actor_script="$ROOT_DIR/actor-run.sh"
4070
+ cat > "$actor_script" <<ACTOR
4071
+ #!/usr/bin/env bash
4072
+ set +e
4073
+ APP_DIR="$APP_DIR"
4074
+ if [[ "\${HUMANISH_OSS_META_ACTOR_FIRST:-0}" == "1" ]]; then
4075
+ PROMPT='You are a Humanish meta-lab actor running in a disposable public-safe repo clone. Your goal is useful product-specific user-study setup, not ceremonial install proof. Inspect package.json, README, routes, scripts, and the running app shape. Install humanish as a dev dependency with the repo package manager if needed; package=humanish, binary=humanish. Run npx --no-install humanish init --yes if Humanish is not initialized. Replace starter humanish/coverage-map.md and humanish/coverage-matrix.md with app-aware screens, roles, states, happy paths, and at least one sad/friction path. Author at least two public-safe app-specific personas and two desktop/mobile browser scenarios; avoid generic first-run-smoke only. Start the local app if feasible. Run npx --no-install humanish run --help and verify --app-url is available. If the local binary is missing, install humanish; one-shot fallback is npx --yes --package humanish@latest humanish run --help. If the app is running locally, run npx --no-install humanish run --app-url <loopback-url> --sims 2; do not use humanish watch --sims as app behavior proof. After run --app-url, render or open the nested Humanish Observer with npx --no-install humanish watch --run latest --detach --no-open --json if feasible. Final summary must include personas/scenarios created, product journeys covered, one observed friction/improvement or none observed, and evidence paths. Do not stop at install/init proof. Do not wait on long-running watchers after rendering proof. Do not print secrets, do not commit, do not push, and do not file issues.'
4076
+ else
4077
+ PROMPT='You are a Humanish meta-lab actor. Inspect this repo, inspect the running app and Humanish artifacts already generated here, and draft the best next public-safe personas and desktop/mobile browser scenarios for human users of this app. Once the draft is written, exit successfully. Do not wait on long-running watchers. Do not print secrets, do not commit, do not push, and do not file issues.'
4078
+ fi
4079
+ printf -v app_dir_q '%q' "\\$APP_DIR"
4080
+ printf -v prompt_q '%q' "\\$PROMPT"
4081
+ printf -v actor_message_q '%q' "$ACTOR_LAST_MESSAGE_PATH"
4082
+ ACTOR_MODEL="\${HUMANISH_OSS_META_ACTOR_MODEL:-gpt-5.4-mini}"
4083
+ ACTOR_TIMEOUT_MS="\${HUMANISH_OSS_META_ACTOR_TIMEOUT_MS:-480000}"
4084
+ ACTOR_TIMEOUT_SECONDS=\\$(( (ACTOR_TIMEOUT_MS + 999) / 1000 ))
4085
+ printf -v actor_model_q '%q' "\\$ACTOR_MODEL"
4086
+ CODEX_COMMAND="npx -y @openai/codex@latest exec --ephemeral --ignore-user-config --skip-git-repo-check -m \\$actor_model_q -C \\$app_dir_q --dangerously-bypass-approvals-and-sandbox --output-last-message \\$actor_message_q \\$prompt_q"
4087
+ if [[ -n "\${HUMANISH_PRIVATE_CODEX_API_KEY:-}" && -n "\${HUMANISH_PRIVATE_CODEX_ACCESS_TOKEN:-}" ]]; then
4088
+ CODEX_API_KEY="\\$HUMANISH_PRIVATE_CODEX_API_KEY" CODEX_ACCESS_TOKEN="\\$HUMANISH_PRIVATE_CODEX_ACCESS_TOKEN" timeout "\\$ACTOR_TIMEOUT_SECONDS" bash -lc "\\$CODEX_COMMAND"
4089
+ elif [[ -n "\${HUMANISH_PRIVATE_CODEX_API_KEY:-}" ]]; then
4090
+ CODEX_API_KEY="\\$HUMANISH_PRIVATE_CODEX_API_KEY" timeout "\\$ACTOR_TIMEOUT_SECONDS" bash -lc "\\$CODEX_COMMAND"
4091
+ elif [[ -n "\${HUMANISH_PRIVATE_CODEX_ACCESS_TOKEN:-}" ]]; then
4092
+ CODEX_ACCESS_TOKEN="\\$HUMANISH_PRIVATE_CODEX_ACCESS_TOKEN" timeout "\\$ACTOR_TIMEOUT_SECONDS" bash -lc "\\$CODEX_COMMAND"
4093
+ else
4094
+ timeout "\\$ACTOR_TIMEOUT_SECONDS" bash -lc "\\$CODEX_COMMAND"
4095
+ fi
4096
+ code=\\$?
4097
+ echo "actor_exit=\\$code"
4098
+ exit "\\$code"
4099
+ ACTOR
4100
+ chmod +x "$actor_script"
4101
+ HUMANISH_PRIVATE_CODEX_API_KEY="$HUMANISH_PRIVATE_CODEX_API_KEY" HUMANISH_PRIVATE_CODEX_ACCESS_TOKEN="$HUMANISH_PRIVATE_CODEX_ACCESS_TOKEN" nohup bash "$actor_script" > "$ACTOR_LOG_PATH" 2>&1 &
4102
+ ACTOR_PID=$!
4103
+ ACTOR_STATUS=running
4104
+ echo "actor_status=$ACTOR_STATUS pid=$ACTOR_PID log=$ACTOR_LOG_PATH"
4105
+ }
4106
+
4107
+ wait_for_actor_attempt_if_required() {
4108
+ if [[ "\${HUMANISH_OSS_META_REQUIRE_ACTOR:-0}" != "1" && "\${HUMANISH_OSS_META_CODEX_APP_SERVER:-0}" != "1" ]]; then
4109
+ return 0
4110
+ fi
4111
+
4112
+ echo
4113
+ echo "== required codex actor readback =="
4114
+ if [[ "\${HUMANISH_OSS_META_CODEX_APP_SERVER:-0}" == "1" ]]; then
4115
+ local timeout_ms="\${HUMANISH_OSS_META_ACTOR_TIMEOUT_MS:-480000}"
4116
+ local started_ms
4117
+ started_ms="$(date +%s%3N)"
4118
+ local next_heartbeat_ms=$((started_ms + 15000))
4119
+ while true; do
4120
+ local status_line
4121
+ status_line="$(node - "$CODEX_APP_SERVER_STATE_PATH" <<'NODE' 2>/dev/null || true
4122
+ const fs = require("node:fs");
4123
+ const [statePath] = process.argv.slice(2);
4124
+ if (!fs.existsSync(statePath)) process.exit(2);
4125
+ const state = JSON.parse(fs.readFileSync(statePath, "utf8"));
4126
+ console.log(String(state.status || "unknown"));
4127
+ NODE
4128
+ )"
4129
+ if [[ "$status_line" != "starting" && "$status_line" != "running" && "$status_line" != "unknown" && -n "$status_line" ]]; then
4130
+ ACTOR_STATUS="$status_line"
4131
+ break
4132
+ fi
4133
+ local now_ms
4134
+ now_ms="$(date +%s%3N)"
4135
+ if [[ "$now_ms" -ge "$next_heartbeat_ms" ]]; then
4136
+ echo "actor_status=running source=codex-app-server elapsed_ms=$((now_ms - started_ms)) state=$CODEX_APP_SERVER_STATE_PATH log=$CODEX_APP_SERVER_LOG_PATH"
4137
+ echo "actor_log_tail_begin"
4138
+ tail -n 20 "$CODEX_APP_SERVER_LOG_PATH" || true
4139
+ echo "actor_log_tail_end"
4140
+ next_heartbeat_ms=$((now_ms + 15000))
4141
+ fi
4142
+ if [[ $((now_ms - started_ms)) -ge "$timeout_ms" ]]; then
4143
+ ACTOR_STATUS=timed_out
4144
+ echo "actor_status=$ACTOR_STATUS timeout_ms=$timeout_ms state=$CODEX_APP_SERVER_STATE_PATH log=$CODEX_APP_SERVER_LOG_PATH"
4145
+ return 1
4146
+ fi
4147
+ sleep 1
4148
+ done
4149
+ echo "actor_status=$ACTOR_STATUS source=codex-app-server state=$CODEX_APP_SERVER_STATE_PATH log=$CODEX_APP_SERVER_LOG_PATH"
4150
+ echo "actor_log_tail_begin"
4151
+ tail -n 80 "$CODEX_APP_SERVER_LOG_PATH" || true
4152
+ echo "actor_log_tail_end"
4153
+ if [[ -f "$CODEX_APP_SERVER_ROOT_PATH/codex-app-server/summary.json" ]]; then
4154
+ node - "$CODEX_APP_SERVER_ROOT_PATH/codex-app-server/summary.json" "$ACTOR_LAST_MESSAGE_PATH" <<'NODE' || true
4155
+ const fs = require("node:fs");
4156
+ const [summaryPath, outPath] = process.argv.slice(2);
4157
+ const summary = JSON.parse(fs.readFileSync(summaryPath, "utf8"));
4158
+ const messages = Array.isArray(summary.messages) ? summary.messages : [];
4159
+ const text = messages
4160
+ .slice(-4)
4161
+ .map((message) => typeof message?.text === "string" ? message.text : "")
4162
+ .filter(Boolean)
4163
+ .join("\\n\\n")
4164
+ .slice(-12000);
4165
+ fs.writeFileSync(outPath, text || "No final Codex app-server agent message captured.\\n");
4166
+ NODE
4167
+ elif [[ -f "$CODEX_APP_SERVER_ROOT_PATH/codex-app-server/transcript.txt" ]]; then
4168
+ tail -n 80 "$CODEX_APP_SERVER_ROOT_PATH/codex-app-server/transcript.txt" > "$ACTOR_LAST_MESSAGE_PATH" || true
4169
+ fi
4170
+ [[ "$ACTOR_STATUS" == "passed" ]]
4171
+ return $?
4172
+ fi
4173
+
4174
+ if [[ -z "$ACTOR_PID" ]]; then
4175
+ ACTOR_STATUS=blocked
4176
+ echo "actor_status=$ACTOR_STATUS reason=no actor process was started"
4177
+ return 1
4178
+ fi
4179
+
4180
+ local timeout_ms="\${HUMANISH_OSS_META_ACTOR_TIMEOUT_MS:-480000}"
4181
+ local started_ms
4182
+ started_ms="$(date +%s%3N)"
4183
+ while kill -0 "$ACTOR_PID" >/dev/null 2>&1; do
4184
+ local now_ms
4185
+ now_ms="$(date +%s%3N)"
4186
+ if [[ $((now_ms - started_ms)) -ge "$timeout_ms" ]]; then
4187
+ ACTOR_STATUS=timed_out
4188
+ kill "$ACTOR_PID" >/dev/null 2>&1 || true
4189
+ echo "actor_status=$ACTOR_STATUS timeout_ms=$timeout_ms log=$ACTOR_LOG_PATH"
4190
+ return 1
4191
+ fi
4192
+ sleep 1
4193
+ done
4194
+
4195
+ local actor_exit=0
4196
+ wait "$ACTOR_PID" || actor_exit=$?
4197
+ if [[ "$actor_exit" -eq 0 ]]; then
4198
+ ACTOR_STATUS=passed
4199
+ else
4200
+ ACTOR_STATUS=failed
4201
+ fi
4202
+ echo "actor_status=$ACTOR_STATUS exit=$actor_exit log=$ACTOR_LOG_PATH"
4203
+ echo "actor_log_tail_begin"
4204
+ tail -n 80 "$ACTOR_LOG_PATH" || true
4205
+ echo "actor_log_tail_end"
4206
+ [[ "$actor_exit" -eq 0 ]]
4207
+ }
4208
+
4209
+ rm -rf "$APP_DIR"
4210
+ ASKPASS="$ROOT_DIR/git-askpass.sh"
4211
+ cat > "$ASKPASS" <<'ASKPASS'
4212
+ #!/usr/bin/env bash
4213
+ case "$1" in
4214
+ *Username*) echo "x-access-token" ;;
4215
+ *Password*) echo "\${HUMANISH_GITHUB_TOKEN_RUNTIME:-}" ;;
4216
+ *) echo "" ;;
4217
+ esac
4218
+ ASKPASS
4219
+ chmod 700 "$ASKPASS"
4220
+ clone_repo() {
4221
+ local repo_url=${shellQuote(repoUrl)}
4222
+ if GIT_CONFIG_GLOBAL=/dev/null GIT_CONFIG_NOSYSTEM=1 GIT_ASKPASS=false SSH_ASKPASS=false GIT_TERMINAL_PROMPT=0 git -c credential.helper= clone --depth=1 "$repo_url" "$APP_DIR"; then
4223
+ echo "clone_auth=anonymous"
4224
+ return 0
4225
+ fi
4226
+ echo "clone_auth=anonymous_failed retry=token_clone"
4227
+ rm -rf "$APP_DIR"
4228
+
4229
+ if [[ -n "$HUMANISH_PRIVATE_GITHUB_TOKEN" ]]; then
4230
+ if GIT_CONFIG_GLOBAL=/dev/null GIT_CONFIG_NOSYSTEM=1 GIT_ASKPASS="$ASKPASS" GIT_TERMINAL_PROMPT=0 HUMANISH_GITHUB_TOKEN_RUNTIME="$HUMANISH_PRIVATE_GITHUB_TOKEN" git -c credential.helper= clone --depth=1 "$repo_url" "$APP_DIR"; then
4231
+ echo "clone_auth=token"
4232
+ return 0
4233
+ fi
4234
+ echo "clone_auth=token_failed"
4235
+ rm -rf "$APP_DIR"
4236
+ fi
4237
+
4238
+ return 1
4239
+ }
4240
+ clone_repo
4241
+ cd "$APP_DIR"
4242
+ echo
4243
+ echo "== repo fingerprint =="
4244
+ git rev-parse --short HEAD || true
4245
+ node --version || true
4246
+ npm --version || true
4247
+
4248
+ echo
4249
+ echo "== humanish skill discovery =="
4250
+ if npx -y skills add danielgwilson/humanish --skill humanish --list >/tmp/humanish-skill-list.txt 2>&1; then
4251
+ echo "skill_discovery=listed"
4252
+ elif npx -y skills add danielgwilson/humanish --skill humanish >/tmp/humanish-skill-install.txt 2>&1; then
4253
+ echo "skill_install=attempted"
4254
+ else
4255
+ echo "skill_install=blocked"
4256
+ tail -n 20 /tmp/humanish-skill-list.txt /tmp/humanish-skill-install.txt 2>/dev/null || true
4257
+ fi
4258
+
4259
+ echo
4260
+ if [[ "\${HUMANISH_OSS_META_ACTOR_FIRST:-0}" == "1" && "\${HUMANISH_OSS_META_HOST_CODEX_ACTOR:-0}" != "1" && "\${HUMANISH_OSS_META_CODEX_APP_SERVER:-0}" != "1" ]]; then
4261
+ start_actor_attempt
4262
+ wait_for_actor_attempt_if_required || true
4263
+ fi
4264
+
4265
+ echo
4266
+ install_humanish_cli
4267
+
4268
+ echo
4269
+ echo "== humanish init =="
4270
+ npx --no-install humanish init --yes
4271
+ apply_host_actor_plan || true
4272
+
4273
+ start_target_app_surface
4274
+
4275
+ if [[ "\${HUMANISH_OSS_META_CODEX_APP_SERVER:-0}" == "1" && "\${HUMANISH_OSS_META_HOST_CODEX_ACTOR:-0}" != "1" ]]; then
4276
+ start_actor_attempt
4277
+ wait_for_actor_attempt_if_required || true
4278
+ fi
4279
+
4280
+ echo
4281
+ echo "== nested humanish proof =="
4282
+ if [[ "$APP_STATUS" == "running" && -n "$APP_URL" ]]; then
4283
+ npx --no-install humanish run --app-url "$APP_URL" --sims 2 --run-id ${shellQuote(runId)}
4284
+ else
4285
+ echo "app_not_running_for_browser_proof=$APP_REASON"
4286
+ npx --no-install humanish run --dry-run --run-id ${shellQuote(runId)}
4287
+ fi
4288
+ if npx --no-install humanish verify --run latest; then
4289
+ NESTED_VERIFY_STATUS=passed
4290
+ else
4291
+ NESTED_VERIFY_STATUS=failed
4292
+ exit 1
4293
+ fi
4294
+ npx --no-install humanish watch --run latest --detach --no-open
4295
+ open_nested_observer "opening nested observer"
4296
+ open_codex_app_server_client
4297
+ arrange_lab_windows
4298
+ if [[ "\${HUMANISH_OSS_META_ACTOR_FIRST:-0}" != "1" && "\${HUMANISH_OSS_META_HOST_CODEX_ACTOR:-0}" != "1" && "\${HUMANISH_OSS_META_CODEX_APP_SERVER:-0}" != "1" ]]; then
4299
+ start_actor_attempt
4300
+ wait_for_actor_attempt_if_required || true
4301
+ fi
4302
+
4303
+ echo
4304
+ echo "== bootstrap complete =="
4305
+ echo "app_status=$APP_STATUS"
4306
+ echo "app_url=$APP_URL"
4307
+ echo "actor_status=$ACTOR_STATUS"
4308
+ echo "nested_observer=$NESTED_OBSERVER"
4309
+ `;
4310
+ }
4311
+ function buildRemoteLauncherScript(args) {
4312
+ const terminalCommand = `bash -lc ${shellQuote(`${args.bootstrapPath}; echo; echo 'Humanish bootstrap finished. Leave this terminal open for review.'; exec bash`)}`;
4313
+ return `#!/usr/bin/env bash
4314
+ set -u
4315
+ BOOTSTRAP=${shellQuote(args.bootstrapPath)}
4316
+ LOG_PATH=${shellQuote(args.logPath)}
4317
+ TITLE=${shellQuote(args.title)}
4318
+ echo "launching visible terminal for $BOOTSTRAP" >> "$LOG_PATH"
4319
+ if command -v xfce4-terminal >/dev/null 2>&1; then
4320
+ nohup xfce4-terminal --hold --title "$TITLE" --command ${shellQuote(terminalCommand)} >> "$LOG_PATH" 2>&1 &
4321
+ elif command -v xterm >/dev/null 2>&1; then
4322
+ nohup xterm -T "$TITLE" -e ${shellQuote(terminalCommand)} >> "$LOG_PATH" 2>&1 &
4323
+ else
4324
+ echo "No GUI terminal found; running bootstrap headless." >> "$LOG_PATH"
4325
+ nohup bash "$BOOTSTRAP" >> "$LOG_PATH" 2>&1 &
4326
+ fi
4327
+ `;
4328
+ }
4329
+ function buildRemoteFocusCommand(title) {
4330
+ return `TITLE=${shellQuote(title)}
4331
+ for attempt in $(seq 1 20); do
4332
+ if command -v wmctrl >/dev/null 2>&1; then
4333
+ wmctrl -a "$TITLE" >/dev/null 2>&1 && exit 0
4334
+ fi
4335
+ if command -v xdotool >/dev/null 2>&1; then
4336
+ WIN="$(xdotool search --name "$TITLE" 2>/dev/null | head -n 1 || true)"
4337
+ if [[ -n "$WIN" ]]; then
4338
+ xdotool windowactivate "$WIN" >/dev/null 2>&1 && exit 0
4339
+ fi
4340
+ fi
4341
+ sleep 0.25
4342
+ done
4343
+ exit 0`;
4344
+ }
4345
+ function buildRemoteScreenshotArrangeCommand(terminalTitle) {
4346
+ return `TERMINAL_TITLE=${shellQuote(terminalTitle ?? "")}
4347
+ EXPECTED_CHROME_WINDOWS=2
4348
+ if [[ "\${HUMANISH_OSS_META_CODEX_APP_SERVER:-0}" == "1" ]]; then
4349
+ EXPECTED_CHROME_WINDOWS=3
4350
+ fi
4351
+ if ! command -v xdotool >/dev/null 2>&1; then
4352
+ exit 0
4353
+ fi
4354
+ for attempt in $(seq 1 24); do
4355
+ CHROME_COUNT="$(xdotool search --onlyvisible --class google-chrome 2>/dev/null | wc -l | tr -d ' ')"
4356
+ OBSERVER_WINDOW="$(xdotool search --onlyvisible --name "Humanish Observer" 2>/dev/null | tail -n 1 || true)"
4357
+ if [[ "$CHROME_COUNT" -ge "$EXPECTED_CHROME_WINDOWS" && -n "$OBSERVER_WINDOW" ]]; then
4358
+ break
4359
+ fi
4360
+ sleep 0.25
4361
+ done
4362
+ OBSERVER_WINDOW="$(xdotool search --onlyvisible --name "Humanish Observer" 2>/dev/null | tail -n 1 || true)"
4363
+ if [[ -n "$OBSERVER_WINDOW" ]]; then
4364
+ xdotool windowsize "$OBSERVER_WINDOW" 680 933 >/dev/null 2>&1 || true
4365
+ xdotool windowmove "$OBSERVER_WINDOW" 760 27 >/dev/null 2>&1 || true
4366
+ fi
4367
+ slot=0
4368
+ while IFS= read -r win; do
4369
+ [[ -z "$win" ]] && continue
4370
+ if [[ -n "$OBSERVER_WINDOW" && "$win" == "$OBSERVER_WINDOW" ]]; then
4371
+ continue
4372
+ fi
4373
+ if [[ "$slot" -eq 0 ]]; then
4374
+ xdotool windowsize "$win" 760 493 >/dev/null 2>&1 || true
4375
+ xdotool windowmove "$win" 0 27 >/dev/null 2>&1 || true
4376
+ elif [[ "$slot" -eq 1 ]]; then
4377
+ xdotool windowsize "$win" 430 420 >/dev/null 2>&1 || true
4378
+ xdotool windowmove "$win" 0 520 >/dev/null 2>&1 || true
4379
+ else
4380
+ xdotool windowsize "$win" 330 420 >/dev/null 2>&1 || true
4381
+ xdotool windowmove "$win" 430 520 >/dev/null 2>&1 || true
4382
+ fi
4383
+ slot=$((slot + 1))
4384
+ done < <(xdotool search --onlyvisible --class google-chrome 2>/dev/null || true)
4385
+ if [[ -n "$TERMINAL_TITLE" ]]; then
4386
+ TERMINAL_WINDOW="$(xdotool search --onlyvisible --name "$TERMINAL_TITLE" 2>/dev/null | head -n 1 || true)"
4387
+ if [[ -n "$TERMINAL_WINDOW" ]]; then
4388
+ xdotool windowlower "$TERMINAL_WINDOW" >/dev/null 2>&1 || xdotool windowminimize "$TERMINAL_WINDOW" >/dev/null 2>&1 || true
4389
+ fi
4390
+ fi
4391
+ if [[ -n "$OBSERVER_WINDOW" ]]; then
4392
+ xdotool windowactivate "$OBSERVER_WINDOW" >/dev/null 2>&1 || true
4393
+ fi
4394
+ exit 0`;
4395
+ }
4396
+ async function runDesktopCommand(desktop, command, options) {
4397
+ const result = await runDesktopCommandOrThrow(() => desktop.commands.run(`bash -lc ${shellQuote(command)}`, options),
4398
+ // The real Sandbox throws CommandExitError on a non-zero exit, so recover the
4399
+ // exit code + output tail from the throw and preserve the formatted error.
4400
+ ({ exitCode, stderrTail }) => new Error(`Remote command failed with exit code ${exitCode ?? "unknown"}.\noutput=${stderrTail}`));
4401
+ if (result.exitCode && result.exitCode !== 0) {
4402
+ throw new Error([
4403
+ `Remote command failed with exit code ${result.exitCode}.`,
4404
+ `stdout=${result.stdout ?? ""}`,
4405
+ `stderr=${result.stderr ?? ""}`
4406
+ ].join("\n"));
4407
+ }
4408
+ return result;
4409
+ }
4410
+ async function packLocalHumanishPackage(cwd, runId) {
4411
+ const packageRoot = moduleRoot;
4412
+ await prepareReusableHumanishStorageDirectory(cwd, "tmp", "oss-meta", runId);
4413
+ const packDir = await prepareReusableHumanishStorageDirectory(cwd, "tmp", "oss-meta", runId, "package");
4414
+ await execFileAsync("pnpm", ["build"], {
4415
+ cwd: packageRoot,
4416
+ env: process.env,
4417
+ maxBuffer: 10 * 1024 * 1024
4418
+ });
4419
+ await execFileAsync("npm", ["pack", "--pack-destination", packDir], {
4420
+ cwd: packageRoot,
4421
+ env: process.env,
4422
+ maxBuffer: 10 * 1024 * 1024
4423
+ });
4424
+ const files = await readdir(packDir);
4425
+ const fileName = files.find((file) => /^humanish-.*\.tgz$/.test(file));
4426
+ if (!fileName) {
4427
+ throw new Error("npm pack did not produce a humanish tarball.");
4428
+ }
4429
+ const archivePath = path.join(packDir, fileName);
4430
+ const archiveStat = await stat(archivePath);
4431
+ return {
4432
+ fileName,
4433
+ path: archivePath,
4434
+ sizeBytes: archiveStat.size
4435
+ };
4436
+ }
4437
+ function toArrayBuffer(buffer) {
4438
+ const copy = new ArrayBuffer(buffer.byteLength);
4439
+ new Uint8Array(copy).set(buffer);
4440
+ return copy;
4441
+ }
4442
+ function shellQuote(value) {
4443
+ if (value.length === 0) {
4444
+ return "''";
4445
+ }
4446
+ return `'${value.replaceAll("'", "'\"'\"'")}'`;
4447
+ }
4448
+ function formatLiveDesktopForResult(desktop, redactRepoNames) {
4449
+ return {
4450
+ ...(desktop.completion?.actorStatus ? { actorStatus: desktop.completion.actorStatus } : {}),
4451
+ ...(desktop.completion?.appStatus ? { appStatus: desktop.completion.appStatus } : {}),
4452
+ ...(desktop.bootstrap ? { bootstrapStatus: desktop.bootstrap.status } : {}),
4453
+ ...(desktop.completion ? { completionReason: desktop.completion.reason, completionStatus: desktop.completion.status } : {}),
4454
+ repo: desktop.repo,
4455
+ ...(desktop.screenshot ? { screenshotPresent: true } : {}),
4456
+ ...(!redactRepoNames && desktop.sandboxId ? { sandboxId: desktop.sandboxId } : {}),
4457
+ streamId: desktop.streamId,
4458
+ urlPresent: Boolean(desktop.url),
4459
+ ...(desktop.completion?.visualStatus ? { visualStatus: desktop.completion.visualStatus } : {}),
4460
+ ...(desktop.completion?.visualWindowCount === undefined ? {} : { visualWindowCount: desktop.completion.visualWindowCount })
4461
+ };
4462
+ }
4463
+ function missingLiveKeys(env) {
4464
+ const missing = ["E2B_API_KEY"].filter((name) => !env[name]?.trim());
4465
+ const actorAuthRequested = remoteActorAuthRequested(env);
4466
+ const actorAuthPresent = Boolean(env.CODEX_API_KEY?.trim() || env.CODEX_ACCESS_TOKEN?.trim() || env.OPENAI_API_KEY?.trim());
4467
+ if (actorAuthRequested && !hostCodexActorRequested(env) && !actorAuthPresent) {
4468
+ missing.push(OSS_META_LAB_ACTOR_AUTH_PLACEHOLDER);
4469
+ }
4470
+ return missing;
4471
+ }
4472
+ function readPositiveInt(value, fallback) {
4473
+ if (!value || !/^\d+$/.test(value)) {
4474
+ return fallback;
4475
+ }
4476
+ const parsed = Number.parseInt(value, 10);
4477
+ return Number.isSafeInteger(parsed) && parsed > 0 ? parsed : fallback;
4478
+ }
4479
+ function readNonNegativeInt(value, fallback) {
4480
+ if (!value || !/^\d+$/.test(value)) {
4481
+ return fallback;
4482
+ }
4483
+ const parsed = Number.parseInt(value, 10);
4484
+ return Number.isSafeInteger(parsed) && parsed >= 0 ? parsed : fallback;
4485
+ }
4486
+ async function wait(ms) {
4487
+ if (ms <= 0) {
4488
+ return;
4489
+ }
4490
+ await new Promise((resolve) => setTimeout(resolve, ms));
4491
+ }
4492
+ function compactError(error) {
4493
+ const message = error instanceof Error ? error.message : String(error);
4494
+ return message
4495
+ .replace(/sk-[A-Za-z0-9_-]{20,}/g, "[redacted-openai-key]")
4496
+ .replace(/e2b_[A-Za-z0-9_-]{12,}/g, "[redacted-e2b-key]")
4497
+ .replace(/gh[pousr]_[A-Za-z0-9_]{12,}/g, "[redacted-github-token]")
4498
+ .replace(/github_pat_[A-Za-z0-9_]{12,}/g, "[redacted-github-token]")
4499
+ .replace(/https?:\/\/[^/\s]*e2b[^)\s]+/gi, "[redacted-e2b-url]")
4500
+ .replace(/\s+/g, " ")
4501
+ .slice(0, 240);
4502
+ }
4503
+ function makeMetaRunId() {
4504
+ const stamp = new Date().toISOString().replace(/[:.]/g, "-");
4505
+ return `oss-meta-${stamp}-${randomBytes(4).toString("hex")}`;
4506
+ }
4507
+ function repoArtifactLabel(assignment) {
4508
+ return `repo-${String(assignment.index).padStart(2, "0")}`;
4509
+ }
4510
+ function redactAssignments(assignments, redactRepoNames) {
4511
+ if (!redactRepoNames) {
4512
+ return assignments;
4513
+ }
4514
+ return assignments.map((assignment) => ({
4515
+ ...assignment,
4516
+ repo: repoArtifactLabel(assignment),
4517
+ scenarioId: `oss-meta-${repoArtifactLabel(assignment)}`
4518
+ }));
4519
+ }
4520
+ function safeArtifactToken(value) {
4521
+ const token = value.toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "");
4522
+ return token || "artifact";
4523
+ }
4524
+ async function writeJson(artifactRoot, relativePath, value) {
4525
+ await writeContainedOutputFile(artifactRoot, relativePath, `${JSON.stringify(value, null, 2)}\n`, "utf8");
4526
+ }
4527
+ // E2B desktop interfaces + the optional-peer loader now live in ./e2b-desktop-launch.js
4528
+ // (shared with the computer-use actor lane) and are imported at the top of this file.
4529
+ //# sourceMappingURL=oss-meta-lab.js.map