humanish 0.84.0 → 0.85.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/dist/cua-actor-lab.js +3 -2
  2. package/dist/cua-actor-lab.js.map +1 -1
  3. package/dist/export.js +17 -10
  4. package/dist/export.js.map +1 -1
  5. package/dist/feedback-proof.d.ts +8 -0
  6. package/dist/feedback-proof.js +44 -0
  7. package/dist/feedback-proof.js.map +1 -0
  8. package/dist/feedback.js +6 -5
  9. package/dist/feedback.js.map +1 -1
  10. package/dist/observer-app.html +55 -9
  11. package/dist/observer-data.d.ts +12 -0
  12. package/dist/observer-data.js +8 -0
  13. package/dist/observer-data.js.map +1 -1
  14. package/dist/observer-library.d.ts +2 -0
  15. package/dist/observer-library.js.map +1 -1
  16. package/dist/observer-static.js +52 -1
  17. package/dist/observer-static.js.map +1 -1
  18. package/dist/observer.d.ts +5 -0
  19. package/dist/observer.js +90 -34
  20. package/dist/observer.js.map +1 -1
  21. package/dist/oss-meta-lab.js +6 -5
  22. package/dist/oss-meta-lab.js.map +1 -1
  23. package/dist/program.js +48 -47
  24. package/dist/program.js.map +1 -1
  25. package/dist/serve-http.d.ts +4 -0
  26. package/dist/serve-http.js +9 -1
  27. package/dist/serve-http.js.map +1 -1
  28. package/dist/tui-actions.d.ts +12 -7
  29. package/dist/tui-actions.js +72 -56
  30. package/dist/tui-actions.js.map +1 -1
  31. package/dist/tui-app.js +1 -1
  32. package/dist/tui-contract.d.ts +1 -1
  33. package/docs/architecture/observer-review.md +130 -0
  34. package/docs/architecture/observer.md +88 -2
  35. package/docs/contracts/feedback.md +9 -0
  36. package/docs/contracts/schemas.md +1 -1
  37. package/docs/goals/current.md +6 -2
  38. package/docs/ramp/README.md +1 -1
  39. package/docs/release/0.84.1-portable-feedback.md +20 -0
  40. package/docs/release/0.85.0-observer-review.md +56 -0
  41. package/package.json +4 -2
@@ -1,5 +1,9 @@
1
1
  export type ServeMode = "loopback" | "exposed" | "share-safe-open";
2
2
  export declare function buildServeSecurityHeaders(): Record<string, string>;
3
+ /** Saved artifacts may contain active HTML, SVG or XML. Give raw documents an
4
+ * opaque origin even on direct navigation, while preserving self-contained scripts.
5
+ * Generated Observer pages use buildServeSecurityHeaders instead. */
6
+ export declare function buildArtifactSecurityHeaders(): Record<string, string>;
3
7
  export declare function hostAllowed(hostHeader: string | undefined, allowlist: ReadonlySet<string>): boolean;
4
8
  export declare function parsePublicOrigin(value: string): {
5
9
  origin: string;
@@ -1,5 +1,5 @@
1
1
  // Shared HTTP hardening primitives for the serve surfaces. Extracted here so BOTH the live
2
- // Observer server (src/observer.ts, exposed mode) and the run-library server
2
+ // Observer server (src/observer.ts) and the run-library server
3
3
  // (src/observer-serve.ts) can enforce the identical Host allowlist + security-header posture
4
4
  // without a module cycle. This file imports nothing from the serve modules.
5
5
  export function buildServeSecurityHeaders() {
@@ -8,9 +8,17 @@ export function buildServeSecurityHeaders() {
8
8
  "referrer-policy": "no-referrer",
9
9
  "x-content-type-options": "nosniff",
10
10
  "x-frame-options": "DENY",
11
+ "content-security-policy": "frame-ancestors 'none'",
11
12
  "x-robots-tag": "noindex, nofollow"
12
13
  };
13
14
  }
15
+ /** Saved artifacts may contain active HTML, SVG or XML. Give raw documents an
16
+ * opaque origin even on direct navigation, while preserving self-contained scripts.
17
+ * Generated Observer pages use buildServeSecurityHeaders instead. */
18
+ export function buildArtifactSecurityHeaders() {
19
+ const headers = buildServeSecurityHeaders();
20
+ return { ...headers, "content-security-policy": `${headers["content-security-policy"]}; sandbox allow-scripts` };
21
+ }
14
22
  export function hostAllowed(hostHeader, allowlist) {
15
23
  return typeof hostHeader === "string" && allowlist.has(hostHeader.trim().toLowerCase());
16
24
  }
@@ -1 +1 @@
1
- {"version":3,"file":"serve-http.js","sourceRoot":"","sources":["../src/serve-http.ts"],"names":[],"mappings":"AAAA,2FAA2F;AAC3F,6EAA6E;AAC7E,6FAA6F;AAC7F,4EAA4E;AAS5E,MAAM,UAAU,yBAAyB;IACvC,OAAO;QACL,eAAe,EAAE,UAAU;QAC3B,iBAAiB,EAAE,aAAa;QAChC,wBAAwB,EAAE,SAAS;QACnC,iBAAiB,EAAE,MAAM;QACzB,cAAc,EAAE,mBAAmB;KACpC,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,UAA8B,EAAE,SAA8B;IACxF,OAAO,OAAO,UAAU,KAAK,QAAQ,IAAI,SAAS,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,CAAC;AAC1F,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,KAAa;IAC7C,IAAI,MAAW,CAAC;IAChB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC;IAC1B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,MAAM,CAAC,QAAQ,KAAK,OAAO,IAAI,MAAM,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;QAChE,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,CAAC,MAAM,CAAC,QAAQ,KAAK,GAAG,IAAI,MAAM,CAAC,QAAQ,KAAK,EAAE,CAAC,IAAI,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;QACxG,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO;QACL,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,WAAW,EAAE;QAC/B,MAAM,EAAE,MAAM,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM;KACxD,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"serve-http.js","sourceRoot":"","sources":["../src/serve-http.ts"],"names":[],"mappings":"AAAA,2FAA2F;AAC3F,+DAA+D;AAC/D,6FAA6F;AAC7F,4EAA4E;AAS5E,MAAM,UAAU,yBAAyB;IACvC,OAAO;QACL,eAAe,EAAE,UAAU;QAC3B,iBAAiB,EAAE,aAAa;QAChC,wBAAwB,EAAE,SAAS;QACnC,iBAAiB,EAAE,MAAM;QACzB,yBAAyB,EAAE,wBAAwB;QACnD,cAAc,EAAE,mBAAmB;KACpC,CAAC;AACJ,CAAC;AAED;;qEAEqE;AACrE,MAAM,UAAU,4BAA4B;IAC1C,MAAM,OAAO,GAAG,yBAAyB,EAAE,CAAC;IAC5C,OAAO,EAAE,GAAG,OAAO,EAAE,yBAAyB,EAAE,GAAG,OAAO,CAAC,yBAAyB,CAAC,yBAAyB,EAAE,CAAC;AACnH,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,UAA8B,EAAE,SAA8B;IACxF,OAAO,OAAO,UAAU,KAAK,QAAQ,IAAI,SAAS,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,CAAC;AAC1F,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,KAAa;IAC7C,IAAI,MAAW,CAAC;IAChB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC;IAC1B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,MAAM,CAAC,QAAQ,KAAK,OAAO,IAAI,MAAM,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;QAChE,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,CAAC,MAAM,CAAC,QAAQ,KAAK,GAAG,IAAI,MAAM,CAAC,QAAQ,KAAK,EAAE,CAAC,IAAI,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;QACxG,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO;QACL,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,WAAW,EAAE;QAC/B,MAAM,EAAE,MAAM,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM;KACxD,CAAC;AACJ,CAAC"}
@@ -1,3 +1,4 @@
1
+ import { openTarget } from "./observer.js";
1
2
  export declare const TUI_ACTION_SCHEMA = "humanish.tui-action.v1";
2
3
  export interface TuiActionResult {
3
4
  schema: typeof TUI_ACTION_SCHEMA;
@@ -5,15 +6,19 @@ export interface TuiActionResult {
5
6
  /** What to tell the operator. Always set: an action that appears to do nothing is a bug. */
6
7
  message: string;
7
8
  }
9
+ /** A TUI owns one evidence server, shared by its open browser tabs and closed when it exits. */
10
+ export interface TuiObserverSession {
11
+ open(cwd: string, observerPath: string): Promise<TuiActionResult>;
12
+ close(): Promise<void>;
13
+ }
8
14
  /**
9
- * Open a run's self-contained Observer artifact in whatever the machine uses for HTML.
10
- *
11
- * A terminal cannot show screenshots, and this is the handoff to the surface that can. It fails
12
- * SOFTLY and usefully: over SSH there is no desktop to open anything, which is not an error — it
13
- * just means the answer is the path, so the operator can forward the port, scp the file, or open it
14
- * on the machine it lives on.
15
+ * The library server projects the latest contained bundle on each browser poll. It can follow
16
+ * captures written by another process without starting a study or recovering desktop credentials.
17
+ * Keeping it in this process avoids detached viewers accumulating each time Open Observer is used.
15
18
  */
16
- export declare function openObserverArtifact(cwd: string, observerPath: string): Promise<TuiActionResult>;
19
+ export declare function createTuiObserverSession(cwdInput: string, options?: {
20
+ openTarget?: typeof openTarget;
21
+ }): TuiObserverSession;
17
22
  /**
18
23
  * Stop a run that is still going.
19
24
  *
@@ -4,68 +4,84 @@
4
4
  // nothing is worse than no action — so this ships the two that are genuinely implementable today
5
5
  // and nothing else. `Share…` waits for the export contract (#471) rather than appearing as a
6
6
  // control that fails.
7
- import { spawn } from "node:child_process";
8
- import { access, readFile } from "node:fs/promises";
7
+ import { readFile } from "node:fs/promises";
9
8
  import path from "node:path";
10
9
  import { RUN_STATUS_FILE, isRunStatusRecord } from "./run-status.js";
11
10
  import { resolveRunPath } from "./run.js";
11
+ import { bindExistingRunArtifactPaths, isSafeRunIdSegment } from "./run-paths.js";
12
+ import { openTarget } from "./observer.js";
13
+ import { serveObserverLibrary } from "./observer-serve.js";
12
14
  export const TUI_ACTION_SCHEMA = "humanish.tui-action.v1";
13
- /** The platform's "open this file" command. */
14
- function opener() {
15
- if (process.platform === "darwin")
16
- return { command: "open", args: [] };
17
- if (process.platform === "win32")
18
- return { command: "cmd", args: ["/c", "start", ""] };
19
- if (process.platform === "linux")
20
- return { command: "xdg-open", args: [] };
21
- return null;
22
- }
23
15
  /**
24
- * Open a run's self-contained Observer artifact in whatever the machine uses for HTML.
25
- *
26
- * A terminal cannot show screenshots, and this is the handoff to the surface that can. It fails
27
- * SOFTLY and usefully: over SSH there is no desktop to open anything, which is not an error — it
28
- * just means the answer is the path, so the operator can forward the port, scp the file, or open it
29
- * on the machine it lives on.
16
+ * The library server projects the latest contained bundle on each browser poll. It can follow
17
+ * captures written by another process without starting a study or recovering desktop credentials.
18
+ * Keeping it in this process avoids detached viewers accumulating each time Open Observer is used.
30
19
  */
31
- export async function openObserverArtifact(cwd, observerPath) {
32
- const absolute = path.isAbsolute(observerPath) ? observerPath : path.join(path.resolve(cwd), observerPath);
33
- try {
34
- await access(absolute);
35
- }
36
- catch {
37
- return {
38
- schema: TUI_ACTION_SCHEMA,
39
- ok: false,
40
- message: `no Observer artifact at ${observerPath} — this run may not have finished writing one`
41
- };
42
- }
43
- const open = opener();
44
- if (open === null) {
45
- return { schema: TUI_ACTION_SCHEMA, ok: true, message: absolute };
46
- }
47
- try {
48
- const child = spawn(open.command, [...open.args, absolute], { detached: true, stdio: "ignore" });
49
- child.on("error", () => {
50
- // Swallowed deliberately: see below — the message already tells the operator the path, which
51
- // is the useful half whether or not a desktop existed to open it.
52
- });
53
- child.unref();
54
- return {
55
- schema: TUI_ACTION_SCHEMA,
56
- ok: true,
57
- // Says the path REGARDLESS. On a headless box `xdg-open` reports success and nothing appears,
58
- // so a message that only said "opened" would be a lie the operator cannot check.
59
- message: `opening ${observerPath} — if nothing appeared, open it yourself (headless or over SSH)`
60
- };
61
- }
62
- catch (cause) {
63
- return {
64
- schema: TUI_ACTION_SCHEMA,
65
- ok: false,
66
- message: `could not open it (${cause instanceof Error ? cause.message : String(cause)}) — the file is at ${absolute}`
67
- };
68
- }
20
+ export function createTuiObserverSession(cwdInput, options = {}) {
21
+ const cwd = path.resolve(cwdInput);
22
+ const open = options.openTarget ?? openTarget;
23
+ let serverPromise;
24
+ let closed = false;
25
+ let closePromise;
26
+ const result = (ok, message) => ({ schema: TUI_ACTION_SCHEMA, ok, message });
27
+ return {
28
+ async open(targetCwd, observerPath) {
29
+ if (closed)
30
+ return result(false, "this terminal session has closed — reopen humanish tui to view the run");
31
+ if (path.resolve(targetCwd) !== cwd)
32
+ return result(false, "Observer can only open runs from this terminal session's project");
33
+ // The capability takes the run-card path, not an arbitrary file, URL, or second project.
34
+ // Validate the exact shape before using its id, then enforce the existing physical storage
35
+ // boundary (including descendant symlinks). An index.html need not exist during an active
36
+ // run: the HTTP server renders it from the contained run.json or observer-data.json.
37
+ const relative = path.relative(cwd, path.resolve(cwd, observerPath));
38
+ const segments = relative.split(path.sep);
39
+ const runId = segments[2];
40
+ if (segments.length !== 5 || segments[0] !== ".humanish" || segments[1] !== "runs"
41
+ || !runId || !isSafeRunIdSegment(runId) || segments[3] !== "observer" || segments[4] !== "index.html") {
42
+ return result(false, "Observer requires a run under this project's .humanish/runs directory");
43
+ }
44
+ try {
45
+ await bindExistingRunArtifactPaths(cwd, runId);
46
+ }
47
+ catch {
48
+ return result(false, "this run's evidence directory is missing or unsafe to open");
49
+ }
50
+ if (closed)
51
+ return result(false, "this terminal session has closed — reopen humanish tui to view the run");
52
+ try {
53
+ // Share the pending start too: two quick selections must not create two listeners.
54
+ serverPromise ??= serveObserverLibrary(cwd, {
55
+ port: 0, safe: false, expose: false, edgeAuthed: false
56
+ }).then((started) => {
57
+ if (!started.ok)
58
+ throw new Error(started.error.message);
59
+ return started.server;
60
+ }).catch((error) => {
61
+ serverPromise = undefined;
62
+ throw error;
63
+ });
64
+ const server = await serverPromise;
65
+ if (closed)
66
+ return result(false, "this terminal session has closed — reopen humanish tui to view the run");
67
+ const url = new URL(`_humanish/runs/${encodeURIComponent(runId)}/observer/index.html`, server.url).href;
68
+ const opened = open(url);
69
+ return result(true, `${url} — follows saved captures; keep this TUI open.${opened.warning ? ` ${opened.warning}` : " If no browser appeared, open this URL on this machine or forward its port over SSH."}`);
70
+ }
71
+ catch {
72
+ return result(false, "Observer could not start — try humanish observe --run with this run's id in another terminal");
73
+ }
74
+ },
75
+ close() {
76
+ closed = true;
77
+ closePromise ??= (async () => {
78
+ // A close racing startup still awaits and releases the listener it owns.
79
+ const server = await serverPromise?.catch(() => undefined);
80
+ await server?.close();
81
+ })();
82
+ return closePromise;
83
+ }
84
+ };
69
85
  }
70
86
  /**
71
87
  * Stop a run that is still going.
@@ -1 +1 @@
1
- {"version":3,"file":"tui-actions.js","sourceRoot":"","sources":["../src/tui-actions.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"tui-actions.js","sourceRoot":"","sources":["../src/tui-actions.ts"],"names":[],"mappings":"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"}
package/dist/tui-app.js CHANGED
@@ -286,7 +286,7 @@ Read about how to prevent this error on https://github.com/vadimdemedes/ink/#isr
286
286
  `)E.name="enter";else if(n===" ")E.name="tab";else if(n==="\b"||n==="\x1B\b")E.name="backspace",E.meta=n.charAt(0)==="\x1B";else if(n==="\x7F"||n==="\x1B\x7F")E.name="backspace",E.meta=n.charAt(0)==="\x1B";else if(n==="\x1B"||n==="\x1B\x1B")E.name="escape",E.meta=n.length===2;else if(n===" "||n==="\x1B ")E.name="space",E.meta=n.length===2;else if(n.length===1&&n<="")E.name=String.fromCharCode(n.charCodeAt(0)+97-1),E.ctrl=!0;else if(n.length===1&&n>="0"&&n<="9")E.name="number";else if(n.length===1&&n>="a"&&n<="z")E.name=n;else if(n.length===1&&n>="A"&&n<="Z")E.name=n.toLowerCase(),E.shift=!0;else if(i=vH.exec(n))E.name=i[1].toLowerCase(),E.meta=!0,E.shift=/^[A-Z]$/.test(i[1]);else if(i=DH.exec(n)){let m=[...n];m[0]==="\x1B"&&m[1]==="\x1B"&&(E.meta=!0);let h=[i[1],i[2],i[4],i[6]].filter(Boolean).join(""),Q=(i[3]||i[5]||1)-1;E.ctrl=!!(Q&4),E.meta=E.meta||!!(Q&10),E.shift=!!(Q&1),E.code=h,E.name=I2[h]??"",E.shift=RH(h)||E.shift,E.ctrl=NH(h)||E.ctrl}return E},"parseKeypress"),R2=UH;var N2=xt(Ue(),1);var _0=u(()=>(0,N2.useContext)(th),"useStdinContext");var HH=u((n,i={})=>{let{setRawMode:o,internal_exitOnCtrlC:c,internal_eventEmitter:E}=_0();(0,Xm.useEffect)(()=>{if(i.isActive!==!1)return o(!0),()=>{o(!1)}},[i.isActive,o]);let m=(0,Xm.useEffectEvent)(h=>{let Q=R2(h),y={upArrow:Q.name==="up",downArrow:Q.name==="down",leftArrow:Q.name==="left",rightArrow:Q.name==="right",pageDown:Q.name==="pagedown",pageUp:Q.name==="pageup",home:Q.name==="home",end:Q.name==="end",return:Q.name==="return",escape:Q.name==="escape",ctrl:Q.ctrl,shift:Q.shift,tab:Q.name==="tab",backspace:Q.name==="backspace",delete:Q.name==="delete",meta:Q.meta,super:Q.super??!1,hyper:Q.hyper??!1,capsLock:Q.capsLock??!1,numLock:Q.numLock??!1,eventType:Q.eventType},N;Q.isKittyProtocol?Q.isPrintable?N=Q.text??Q.name:Q.ctrl&&Q.name.length===1?N=Q.name:N="":Q.ctrl?N=Q.name??"":N=Q.sequence,!Q.isKittyProtocol&&Q2.includes(Q.name)&&(N=""),N.startsWith("\x1B")&&(N=N.slice(1)),N.length===1&&/[A-Z]/.test(N)&&(y.shift=!0),!(N==="c"&&y.ctrl&&c)&&Oa.discreteUpdates(()=>{n(N,y)})});(0,Xm.useEffect)(()=>{if(i.isActive!==!1)return E.on("input",m),()=>{E.removeListener("input",m)}},[i.isActive,E])},"useInput"),M0=HH;var b2=xt(Ue(),1);var T2=xt(Ue(),1);var OH=u(()=>(0,T2.useContext)($B),"useApp"),U0=OH;var x2=xt(Ue(),1);var GH=u(()=>(0,x2.useContext)(eh),"useStdout"),rh=GH;var wH=xt(Ue(),1);var H0=xt(Ue(),1);var zH=xt(Ue(),1);var YH=xt(Ue(),1);var sh=xt(Ue(),1);var Zm=xt(Ue(),1);var _2=xt(Ue(),1);var ch=xt(Ue(),1);var JH=/utf-?8/i;function Rc(n=process.env){if(process.platform==="win32")return!0;let i=n.LC_ALL??n.LC_CTYPE??n.LANG;return i===void 0||i.trim().length===0?!1:JH.test(i)}u(Rc,"terminalRendersUnicode");var KH=new Map([["\u2014","--"],["\u2013","-"],["\u2019","'"],["\u2018","'"],["\u201C",'"'],["\u201D",'"'],["\u2026","..."],["\xB7","-"],["\u276F",">"],["\u2039","<"],["\u25B8",">"],["\u2713","+"],["\u2691","!"],["\u23CE","Enter"],["\u2191","^"],["\u2193","v"],["\u2190","<-"],["\u2192","->"],["\xD7","x"],["\u2248","~"],["\u2717","x"]]);function M2(n,i=process.env){if(Rc(i))return n;let o="";for(let c of n){if(c.codePointAt(0)<128){o+=c;continue}o+=KH.get(c)??"?"}return o}u(M2,"forTerminal");var Xe=xt(Ue(),1);var be={accent:"#93a2ff",ok:"#7dc9a0",warn:"#d9a441",bad:"#e0796b"};var Di=xt(ha(),1);function G2({columns:n}){let i=[["\u2191 \u2193 \xB7 k j","move the cursor"],["\u23CE \xB7 \u2192","open what the cursor is on, or run the action it names"],["esc \xB7 \u2190","back \u2014 and it cancels an armed confirmation first"],["g \xB7 G","jump to the top, jump to the bottom"],["?","these keys"],["q","quit \u2014 a run you started keeps going without this window"]];return(0,Di.jsxs)(ht,{flexDirection:"column",width:n,children:[(0,Di.jsx)(rt,{color:be.accent,bold:!0,children:"Keys"}),(0,Di.jsx)(ht,{marginTop:1,flexDirection:"column",children:i.map(([o,c])=>(0,Di.jsxs)(ht,{children:[(0,Di.jsx)(ht,{width:16,children:(0,Di.jsx)(rt,{bold:!0,children:o})}),(0,Di.jsx)(rt,{dimColor:!0,children:c})]},o))}),(0,Di.jsxs)(ht,{marginTop:1,flexDirection:"column",children:[(0,Di.jsx)(rt,{dimColor:!0,children:"Starting a run asks twice when it spends money: the first \u23CE arms and"}),(0,Di.jsx)(rt,{dimColor:!0,children:"restates the cost, the second commits. A dry run never asks."})]})]})}u(G2,"HelpScreen");function jH(n){let i=new Map,o=[];for(let E of n){let m=E.lab?.id;if(m===void 0){o.push(E);continue}let h=i.get(m);h===void 0?i.set(m,[E]):h.push(E)}let c=[...i.entries()].map(([E,m])=>{let h=m.filter(Q=>Q.liveness==="running");return{labId:E,runs:m.length,live:h.length,...m[0]===void 0?{}:{latest:m[0]},liveRuns:h}});return c.sort((E,m)=>E.live!==m.live?m.live-E.live:Eh(m.latest)-Eh(E.latest)),{labs:c,unattributed:o}}u(jH,"groupRunsByLab");function Eh(n){if(n===void 0)return 0;for(let i of[n.completedAt,n.updatedAt,n.startedAt]){if(i===void 0)continue;let o=Date.parse(i);if(Number.isFinite(o))return o}return 0}u(Eh,"recencyOf");function w2(n,i){let c=(i===void 0?n:n.filter(Q=>Q.mode===i)).filter(Q=>Q.liveness==="finished"),E=c.map(Q=>Q.durationMs).filter(Q=>typeof Q=="number"&&Number.isFinite(Q)&&Q>=0).sort((Q,y)=>Q-y),m=c.map(Q=>Q.estimatedCostUsd).filter(Q=>typeof Q=="number"&&Number.isFinite(Q)).sort((Q,y)=>Q-y),h=c.filter(Q=>Q.estimatedCostUsd===null||Q.estimatedCostUsd===void 0).length;return{sample:c.length,...E.length===0?{}:{medianDurationMs:L2(E),durationRangeMs:{min:E[0],max:E[E.length-1]}},...m.length===0?{}:{medianCostUsd:L2(m)},costUnknown:h}}u(w2,"expectationFor");function L2(n){let i=Math.floor(n.length/2);return n.length%2===1?n[i]:(n[i-1]+n[i])/2}u(L2,"median");function Wm(n){if(n.sample===0)return"no runs yet";let i=[];if(n.durationRangeMs!==void 0){let{min:m,max:h}=n.durationRangeMs;i.push(m===h?Ri(m):`${Ri(m)}\u2013${Ri(h)}`)}n.medianCostUsd!==void 0&&i.push(`~$${n.medianCostUsd.toFixed(2)} median`);let o=`${n.sample} run${n.sample===1?"":"s"}`,c=n.sample>0&&n.costUnknown===n.sample,E=n.costUnknown===0?"":c?", none priced":`, ${n.costUnknown} unpriced`;return i.length===0?c?`${o}, nothing recorded`:`${o}${E}, nothing timed`:`${i.join(" \xB7 ")} \xB7 ${o}${E}`}u(Wm,"expectationLine");function dh(n){return n.runs===0?n.declared?"never run":"no runs":n.liveExpectation.sample>0?Wm(n.liveExpectation):`${n.runs} ${n.runs===1?"run":"runs"}, none live`}u(dh,"labSummaryLine");function Ri(n){let i=Math.max(0,Math.round(n/1e3));if(i<60)return`${i}s`;let o=Math.floor(i/60);if(o<60)return i%60===0?`${o}m`:`${o}m ${i%60}s`;let c=Math.floor(o/60);return o%60===0?`${c}h`:`${c}h ${o%60}m`}u(Ri,"formatDuration");function cE(n,i){let o=Math.max(8,Math.floor(i.width)),c=Math.max(1,Math.floor(i.maxLines)),E=n.replace(/\*\*([^*]+)\*\*/g,"$1").replace(/\s*\n+\s*/g," ").replace(/\s{2,}/g," ").trim();if(E==="")return{lines:[],truncated:!1};let m=E.split(" "),h=[],Q="",y=0;for(;y<m.length;y+=1){let x=m[y],w=Q===""?x:`${Q} ${x}`;if(w.length<=o){Q=w;continue}if(Q!==""&&h.push(Q),Q=x.length<=o?x:`${x.slice(0,o-1)}\u2026`,x.length>o&&(h.push(Q),Q=""),h.length===c)break}h.length<c&&Q!==""&&(h.push(Q),y=m.length);let N=y<m.length;if(N&&h.length>0){let x=h[h.length-1];h[h.length-1]=x.length+1<=o?`${x}\u2026`:`${x.slice(0,o-1)}\u2026`}return{lines:h,truncated:N}}u(cE,"normalizeThought");function fE(n){let i=Math.max(0,Math.floor(n.total)),o=Math.max(1,Math.floor(n.viewport));if(i<=o)return{start:0,end:i};let c=Math.min(Math.max(0,Math.floor(n.selected)),i-1),E=Math.min(Math.max(0,Math.floor(n.margin??1)),Math.floor((o-1)/2)),m=Math.min(Math.max(0,c-E),i-o);return c>=m+o-E&&(m=Math.min(c-o+1+E,i-o)),m=Math.max(0,Math.min(m,i-o)),{start:m,end:m+o}}u(fE,"listWindow");function z2(n){return(n.split("/").pop()??n).replace(/\.(ya?ml)$/i,"")}u(z2,"labNameFromPath");function Y2(n,i){let{labs:o,unattributed:c}=jH(i),E=new Map(o.map(w=>[w.labId,w])),m=new Map;for(let w of i){let b=w.lab?.id;if(b===void 0)continue;let L=m.get(b);L===void 0?m.set(b,[w]):L.push(w)}let h=new Map;for(let w of n)h.set(w.id,(h.get(w.id)??0)+1);let Q=u((w,b,L)=>{let j=E.get(w),q=b?.path;return{key:q??`id:${w}`,labId:w,name:q===void 0?w:z2(q),...b?.title===void 0?{}:{title:b.title},...q===void 0?{}:{path:q},...b?.origin===void 0?{}:{origin:b.origin},declared:L,label:b?.title??(q===void 0?w:z2(q)),...b?.description===void 0?{}:{description:b.description},sharesIdWith:Math.max(0,(h.get(w)??0)-1),runs:j?.runs??0,live:j?.live??0,...j?.latest===void 0?{}:{latest:j.latest},liveRuns:j?.liveRuns??[],expectation:w2(m.get(w)??[]),liveExpectation:w2(m.get(w)??[],"live")}},"build"),y=new Set(n.map(w=>w.id)),N=[...n.map(w=>Q(w.id,w,!0)),...o.filter(w=>!y.has(w.labId)).map(w=>Q(w.labId,void 0,!1))];kH(N);let x=u(w=>w.label,"label");return N.sort((w,b)=>{if(w.live!==b.live)return b.live-w.live;let L=u(q=>q.runs>0?0:q.declared?1:2,"rank");if(L(w)!==L(b))return L(w)-L(b);if(w.runs>0&&b.runs>0){let q=Eh(b.latest)-Eh(w.latest);if(q!==0)return q}let j=x(w).localeCompare(x(b));return j!==0?j:w.key.localeCompare(b.key)}),{rows:N,unattributed:c}}u(Y2,"labRows");function kH(n){let i=u(E=>{let m=new Map;for(let h of E)m.set(h,(m.get(h)??0)+1);return m},"count"),o=i(n.map(E=>E.title??E.name)),c=i(n.map(E=>E.name));for(let E of n){let m=E.title??E.name;if((o.get(m)??0)===1){E.label=m;continue}E.label=(c.get(E.name)??0)===1?E.name:E.path??E.name}}u(kH,"assignLabels");function G0(n){switch(n.name){case"labs":return"labs";case"all-runs":return"all-runs";case"lab":return`lab:${n.labKey}`;default:return`run:${n.runId}`}}u(G0,"screenKey");function J2(){return{stack:[{name:"labs"}],selection:{},quit:!1}}u(J2,"initialNav");function Pm(n){return n.stack[n.stack.length-1]??{name:"labs"}}u(Pm,"currentScreen");function K2(n){return n.selection[G0(Pm(n))]??0}u(K2,"selectedIndex");function F2(n,i){switch(i.type){case"enter":return{...n,stack:[...n.stack,i.screen]};case"back":return n.stack.length<=1?{...n,quit:!0}:{...n,stack:n.stack.slice(0,-1)};case"move":{if(i.total<=0)return n;let o=G0(Pm(n)),c=n.selection[o]??0,E=Math.min(Math.max(0,c+i.delta),i.total-1);return E===c?n:{...n,selection:{...n.selection,[o]:E}}}case"select":{if(i.total<=0)return n;let o=G0(Pm(n)),c=Math.min(Math.max(0,i.index),i.total-1);return{...n,selection:{...n.selection,[o]:c}}}default:return{...n,quit:!0}}}u(F2,"navigate");function rA(n){return n===void 0?{}:{color:n}}u(rA,"color");var gu=xt(ha(),1),VH=96;function mh(n){return Math.max(20,Math.min(VH,n))}u(mh,"contentWidth");function q2({columns:n,context:i,breadcrumb:o,hints:c,children:E}){let m=mh(n);return(0,gu.jsxs)(ht,{flexDirection:"column",width:m,children:[(0,gu.jsxs)(ht,{width:m,children:[(0,gu.jsx)(rt,{bold:!0,children:"human(ish)"}),(0,gu.jsx)(ht,{flexGrow:1}),i===void 0?null:(0,gu.jsx)(rt,{dimColor:!0,wrap:"truncate-start",children:i})]}),o===void 0?null:(0,gu.jsx)(rt,{dimColor:!0,wrap:"truncate-start",children:o}),(0,gu.jsx)(ht,{marginTop:1,flexDirection:"column",children:E}),(0,gu.jsx)(ht,{marginTop:1,children:(0,gu.jsx)(rt,{dimColor:!0,children:c})})]})}u(q2,"Frame");var w0=Rc()?["\u280B","\u2819","\u2839","\u2838","\u283C","\u2834","\u2826","\u2827","\u2807","\u280F"]:["|","/","-","\\","|","/","-","\\","|","/"];function $m(n){return w0[Math.abs(Math.floor(n))%w0.length]??w0[0]}u($m,"spinnerFrame");function Ni(n){return n?Rc()?"\u276F":">":" "}u(Ni,"gutter");function tp(n){if(n.liveness==="running")return $m(n.tick??0);let i=Rc();return n.liveness==="interrupted"||n.verdict==="fail"?i?"\u2691":"!":n.verdict===void 0?i?"\xB7":"-":i?"\u2713":"+"}u(tp,"verdictGlyph");function XH(n){if(n.liveness==="running")return be.ok;if(n.liveness==="interrupted")return be.warn;if(n.verdict==="fail")return be.bad}u(XH,"verdictColor");var gE=u(n=>rA(XH(n)),"glyphColor");function jr(n,i){let o=Math.max(4,Math.floor(i));return n.length<=o?n:`${n.slice(0,o-1)}\u2026`}u(jr,"fitLabelToWidth");var Dn=xt(ha(),1);function V2({runs:n,details:i,labels:o,expected:c,selected:E,columns:m,viewport:h,tick:Q,now:y}){if(n.length===0)return(0,Dn.jsxs)(ht,{flexDirection:"column",children:[(0,Dn.jsx)(rt,{children:"nobody is working right now"}),(0,Dn.jsx)(rt,{dimColor:!0,children:"start a run from any lab and it appears here"})]});let N=fE({total:n.length,selected:E,viewport:Math.max(2,h-6)}),x=n[Math.min(Math.max(0,E),n.length-1)],w=x===void 0?void 0:i.get(x.runId),b=w?.participants[0],L=b?.thought===void 0?void 0:cE(b.thought.text,{width:Math.max(16,m-2),maxLines:2});return(0,Dn.jsxs)(ht,{flexDirection:"column",children:[n.slice(N.start,N.end).map((j,q)=>(0,Dn.jsx)(ZH,{run:j,who:j2(i.get(j.runId))??"starting\u2026",lab:o.get(j.runId)??j.lab?.id??"",expectedMs:c.get(j.lab?.id??""),active:N.start+q===E,columns:m,tick:Q,now:y},j.runId)),L===void 0?null:(0,Dn.jsxs)(ht,{marginTop:1,flexDirection:"column",children:[L.lines.map((j,q)=>(0,Dn.jsxs)(ht,{width:m,children:[(0,Dn.jsx)(ht,{width:2,flexShrink:0,children:(0,Dn.jsx)(rt,{color:be.accent,children:"\u258C"})}),(0,Dn.jsxs)(rt,{dimColor:!0,wrap:"truncate-end",children:[q===0?`"${j}`:j,q===L.lines.length-1?'"':""]})]},q)),(0,Dn.jsxs)(ht,{width:m,children:[(0,Dn.jsx)(ht,{width:2,flexShrink:0,children:(0,Dn.jsx)(rt,{children:" "})}),(0,Dn.jsx)(rt,{dimColor:!0,wrap:"truncate-end",children:jr([j2(w),o.get(x?.runId??"")].filter(Boolean).join(" \xB7 "),Math.max(10,m-2))})]})]}),(0,Dn.jsx)(ht,{marginTop:1,children:(0,Dn.jsx)(rt,{dimColor:!0,children:WH(n,i)})})]})}u(V2,"AllRunsScreen");function j2(n){let i=n?.participants[0];return i?.personaId??i?.label}u(j2,"personaOf");function ZH({run:n,who:i,lab:o,expectedMs:c,active:E,columns:m,tick:h,now:Q}){let y=n.startedAt===void 0?Number.NaN:Date.parse(n.startedAt),N=Number.isFinite(y)?k2(Q-y):"",x=c===void 0?N:`${N} / ~${k2(c)}`,w=Math.max(10,Math.floor((m-x.length-6)*.5)),b=Math.max(8,m-x.length-w-6);return(0,Dn.jsxs)(ht,{width:m,children:[(0,Dn.jsxs)(rt,{...rA(E?be.accent:void 0),bold:E,children:[Ni(E)," "]}),(0,Dn.jsxs)(rt,{color:be.ok,children:[$m(h)," "]}),(0,Dn.jsx)(ht,{width:w,children:(0,Dn.jsx)(rt,{...rA(E?be.accent:void 0),bold:E,wrap:"truncate-end",children:jr(i,w)})}),(0,Dn.jsx)(ht,{width:b,children:(0,Dn.jsx)(rt,{dimColor:!0,wrap:"truncate-end",children:jr(o,b)})}),(0,Dn.jsx)(ht,{flexGrow:1}),(0,Dn.jsx)(rt,{dimColor:!0,children:x})]})}u(ZH,"RunRow");function WH(n,i){let o=0,c=0;for(let h of n){let Q=h.estimatedCostUsd??i.get(h.runId)?.participants[0]?.estimatedCostUsd;typeof Q=="number"&&(o+=Q,c+=1)}if(c===0)return"no spend recorded yet \u2014 a live run prices itself as it goes";let E=n.length-c,m=E===0?"":` \xB7 ${E} not priced yet`;return`spend ~$${o.toFixed(2)} so far across ${c} of ${n.length}${m}`}u(WH,"spendLine");function k2(n){if(!Number.isFinite(n)||n<0)return"0:00";let i=Math.floor(n/1e3),o=Math.floor(i/60);return o<60?`${o}:${String(i%60).padStart(2,"0")}`:Ri(n)}u(k2,"clockOf");var ee=xt(ha(),1);function ph(n,i){return[...i?[{kind:"start",mode:"dry-run"},{kind:"start",mode:"live"}]:[],...n.map(c=>({kind:"run",run:c}))]}u(ph,"labItems");function P2(n){let{row:i,summary:o,runs:c,selected:E,columns:m,viewport:h,now:Q,tick:y,canStart:N}=n,x=ph(c,N);return(0,ee.jsxs)(ht,{flexDirection:"column",children:[o?.description===void 0?null:(0,ee.jsx)(rt,{wrap:"truncate-end",children:AO(o.description)}),o?.subject===void 0?null:(0,ee.jsx)(rt,{dimColor:!0,wrap:"truncate-end",children:[o.subject,o.participants,o.model,o.reasoningEffort===void 0?void 0:`${o.reasoningEffort} effort`].filter(Boolean).join(" \xB7 ")}),(0,ee.jsxs)(ht,{width:m,children:[(0,ee.jsxs)(rt,{dimColor:!0,wrap:"truncate-end",children:[dh(i),nO(o)]}),(0,ee.jsx)(ht,{flexGrow:1}),o?.keysReady===void 0?null:(0,ee.jsx)(rt,{...rA(o.keysReady?be.ok:be.warn),children:o.keysReady?"keys \u2713":"keys \u2717"})]}),o?.keysReady===!1?(0,ee.jsxs)(ht,{flexDirection:"column",width:m,children:[(0,ee.jsxs)(rt,{...rA(be.warn),children:[o.missingKeys?.join(", ")," not found"]}),(0,ee.jsxs)(rt,{dimColor:!0,children:[" ","humanish keys set openai"," ","\u2014 stores them for every project"]}),(0,ee.jsxs)(rt,{dimColor:!0,children:[" ","humanish tui --env-file .env"," ","\u2014 or just this session"]})]}):null,n.launchNote===void 0?null:(0,ee.jsx)(ht,{marginTop:1,children:(0,ee.jsx)(rt,{dimColor:!0,children:n.launchNote})}),n.launchError===void 0?null:(0,ee.jsx)(ht,{marginTop:1,children:(0,ee.jsx)(rt,{color:be.bad,children:n.launchError})}),N?(0,ee.jsxs)(ht,{marginTop:1,flexDirection:"column",children:[(0,ee.jsx)(Z2,{...n,mode:"dry-run",active:X2(x,E)==="dry-run"}),(0,ee.jsx)(Z2,{...n,mode:"live",active:X2(x,E)==="live"}),n.confirming==="live"?(0,ee.jsx)(ht,{marginTop:1,children:(0,ee.jsxs)(rt,{color:be.warn,children:[" ","start a live run? ",Wm(n.row.liveExpectation)," \xB7 \u23CE confirm \xB7 esc cancel"]})}):null]}):null,i.declared?null:(0,ee.jsx)(ht,{marginTop:1,children:(0,ee.jsx)(rt,{color:be.warn,children:"no manifest here \u2014 renamed, deleted, or run from elsewhere"})}),i.sharesIdWith>0?(0,ee.jsxs)(rt,{color:be.warn,children:[i.sharesIdWith+1,' manifests declare "',i.labId,'" \u2014 these runs are shared between them']}):null,(0,ee.jsxs)(ht,{marginTop:1,flexDirection:"column",children:[(0,ee.jsx)(rt,{dimColor:!0,children:"Runs"}),c.length===0?(0,ee.jsx)(rt,{dimColor:!0,children:" none yet"}):(0,ee.jsx)(PH,{runs:c,liveDetail:n.liveDetail,selected:E-ph([],N).length,columns:m,viewport:Math.max(2,h-6),now:Q,tick:y,expectedMs:i.liveExpectation.medianDurationMs})]})]})}u(P2,"LabScreen");function X2(n,i){let o=n[i];return o?.kind==="start"?o.mode:void 0}u(X2,"activeStart");function Z2({mode:n,confirming:i,active:o,columns:c,row:E,summary:m}){let h=n==="live",Q=h&&m?.keysReady===!1,y=h?be.warn:be.accent;return(0,ee.jsxs)(ht,{width:c,children:[(0,ee.jsxs)(rt,{...rA(o?y:void 0),bold:o,dimColor:Q&&!o,children:[Ni(o)," ",h?"Start a LIVE run":"Start a dry run"]}),(0,ee.jsx)(ht,{flexGrow:1}),(0,ee.jsx)(rt,{dimColor:!Q,...rA(Q?be.warn:void 0),children:h?Q?`${Wm(E.liveExpectation)} \xB7 needs keys`:Wm(E.liveExpectation):"free \xB7 no keys, no spend"})]})}u(Z2,"StartRow");function PH({runs:n,liveDetail:i,selected:o,columns:c,viewport:E,now:m,tick:h,expectedMs:Q}){let y=fE({total:n.length,selected:o,viewport:E});return(0,ee.jsxs)(ht,{flexDirection:"column",children:[y.start>0?(0,ee.jsxs)(rt,{dimColor:!0,children:[" \u2191 ",y.start," more"]}):null,n.slice(y.start,y.end).map((N,x)=>N.liveness==="running"?(0,ee.jsx)($H,{run:N,detail:i,active:y.start+x===o,columns:c,now:m,tick:h,expectedMs:Q},N.runId):(0,ee.jsx)(eO,{run:N,active:y.start+x===o,columns:c},N.runId)),y.end<n.length?(0,ee.jsxs)(rt,{dimColor:!0,children:[" \u2193 ",n.length-y.end," more"]}):null]})}u(PH,"RunList");function $H({run:n,detail:i,active:o,columns:c,now:E,tick:m,expectedMs:h}){let Q=i?.participants[0],y=n.startedAt===void 0?Number.NaN:Date.parse(n.startedAt),N=Number.isFinite(y)?W2(E-y):void 0,x=h===void 0?"":` of ~${W2(h)}`,w=Q?.thought===void 0?void 0:cE(Q.thought.text,{width:Math.max(16,c-6),maxLines:3});return(0,ee.jsxs)(ht,{flexDirection:"column",children:[(0,ee.jsxs)(ht,{width:c,children:[(0,ee.jsxs)(rt,{...rA(o?be.accent:void 0),bold:o,children:[Ni(o)," "]}),(0,ee.jsxs)(rt,{color:be.ok,children:[tp({liveness:"running",tick:m})," "]}),(0,ee.jsx)(rt,{...rA(o?be.accent:void 0),bold:o,wrap:"truncate-end",children:Q?.personaId??Q?.label??"starting\u2026"}),(0,ee.jsx)(ht,{flexGrow:1}),(0,ee.jsx)(rt,{dimColor:!0,children:N===void 0?"":`${N}${x}`})]}),w===void 0?null:(0,ee.jsx)(ht,{flexDirection:"column",marginLeft:4,children:w.lines.map((b,L)=>(0,ee.jsxs)(rt,{dimColor:L>0,children:[L===0?`"${b}`:b,L===w.lines.length-1?'"':""]},L))}),Q===void 0?null:(0,ee.jsx)(ht,{width:c,children:(0,ee.jsxs)(rt,{dimColor:!0,children:[" ",tO(Q)]})})]})}u($H,"LiveRun");function tO(n){let i=[];return n.actions!==void 0&&i.push(`${n.actions} actions`),n.thoughts!==void 0&&i.push(`${n.thoughts} thoughts`),n.turns!==void 0&&i.push(`${n.turns} turns`),i.length===0?"working\u2026":i.join(" \xB7 ")}u(tO,"activityLine");function eO({run:n,active:i,columns:o}){let c=aO(n.completedAt??n.startedAt),E=n.participants===void 0?n.verdict??"no verdict":`${n.participants.reachedGoal}/${n.participants.total} reached the goal`,m=n.estimatedCostUsd===void 0||n.estimatedCostUsd===null?"":` \xB7 ~$${n.estimatedCostUsd.toFixed(2)}`,h=[c,E].filter(Boolean).join(" \xB7 ")+m;return(0,ee.jsxs)(ht,{width:o,children:[(0,ee.jsxs)(rt,{...rA(i?be.accent:void 0),bold:i,children:[Ni(i)," "]}),(0,ee.jsxs)(rt,{...gE(n),children:[tp(n)," "]}),(0,ee.jsx)(rt,{...rA(i?be.accent:void 0),bold:i,wrap:"truncate-end",children:h})]})}u(eO,"PastRun");function nO(n){let i=n?.caps.laneUsd,o=n?.caps.studyUsd;if(i===void 0&&o===void 0)return"";let c=[];return i!==void 0&&c.push(`$${i} lane`),o!==void 0&&c.push(`$${o} study`),` \xB7 caps ${c.join(" / ")}`}u(nO,"capsLine");function AO(n){let i=n.replace(/\s+/g," ").trim(),o=i.indexOf(". ");return o===-1?i:i.slice(0,o+1)}u(AO,"firstSentence");function aO(n){if(n===void 0)return"";let i=Date.parse(n);if(!Number.isFinite(i))return"";let o=new Date(i),c=u(E=>String(E).padStart(2,"0"),"pad");return`${c(o.getUTCMonth()+1)}-${c(o.getUTCDate())} ${c(o.getUTCHours())}:${c(o.getUTCMinutes())}`}u(aO,"shortDate");function W2(n){if(!Number.isFinite(n)||n<0)return"0:00";let i=Math.floor(n/1e3),o=Math.floor(i/60);return o<60?`${o}:${String(i%60).padStart(2,"0")}`:Ri(n)}u(W2,"clockOf");var sA=xt(ha(),1);function Ch(n,i){return n.liveness==="interrupted"?i?.observerPath===void 0?["reclaim"]:["reclaim","observer"]:n.liveness==="running"?["stop"]:i?.observerPath===void 0?["again"]:["observer","again"]}u(Ch,"runActions");function lO(n){switch(n){case"observer":return"Open in Observer";case"again":return"Run again";case"stop":return"Stop this run";default:return"Reclaim \u2014 stop sandboxes, keep evidence"}}u(lO,"actionLabel");function $2({run:n,detail:i,columns:o,selected:c,tick:E,now:m,actionNote:h}){let Q=i?.participants[0],y=Ch(n,i),N=n.liveness==="interrupted";return(0,sA.jsxs)(ht,{flexDirection:"column",children:[(0,sA.jsx)(ht,{children:(0,sA.jsxs)(rt,{...gE(n),bold:!0,children:[tp({...n,tick:E})," ",iO(n,Q)]})}),N?(0,sA.jsx)(oO,{run:n,detail:i,participant:Q,now:m,columns:o}):(0,sA.jsx)(uO,{run:n,participant:Q,columns:o}),y.length===0?null:(0,sA.jsx)(ht,{marginTop:1,flexDirection:"column",children:y.map((x,w)=>(0,sA.jsx)(ht,{children:(0,sA.jsxs)(rt,{...rA(w===c?be.accent:void 0),bold:w===c,children:[Ni(w===c)," ",jr(lO(x),Math.max(10,o-3))]})},x))}),h===void 0?null:(0,sA.jsx)(ht,{marginTop:1,children:(0,sA.jsx)(rt,{dimColor:!0,wrap:"truncate-end",children:h})})]})}u($2,"RunScreen");function iO(n,i){if(n.liveness==="interrupted")return"interrupted \u2014 no outcome recorded";if(n.liveness==="running"){let E=i?.personaId??i?.label;return E===void 0?"starting\u2026":`${E} is working`}let o=n.participants;if(o===void 0)return n.verdict??"finished, no verdict recorded";let c=o.reportedFriction===void 0||o.reportedFriction===0?"":` \xB7 ${o.reportedFriction} reported friction`;return`${o.reachedGoal}/${o.total} reached the goal${c}`}u(iO,"headline");function uO({run:n,participant:i,columns:o}){let c=i?.thought===void 0?void 0:cE(i.thought.text,{width:Math.max(16,o),maxLines:3}),E=[n.durationMs===void 0?void 0:Ri(n.durationMs),i?.turns===void 0?void 0:`${i.turns} turns`,i?.thoughts===void 0?void 0:`${i.thoughts} thoughts`].filter(Boolean).join(" \xB7 ");return(0,sA.jsxs)(ht,{flexDirection:"column",children:[c===void 0?null:(0,sA.jsx)(ht,{marginTop:1,flexDirection:"column",children:c.lines.map((m,h)=>(0,sA.jsxs)(rt,{children:[h===0?`"${m}`:m,h===c.lines.length-1?'"':""]},h))}),(0,sA.jsxs)(ht,{marginTop:1,flexDirection:"column",children:[E===""?null:(0,sA.jsx)(rt,{dimColor:!0,children:E}),(0,sA.jsx)(rt,{dimColor:!0,children:tN(n,i)})]})]})}u(uO,"FinishedFacts");function oO({run:n,detail:i,participant:o,now:c,columns:E}){let m=n.startedAt===void 0?Number.NaN:Date.parse(n.startedAt),h=n.updatedAt===void 0?Number.NaN:c-Date.parse(n.updatedAt),Q=[o?.thoughts===void 0?void 0:`${o.thoughts} thoughts`,o?.actions===void 0?void 0:`${o.actions} actions`].filter(Boolean).join(" \xB7 ");return(0,sA.jsxs)(ht,{marginTop:1,flexDirection:"column",width:E,children:[(0,sA.jsxs)(rt,{dimColor:!0,children:[Number.isFinite(m)?`started ${rO(m)}`:"start time not recorded",Number.isFinite(h)?` \xB7 quiet ${Ri(h)}`:"",Q===""?"":` \xB7 ${Q} captured`]}),(0,sA.jsxs)(ht,{children:[(0,sA.jsx)(rt,{dimColor:!0,children:n.estimatedCostUsd===void 0&&o?.estimatedCostUsd===void 0?"cost unknown \u2014 it ended before pricing itself":tN(n,o)}),(0,sA.jsx)(rt,{color:be.warn,children:" sandboxes may still be running"})]})]})}u(oO,"InterruptedFacts");function tN(n,i){let o=n.estimatedCostUsd===void 0?i?.estimatedCostUsd:n.estimatedCostUsd;return o===void 0?"cost not recorded":o===null?"cost declared absent":`~$${o.toFixed(2)} estimated`}u(tN,"costLine");function rO(n){let i=new Date(n),o=u(c=>String(c).padStart(2,"0"),"pad");return`${o(i.getUTCHours())}:${o(i.getUTCMinutes())}`}u(rO,"clockTime");var eN=xt(Ue(),1);var Je=xt(ha(),1);function nN({rows:n,selected:i,columns:o,viewport:c,unattributed:E,tick:m,liveParticipants:h,now:Q,initialized:y,peerSelected:N,liveTotal:x,initArmed:w,actionNote:b}){if(n.length===0)return y?(0,Je.jsxs)(ht,{flexDirection:"column",children:[(0,Je.jsx)(rt,{children:"no labs here yet"}),(0,Je.jsxs)(ht,{marginTop:1,flexDirection:"column",children:[(0,Je.jsx)(rt,{dimColor:!0,children:"a lab is a study: who to send, to what, and what counts as done."}),(0,Je.jsx)(rt,{dimColor:!0,children:"write one in humanish/labs/, or `humanish init` for three to start from."})]})]}):(0,Je.jsxs)(ht,{flexDirection:"column",children:[(0,Je.jsx)(rt,{children:"this directory is not a humanish project"}),(0,Je.jsxs)(ht,{marginTop:1,flexDirection:"column",children:[(0,Je.jsx)(rt,{dimColor:!0,children:"humanish studies live in a project: a humanish/ directory of labs and"}),(0,Je.jsx)(rt,{dimColor:!0,children:"personas beside the app they study."})]}),(0,Je.jsxs)(ht,{marginTop:1,children:[(0,Je.jsxs)(rt,{...rA(be.accent),bold:!0,children:[Ni(!0)," Set up humanish here"]}),(0,Je.jsx)(ht,{flexGrow:1}),(0,Je.jsx)(rt,{dimColor:!0,children:w?"\u23CE again to confirm \xB7 esc cancel":"writes humanish/ and updates package.json"})]}),b===void 0?null:(0,Je.jsx)(ht,{marginTop:1,children:(0,Je.jsx)(rt,{dimColor:!0,children:b})})]});let L=fE({total:n.length,selected:i,viewport:c});return(0,Je.jsxs)(ht,{flexDirection:"column",children:[L.start>0?(0,Je.jsxs)(rt,{dimColor:!0,children:[" \u2191 ",L.start," more"]}):null,n.slice(L.start,L.end).map((j,q)=>{let F=L.start+q,ft=F>0?n[F-1]:void 0,_t=j.runs===0&&ft!==void 0&&ft.runs>0;return(0,Je.jsxs)(eN.default.Fragment,{children:[_t?(0,Je.jsx)(ht,{marginTop:1,children:(0,Je.jsxs)(rt,{dimColor:!0,children:[" ","not yet run"]})}):null,(0,Je.jsx)(cO,{row:j,columns:o,active:F===i,tick:m,liveParticipants:h,now:Q})]},j.key)}),L.end<n.length?(0,Je.jsxs)(rt,{dimColor:!0,children:[" \u2193 ",n.length-L.end," more"]}):null,(0,Je.jsxs)(ht,{marginTop:1,children:[(0,Je.jsxs)(rt,{...rA(N?be.accent:void 0),bold:N,dimColor:!N,children:[Ni(N)," All runs"]}),x>0?(0,Je.jsxs)(rt,{color:be.ok,children:[" ",x," working"]}):null]}),E>0?(0,Je.jsx)(ht,{marginTop:1,children:(0,Je.jsxs)(rt,{dimColor:!0,children:[E," ",E===1?"run":"runs"," with no lab"]})}):null,(0,Je.jsxs)(ht,{marginTop:1,width:o,children:[(0,Je.jsx)(rt,{...rA(be.accent),children:"\u25B8"}),(0,Je.jsx)(rt,{dimColor:!0,wrap:"truncate-end",children:` ${sO(n[i],N)}`})]})]})}u(nN,"LabsScreen");function sO(n,i){if(i)return"every run in this project, newest first \u2014 across every lab";if(n===void 0)return"";let o=n.description?.split(/(?<=[.!?])\s/)[0]?.trim();return o!==void 0&&o.length>0?o:"no description in the manifest"}u(sO,"describe");function cO({row:n,columns:i,active:o,tick:c,liveParticipants:E,now:m}){let h=n.liveRuns[0],Q=h===void 0?dh(n):fO(n,h,E.get(h.runId),m),y=Math.max(10,Math.floor(i/2)-2),N=Q.length<=y?Q:gO(n),x=Math.max(8,i-N.length-6);return(0,Je.jsxs)(ht,{width:i,children:[(0,Je.jsxs)(rt,{...rA(o?be.accent:void 0),bold:o,children:[Ni(o)," "]}),(0,Je.jsxs)(rt,{...gE({liveness:h===void 0?"finished":"running"}),children:[h===void 0?" ":$m(c)," "]}),(0,Je.jsx)(rt,{...rA(o?be.accent:void 0),bold:o,wrap:"truncate-end",children:jr(n.label,x)}),(0,Je.jsx)(ht,{flexGrow:1}),(0,Je.jsx)(rt,{...rA(h===void 0?void 0:be.ok),dimColor:h===void 0,children:N})]})}u(cO,"LabRowView");function fO(n,i,o,c){let E=i.startedAt===void 0?Number.NaN:Date.parse(i.startedAt),m=Number.isFinite(E)?EO(c-E):void 0,h=o??"starting\u2026",Q=n.live>1?` +${n.live-1}`:"";return m===void 0?`${h}${Q}`:`${h} \xB7 ${m}${Q}`}u(fO,"liveStatus");function gO(n){return n.live>0?`${n.live} running`:n.runs===0?n.declared?"never run":"no runs":`${n.runs} runs`}u(gO,"fallbackStatus");function EO(n){if(!Number.isFinite(n)||n<0)return"0:00";let i=Math.floor(n/1e3),o=Math.floor(i/60),c=i%60;return o<60?`${o}:${String(c).padStart(2,"0")}`:Ri(n)}u(EO,"clockOf");var Bh=xt(Ue(),1);function AN(){let{stdout:n}=rh(),i=u(()=>({columns:Math.max(20,n.columns||80),rows:Math.max(6,n.rows||24)}),"read"),[o,c]=(0,Bh.useState)(i);return(0,Bh.useEffect)(()=>{let E=u(()=>{c(h=>{let Q=i();return Q.columns===h.columns&&Q.rows===h.rows?h:Q})},"update");n.on("resize",E);let m=setTimeout(E,0);return()=>{n.off("resize",E),clearTimeout(m)}},[n]),o}u(AN,"useTerminalSize");var bi=xt(ha(),1),dO=6,aN=2e3,mO=5e3,pO=100,L0=400,CO=120;function uN({options:n,onReady:i,now:o,tick:c}){let{exit:E}=U0(),m=AN(),[h,Q]=(0,Xe.useReducer)(F2,void 0,J2),[y,N]=(0,Xe.useState)(void 0),[x,w]=(0,Xe.useState)(void 0),[b,L]=(0,Xe.useState)(void 0),[j,q]=(0,Xe.useState)(void 0),[F,ft]=(0,Xe.useState)(void 0),[_t,Vt]=(0,Xe.useState)(void 0),[Gt,pe]=(0,Xe.useState)(void 0),[Ce,Yt]=(0,Xe.useState)(void 0),[wt,pt]=(0,Xe.useState)(void 0),[Lt,ce]=(0,Xe.useState)(!1),[Dt,rn]=(0,Xe.useState)(void 0),[He,ct]=(0,Xe.useState)(0),Mt=c??He,[Jt,Nt]=(0,Xe.useState)(void 0),[ue,sn]=(0,Xe.useState)(new Map),tt=(0,Xe.useMemo)(()=>n.capabilities.readProjectState(n.cwd),[n]),Ot=o??Date.now(),oe=(0,Xe.useRef)(void 0),ie=(0,Xe.useRef)({name:"labs"});(0,Xe.useEffect)(()=>{let Bt=!1,Kt=u(async()=>{try{let[$t,Ke]=await Promise.all([n.capabilities.readRunIndex(n.cwd),n.capabilities.listLabs(n.cwd)]);if(Bt)return;w(void 0),N(lN($t,Ke.labs))}catch($t){if(Bt)return;w($t instanceof Error?$t.message:String($t))}},"read");Kt();let It=setInterval(()=>{Kt()},aN);return It.unref?.(),()=>{Bt=!0,clearInterval(It)}},[n]),(0,Xe.useEffect)(()=>{(y!==void 0||x!==void 0)&&i?.()},[y,x,i]);let kt=Pm(h),fn=K2(h),fe=iN(kt,y);(0,Xe.useEffect)(()=>{ie.current=kt;let Bt=yO(kt,y,fn);Bt!==void 0&&(oe.current=Bt)},[kt,y,fn]),(0,Xe.useEffect)(()=>{if(y===void 0)return;let Bt=oe.current;if(Bt===void 0)return;let Kt=SO(kt,y,Bt);Kt>=0&&Kt!==fn&&Q({type:"select",index:Kt,total:iN(kt,y)})},[y,kt]);let Oe=(0,Xe.useCallback)(async(Bt,Kt)=>{if(Kt==="live"&&b!=="live"){L("live"),Vt(Date.now());return}if(Kt==="live"&&_t!==void 0&&Date.now()-_t<L0)return;L(void 0),Vt(void 0),q(void 0),ft({labKey:Bt.key,text:`starting ${Bt.name}\u2026`});let It=await n.capabilities.startRun({cwd:n.cwd,lab:Bt.name,mode:Kt});if(!It.ok){ft(void 0),q({labKey:Bt.key,text:It.error.message});return}let $t=Date.parse(It.run.launchedAt),Ke=u(On=>{if(On.pid!==It.run.pid)return!1;let GA=On.startedAt===void 0?Number.NaN:Date.parse(On.startedAt);return!Number.isFinite(GA)||!Number.isFinite($t)?!1:GA>=$t-1e3},"isOurs"),he=Date.now()+mO;for(;;){let On=await n.capabilities.readRunIndex(n.cwd),GA=On.runs.find(Ke);if(GA!==void 0){let Ia=await n.capabilities.listLabs(n.cwd);N(lN(On,Ia.labs)),ft(void 0),ie.current.name==="lab"&&ie.current.labKey===Bt.key&&Q({type:"enter",screen:{name:"run",labId:Bt.labId,runId:GA.runId}});return}if(Date.now()>=he)break;await new Promise(Ia=>setTimeout(Ia,pO))}let un=await n.capabilities.readLaunchLog(It.run.logPath);ft(void 0),q({labKey:Bt.key,text:un===""?`${Bt.name} started (pid ${It.run.pid}) but has not reported in. Check ${It.run.logPath}.`:`${Bt.name} did not report in. Its log ends:
287
287
  ${un.split(`
288
288
  `).slice(-3).join(`
289
- `)}`})},[b,_t,n]),Pt=(0,Xe.useCallback)(async(Bt,Kt)=>{if(Kt==="stop"){if(wt===void 0){pt(Date.now()),Yt("stop this run? \u23CE again to confirm \xB7 esc cancel");return}if(Date.now()-wt<L0)return;pt(void 0),Yt("stopping\u2026");let he=await n.capabilities.stopRun(n.cwd,Bt.runId);Yt(he.message);return}if(Kt==="observer"){let he=Gt?.observerPath;if(he===void 0){Yt("this run has no Observer artifact on disk");return}Yt("opening\u2026");let un=await n.capabilities.openObserver(n.cwd,he);Yt(un.message);return}if(Kt==="reclaim"){Yt("reclaiming \u2014 stopping sandboxes, keeping evidence\u2026");let he=await n.capabilities.reclaimRun(n.cwd,Bt.runId);Yt(he.ok?`reclaimed ${he.receiptCount} recorded resource${he.receiptCount===1?"":"s"}`:`could not reclaim: ${he.error?.message??"unknown"}`);return}let It=Bt.lab?.id,$t=It===void 0?void 0:y?.rows.find(he=>he.labId===It);if($t===void 0||!$t.declared){Yt("cannot run this again \u2014 its lab has no manifest here any more");return}Yt(`starting ${$t.name}\u2026`);let Ke=await n.capabilities.startRun({cwd:n.cwd,lab:$t.name,mode:Bt.mode==="live"?"live":"dry-run"});Yt(Ke.ok?`started ${$t.name} (pid ${Ke.run.pid})`:Ke.error.message)},[Gt,n,y,wt]);M0((0,Xe.useCallback)((Bt,Kt)=>{if(Lt){ce(!1),Bt==="q"&&E();return}if(Bt==="?"){ce(!0);return}if(Bt==="q"){E();return}if(Bt==="g"||Bt==="G"){b!==void 0&&(L(void 0),Vt(void 0)),Q({type:"move",delta:Bt==="g"?-fe:fe,total:fe});return}if(Kt.upArrow||Bt==="k"||Kt.downArrow||Bt==="j"){b!==void 0&&(L(void 0),Vt(void 0)),Q({type:"move",delta:Kt.upArrow||Bt==="k"?-1:1,total:fe});return}if(Kt.escape||Kt.leftArrow){if(b!==void 0){L(void 0),Vt(void 0);return}if(wt!==void 0){pt(void 0),Yt(void 0);return}if(Dt!==void 0){rn(void 0);return}Q({type:"back"});return}if(Kt.return||Kt.rightArrow){if(kt.name==="labs"&&tt.initialized===!1){if(Dt===void 0){rn(Date.now());return}if(Date.now()-Dt<L0)return;rn(void 0),(async()=>{let $t=await n.capabilities.initProject(n.cwd);Yt($t.message)})();return}if(kt.name==="run"&&y!==void 0){let $t=y.runsById.get(kt.runId);if($t!==void 0){let Ke=Ch($t,Gt)[fn];if(Ke!==void 0){Pt($t,Ke);return}}}if(kt.name==="lab"&&y!==void 0){let{row:$t,items:Ke}=EE(y,kt.labKey),he=Ke[fn];if($t!==void 0&&he?.kind==="start"){Oe($t,he.mode);return}}let It=vO(kt,y,fn);It!==void 0&&Q({type:"enter",screen:It})}},[E,fe,kt,y,fn,b,Oe,Gt,Pt,wt,Lt,Dt,tt,n])),(0,Xe.useEffect)(()=>{h.quit&&E()},[h.quit,E]);let Ee=kt.name==="run"?kt.runId:void 0;(0,Xe.useEffect)(()=>{if(Yt(void 0),pt(void 0),Ee===void 0){pe(void 0);return}let Bt=!1;pe(void 0);let Kt=u(async()=>{try{let $t=await n.capabilities.readRunDetail(n.cwd,Ee);Bt||pe($t)}catch{Bt||pe(null)}},"read");Kt();let It=setInterval(()=>{Kt()},aN);return It.unref?.(),()=>{Bt=!0,clearInterval(It)}},[Ee,n]),(0,Xe.useEffect)(()=>{if(c!==void 0)return;let Bt=setInterval(()=>ct(Kt=>Kt+1),CO);return Bt.unref?.(),()=>clearInterval(Bt)},[c]);let tA=(y?.rows??[]).flatMap(Bt=>Bt.liveRuns.map(Kt=>Kt.runId)).join(",");(0,Xe.useEffect)(()=>{if(tA===""){sn(new Map);return}let Bt=!1;return(async()=>{let Kt=tA.split(","),It=await Promise.all(Kt.map(async $t=>{let Ke=await n.capabilities.readRunDetail(n.cwd,$t).catch(()=>null);return Ke===null?null:[$t,Ke]}));Bt||sn(new Map(It.filter($t=>$t!==null)))})(),()=>{Bt=!0}},[tA,n,Mt]);let CA=kt.name==="lab"?kt.labKey:void 0,vA=CA===void 0?void 0:y?.rows.find(Bt=>Bt.key===CA)?.name;(0,Xe.useEffect)(()=>{if(vA===void 0){Nt(void 0);return}let Bt=!1;return Nt(void 0),(async()=>{let Kt=await n.capabilities.readLabSummary(n.cwd,vA,{checkKeys:!0}).catch(()=>null);Bt||Nt(Kt)})(),()=>{Bt=!0}},[vA,n]);let Be=Math.max(1,m.rows-dO),Ve=(0,Xe.useMemo)(()=>Lt?(0,bi.jsx)(G2,{columns:mh(m.columns)}):x!==void 0?(0,bi.jsxs)(rt,{color:be.bad,children:["could not read this project: ",x]}):y===void 0?(0,bi.jsx)(rt,{dimColor:!0,children:"reading project\u2026"}):DO({screen:kt,data:y,selected:fn,columns:mh(m.columns),viewport:Be,now:Ot,confirming:b,launchError:j,launchNote:F,detail:Gt,summary:Jt,liveDetails:ue,tick:Mt,initialized:tt.initialized,actionNote:Ce,initArmed:Dt!==void 0}),[Lt,x,y,kt,fn,m.columns,Be,Ot,b,j,F,Gt,Jt,ue,Mt,tt,Ce]);return(0,bi.jsx)(q2,{columns:m.columns,context:BO(kt,y,n),breadcrumb:hO(kt,y),hints:Lt?"any key returns q quit":IO(kt,y,fn,b,tt.initialized),children:Ve})}u(uN,"App");function BO(n,i,o){let c=o.cwd.split("/").filter(Boolean).pop();if(n.name==="run"&&i!==void 0){let m=i.runsById.get(n.runId),h=n.runId.split("-").pop()??n.runId;return[m?.lab?.id,h].filter(Boolean).join(" \xB7 ")}if(i===void 0)return c;let E=Nc(i).length;return E===0?c:`${c} \xB7 ${E} participant${E===1?"":"s"} working`}u(BO,"contextLine");function hO(n,i){if(n.name==="labs")return;if(n.name==="all-runs")return"\u2039 labs / all runs";if(n.name==="lab")return`\u2039 labs / ${i?.rows.find(E=>E.key===n.labKey)?.name??n.labKey}`;let o=n.labId;return o===void 0?"\u2039 labs / run":`\u2039 labs / ${o} / run`}u(hO,"breadcrumbOf");function IO(n,i,o,c,E){let m="\u2191\u2193 move";switch(n.name){case"labs":return(i?.rows.length??0)>0?`${m} \u23CE open ? keys q quit`:E===!1?"\u23CE set up humanish here ? keys q quit":"? keys q quit";case"lab":{if(c!==void 0)return"\u21B5 confirm \xB7 esc cancel";let Q=(i===void 0?void 0:EE(i,n.labKey).items[o])?.kind==="start"?"\u23CE start":"\u23CE open run";return`${m} ${Q} esc back ? keys q quit`}case"all-runs":return`${m} \u23CE open run esc back ? keys q quit`;default:{let h=i===void 0?void 0:i.runsById.get(n.name==="run"?n.runId:"");return h!==void 0&&Ch(h,void 0).length>0?`${m} \u23CE select esc back ? keys q quit`:"esc back ? keys q quit"}}}u(IO,"keyHints");function lN(n,i){let{rows:o,unattributed:c}=Y2(i.map(h=>({id:h.id,...h.title===void 0?{}:{title:h.title},...h.description===void 0?{}:{description:h.description},path:h.path,origin:h.origin})),n.runs),E=new Map,m=new Map;for(let h of n.runs){m.set(h.runId,h);let Q=h.lab?.id;if(Q===void 0)continue;let y=E.get(Q);y===void 0?E.set(Q,[h]):y.push(h)}return{rows:o,unattributed:c,runsByLab:E,runsById:m,unreadable:n.unreadable}}u(lN,"project");function Nc(n){let i=new Set,o=[];for(let c of n.rows.flatMap(E=>E.liveRuns))i.has(c.runId)||(i.add(c.runId),o.push(c));return o}u(Nc,"liveRunsOf");function QO(n,i){return i===void 0?"":n.rows.find(o=>o.labId===i)?.label??i}u(QO,"labelForLab");function EE(n,i){let o=n.rows.find(c=>c.key===i);return o===void 0?{items:[]}:{row:o,items:ph(n.runsByLab.get(o.labId)??[],o.declared)}}u(EE,"itemsForLab");function iN(n,i){if(i===void 0)return 0;switch(n.name){case"labs":return i.rows.length+1;case"all-runs":return Nc(i).length;case"lab":return EE(i,n.labKey).items.length;default:return 0}}u(iN,"countRows");function yO(n,i,o){if(i!==void 0){if(n.name==="labs")return i.rows[o]?.key??"peer:all-runs";if(n.name==="all-runs")return Nc(i)[o]?.runId;if(n.name==="lab"){let c=EE(i,n.labKey).items[o];return c===void 0?void 0:c.kind==="start"?"start":`run:${c.run.runId}`}}}u(yO,"identityOf");function SO(n,i,o){return n.name==="labs"?o==="peer:all-runs"?i.rows.length:i.rows.findIndex(c=>c.key===o):n.name==="all-runs"?Nc(i).findIndex(c=>c.runId===o):n.name==="lab"?EE(i,n.labKey).items.findIndex(c=>c.kind==="start"?o==="start":`run:${c.run.runId}`===o):-1}u(SO,"indexOfIdentity");function vO(n,i,o){if(i!==void 0){if(n.name==="labs"){let c=i.rows[o];return c===void 0?o===i.rows.length?{name:"all-runs"}:void 0:{name:"lab",labKey:c.key}}if(n.name==="all-runs"){let c=Nc(i)[o];return c===void 0?void 0:{name:"run",...c.lab?.id===void 0?{}:{labId:c.lab.id},runId:c.runId}}if(n.name==="lab"){let{row:c,items:E}=EE(i,n.labKey),m=E[o];return c===void 0||m===void 0||m.kind!=="run"?void 0:{name:"run",labId:c.labId,runId:m.run.runId}}}}u(vO,"openSelected");function DO(n){let{screen:i,data:o,selected:c,columns:E,viewport:m,now:h,confirming:Q,launchError:y,launchNote:N,detail:x}=n,{summary:w,liveDetails:b,tick:L,initialized:j,actionNote:q}=n;if(i.name==="labs")return(0,bi.jsx)(nN,{rows:o.rows,selected:c,columns:E,viewport:m,unattributed:o.unattributed.length,tick:L,initialized:j,peerSelected:c===o.rows.length,liveTotal:Nc(o).length,...n.initArmed===!0?{initArmed:!0}:{},...n.actionNote===void 0?{}:{actionNote:n.actionNote},liveParticipants:new Map([...b.entries()].map(([ft,_t])=>{let Vt=_t.participants[0]?.personaId??_t.participants[0]?.label;return Vt===void 0?null:[ft,Vt]}).filter(ft=>ft!==null)),now:h});if(i.name==="lab"){let ft=o.rows.find(_t=>_t.key===i.labKey);return ft===void 0?(0,bi.jsx)(rt,{color:be.warn,children:"that lab is no longer in this project"}):(0,bi.jsx)(P2,{row:ft,summary:w,runs:o.runsByLab.get(ft.labId)??[],liveDetail:b.get(ft.liveRuns[0]?.runId??""),selected:c,columns:E,viewport:m,now:h,tick:L,canStart:ft.declared,confirming:Q,launchError:y?.labKey===ft.key?y.text:void 0,launchNote:N?.labKey===ft.key?N.text:void 0})}if(i.name==="all-runs"){let ft=Nc(o);return(0,bi.jsx)(V2,{runs:ft,details:b,labels:new Map(ft.map(_t=>[_t.runId,QO(o,_t.lab?.id)])),expected:new Map(o.rows.map(_t=>_t.liveExpectation.medianDurationMs===void 0?null:[_t.labId,_t.liveExpectation.medianDurationMs]).filter(_t=>_t!==null)),selected:c,columns:E,viewport:m,tick:L,now:h})}let F=o.runsById.get(i.runId);return F===void 0?(0,bi.jsx)(rt,{color:be.warn,children:"that run is no longer on disk"}):(0,bi.jsx)($2,{run:F,detail:x,columns:E,viewport:m,selected:c,tick:L,now:h,actionNote:q})}u(DO,"renderScreen");var oN=xt(ha(),1);function RO(n){if(Rc())return n;let i=Object.create(n);return i.write=((o,...c)=>n.write(typeof o=="string"?M2(o):o,...c)),i}u(RO,"encodeFor");var f7=u(async n=>{let i=u(()=>{},"ready"),o=new Promise(m=>{i=m}),c=RO(n.stdout),E=T0((0,oN.jsx)(uN,{options:n,onReady:i}),{stdin:n.stdin,stdout:c,patchConsole:!1,exitOnCtrlC:!0,interactive:!0});n.exitAfterFirstFrame===!0&&(await o,await new Promise(m=>setImmediate(m)),E.unmount());try{return await E.waitUntilExit(),0}catch(m){return E.unmount(),n.stdout.write(`humanish tui exited: ${m instanceof Error?m.message:String(m)}
289
+ `)}`})},[b,_t,n]),Pt=(0,Xe.useCallback)(async(Bt,Kt)=>{if(Kt==="stop"){if(wt===void 0){pt(Date.now()),Yt("stop this run? \u23CE again to confirm \xB7 esc cancel");return}if(Date.now()-wt<L0)return;pt(void 0),Yt("stopping\u2026");let he=await n.capabilities.stopRun(n.cwd,Bt.runId);Yt(he.message);return}if(Kt==="observer"){let he=Gt?.observerPath;if(he===void 0){Yt("this run has no Observer artifact on disk");return}Yt("opening\u2026");let un=await n.capabilities.openObserver(n.cwd,he);Yt(un.message);return}if(Kt==="reclaim"){Yt("reclaiming \u2014 stopping sandboxes, keeping evidence\u2026");let he=await n.capabilities.reclaimRun(n.cwd,Bt.runId);Yt(he.ok?`reclaimed ${he.receiptCount} recorded resource${he.receiptCount===1?"":"s"}`:`could not reclaim: ${he.error?.message??"unknown"}`);return}let It=Bt.lab?.id,$t=It===void 0?void 0:y?.rows.find(he=>he.labId===It);if($t===void 0||!$t.declared){Yt("cannot run this again \u2014 its lab has no manifest here any more");return}Yt(`starting ${$t.name}\u2026`);let Ke=await n.capabilities.startRun({cwd:n.cwd,lab:$t.name,mode:Bt.mode==="live"?"live":"dry-run"});Yt(Ke.ok?`started ${$t.name} (pid ${Ke.run.pid})`:Ke.error.message)},[Gt,n,y,wt]);M0((0,Xe.useCallback)((Bt,Kt)=>{if(Lt){ce(!1),Bt==="q"&&E();return}if(Bt==="?"){ce(!0);return}if(Bt==="q"){E();return}if(Bt==="g"||Bt==="G"){b!==void 0&&(L(void 0),Vt(void 0)),Q({type:"move",delta:Bt==="g"?-fe:fe,total:fe});return}if(Kt.upArrow||Bt==="k"||Kt.downArrow||Bt==="j"){b!==void 0&&(L(void 0),Vt(void 0)),Q({type:"move",delta:Kt.upArrow||Bt==="k"?-1:1,total:fe});return}if(Kt.escape||Kt.leftArrow){if(b!==void 0){L(void 0),Vt(void 0);return}if(wt!==void 0){pt(void 0),Yt(void 0);return}if(Dt!==void 0){rn(void 0);return}Q({type:"back"});return}if(Kt.return||Kt.rightArrow){if(kt.name==="labs"&&tt.initialized===!1){if(Dt===void 0){rn(Date.now());return}if(Date.now()-Dt<L0)return;rn(void 0),(async()=>{let $t=await n.capabilities.initProject(n.cwd);Yt($t.message)})();return}if(kt.name==="run"&&y!==void 0){let $t=y.runsById.get(kt.runId);if($t!==void 0){let Ke=Ch($t,Gt)[fn];if(Ke!==void 0){Pt($t,Ke);return}}}if(kt.name==="lab"&&y!==void 0){let{row:$t,items:Ke}=EE(y,kt.labKey),he=Ke[fn];if($t!==void 0&&he?.kind==="start"){Oe($t,he.mode);return}}let It=vO(kt,y,fn);It!==void 0&&Q({type:"enter",screen:It})}},[E,fe,kt,y,fn,b,Oe,Gt,Pt,wt,Lt,Dt,tt,n])),(0,Xe.useEffect)(()=>{h.quit&&E()},[h.quit,E]);let Ee=kt.name==="run"?kt.runId:void 0;(0,Xe.useEffect)(()=>{if(Yt(void 0),pt(void 0),Ee===void 0){pe(void 0);return}let Bt=!1;pe(void 0);let Kt=u(async()=>{try{let $t=await n.capabilities.readRunDetail(n.cwd,Ee);Bt||pe($t)}catch{Bt||pe(null)}},"read");Kt();let It=setInterval(()=>{Kt()},aN);return It.unref?.(),()=>{Bt=!0,clearInterval(It)}},[Ee,n]),(0,Xe.useEffect)(()=>{if(c!==void 0)return;let Bt=setInterval(()=>ct(Kt=>Kt+1),CO);return Bt.unref?.(),()=>clearInterval(Bt)},[c]);let tA=(y?.rows??[]).flatMap(Bt=>Bt.liveRuns.map(Kt=>Kt.runId)).join(",");(0,Xe.useEffect)(()=>{if(tA===""){sn(new Map);return}let Bt=!1;return(async()=>{let Kt=tA.split(","),It=await Promise.all(Kt.map(async $t=>{let Ke=await n.capabilities.readRunDetail(n.cwd,$t).catch(()=>null);return Ke===null?null:[$t,Ke]}));Bt||sn(new Map(It.filter($t=>$t!==null)))})(),()=>{Bt=!0}},[tA,n,Mt]);let CA=kt.name==="lab"?kt.labKey:void 0,vA=CA===void 0?void 0:y?.rows.find(Bt=>Bt.key===CA)?.name;(0,Xe.useEffect)(()=>{if(vA===void 0){Nt(void 0);return}let Bt=!1;return Nt(void 0),(async()=>{let Kt=await n.capabilities.readLabSummary(n.cwd,vA,{checkKeys:!0}).catch(()=>null);Bt||Nt(Kt)})(),()=>{Bt=!0}},[vA,n]);let Be=Math.max(1,m.rows-dO),Ve=(0,Xe.useMemo)(()=>Lt?(0,bi.jsx)(G2,{columns:mh(m.columns)}):x!==void 0?(0,bi.jsxs)(rt,{color:be.bad,children:["could not read this project: ",x]}):y===void 0?(0,bi.jsx)(rt,{dimColor:!0,children:"reading project\u2026"}):DO({screen:kt,data:y,selected:fn,columns:mh(m.columns),viewport:Be,now:Ot,confirming:b,launchError:j,launchNote:F,detail:Gt,summary:Jt,liveDetails:ue,tick:Mt,initialized:tt.initialized,actionNote:Ce,initArmed:Dt!==void 0}),[Lt,x,y,kt,fn,m.columns,Be,Ot,b,j,F,Gt,Jt,ue,Mt,tt,Ce]);return(0,bi.jsx)(q2,{columns:m.columns,context:BO(kt,y,n),breadcrumb:hO(kt,y),hints:Lt?"any key returns q quit":IO(kt,y,fn,b,tt.initialized),children:Ve})}u(uN,"App");function BO(n,i,o){let c=o.cwd.split("/").filter(Boolean).pop();if(n.name==="run"&&i!==void 0){let m=i.runsById.get(n.runId),h=n.runId.split("-").pop()??n.runId;return[m?.lab?.id,h].filter(Boolean).join(" \xB7 ")}if(i===void 0)return c;let E=Nc(i).length;return E===0?c:`${c} \xB7 ${E} participant${E===1?"":"s"} working`}u(BO,"contextLine");function hO(n,i){if(n.name==="labs")return;if(n.name==="all-runs")return"\u2039 labs / all runs";if(n.name==="lab")return`\u2039 labs / ${i?.rows.find(E=>E.key===n.labKey)?.name??n.labKey}`;let o=n.labId;return o===void 0?"\u2039 labs / run":`\u2039 labs / ${o} / run`}u(hO,"breadcrumbOf");function IO(n,i,o,c,E){let m="\u2191\u2193 move";switch(n.name){case"labs":return(i?.rows.length??0)>0?`${m} \u23CE open ? keys q quit`:E===!1?"\u23CE set up humanish here ? keys q quit":"? keys q quit";case"lab":{if(c!==void 0)return"\u21B5 confirm \xB7 esc cancel";let Q=(i===void 0?void 0:EE(i,n.labKey).items[o])?.kind==="start"?"\u23CE start":"\u23CE open run";return`${m} ${Q} esc back ? keys q quit`}case"all-runs":return`${m} \u23CE open run esc back ? keys q quit`;default:{let h=i===void 0?void 0:i.runsById.get(n.name==="run"?n.runId:"");return h!==void 0&&Ch(h,void 0).length>0?`${m} \u23CE select esc back ? keys q quit`:"esc back ? keys q quit"}}}u(IO,"keyHints");function lN(n,i){let{rows:o,unattributed:c}=Y2(i.map(h=>({id:h.id,...h.title===void 0?{}:{title:h.title},...h.description===void 0?{}:{description:h.description},path:h.path,origin:h.origin})),n.runs),E=new Map,m=new Map;for(let h of n.runs){m.set(h.runId,h);let Q=h.lab?.id;if(Q===void 0)continue;let y=E.get(Q);y===void 0?E.set(Q,[h]):y.push(h)}return{rows:o,unattributed:c,runsByLab:E,runsById:m,unreadable:n.unreadable}}u(lN,"project");function Nc(n){let i=new Set,o=[];for(let c of n.rows.flatMap(E=>E.liveRuns))i.has(c.runId)||(i.add(c.runId),o.push(c));return o}u(Nc,"liveRunsOf");function QO(n,i){return i===void 0?"":n.rows.find(o=>o.labId===i)?.label??i}u(QO,"labelForLab");function EE(n,i){let o=n.rows.find(c=>c.key===i);return o===void 0?{items:[]}:{row:o,items:ph(n.runsByLab.get(o.labId)??[],o.declared)}}u(EE,"itemsForLab");function iN(n,i){if(i===void 0)return 0;switch(n.name){case"labs":return i.rows.length+1;case"all-runs":return Nc(i).length;case"lab":return EE(i,n.labKey).items.length;default:return 0}}u(iN,"countRows");function yO(n,i,o){if(i!==void 0){if(n.name==="labs")return i.rows[o]?.key??"peer:all-runs";if(n.name==="all-runs")return Nc(i)[o]?.runId;if(n.name==="lab"){let c=EE(i,n.labKey).items[o];return c===void 0?void 0:c.kind==="start"?`start:${c.mode}`:`run:${c.run.runId}`}}}u(yO,"identityOf");function SO(n,i,o){return n.name==="labs"?o==="peer:all-runs"?i.rows.length:i.rows.findIndex(c=>c.key===o):n.name==="all-runs"?Nc(i).findIndex(c=>c.runId===o):n.name==="lab"?EE(i,n.labKey).items.findIndex(c=>c.kind==="start"?o===`start:${c.mode}`:`run:${c.run.runId}`===o):-1}u(SO,"indexOfIdentity");function vO(n,i,o){if(i!==void 0){if(n.name==="labs"){let c=i.rows[o];return c===void 0?o===i.rows.length?{name:"all-runs"}:void 0:{name:"lab",labKey:c.key}}if(n.name==="all-runs"){let c=Nc(i)[o];return c===void 0?void 0:{name:"run",...c.lab?.id===void 0?{}:{labId:c.lab.id},runId:c.runId}}if(n.name==="lab"){let{row:c,items:E}=EE(i,n.labKey),m=E[o];return c===void 0||m===void 0||m.kind!=="run"?void 0:{name:"run",labId:c.labId,runId:m.run.runId}}}}u(vO,"openSelected");function DO(n){let{screen:i,data:o,selected:c,columns:E,viewport:m,now:h,confirming:Q,launchError:y,launchNote:N,detail:x}=n,{summary:w,liveDetails:b,tick:L,initialized:j,actionNote:q}=n;if(i.name==="labs")return(0,bi.jsx)(nN,{rows:o.rows,selected:c,columns:E,viewport:m,unattributed:o.unattributed.length,tick:L,initialized:j,peerSelected:c===o.rows.length,liveTotal:Nc(o).length,...n.initArmed===!0?{initArmed:!0}:{},...n.actionNote===void 0?{}:{actionNote:n.actionNote},liveParticipants:new Map([...b.entries()].map(([ft,_t])=>{let Vt=_t.participants[0]?.personaId??_t.participants[0]?.label;return Vt===void 0?null:[ft,Vt]}).filter(ft=>ft!==null)),now:h});if(i.name==="lab"){let ft=o.rows.find(_t=>_t.key===i.labKey);return ft===void 0?(0,bi.jsx)(rt,{color:be.warn,children:"that lab is no longer in this project"}):(0,bi.jsx)(P2,{row:ft,summary:w,runs:o.runsByLab.get(ft.labId)??[],liveDetail:b.get(ft.liveRuns[0]?.runId??""),selected:c,columns:E,viewport:m,now:h,tick:L,canStart:ft.declared,confirming:Q,launchError:y?.labKey===ft.key?y.text:void 0,launchNote:N?.labKey===ft.key?N.text:void 0})}if(i.name==="all-runs"){let ft=Nc(o);return(0,bi.jsx)(V2,{runs:ft,details:b,labels:new Map(ft.map(_t=>[_t.runId,QO(o,_t.lab?.id)])),expected:new Map(o.rows.map(_t=>_t.liveExpectation.medianDurationMs===void 0?null:[_t.labId,_t.liveExpectation.medianDurationMs]).filter(_t=>_t!==null)),selected:c,columns:E,viewport:m,tick:L,now:h})}let F=o.runsById.get(i.runId);return F===void 0?(0,bi.jsx)(rt,{color:be.warn,children:"that run is no longer on disk"}):(0,bi.jsx)($2,{run:F,detail:x,columns:E,viewport:m,selected:c,tick:L,now:h,actionNote:q})}u(DO,"renderScreen");var oN=xt(ha(),1);function RO(n){if(Rc())return n;let i=Object.create(n);return i.write=((o,...c)=>n.write(typeof o=="string"?M2(o):o,...c)),i}u(RO,"encodeFor");var f7=u(async n=>{let i=u(()=>{},"ready"),o=new Promise(m=>{i=m}),c=RO(n.stdout),E=T0((0,oN.jsx)(uN,{options:n,onReady:i}),{stdin:n.stdin,stdout:c,patchConsole:!1,exitOnCtrlC:!0,interactive:!0});n.exitAfterFirstFrame===!0&&(await o,await new Promise(m=>setImmediate(m)),E.unmount());try{return await E.waitUntilExit(),0}catch(m){return E.unmount(),n.stdout.write(`humanish tui exited: ${m instanceof Error?m.message:String(m)}
290
290
  `),1}},"startTui");export{f7 as startTui};
291
291
  /*! Bundled license information:
292
292
 
@@ -47,7 +47,7 @@ export interface TuiCapabilities {
47
47
  /** Whether this directory is a humanish project — an empty project and a wrong directory are
48
48
  * different problems and must not share a screen. */
49
49
  readProjectState(cwd: string): TuiProjectState;
50
- /** Hand off to the Observer: a terminal cannot show screenshots, and that surface can. */
50
+ /** Open the selected run in the session-owned local evidence server; closes when the TUI exits. */
51
51
  openObserver(cwd: string, observerPath: string): Promise<TuiActionResult>;
52
52
  /** Stop the sandboxes an interrupted run left behind, keeping its evidence. */
53
53
  reclaimRun(cwd: string, runId: string): Promise<ReclaimResult>;
@@ -0,0 +1,130 @@
1
+ # Watching and reviewing evidence
2
+
3
+ Observer shows a study's participants, their recorded screens, and the events
4
+ that explain those screens. The grid, player and exported HTML use the same
5
+ current renderer. Screens fit their actual aspect ratio; a portrait capture is
6
+ never cropped to fill a landscape tile.
7
+
8
+ ## Choose the right entry point
9
+
10
+ `humanish watch <lab>` starts a study and keeps its viewer attached. The process
11
+ that owns a hosted desktop can offer its live stream while it runs. `--no-open`
12
+ keeps that attachment without launching a browser. `--json` is the machine-result
13
+ path and does not keep an interactive stream server attached.
14
+
15
+ `humanish observe --run <id>` opens an existing study without starting another.
16
+ The TUI's **Open Observer** action does the same through its session-owned
17
+ loopback library. These entry points follow saved captures; they cannot recover
18
+ live desktop credentials owned by another process. Keep the owning CLI or TUI
19
+ open while using its viewer. Exported HTML is an independent offline recording.
20
+
21
+ ## Read status without guessing
22
+
23
+ The top band separates the run's current status from evidence freshness. A
24
+ finished study never uses its execution mode (`live`, as opposed to `dry-run`)
25
+ as a claim that it is still running. When updates fail, the last usable evidence
26
+ stays visible, the band reports the failure, and **Retry** requests another
27
+ snapshot. A stale process heartbeat does not prove that the process died.
28
+
29
+ A player's **Live desktop** label identifies what it displays. An iframe loading
30
+ is not a connection-health signal: the provider owns that connection. **Reload
31
+ stream** reloads the desktop viewer without restarting a participant. **Latest capture**
32
+ follows the latest saved screenshot without a desktop stream. **Replay** means
33
+ the researcher has chosen a recorded moment. Offline exports say so explicitly.
34
+
35
+ Freshness uses recorded activity and capture timestamps, not the time the server
36
+ reserialized the same data. Browser tabs poll less frequently while hidden and
37
+ refresh when visible again. Requests are serialized, bounded by a timeout and
38
+ cancelled when the viewer closes.
39
+
40
+ ## Work through a study
41
+
42
+ Previews share a height and wrap into rows; desktop cards are wider and portrait
43
+ cards narrower. Complete screens remain contained when the available width is
44
+ smaller than a preview. Sparse rows keep their chosen size. Open **View and filter
45
+ participants** in the top right to search, change **Preview size**, or enter
46
+ **Monitor** for a larger evidence area. **Exit monitor** stays beside the run status.
47
+ Open a card's **Participant details** to **Pin** it at the beginning. Ordering changes only
48
+ when the researcher changes it. Large studies have reachable participant pages.
49
+ At most four desktop previews attach in the grid; allocation prioritizes screens
50
+ that are actually visible, with the hovered or keyboard-focused participant taking
51
+ priority. Other cards show their latest saved capture.
52
+
53
+ Each card follows its screen's aspect ratio without a minimum width that adds
54
+ portrait gutters. Its 44px caption holds the participant name, a short preview
55
+ source (**Live**, **Capture**, or **Snapshot**) or finished outcome, and a stable
56
+ **Participant details** button. Labels and controls never cover captured pixels.
57
+ Details contains labeled **Pin** and **Compare** toggles, the full source and
58
+ freshness, final messages, recorded notices, duration and dimensions. Historical
59
+ notices do not turn the details control into a warning; exceptional outcomes
60
+ remain in the caption. Comparison permits three participants and explains the
61
+ limit while keeping selected participants removable.
62
+
63
+ Icon buttons use Lucide glyphs, accessible names, hover/focus hints and visible
64
+ keyboard focus. Touch targets expand to 44px. Popovers have explicit close buttons;
65
+ Escape dismisses a hint without navigating the player. These conventions follow
66
+ [Carbon icon usage](https://carbondesignsystem.com/elements/icons/usage/),
67
+ [ARIA toggle-button semantics](https://www.w3.org/WAI/ARIA/apg/patterns/button/),
68
+ and [WCAG target sizing](https://www.w3.org/WAI/WCAG22/Understanding/target-size-minimum.html).
69
+ The bookmark icon in the top bar opens **Saved moments**; recalling an available
70
+ moment closes the popover, while an unavailable moment leaves the error visible.
71
+
72
+ Opening a participant provides elapsed-time seeking, the original frame number,
73
+ playback speed, **Fit**, **Actual size**, zoom/pan and fullscreen. The inspector can
74
+ be hidden or resized. Click markers use the recorded desktop coordinate system
75
+ and the image's rendered rectangle, including at zoomed sizes.
76
+
77
+ Arrow keys, mouse controls, same-participant links, browser navigation and reload
78
+ agree about the selected moment. Seeking leaves live mode. Pausing on the newest
79
+ frame stays paused when new evidence arrives; **Go live** explicitly resumes
80
+ following. A missing addressed moment is reported instead of silently replaced.
81
+
82
+ Screenshot playback is sparse evidence, not a video recording. Capture gaps stay
83
+ visible. **Skip waits** compresses intervals associated with recorded waits and
84
+ reports the skipped duration. **Next action**, **Next finding**, activity filters
85
+ and grouped waits make long traces easier to inspect without deleting events.
86
+ Older unstamped recordings use labeled estimated pacing. Long feeds, filmstrips,
87
+ terminal output and event lists stay bounded while earlier/later content remains
88
+ reachable.
89
+
90
+ **Copy moment link** creates a frame address; denied clipboard access exposes a
91
+ selectable link. **Original frame** opens/downloads the captured raster. **Saved
92
+ moments** keeps bounded run/participant/frame identifiers in this browser. It does
93
+ not save credentials, publish annotations or change the evidence bundle. Storage
94
+ failure leaves the controls usable for the current visit.
95
+
96
+ ## Compare recorded evidence
97
+
98
+ Select participants and open **Compare selected**. Capture-time alignment chooses
99
+ the latest frame at or before the cursor, never a future frame. Before/after
100
+ coverage and each frame's age are explicit. Clock differences between desktops
101
+ remain an uncertainty; matching timestamps alone do not establish causality.
102
+
103
+ Elapsed-time alignment starts each participant at its first capture and compares
104
+ progress rather than simultaneous events. Missing or descending timestamps cannot
105
+ be used as a shared clock. Another run can be loaded from the available library
106
+ as recorded evidence. The address retains the chosen runs, participants, clock
107
+ and cursor so reopening it restores the comparison.
108
+
109
+ ## Boundaries and checks
110
+
111
+ Only the attached process can grant a desktop iframe its own origin for provider
112
+ module loading. Stored grants are removed, exports contain no runtime grants,
113
+ and every local viewer response refuses to be framed. Generic imported embeds
114
+ retain an opaque sandbox. Artifact and history links cannot introduce arbitrary
115
+ URL schemes or traverse outside their evidence roots.
116
+
117
+ `pnpm observer:browser:proof` exercises generated synthetic evidence in the
118
+ production artifact. `pnpm observer:iframe:proof`, after `pnpm build`, checks the
119
+ actual serving boundary against direct, redirected and scripted iframe attacks.
120
+ CI retains their screenshots and JSON receipts. These Chromium checks do not
121
+ claim physical-device or non-Chromium coverage. Actual provider checks and their
122
+ resource-cleanup receipts are separate acceptance evidence.
123
+
124
+ Interaction references rechecked September 8, 2026:
125
+ [Base UI popover](https://base-ui.com/react/components/popover),
126
+ [React state structure](https://react.dev/learn/choosing-the-state-structure),
127
+ [WAI-ARIA tabs](https://www.w3.org/WAI/ARIA/apg/patterns/tabs/),
128
+ [iframe sandboxing](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/iframe),
129
+ [Playwright Trace Viewer](https://playwright.dev/docs/trace-viewer), and
130
+ [PostHog recording controls](https://posthog.com/docs/session-replay/how-to-watch-recordings).