humanish 0.15.0 → 0.15.2

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 (72) hide show
  1. package/README.md +33 -31
  2. package/dist/claude-agent-sdk.js +14 -7
  3. package/dist/claude-agent-sdk.js.map +1 -1
  4. package/dist/codex-app-server-ui.d.ts +1 -1
  5. package/dist/codex-app-server-ui.js +69 -39
  6. package/dist/codex-app-server-ui.js.map +1 -1
  7. package/dist/codex-app-server.d.ts +3 -0
  8. package/dist/codex-app-server.js +15 -8
  9. package/dist/codex-app-server.js.map +1 -1
  10. package/dist/concurrent-shared-world-lab.js +18 -16
  11. package/dist/concurrent-shared-world-lab.js.map +1 -1
  12. package/dist/core/git-state.d.ts +2 -0
  13. package/dist/core/git-state.js +152 -17
  14. package/dist/core/git-state.js.map +1 -1
  15. package/dist/core/git-workspace.d.ts +31 -0
  16. package/dist/core/git-workspace.js +447 -0
  17. package/dist/core/git-workspace.js.map +1 -0
  18. package/dist/cua-actor-lab.d.ts +3 -2
  19. package/dist/cua-actor-lab.js +34 -23
  20. package/dist/cua-actor-lab.js.map +1 -1
  21. package/dist/e2b-terminal-lab.js +111 -46
  22. package/dist/e2b-terminal-lab.js.map +1 -1
  23. package/dist/feedback.js +139 -101
  24. package/dist/feedback.js.map +1 -1
  25. package/dist/init.d.ts +1 -1
  26. package/dist/init.js +108 -22
  27. package/dist/init.js.map +1 -1
  28. package/dist/lab-config.d.ts +5 -0
  29. package/dist/lab-config.js +38 -0
  30. package/dist/lab-config.js.map +1 -1
  31. package/dist/labs.js +258 -52
  32. package/dist/labs.js.map +1 -1
  33. package/dist/observer-assets.js +3 -2
  34. package/dist/observer-assets.js.map +1 -1
  35. package/dist/observer-static.js +131 -17
  36. package/dist/observer-static.js.map +1 -1
  37. package/dist/observer.js +263 -66
  38. package/dist/observer.js.map +1 -1
  39. package/dist/openai-responses-cu.js +18 -4
  40. package/dist/openai-responses-cu.js.map +1 -1
  41. package/dist/oss-lab.js +59 -14
  42. package/dist/oss-lab.js.map +1 -1
  43. package/dist/oss-meta-lab.d.ts +2 -4
  44. package/dist/oss-meta-lab.js +117 -477
  45. package/dist/oss-meta-lab.js.map +1 -1
  46. package/dist/program.js +23 -21
  47. package/dist/program.js.map +1 -1
  48. package/dist/run-paths.d.ts +56 -0
  49. package/dist/run-paths.js +340 -0
  50. package/dist/run-paths.js.map +1 -0
  51. package/dist/run.d.ts +14 -4
  52. package/dist/run.js +595 -306
  53. package/dist/run.js.map +1 -1
  54. package/dist/scripted-browser-actor.d.ts +7 -4
  55. package/dist/scripted-browser-actor.js +154 -41
  56. package/dist/scripted-browser-actor.js.map +1 -1
  57. package/dist/scripted-browser-lab.js +95 -45
  58. package/dist/scripted-browser-lab.js.map +1 -1
  59. package/dist/selected-output-paths.d.ts +49 -0
  60. package/dist/selected-output-paths.js +354 -0
  61. package/dist/selected-output-paths.js.map +1 -0
  62. package/dist/shared-world-lab.js +13 -12
  63. package/dist/shared-world-lab.js.map +1 -1
  64. package/dist/source-archive.js +44 -1
  65. package/dist/source-archive.js.map +1 -1
  66. package/docs/architecture/oss-lab-poc.md +8 -0
  67. package/docs/assets/humanish-observer-hero.png +0 -0
  68. package/docs/contracts/policy.md +7 -1
  69. package/docs/goals/current.md +20 -1
  70. package/docs/product/open-source-install-experience.md +9 -1
  71. package/docs/ramp/README.md +14 -7
  72. package/package.json +1 -1
@@ -0,0 +1,447 @@
1
+ import { constants } from "node:fs";
2
+ import { lstat, open, readdir, realpath } from "node:fs/promises";
3
+ import path from "node:path";
4
+ export const GIT_METADATA_INSPECTION_FAILED_NOTE = "Git metadata could not be inspected safely.";
5
+ export const GIT_METADATA_CONTAINMENT_FAILED_NOTE = "Git metadata failed containment validation.";
6
+ /**
7
+ * Resolve one physical Git worktree without trusting ambient Git discovery.
8
+ *
9
+ * A regular `.git` file is accepted only for Git's exact linked-worktree
10
+ * topology: `<common>/.git/worktrees/<name>` plus single-link `commondir` and
11
+ * `gitdir` backpointer files. Arbitrary `gitdir:` redirects (including valid
12
+ * separate-git-dir repositories) are deliberately unavailable because they
13
+ * delegate metadata authority outside the selected project without a
14
+ * verifiable backlink.
15
+ */
16
+ export async function inspectVerifiedGitWorkspace(cwdInput) {
17
+ let current;
18
+ try {
19
+ current = await realpath(path.resolve(cwdInput));
20
+ const cwdStats = await lstat(current);
21
+ if (cwdStats.isSymbolicLink() || !cwdStats.isDirectory()) {
22
+ return unsafeInspection(current, GIT_METADATA_INSPECTION_FAILED_NOTE);
23
+ }
24
+ }
25
+ catch {
26
+ return unsafeInspection(path.resolve(cwdInput), GIT_METADATA_INSPECTION_FAILED_NOTE);
27
+ }
28
+ while (true) {
29
+ const dotGitPath = path.join(current, ".git");
30
+ let dotGitStats;
31
+ try {
32
+ dotGitStats = await lstat(dotGitPath, { bigint: true });
33
+ }
34
+ catch (error) {
35
+ if (!isNodeError(error) || error.code !== "ENOENT") {
36
+ return unsafeInspection(current, GIT_METADATA_INSPECTION_FAILED_NOTE);
37
+ }
38
+ const parent = path.dirname(current);
39
+ if (parent === current) {
40
+ return { status: "missing" };
41
+ }
42
+ current = parent;
43
+ continue;
44
+ }
45
+ if (dotGitStats.isSymbolicLink()) {
46
+ return unsafeInspection(current, GIT_METADATA_CONTAINMENT_FAILED_NOTE);
47
+ }
48
+ if (dotGitStats.isDirectory()) {
49
+ try {
50
+ if (await realpath(dotGitPath) !== dotGitPath) {
51
+ return unsafeInspection(current, GIT_METADATA_CONTAINMENT_FAILED_NOTE);
52
+ }
53
+ // A normal worktree does not need commondir. Accepting one here would
54
+ // let a contained `.git/` silently redirect config, refs, and objects.
55
+ if (await pathExists(path.join(dotGitPath, "commondir"))) {
56
+ return unsafeInspection(current, GIT_METADATA_CONTAINMENT_FAILED_NOTE);
57
+ }
58
+ const configOverrides = [];
59
+ const workspace = {
60
+ commonDir: dotGitPath,
61
+ configOverrides,
62
+ gitDir: dotGitPath,
63
+ kind: "directory",
64
+ trustRoot: current,
65
+ worktreeRoot: current
66
+ };
67
+ return await validateCriticalGitMetadata(workspace)
68
+ ? { status: "verified", workspace: freezeVerifiedWorkspace(workspace, configOverrides) }
69
+ : unsafeInspection(current, GIT_METADATA_CONTAINMENT_FAILED_NOTE);
70
+ }
71
+ catch {
72
+ return unsafeInspection(current, GIT_METADATA_CONTAINMENT_FAILED_NOTE);
73
+ }
74
+ }
75
+ if (!dotGitStats.isFile() || dotGitStats.nlink !== 1n) {
76
+ return unsafeInspection(current, GIT_METADATA_CONTAINMENT_FAILED_NOTE);
77
+ }
78
+ try {
79
+ const dotGit = await readSingleLinkRegularFile(dotGitPath);
80
+ const declaredGitDir = dotGit ? parseGitdirFile(dotGit.text) : null;
81
+ if (!dotGit || !declaredGitDir) {
82
+ return unsafeInspection(current, GIT_METADATA_CONTAINMENT_FAILED_NOTE);
83
+ }
84
+ const gitDir = await realpath(path.isAbsolute(declaredGitDir)
85
+ ? declaredGitDir
86
+ : path.resolve(current, declaredGitDir));
87
+ if (!await isPhysicalDirectory(gitDir)) {
88
+ return unsafeInspection(current, GIT_METADATA_CONTAINMENT_FAILED_NOTE);
89
+ }
90
+ const commondir = await readSingleLinkRegularFile(path.join(gitDir, "commondir"));
91
+ const backpointer = await readSingleLinkRegularFile(path.join(gitDir, "gitdir"));
92
+ const declaredCommonDir = commondir ? parseSinglePathLine(commondir.text) : null;
93
+ const declaredBackpointer = backpointer ? parseSinglePathLine(backpointer.text) : null;
94
+ if (!commondir || !backpointer || !declaredCommonDir || !declaredBackpointer) {
95
+ return unsafeInspection(current, GIT_METADATA_CONTAINMENT_FAILED_NOTE);
96
+ }
97
+ const commonDir = await realpath(path.resolve(gitDir, declaredCommonDir));
98
+ if (!await isPhysicalDirectory(commonDir)
99
+ || path.basename(commonDir) !== ".git"
100
+ || path.dirname(gitDir) !== path.join(commonDir, "worktrees")
101
+ || path.basename(gitDir).length === 0) {
102
+ return unsafeInspection(current, GIT_METADATA_CONTAINMENT_FAILED_NOTE);
103
+ }
104
+ const physicalBackpointer = await realpath(path.isAbsolute(declaredBackpointer)
105
+ ? declaredBackpointer
106
+ : path.resolve(gitDir, declaredBackpointer));
107
+ if (physicalBackpointer !== dotGitPath) {
108
+ return unsafeInspection(current, GIT_METADATA_CONTAINMENT_FAILED_NOTE);
109
+ }
110
+ const trustRoot = path.dirname(commonDir);
111
+ if (!await isPhysicalDirectory(trustRoot)
112
+ || await realpath(path.join(trustRoot, ".git")) !== commonDir
113
+ || !await stillSameSingleLinkFile(dotGitPath, dotGit.identity)
114
+ || !await stillSameSingleLinkFile(path.join(gitDir, "commondir"), commondir.identity)
115
+ || !await stillSameSingleLinkFile(path.join(gitDir, "gitdir"), backpointer.identity)) {
116
+ return unsafeInspection(current, GIT_METADATA_CONTAINMENT_FAILED_NOTE);
117
+ }
118
+ const configOverrides = [];
119
+ const workspace = {
120
+ commonDir,
121
+ configOverrides,
122
+ gitDir,
123
+ kind: "linked-worktree",
124
+ trustRoot,
125
+ worktreeRoot: current
126
+ };
127
+ return await validateCriticalGitMetadata(workspace)
128
+ ? { status: "verified", workspace: freezeVerifiedWorkspace(workspace, configOverrides) }
129
+ : unsafeInspection(current, GIT_METADATA_CONTAINMENT_FAILED_NOTE);
130
+ }
131
+ catch {
132
+ return unsafeInspection(current, GIT_METADATA_CONTAINMENT_FAILED_NOTE);
133
+ }
134
+ }
135
+ }
136
+ async function validateCriticalGitMetadata(workspace) {
137
+ if (!await isPhysicalDirectory(workspace.gitDir)
138
+ || !await isPhysicalDirectory(workspace.commonDir)
139
+ || !await isPhysicalDirectory(workspace.worktreeRoot)) {
140
+ return false;
141
+ }
142
+ const criticalFiles = new Set([
143
+ path.join(workspace.gitDir, "HEAD"),
144
+ path.join(workspace.gitDir, "index"),
145
+ path.join(workspace.gitDir, "config.worktree"),
146
+ path.join(workspace.commonDir, "config"),
147
+ path.join(workspace.commonDir, "config.worktree"),
148
+ path.join(workspace.commonDir, "info", "attributes"),
149
+ path.join(workspace.commonDir, "info", "exclude"),
150
+ path.join(workspace.commonDir, "packed-refs")
151
+ ]);
152
+ for (const filePath of criticalFiles) {
153
+ if (!await isSingleLinkRegularFileOrMissing(filePath)) {
154
+ return false;
155
+ }
156
+ }
157
+ // Split indexes are selected by the contained index but read as sibling
158
+ // metadata. Do not let one redirect Git through a symlink or hardlink.
159
+ const gitDirEntries = await readdir(workspace.gitDir).catch(() => []);
160
+ for (const entry of gitDirEntries) {
161
+ if (/^sharedindex\.[0-9a-f]+$/i.test(entry)) {
162
+ if (!await isSingleLinkRegularFileOrMissing(path.join(workspace.gitDir, entry))) {
163
+ return false;
164
+ }
165
+ }
166
+ }
167
+ for (const directory of [
168
+ path.join(workspace.commonDir, "objects"),
169
+ path.join(workspace.commonDir, "refs")
170
+ ]) {
171
+ if (!await isPhysicalDirectoryOrMissing(directory)) {
172
+ return false;
173
+ }
174
+ }
175
+ const head = await readSingleLinkRegularFile(path.join(workspace.gitDir, "HEAD"), true);
176
+ if (head && !await validateHeadReference(workspace, head.text)) {
177
+ return false;
178
+ }
179
+ const alternatesPath = path.join(workspace.commonDir, "objects", "info", "alternates");
180
+ if (!await isSingleLinkRegularFileOrMissing(alternatesPath)) {
181
+ return false;
182
+ }
183
+ const alternates = await readSingleLinkRegularFile(alternatesPath, true);
184
+ if (alternates && !await alternatesStayInsideCommonDir(workspace.commonDir, alternates.text)) {
185
+ return false;
186
+ }
187
+ const overrides = await collectExecutableConfigOverrides(workspace);
188
+ if (!overrides) {
189
+ return false;
190
+ }
191
+ workspace.configOverrides.push(...overrides);
192
+ return true;
193
+ }
194
+ async function collectExecutableConfigOverrides(workspace) {
195
+ const filters = new Set();
196
+ const diffs = new Set();
197
+ for (const configPath of new Set([
198
+ path.join(workspace.commonDir, "config"),
199
+ path.join(workspace.gitDir, "config.worktree")
200
+ ])) {
201
+ const config = await readSingleLinkRegularFile(configPath, true);
202
+ if (!config)
203
+ continue;
204
+ for (const line of config.text.split(/\r?\n/)) {
205
+ if (/^\s*\[\s*include(?:\s*\]|if\b)/i.test(line)) {
206
+ // Includes can introduce executable config from an unbound path after
207
+ // this file was inspected. Provenance capture does not need them.
208
+ return null;
209
+ }
210
+ const sectionStart = /^\s*\[\s*(filter|diff)\b/i.exec(line);
211
+ if (!sectionStart)
212
+ continue;
213
+ const section = /^\s*\[\s*(filter|diff)\s+(?:"((?:\\.|[^"\\])*)"|\.\s*([^\]\s]+))\s*\]\s*(?:[#;].*)?$/i.exec(line);
214
+ if (!section)
215
+ return null;
216
+ const rawDriver = section[2] === undefined ? section[3] : unescapeGitConfigSubsection(section[2]);
217
+ if (!rawDriver || !/^[A-Za-z0-9_.-]+$/.test(rawDriver))
218
+ return null;
219
+ if (section[1]?.toLowerCase() === "filter")
220
+ filters.add(rawDriver);
221
+ else
222
+ diffs.add(rawDriver);
223
+ }
224
+ }
225
+ const overrides = [];
226
+ for (const driver of [...filters].sort()) {
227
+ overrides.push(`filter.${driver}.clean=`, `filter.${driver}.smudge=`, `filter.${driver}.process=`, `filter.${driver}.required=false`);
228
+ }
229
+ for (const driver of [...diffs].sort()) {
230
+ overrides.push(`diff.${driver}.command=`, `diff.${driver}.textconv=`);
231
+ }
232
+ return overrides;
233
+ }
234
+ function unescapeGitConfigSubsection(value) {
235
+ let result = "";
236
+ for (let index = 0; index < value.length; index += 1) {
237
+ const char = value[index];
238
+ if (char !== "\\") {
239
+ result += char;
240
+ continue;
241
+ }
242
+ const escaped = value[index + 1];
243
+ if (escaped !== "\\" && escaped !== "\"")
244
+ return null;
245
+ result += escaped;
246
+ index += 1;
247
+ }
248
+ return result;
249
+ }
250
+ function freezeVerifiedWorkspace(workspace, configOverrides) {
251
+ Object.freeze(configOverrides);
252
+ return Object.freeze(workspace);
253
+ }
254
+ async function validateHeadReference(workspace, headText) {
255
+ const value = headText.trim();
256
+ if (/^[0-9a-f]{40}(?:[0-9a-f]{24})?$/i.test(value)) {
257
+ return true;
258
+ }
259
+ if (!value.startsWith("ref: ")) {
260
+ return false;
261
+ }
262
+ const ref = value.slice("ref: ".length);
263
+ if (!isSafeGitRef(ref)) {
264
+ return false;
265
+ }
266
+ // Normal branch refs live under the common directory. Per-worktree refs may
267
+ // live under the admin directory. Validate either existing path without
268
+ // requiring an unborn branch to have a loose ref.
269
+ const candidates = [path.join(workspace.commonDir, ...ref.split("/"))];
270
+ if (workspace.gitDir !== workspace.commonDir) {
271
+ candidates.push(path.join(workspace.gitDir, ...ref.split("/")));
272
+ }
273
+ for (const candidate of candidates) {
274
+ if (!await validateOptionalContainedFileChain(candidate.startsWith(`${workspace.gitDir}${path.sep}`) ? workspace.gitDir : workspace.commonDir, candidate)) {
275
+ return false;
276
+ }
277
+ }
278
+ return true;
279
+ }
280
+ async function validateOptionalContainedFileChain(root, filePath) {
281
+ const relative = path.relative(root, filePath);
282
+ if (!relative || relative === ".." || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative)) {
283
+ return false;
284
+ }
285
+ let current = root;
286
+ const segments = relative.split(path.sep);
287
+ for (const [index, segment] of segments.entries()) {
288
+ current = path.join(current, segment);
289
+ let stats;
290
+ try {
291
+ stats = await lstat(current, { bigint: true });
292
+ }
293
+ catch (error) {
294
+ return isNodeError(error) && error.code === "ENOENT";
295
+ }
296
+ if (stats.isSymbolicLink())
297
+ return false;
298
+ if (index < segments.length - 1) {
299
+ if (!stats.isDirectory())
300
+ return false;
301
+ }
302
+ else if (!stats.isFile() || stats.nlink !== 1n) {
303
+ return false;
304
+ }
305
+ }
306
+ return true;
307
+ }
308
+ async function alternatesStayInsideCommonDir(commonDir, text) {
309
+ const objectDir = path.join(commonDir, "objects");
310
+ for (const line of text.split(/\r?\n/).map((value) => value.trim()).filter(Boolean)) {
311
+ const candidate = path.isAbsolute(line) ? line : path.resolve(objectDir, line);
312
+ let physical;
313
+ try {
314
+ physical = await realpath(candidate);
315
+ }
316
+ catch {
317
+ return false;
318
+ }
319
+ const relative = path.relative(commonDir, physical);
320
+ if (relative === ".." || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative)) {
321
+ return false;
322
+ }
323
+ }
324
+ return true;
325
+ }
326
+ function parseGitdirFile(text) {
327
+ const match = /^gitdir:\s*([^\r\n]+)\r?\n?$/.exec(text);
328
+ return match?.[1]?.trim() || null;
329
+ }
330
+ function parseSinglePathLine(text) {
331
+ const match = /^([^\r\n]+)\r?\n?$/.exec(text);
332
+ return match?.[1]?.trim() || null;
333
+ }
334
+ function isSafeGitRef(value) {
335
+ return value.startsWith("refs/")
336
+ && !value.includes("\\")
337
+ && !value.includes("\0")
338
+ && !value.includes("//")
339
+ && value.split("/").every((segment) => segment.length > 0 && segment !== "." && segment !== "..");
340
+ }
341
+ async function readSingleLinkRegularFile(filePath, allowMissing = false) {
342
+ let before;
343
+ try {
344
+ before = await lstat(filePath, { bigint: true });
345
+ }
346
+ catch (error) {
347
+ if (allowMissing && isNodeError(error) && error.code === "ENOENT") {
348
+ return null;
349
+ }
350
+ throw error;
351
+ }
352
+ if (before.isSymbolicLink() || !before.isFile() || before.nlink !== 1n) {
353
+ return null;
354
+ }
355
+ const handle = await open(filePath, constants.O_RDONLY | constants.O_NOFOLLOW);
356
+ try {
357
+ const opened = await handle.stat({ bigint: true });
358
+ if (!opened.isFile()
359
+ || opened.nlink !== 1n
360
+ || opened.dev !== before.dev
361
+ || opened.ino !== before.ino) {
362
+ return null;
363
+ }
364
+ const text = await handle.readFile("utf8");
365
+ return Object.freeze({
366
+ identity: Object.freeze({ dev: opened.dev, ino: opened.ino }),
367
+ text
368
+ });
369
+ }
370
+ finally {
371
+ await handle.close();
372
+ }
373
+ }
374
+ async function isSingleLinkRegularFileOrMissing(filePath) {
375
+ let before;
376
+ try {
377
+ before = await lstat(filePath, { bigint: true });
378
+ }
379
+ catch (error) {
380
+ return isNodeError(error) && error.code === "ENOENT";
381
+ }
382
+ if (before.isSymbolicLink() || !before.isFile() || before.nlink !== 1n) {
383
+ return false;
384
+ }
385
+ const handle = await open(filePath, constants.O_RDONLY | constants.O_NOFOLLOW).catch(() => null);
386
+ if (!handle)
387
+ return false;
388
+ try {
389
+ const opened = await handle.stat({ bigint: true });
390
+ return opened.isFile()
391
+ && opened.nlink === 1n
392
+ && opened.dev === before.dev
393
+ && opened.ino === before.ino;
394
+ }
395
+ finally {
396
+ await handle.close();
397
+ }
398
+ }
399
+ async function stillSameSingleLinkFile(filePath, identity) {
400
+ try {
401
+ const stats = await lstat(filePath, { bigint: true });
402
+ return !stats.isSymbolicLink()
403
+ && stats.isFile()
404
+ && stats.nlink === 1n
405
+ && stats.dev === identity.dev
406
+ && stats.ino === identity.ino;
407
+ }
408
+ catch {
409
+ return false;
410
+ }
411
+ }
412
+ async function isPhysicalDirectory(directory) {
413
+ try {
414
+ const stats = await lstat(directory);
415
+ return !stats.isSymbolicLink() && stats.isDirectory() && await realpath(directory) === directory;
416
+ }
417
+ catch {
418
+ return false;
419
+ }
420
+ }
421
+ async function isPhysicalDirectoryOrMissing(directory) {
422
+ try {
423
+ const stats = await lstat(directory);
424
+ return !stats.isSymbolicLink() && stats.isDirectory() && await realpath(directory) === directory;
425
+ }
426
+ catch (error) {
427
+ return isNodeError(error) && error.code === "ENOENT";
428
+ }
429
+ }
430
+ async function pathExists(filePath) {
431
+ try {
432
+ await lstat(filePath);
433
+ return true;
434
+ }
435
+ catch (error) {
436
+ if (isNodeError(error) && error.code === "ENOENT")
437
+ return false;
438
+ throw error;
439
+ }
440
+ }
441
+ function unsafeInspection(worktreeRoot, note) {
442
+ return { note, status: "unsafe", worktreeRoot };
443
+ }
444
+ function isNodeError(error) {
445
+ return error instanceof Error && "code" in error;
446
+ }
447
+ //# sourceMappingURL=git-workspace.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"git-workspace.js","sourceRoot":"","sources":["../../src/core/git-workspace.ts"],"names":[],"mappings":"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"}
@@ -8,6 +8,7 @@ import { type DetachedTimers } from "./e2b-detached.js";
8
8
  import { type DevicePreset } from "./device-presets.js";
9
9
  import { type LabActorLane, type LabConfig, type LabDesktopBrowser, type LabSubjectServe, type LabSubjectState } from "./lab-config.js";
10
10
  import { renderObserver, type ObserverResult } from "./observer.js";
11
+ import { type PreparedOutputDirectory } from "./selected-output-paths.js";
11
12
  import { type LocalTreeArchive } from "./source-archive.js";
12
13
  import type { StopWhen } from "./stop-conditions.js";
13
14
  import { type RunBundle, type RunRerunLineage, type RunProviderResource, type RunSubjectProvenance, type RunSubjectStateStepRecord } from "./run.js";
@@ -395,7 +396,7 @@ export interface CuaLaneDeps {
395
396
  perLaneSandboxMs: number;
396
397
  timeoutMs: number;
397
398
  laneCount: number;
398
- artifactRoot: string;
399
+ artifactRoot: PreparedOutputDirectory;
399
400
  redactScreenshots: boolean;
400
401
  scrubKnownValues: (text: string) => string;
401
402
  runSession: (options: CuaActorSessionOptions) => Promise<CuaLoopResult>;
@@ -430,7 +431,7 @@ export interface LaneRunOutcome {
430
431
  /** Build a lane's writeScreenshot closure: writes under screenshots/<screenshotDir>/ and records
431
432
  * the relative path the trace references (screenshots/<name> at N=1; screenshots/<laneId>/<name>
432
433
  * at N>1). */
433
- export declare function makeLaneWriteScreenshot(artifactRoot: string, spec: {
434
+ export declare function makeLaneWriteScreenshot(artifactRoot: PreparedOutputDirectory, spec: {
434
435
  screenshotDir: string;
435
436
  }, screenshots: string[]): (name: string, bytes: Buffer) => Promise<string>;
436
437
  /**
@@ -21,7 +21,7 @@
21
21
  // conformant humanish.actor-trace.v1 projection, whose `redaction.screenshots` records the
22
22
  // run's actual mode ("raw" | "blurred" | "n/a") — every label downstream derives from it.
23
23
  import { randomBytes } from "node:crypto";
24
- import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
24
+ import { readFile, realpath, rm } from "node:fs/promises";
25
25
  import path from "node:path";
26
26
  import { runDesktopCommandOrThrow, toErrorMessage } from "./command-failure.js";
27
27
  import { pathToFileURL } from "node:url";
@@ -37,6 +37,8 @@ import { assertScreenshotEvidence } from "./image-evidence.js";
37
37
  import { buildObserverData } from "./observer-data.js";
38
38
  import { attachObserverRuntimeStreamUrls, renderObserver } from "./observer.js";
39
39
  import { containsSensitive, digestText, redactedTail, redactText } from "./redaction.js";
40
+ import { assertPreparedSelectedOutputDirectory, assertSafeOutputPathSegment, prepareContainedOutputDirectory, prepareSelectedOutputDirectory, writeContainedOutputFile, writePreparedRunLatestPointer } from "./selected-output-paths.js";
41
+ import { prepareRunArtifactPaths, validatePreparedRunArtifactPaths } from "./run-paths.js";
40
42
  import { createLocalTreeArchive } from "./source-archive.js";
41
43
  import { buildRunSource, loadRunBundle, PUBLIC_TARGET_CWD, REVIEW_SCHEMA, RUN_BUNDLE_SCHEMA } from "./run.js";
42
44
  export const CUA_ACTOR_LAB_SCHEMA = "humanish.cua-lab-result.v2";
@@ -386,13 +388,16 @@ function phaseEventIdSuffix(type) {
386
388
  * the relative path the trace references (screenshots/<name> at N=1; screenshots/<laneId>/<name>
387
389
  * at N>1). */
388
390
  export function makeLaneWriteScreenshot(artifactRoot, spec, screenshots) {
391
+ if (spec.screenshotDir) {
392
+ assertSafeOutputPathSegment(spec.screenshotDir, "Screenshot lane id");
393
+ }
389
394
  const dirParts = spec.screenshotDir ? ["screenshots", spec.screenshotDir] : ["screenshots"];
390
395
  const relPrefix = spec.screenshotDir ? path.posix.join("screenshots", spec.screenshotDir) : "screenshots";
391
396
  return async (name, bytes) => {
397
+ assertSafeOutputPathSegment(name, "Screenshot name");
392
398
  const rel = path.posix.join(relPrefix, name);
393
399
  assertScreenshotEvidence(rel, bytes);
394
- await mkdir(path.join(artifactRoot, ...dirParts), { recursive: true });
395
- await writeFile(path.join(artifactRoot, ...dirParts, name), bytes);
400
+ await writeContainedOutputFile(artifactRoot, path.join(...dirParts, name), bytes);
396
401
  screenshots.push(rel);
397
402
  return rel;
398
403
  };
@@ -881,8 +886,7 @@ export async function runCuaLane(spec, deps) {
881
886
  }
882
887
  }
883
888
  if (session) {
884
- await mkdir(path.dirname(path.join(deps.artifactRoot, spec.traceArtifactPath)), { recursive: true });
885
- await writeFile(path.join(deps.artifactRoot, spec.traceArtifactPath), `${JSON.stringify(session.trace, null, 2)}\n`, "utf8");
889
+ await writeContainedOutputFile(deps.artifactRoot, spec.traceArtifactPath, `${JSON.stringify(session.trace, null, 2)}\n`, "utf8");
886
890
  if (session.trace.redaction.screenshots === "raw") {
887
891
  warnings.push("Screenshots are full-fidelity (raw) for local use — the bundle stays in gitignored .humanish and nothing scans these pixels; review them before sharing anywhere. Set policies.redactScreenshots: true to blur a share-as-is bundle.");
888
892
  }
@@ -949,8 +953,7 @@ async function runInProcessLane(spec, deps) {
949
953
  sessionError = redactText(deps.scrubKnownValues(toErrorMessage(error)));
950
954
  }
951
955
  if (session) {
952
- await mkdir(path.dirname(path.join(deps.artifactRoot, spec.traceArtifactPath)), { recursive: true });
953
- await writeFile(path.join(deps.artifactRoot, spec.traceArtifactPath), `${JSON.stringify(session.trace, null, 2)}\n`, "utf8");
956
+ await writeContainedOutputFile(deps.artifactRoot, spec.traceArtifactPath, `${JSON.stringify(session.trace, null, 2)}\n`, "utf8");
954
957
  if (session.trace.redaction.screenshots === "raw") {
955
958
  warnings.push("Screenshots are full-fidelity (raw) for local use — the bundle stays in gitignored .humanish and nothing scans these pixels; review them before sharing anywhere. Set policies.redactScreenshots: true to blur a share-as-is bundle.");
956
959
  }
@@ -1180,7 +1183,12 @@ function subjectProvenanceArg(subject, publicRepo, subjectEnvNames) {
1180
1183
  }
1181
1184
  export async function runCuaActorLab(options) {
1182
1185
  const { config, dryRun } = options;
1183
- const cwd = path.resolve(options.cwd);
1186
+ // Capture the physical project before reading or invoking any caller hook. A supported
1187
+ // symlink cwd remains valid, but retargeting that alias from a hook cannot redirect source
1188
+ // reads, local-tree packing, managed run storage, or Observer output into another project.
1189
+ const physicalCwd = await realpath(path.resolve(options.cwd));
1190
+ const projectRoot = await prepareSelectedOutputDirectory(path.dirname(physicalCwd), physicalCwd);
1191
+ const cwd = projectRoot.physicalPath;
1184
1192
  const hooks = options.hooks ?? {};
1185
1193
  let liveObserver;
1186
1194
  const runtimeStreamUrls = [];
@@ -1320,6 +1328,7 @@ export async function runCuaActorLab(options) {
1320
1328
  emitPreflightPlan(plan, config.id);
1321
1329
  }
1322
1330
  hooks.onPreflight?.(plan);
1331
+ await assertPreparedSelectedOutputDirectory(projectRoot);
1323
1332
  // Read keys once into locals (names only; values never logged or persisted).
1324
1333
  const openaiApiKey = env.OPENAI_API_KEY?.trim() ?? "";
1325
1334
  const e2bApiKey = env.E2B_API_KEY?.trim() ?? "";
@@ -1348,12 +1357,14 @@ export async function runCuaActorLab(options) {
1348
1357
  }
1349
1358
  }
1350
1359
  const runId = options.runId ?? makeCuaRunId();
1351
- const artifactRoot = path.join(cwd, ".humanish", "runs", runId);
1360
+ const runPaths = await prepareRunArtifactPaths(cwd, runId);
1361
+ const artifactRoot = runPaths.absoluteRunRoot;
1362
+ const physicalArtifactRoot = runPaths.physicalRunRoot;
1352
1363
  const createdAt = new Date().toISOString();
1353
1364
  const timeoutMs = config.execution?.timeoutMs ?? DEFAULT_SESSION_TIMEOUT_MS;
1354
1365
  const requestTimeoutMs = readPositiveInt(env.HUMANISH_E2B_REQUEST_TIMEOUT_MS, 60_000);
1355
1366
  const redactScreenshots = config.policies?.redactScreenshots === true;
1356
- await mkdir(path.join(artifactRoot, "screenshots"), { recursive: true });
1367
+ await prepareContainedOutputDirectory(runPaths, "screenshots");
1357
1368
  const source = await buildRunSource({
1358
1369
  capturedAt: createdAt,
1359
1370
  cwd,
@@ -1404,7 +1415,7 @@ export async function runCuaActorLab(options) {
1404
1415
  perLaneSandboxMs: resolvePerLaneSandboxMs(config),
1405
1416
  timeoutMs,
1406
1417
  laneCount,
1407
- artifactRoot,
1418
+ artifactRoot: runPaths,
1408
1419
  redactScreenshots,
1409
1420
  scrubKnownValues,
1410
1421
  runSession,
@@ -1461,7 +1472,7 @@ export async function runCuaActorLab(options) {
1461
1472
  subjectEnvNames,
1462
1473
  inProgress: true
1463
1474
  });
1464
- await writeCuaRunArtifacts(cwd, artifactRoot, inProgressBundle, createdAt);
1475
+ await writeCuaRunArtifacts(inProgressBundle, createdAt, runPaths);
1465
1476
  liveObserver = observerResultForCuaArtifacts(cwd, runId, artifactRoot, [
1466
1477
  "Live CUA Observer is attached before final verification; stream auth URLs are runtime-only and are not persisted."
1467
1478
  ]);
@@ -1570,7 +1581,7 @@ export async function runCuaActorLab(options) {
1570
1581
  bundle,
1571
1582
  context: {
1572
1583
  bundle,
1573
- runDir: artifactRoot,
1584
+ runDir: physicalArtifactRoot,
1574
1585
  labId: config.id,
1575
1586
  runId,
1576
1587
  actor: descriptor.id,
@@ -1582,7 +1593,7 @@ export async function runCuaActorLab(options) {
1582
1593
  warnings: adapterWarnings,
1583
1594
  hookLabel: "cuaHooks"
1584
1595
  });
1585
- await writeCuaRunArtifacts(cwd, artifactRoot, bundle, createdAt);
1596
+ await writeCuaRunArtifacts(bundle, createdAt, runPaths);
1586
1597
  const observer = await render(cwd, runId, { open: options.open === true });
1587
1598
  if (observer.ok && runtimeStreamUrls.length > 0) {
1588
1599
  attachObserverRuntimeStreamUrls(observer, runtimeStreamUrls);
@@ -1705,21 +1716,21 @@ function buildLaneSummary(outcomes, laneCount, plan, dryRun) {
1705
1716
  waves: plan.waves
1706
1717
  };
1707
1718
  }
1708
- async function writeCuaRunArtifacts(cwd, artifactRoot, bundle, updatedAt) {
1719
+ async function writeCuaRunArtifacts(bundle, updatedAt, preparedRunPaths) {
1720
+ const runPaths = await validatePreparedRunArtifactPaths(preparedRunPaths);
1709
1721
  const publicBundle = {
1710
1722
  ...bundle,
1711
1723
  cwd: PUBLIC_TARGET_CWD
1712
1724
  };
1713
- await writeFile(path.join(artifactRoot, "run.json"), `${JSON.stringify(publicBundle, null, 2)}\n`, "utf8");
1714
- await writeFile(path.join(artifactRoot, "review.json"), `${JSON.stringify(publicBundle.review, null, 2)}\n`, "utf8");
1715
- await writeFile(path.join(artifactRoot, "review.md"), renderCuaReviewMarkdown(publicBundle), "utf8");
1716
- await writeFile(path.join(artifactRoot, "events.ndjson"), `${publicBundle.events.map((event) => JSON.stringify(event)).join("\n")}\n`, "utf8");
1717
- await mkdir(path.join(artifactRoot, "observer"), { recursive: true });
1718
- await writeFile(path.join(artifactRoot, "observer", "observer-data.json"), `${JSON.stringify(buildObserverData(publicBundle), null, 2)}\n`, "utf8");
1719
- await writeFile(path.join(cwd, ".humanish", "runs", "latest.json"), `${JSON.stringify({
1725
+ await writeContainedOutputFile(runPaths, "run.json", `${JSON.stringify(publicBundle, null, 2)}\n`, "utf8");
1726
+ await writeContainedOutputFile(runPaths, "review.json", `${JSON.stringify(publicBundle.review, null, 2)}\n`, "utf8");
1727
+ await writeContainedOutputFile(runPaths, "review.md", renderCuaReviewMarkdown(publicBundle), "utf8");
1728
+ await writeContainedOutputFile(runPaths, "events.ndjson", `${publicBundle.events.map((event) => JSON.stringify(event)).join("\n")}\n`, "utf8");
1729
+ await writeContainedOutputFile(runPaths, "observer/observer-data.json", `${JSON.stringify(buildObserverData(publicBundle), null, 2)}\n`, "utf8");
1730
+ await writePreparedRunLatestPointer(runPaths, `${JSON.stringify({
1720
1731
  schema: "humanish.latest-run.v1",
1721
1732
  runId: publicBundle.runId,
1722
- path: path.join(".humanish", "runs", publicBundle.runId),
1733
+ path: runPaths.relativeRunRoot,
1723
1734
  updatedAt
1724
1735
  }, null, 2)}\n`, "utf8");
1725
1736
  }