humanish 0.80.0 → 0.82.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 (69) hide show
  1. package/README.md +121 -615
  2. package/dist/actor-contract.d.ts +30 -0
  3. package/dist/actor-contract.js.map +1 -1
  4. package/dist/chrome-cdp-probe.js +13 -0
  5. package/dist/chrome-cdp-probe.js.map +1 -1
  6. package/dist/computer-use.d.ts +13 -3
  7. package/dist/computer-use.js +179 -15
  8. package/dist/computer-use.js.map +1 -1
  9. package/dist/cua-actor-lab.d.ts +20 -8
  10. package/dist/cua-actor-lab.js +154 -49
  11. package/dist/cua-actor-lab.js.map +1 -1
  12. package/dist/e2b-desktop-executor.d.ts +9 -1
  13. package/dist/e2b-desktop-executor.js +67 -4
  14. package/dist/e2b-desktop-executor.js.map +1 -1
  15. package/dist/e2b-desktop-launch.d.ts +36 -1
  16. package/dist/e2b-desktop-launch.js +88 -1
  17. package/dist/e2b-desktop-launch.js.map +1 -1
  18. package/dist/e2b-desktop-resources.d.ts +11 -0
  19. package/dist/e2b-desktop-resources.js +33 -0
  20. package/dist/e2b-desktop-resources.js.map +1 -0
  21. package/dist/e2b-desktop-screenshot-cleanup.d.ts +15 -0
  22. package/dist/e2b-desktop-screenshot-cleanup.js +67 -0
  23. package/dist/e2b-desktop-screenshot-cleanup.js.map +1 -0
  24. package/dist/e2b-terminal-lab.d.ts +5 -2
  25. package/dist/e2b-terminal-lab.js +238 -86
  26. package/dist/e2b-terminal-lab.js.map +1 -1
  27. package/dist/first-run-path.js +2 -2
  28. package/dist/first-run-path.js.map +1 -1
  29. package/dist/index.d.ts +3 -3
  30. package/dist/index.js +1 -1
  31. package/dist/index.js.map +1 -1
  32. package/dist/lab-config.d.ts +15 -14
  33. package/dist/lab-config.js +11 -2
  34. package/dist/lab-config.js.map +1 -1
  35. package/dist/observer-data.d.ts +2 -0
  36. package/dist/observer-data.js +10 -1
  37. package/dist/observer-data.js.map +1 -1
  38. package/dist/openai-responses-cu.js +76 -49
  39. package/dist/openai-responses-cu.js.map +1 -1
  40. package/dist/pricing.d.ts +19 -1
  41. package/dist/pricing.js +43 -10
  42. package/dist/pricing.js.map +1 -1
  43. package/dist/program.js +17 -8
  44. package/dist/program.js.map +1 -1
  45. package/dist/redaction.js +9 -2
  46. package/dist/redaction.js.map +1 -1
  47. package/dist/run.d.ts +14 -2
  48. package/dist/run.js.map +1 -1
  49. package/dist/terminal-node-bootstrap.d.ts +4 -0
  50. package/dist/terminal-node-bootstrap.js +58 -0
  51. package/dist/terminal-node-bootstrap.js.map +1 -0
  52. package/dist/terminal-runtime-auth.d.ts +13 -0
  53. package/dist/terminal-runtime-auth.js +24 -0
  54. package/dist/terminal-runtime-auth.js.map +1 -0
  55. package/dist/terminal-runtime.d.ts +16 -0
  56. package/dist/terminal-runtime.js +42 -0
  57. package/dist/terminal-runtime.js.map +1 -0
  58. package/dist/terminal-token-usage.d.ts +2 -1
  59. package/dist/terminal-token-usage.js +3 -2
  60. package/dist/terminal-token-usage.js.map +1 -1
  61. package/docs/architecture/actor-contract.md +22 -0
  62. package/docs/architecture/state-driven-executor.md +10 -1
  63. package/docs/architecture/terminal-product-lane.md +142 -6
  64. package/docs/contracts/feedback.md +14 -0
  65. package/docs/contracts/run-bundle.md +5 -1
  66. package/docs/contracts/schemas.md +17 -8
  67. package/docs/goals/current.md +61 -13
  68. package/docs/ramp/README.md +1 -1
  69. package/package.json +4 -2
@@ -0,0 +1,4 @@
1
+ export declare const TERMINAL_NODE_VERSION = "22.23.2";
2
+ export declare const TERMINAL_NODE_NPM_PREFIX_SCRIPT: string;
3
+ /** Unkeyed runtime prerequisite for stock Linux desktops. No apt repository refresh (#674). */
4
+ export declare const TERMINAL_NODE_BOOTSTRAP_COMMAND: string;
@@ -0,0 +1,58 @@
1
+ // Node 22 remains a supported LTS line. Pin the official release and trusted archive hashes:
2
+ // https://nodejs.org/en/about/previous-releases
3
+ // https://nodejs.org/dist/v22.23.2/SHASUMS256.txt (checked 2026-09-05)
4
+ // A checksum fetched alongside the archive at runtime would not pin what we trust.
5
+ export const TERMINAL_NODE_VERSION = "22.23.2";
6
+ // npm normally derives its global prefix from the real Node executable. This distribution
7
+ // lives under /opt, while its public executables use /usr/local/bin. Set only a missing
8
+ // built-in default; npm's environment, CLI, user and global config still take precedence.
9
+ export const TERMINAL_NODE_NPM_PREFIX_SCRIPT = [
10
+ 'const fs = require("node:fs");',
11
+ 'const file = process.argv[1];',
12
+ 'let existing = "";',
13
+ 'try { existing = fs.readFileSync(file, "utf8"); } catch (error) { if (error.code !== "ENOENT") throw error; }',
14
+ 'if (!/^[\\t ]*prefix[\\t ]*=/m.test(existing)) fs.appendFileSync(file, "\\nprefix=/usr/local\\n");'
15
+ ].join(" ");
16
+ /** Unkeyed runtime prerequisite for stock Linux desktops. No apt repository refresh (#674). */
17
+ export const TERMINAL_NODE_BOOTSTRAP_COMMAND = [
18
+ "set -eu",
19
+ "# humanish terminal-node-bootstrap",
20
+ "node_major=0",
21
+ `if command -v node >/dev/null 2>&1; then node_major=$(node -e 'console.log(Number(process.versions.node.split(".")[0]))' 2>/dev/null || echo 0); fi`,
22
+ `if command -v node >/dev/null 2>&1 && command -v npm >/dev/null 2>&1 && [ "$node_major" -ge 20 ] && npm --version >/dev/null 2>&1; then`,
23
+ // An explicit exit under set -e runs the stock login shell's failing clear_console logout hook
24
+ // and turns this successful fast path into exit 1. Finish the compound command naturally.
25
+ " :",
26
+ "else",
27
+ `case "$(uname -s):$(uname -m)" in`,
28
+ ` Linux:x86_64) node_arch=x64; node_sha=b294a556e639d64338823920e5866c21c02741742d2e1529ee1a225c1ec9252a ;;`,
29
+ ` Linux:aarch64|Linux:arm64) node_arch=arm64; node_sha=013b59cfd2819703a6f4a14ab891fc46fc2a4e3f5bcd92de3fb4929b43e35b30 ;;`,
30
+ ` *) echo 'humanish: terminal runtime requires a supported Linux x64/arm64 desktop or working Node >=20 and npm' >&2; exit 1 ;;`,
31
+ "esac",
32
+ "for prerequisite in curl sha256sum tar gzip mktemp sudo; do",
33
+ ` command -v "$prerequisite" >/dev/null 2>&1 || { echo "humanish: terminal runtime bootstrap requires $prerequisite" >&2; exit 1; }`,
34
+ "done",
35
+ `node_archive="node-v${TERMINAL_NODE_VERSION}-linux-$node_arch.tar.gz"`,
36
+ `node_target="/opt/humanish/node-v${TERMINAL_NODE_VERSION}-linux-$node_arch"`,
37
+ `node_temp=$(mktemp -d /tmp/humanish-node.XXXXXX)`,
38
+ `trap 'rm -rf "$node_temp"' EXIT`,
39
+ `curl --fail --silent --show-error --location --proto '=https' --proto-redir '=https' --connect-timeout 10 --max-time 90 --retry 1 --retry-max-time 120 --output "$node_temp/$node_archive" "https://nodejs.org/dist/v${TERMINAL_NODE_VERSION}/$node_archive"`,
40
+ `(cd "$node_temp" && printf '%s %s\\n' "$node_sha" "$node_archive" | sha256sum --check --status) || { echo 'humanish: terminal Node archive checksum did not match the trusted release' >&2; exit 1; }`,
41
+ // Only a verified official archive reaches privileged extraction. The versioned install is
42
+ // root-owned; subsequent ordinary and sudo shells find it through standard /usr/local/bin.
43
+ "sudo -n mkdir -p /opt/humanish /usr/local/bin",
44
+ `sudo -n tar --extract --gzip --file "$node_temp/$node_archive" --directory /opt/humanish --no-same-owner`,
45
+ "for executable in node npm npx; do",
46
+ ` sudo -n ln -sfn "$node_target/bin/$executable" "/usr/local/bin/$executable"`,
47
+ "done",
48
+ // Keep global product executables on the existing ordinary/sudo PATH (#679). The new
49
+ // versioned distribution is the only config we modify; no user/global npmrc or profile.
50
+ `sudo -n "$node_target/bin/node" -e '${TERMINAL_NODE_NPM_PREFIX_SCRIPT}' "$node_target/lib/node_modules/npm/npmrc"`,
51
+ "hash -r",
52
+ `node -e 'process.exit(Number(process.versions.node.split(".")[0]) >= 20 ? 0 : 1)' && npm --version >/dev/null`,
53
+ // Product installation already uses sudo; detect a template whose sudo PATH cannot see Node
54
+ // rather than changing global PATH or permissions to make it look supported.
55
+ `sudo -n node -e 'process.exit(Number(process.versions.node.split(".")[0]) >= 20 ? 0 : 1)' && sudo -n npm --version >/dev/null`,
56
+ "fi"
57
+ ].join("\n");
58
+ //# sourceMappingURL=terminal-node-bootstrap.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"terminal-node-bootstrap.js","sourceRoot":"","sources":["../src/terminal-node-bootstrap.ts"],"names":[],"mappings":"AAAA,6FAA6F;AAC7F,gDAAgD;AAChD,uEAAuE;AACvE,mFAAmF;AACnF,MAAM,CAAC,MAAM,qBAAqB,GAAG,SAAS,CAAC;AAE/C,0FAA0F;AAC1F,wFAAwF;AACxF,0FAA0F;AAC1F,MAAM,CAAC,MAAM,+BAA+B,GAAG;IAC7C,gCAAgC;IAChC,+BAA+B;IAC/B,oBAAoB;IACpB,+GAA+G;IAC/G,oGAAoG;CACrG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAEZ,+FAA+F;AAC/F,MAAM,CAAC,MAAM,+BAA+B,GAAG;IAC7C,SAAS;IACT,oCAAoC;IACpC,cAAc;IACd,qJAAqJ;IACrJ,yIAAyI;IACzI,+FAA+F;IAC/F,0FAA0F;IAC1F,KAAK;IACL,MAAM;IACN,mCAAmC;IACnC,6GAA6G;IAC7G,4HAA4H;IAC5H,iIAAiI;IACjI,MAAM;IACN,6DAA6D;IAC7D,qIAAqI;IACrI,MAAM;IACN,uBAAuB,qBAAqB,2BAA2B;IACvE,oCAAoC,qBAAqB,oBAAoB;IAC7E,kDAAkD;IAClD,iCAAiC;IACjC,wNAAwN,qBAAqB,iBAAiB;IAC9P,wMAAwM;IACxM,2FAA2F;IAC3F,2FAA2F;IAC3F,+CAA+C;IAC/C,0GAA0G;IAC1G,oCAAoC;IACpC,+EAA+E;IAC/E,MAAM;IACN,qFAAqF;IACrF,wFAAwF;IACxF,uCAAuC,+BAA+B,6CAA6C;IACnH,SAAS;IACT,+GAA+G;IAC/G,4FAA4F;IAC5F,6EAA6E;IAC7E,+HAA+H;IAC/H,IAAI;CACL,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC"}
@@ -0,0 +1,13 @@
1
+ import type { E2BNetworkOptions } from "./e2b-desktop-launch.js";
2
+ export declare const OPENAI_EGRESS_HOST = "api.openai.com";
3
+ /** E2B envd installs the sandbox-specific proxy CA into this system bundle before routing. */
4
+ export declare const E2B_SYSTEM_CA_BUNDLE = "/etc/ssl/certs/ca-certificates.crt";
5
+ /** An inert nonsecret value satisfies Codex exec's auth prerequisite. E2B replaces the header. */
6
+ export declare const OPENAI_EGRESS_PLACEHOLDER = "humanish-egress-auth-placeholder";
7
+ /**
8
+ * Keep the runtime key in the host-side E2B request, outside sandbox env/files. The installed
9
+ * E2B SDK declares that transform headers override existing request headers. This adds no routing
10
+ * restrictions: every sandbox process can still spend via the provider proxy. Never log the
11
+ * returned options. An adopter's existing exact-host rule must not be silently overwritten.
12
+ */
13
+ export declare function buildOpenAiEgressNetwork(keyValue: string, existing?: E2BNetworkOptions): E2BNetworkOptions;
@@ -0,0 +1,24 @@
1
+ export const OPENAI_EGRESS_HOST = "api.openai.com";
2
+ /** E2B envd installs the sandbox-specific proxy CA into this system bundle before routing. */
3
+ export const E2B_SYSTEM_CA_BUNDLE = "/etc/ssl/certs/ca-certificates.crt";
4
+ /** An inert nonsecret value satisfies Codex exec's auth prerequisite. E2B replaces the header. */
5
+ export const OPENAI_EGRESS_PLACEHOLDER = "humanish-egress-auth-placeholder";
6
+ /**
7
+ * Keep the runtime key in the host-side E2B request, outside sandbox env/files. The installed
8
+ * E2B SDK declares that transform headers override existing request headers. This adds no routing
9
+ * restrictions: every sandbox process can still spend via the provider proxy. Never log the
10
+ * returned options. An adopter's existing exact-host rule must not be silently overwritten.
11
+ */
12
+ export function buildOpenAiEgressNetwork(keyValue, existing) {
13
+ if (Object.keys(existing?.rules ?? {}).some((host) => host.toLowerCase().replace(/\.$/, "") === OPENAI_EGRESS_HOST)) {
14
+ throw new Error("openai-egress conflicts with an existing api.openai.com network rule; refusing to overwrite it.");
15
+ }
16
+ return {
17
+ ...existing,
18
+ rules: {
19
+ ...existing?.rules,
20
+ [OPENAI_EGRESS_HOST]: [{ transform: { headers: { Authorization: `Bearer ${keyValue}` } } }]
21
+ }
22
+ };
23
+ }
24
+ //# sourceMappingURL=terminal-runtime-auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"terminal-runtime-auth.js","sourceRoot":"","sources":["../src/terminal-runtime-auth.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,kBAAkB,GAAG,gBAAgB,CAAC;AACnD,8FAA8F;AAC9F,MAAM,CAAC,MAAM,oBAAoB,GAAG,oCAAoC,CAAC;AACzE,kGAAkG;AAClG,MAAM,CAAC,MAAM,yBAAyB,GAAG,kCAAkC,CAAC;AAE5E;;;;;GAKG;AACH,MAAM,UAAU,wBAAwB,CACtC,QAAgB,EAChB,QAA4B;IAE5B,IAAI,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,kBAAkB,CAAC,EAAE,CAAC;QACpH,MAAM,IAAI,KAAK,CAAC,iGAAiG,CAAC,CAAC;IACrH,CAAC;IACD,OAAO;QACL,GAAG,QAAQ;QACX,KAAK,EAAE;YACL,GAAG,QAAQ,EAAE,KAAK;YAClB,CAAC,kBAAkB,CAAC,EAAE,CAAC,EAAE,SAAS,EAAE,EAAE,OAAO,EAAE,EAAE,aAAa,EAAE,UAAU,QAAQ,EAAE,EAAE,EAAE,EAAE,CAAC;SAC5F;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,16 @@
1
+ import type { ActorRuntimeProvenance } from "./actor-contract.js";
2
+ import type { ReasoningEffort } from "./reasoning-effort.js";
3
+ export declare const TERMINAL_RUNTIME_PACKAGE = "@openai/codex";
4
+ export declare const TERMINAL_RUNTIME_VERSION_TIMEOUT_MS = 60000;
5
+ /** Exact semver only: no registry tags, ranges, URLs, or shell syntax. */
6
+ export declare function isExactRuntimeVersion(value: unknown): value is string;
7
+ /** Captured Codex CLI output: `codex-cli 0.153.3\n`. Other shapes fail closed. */
8
+ export declare function parseTerminalRuntimeVersion(stdout: string): string | undefined;
9
+ export declare function shellQuote(value: string): string;
10
+ export declare function buildRuntimeVersionCommand(requestedVersion?: string): string;
11
+ export declare function buildRuntimeExecPrefix(version: string, model?: string, reasoningEffort?: ReasoningEffort): string;
12
+ export declare function declaredRuntimeProvenance(args: {
13
+ version?: string;
14
+ model?: string;
15
+ reasoningEffort?: ReasoningEffort;
16
+ }): ActorRuntimeProvenance;
@@ -0,0 +1,42 @@
1
+ export const TERMINAL_RUNTIME_PACKAGE = "@openai/codex";
2
+ export const TERMINAL_RUNTIME_VERSION_TIMEOUT_MS = 60_000;
3
+ /** Exact semver only: no registry tags, ranges, URLs, or shell syntax. */
4
+ export function isExactRuntimeVersion(value) {
5
+ if (typeof value !== "string" || value.length > 120)
6
+ return false;
7
+ const match = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?$/.exec(value);
8
+ return match !== null && (match[4]?.split(".").every((part) => !/^\d+$/.test(part) || part === "0" || !part.startsWith("0")) ?? true);
9
+ }
10
+ /** Captured Codex CLI output: `codex-cli 0.153.3\n`. Other shapes fail closed. */
11
+ export function parseTerminalRuntimeVersion(stdout) {
12
+ const match = /^codex-cli ([^\s]+)$/.exec(stdout.trim());
13
+ return match && isExactRuntimeVersion(match[1]) ? match[1] : undefined;
14
+ }
15
+ export function shellQuote(value) {
16
+ return `'${value.replace(/'/g, "'\\''")}'`;
17
+ }
18
+ export function buildRuntimeVersionCommand(requestedVersion) {
19
+ if (requestedVersion !== undefined && !isExactRuntimeVersion(requestedVersion))
20
+ throw new Error("Invalid exact Codex runtime version.");
21
+ return `npm_config_update_notifier=false npx -y ${TERMINAL_RUNTIME_PACKAGE}@${requestedVersion ?? "latest"} --version`;
22
+ }
23
+ export function buildRuntimeExecPrefix(version, model, reasoningEffort) {
24
+ if (!isExactRuntimeVersion(version))
25
+ throw new Error("Codex execution requires a verified exact runtime version.");
26
+ return `npm_config_update_notifier=false npx -y ${TERMINAL_RUNTIME_PACKAGE}@${version} exec`
27
+ + (model === undefined ? "" : ` --model ${shellQuote(model)}`)
28
+ + (reasoningEffort === undefined ? "" : ` -c ${shellQuote(`model_reasoning_effort=${JSON.stringify(reasoningEffort)}`)}`);
29
+ }
30
+ export function declaredRuntimeProvenance(args) {
31
+ return {
32
+ schema: "humanish.actor-runtime.v1",
33
+ package: TERMINAL_RUNTIME_PACKAGE,
34
+ requestedVersion: args.version ?? "latest",
35
+ versionStatus: "unobserved",
36
+ ...(args.model === undefined ? {} : { requestedModel: args.model }),
37
+ modelStatus: args.model === undefined ? "runtime_default_unobserved" : "declared",
38
+ ...(args.reasoningEffort === undefined ? {} : { requestedReasoningEffort: args.reasoningEffort }),
39
+ usageGranularity: "runtime_turn"
40
+ };
41
+ }
42
+ //# sourceMappingURL=terminal-runtime.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"terminal-runtime.js","sourceRoot":"","sources":["../src/terminal-runtime.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,7 @@
1
1
  import type { ActorTokenUsage } from "./actor-contract.js";
2
2
  /**
3
- * Accumulate per-turn provider token usage from a captured `codex exec --json` stream.
3
+ * Accumulate runtime-turn usage from a captured `codex exec --json` stream. A Codex turn may
4
+ * include multiple provider requests; these records cannot establish per-request pricing tiers.
4
5
  *
5
6
  * Returns undefined when the stream carried no usage record at all, which is the honest "no
6
7
  * signal" case and must stay distinguishable from a measured zero. Per-turn records are preserved
@@ -21,7 +21,8 @@ function num(value) {
21
21
  return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : undefined;
22
22
  }
23
23
  /**
24
- * Accumulate per-turn provider token usage from a captured `codex exec --json` stream.
24
+ * Accumulate runtime-turn usage from a captured `codex exec --json` stream. A Codex turn may
25
+ * include multiple provider requests; these records cannot establish per-request pricing tiers.
25
26
  *
26
27
  * Returns undefined when the stream carried no usage record at all, which is the honest "no
27
28
  * signal" case and must stay distinguishable from a measured zero. Per-turn records are preserved
@@ -88,6 +89,6 @@ export function describeTokenUsage(usage) {
88
89
  if (usage.output !== undefined)
89
90
  parts.push(`${usage.output.toLocaleString("en-US")} output`);
90
91
  const turns = usage.turns?.length ?? 0;
91
- return `${parts.join(", ")} tokens over ${turns} provider turn${turns === 1 ? "" : "s"}`;
92
+ return `${parts.join(", ")} tokens over ${turns} Codex turn${turns === 1 ? "" : "s"}`;
92
93
  }
93
94
  //# sourceMappingURL=terminal-token-usage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"terminal-token-usage.js","sourceRoot":"","sources":["../src/terminal-token-usage.ts"],"names":[],"mappings":"AAAA,0DAA0D;AAC1D,EAAE;AACF,6FAA6F;AAC7F,iGAAiG;AACjG,+FAA+F;AAC/F,kGAAkG;AAClG,6FAA6F;AAC7F,EAAE;AACF,8FAA8F;AAC9F,0FAA0F;AAC1F,2FAA2F;AAC3F,EAAE;AACF,mGAAmG;AACnG,iGAAiG;AACjG,wFAAwF;AACxF,iEAAiE;AAcjE,iGAAiG;AACjG,2FAA2F;AAC3F,MAAM,QAAQ,GAAG,mEAAmE,CAAC;AAErF,SAAS,GAAG,CAAC,KAAc;IACzB,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;AAC/F,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,uBAAuB,CAAC,UAAkB;IACxD,MAAM,KAAK,GAA0C,EAAE,CAAC;IACxD,KAAK,MAAM,KAAK,IAAI,UAAU,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;QAClD,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QACtB,IAAI,IAAI,KAAK,SAAS;YAAE,SAAS;QACjC,IAAI,GAAkB,CAAC;QACvB,IAAI,CAAC;YACH,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAkB,CAAC;QAC1C,CAAC;QAAC,MAAM,CAAC;YACP,SAAS,CAAC,uEAAuE;QACnF,CAAC;QACD,MAAM,KAAK,GAAG,GAAG,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;QACpC,MAAM,MAAM,GAAG,GAAG,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;QACtC,MAAM,WAAW,GAAG,GAAG,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAC;QACjD,MAAM,eAAe,GAAG,GAAG,CAAC,GAAG,CAAC,wBAAwB,CAAC,CAAC;QAC1D,IAAI,KAAK,KAAK,SAAS,IAAI,MAAM,KAAK,SAAS;YAAE,SAAS;QAC1D,KAAK,CAAC,IAAI,CAAC;YACT,GAAG,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC;YACzC,GAAG,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC;YAC3C,GAAG,CAAC,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC;YACrD,GAAG,CAAC,eAAe,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,eAAe,EAAE,CAAC;SAC9D,CAAC,CAAC;IACL,CAAC;IACD,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IAEzC,MAAM,GAAG,GAAG,CAAC,KAA6D,EAAsB,EAAE;QAChG,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,SAAS,CAAC,CAAC;QAC5D,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,SAAS,CAAC;QAC3C,OAAO,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAC9D,CAAC,CAAC;IACF,MAAM,KAAK,GAAG,GAAG,CAAC,OAAO,CAAC,CAAC;IAC3B,MAAM,MAAM,GAAG,GAAG,CAAC,QAAQ,CAAC,CAAC;IAC7B,MAAM,WAAW,GAAG,GAAG,CAAC,aAAa,CAAC,CAAC;IACvC,MAAM,eAAe,GAAG,GAAG,CAAC,iBAAiB,CAAC,CAAC;IAC/C,OAAO;QACL,GAAG,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC;QACzC,GAAG,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC;QAC3C,GAAG,CAAC,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC;QACrD,GAAG,CAAC,eAAe,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,eAAe,EAAE,CAAC;QAC7D,GAAG,CAAC,KAAK,KAAK,SAAS,IAAI,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,KAAK,IAAI,CAAC,CAAC,GAAG,CAAC,MAAM,IAAI,CAAC,CAAC,EAAE,CAAC;QAC/F,KAAK;KACN,CAAC;AACJ,CAAC;AAED;;8BAE8B;AAC9B,MAAM,UAAU,kBAAkB,CAAC,KAAsB;IACvD,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,IAAI,KAAK,CAAC,KAAK,KAAK,SAAS;QAAE,KAAK,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IAC1F,IAAI,KAAK,CAAC,WAAW,KAAK,SAAS,EAAE,CAAC;QACpC,KAAK,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,WAAW,CAAC,cAAc,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC;IAC5E,CAAC;IACD,IAAI,KAAK,CAAC,MAAM,KAAK,SAAS;QAAE,KAAK,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC,cAAc,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAC7F,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,EAAE,MAAM,IAAI,CAAC,CAAC;IACvC,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,gBAAgB,KAAK,iBAAiB,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC;AAC3F,CAAC"}
1
+ {"version":3,"file":"terminal-token-usage.js","sourceRoot":"","sources":["../src/terminal-token-usage.ts"],"names":[],"mappings":"AAAA,0DAA0D;AAC1D,EAAE;AACF,6FAA6F;AAC7F,iGAAiG;AACjG,+FAA+F;AAC/F,kGAAkG;AAClG,6FAA6F;AAC7F,EAAE;AACF,8FAA8F;AAC9F,0FAA0F;AAC1F,2FAA2F;AAC3F,EAAE;AACF,mGAAmG;AACnG,iGAAiG;AACjG,wFAAwF;AACxF,iEAAiE;AAcjE,iGAAiG;AACjG,2FAA2F;AAC3F,MAAM,QAAQ,GAAG,mEAAmE,CAAC;AAErF,SAAS,GAAG,CAAC,KAAc;IACzB,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;AAC/F,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,uBAAuB,CAAC,UAAkB;IACxD,MAAM,KAAK,GAA0C,EAAE,CAAC;IACxD,KAAK,MAAM,KAAK,IAAI,UAAU,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;QAClD,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QACtB,IAAI,IAAI,KAAK,SAAS;YAAE,SAAS;QACjC,IAAI,GAAkB,CAAC;QACvB,IAAI,CAAC;YACH,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAkB,CAAC;QAC1C,CAAC;QAAC,MAAM,CAAC;YACP,SAAS,CAAC,uEAAuE;QACnF,CAAC;QACD,MAAM,KAAK,GAAG,GAAG,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;QACpC,MAAM,MAAM,GAAG,GAAG,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;QACtC,MAAM,WAAW,GAAG,GAAG,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAC;QACjD,MAAM,eAAe,GAAG,GAAG,CAAC,GAAG,CAAC,wBAAwB,CAAC,CAAC;QAC1D,IAAI,KAAK,KAAK,SAAS,IAAI,MAAM,KAAK,SAAS;YAAE,SAAS;QAC1D,KAAK,CAAC,IAAI,CAAC;YACT,GAAG,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC;YACzC,GAAG,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC;YAC3C,GAAG,CAAC,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC;YACrD,GAAG,CAAC,eAAe,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,eAAe,EAAE,CAAC;SAC9D,CAAC,CAAC;IACL,CAAC;IACD,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IAEzC,MAAM,GAAG,GAAG,CAAC,KAA6D,EAAsB,EAAE;QAChG,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,SAAS,CAAC,CAAC;QAC5D,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,SAAS,CAAC;QAC3C,OAAO,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAC9D,CAAC,CAAC;IACF,MAAM,KAAK,GAAG,GAAG,CAAC,OAAO,CAAC,CAAC;IAC3B,MAAM,MAAM,GAAG,GAAG,CAAC,QAAQ,CAAC,CAAC;IAC7B,MAAM,WAAW,GAAG,GAAG,CAAC,aAAa,CAAC,CAAC;IACvC,MAAM,eAAe,GAAG,GAAG,CAAC,iBAAiB,CAAC,CAAC;IAC/C,OAAO;QACL,GAAG,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC;QACzC,GAAG,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC;QAC3C,GAAG,CAAC,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC;QACrD,GAAG,CAAC,eAAe,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,eAAe,EAAE,CAAC;QAC7D,GAAG,CAAC,KAAK,KAAK,SAAS,IAAI,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,KAAK,IAAI,CAAC,CAAC,GAAG,CAAC,MAAM,IAAI,CAAC,CAAC,EAAE,CAAC;QAC/F,KAAK;KACN,CAAC;AACJ,CAAC;AAED;;8BAE8B;AAC9B,MAAM,UAAU,kBAAkB,CAAC,KAAsB;IACvD,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,IAAI,KAAK,CAAC,KAAK,KAAK,SAAS;QAAE,KAAK,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IAC1F,IAAI,KAAK,CAAC,WAAW,KAAK,SAAS,EAAE,CAAC;QACpC,KAAK,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,WAAW,CAAC,cAAc,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC;IAC5E,CAAC;IACD,IAAI,KAAK,CAAC,MAAM,KAAK,SAAS;QAAE,KAAK,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC,cAAc,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAC7F,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,EAAE,MAAM,IAAI,CAAC,CAAC;IACvC,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,gBAAgB,KAAK,cAAc,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC;AACxF,CAAC"}
@@ -293,6 +293,28 @@ not cost — a live scripted run drives a real browser against a real running ap
293
293
  declaration as spend. "Live" on this route must never silently come to mean "costs money";
294
294
  this paragraph is the record of that decision.
295
295
 
296
+ ## Optional closing report
297
+
298
+ `CuaProvider.debrief` is an optional read-only request after a structured
299
+ `stopWhen` or dwell stop. It returns a `CuaTurn` with `closingReport` containing
300
+ `summary` and `frictionReports`; an empty friction list is valid. The loop rejects
301
+ actions, pending safety checks, and invalid report shapes. It redacts accepted
302
+ reports, records them in `ActorTrace.debrief`, and projects one readable message
303
+ without invoking action or communication callbacks.
304
+
305
+ This request uses the final observation and retained provider history. It does
306
+ not expose the hidden stop criterion or change the original task outcome. It is
307
+ skipped before any participant turn, after natural completion, without provider
308
+ support, or without remaining time and known budget. An attempted request's
309
+ unreported usage remains an unknown cost line. `counts.debriefCalls` is separate
310
+ from interaction turns, and reported usage contributes to aggregate cost.
311
+
312
+ The OpenAI implementation makes one request with tools disabled and structured
313
+ output; no HTTP or policy retries. Stateless/ZDR mode does not offer retrospective
314
+ reporting because it does not retain the required session history. The
315
+ [paired live receipt](../goals/computer-use-actor/receipts/structured-closing-report-2026-09-05.md)
316
+ records both report recovery and control failures in the separate legacy parser.
317
+
296
318
  ## The state-driven executor seam (shipped — the transport-agnostic intent, made real)
297
319
 
298
320
  The `CuaExecutor` / `CuaProvider` ports are the concrete realization of the "plural harnesses /
@@ -28,7 +28,7 @@ This is the "plural harnesses / transport-agnostic" intent of
28
28
  ```ts
29
29
  interface CuaExecutor {
30
30
  observe(): Promise<CuaObservation>; // capture current state
31
- execute(action: CuaAction): Promise<void>; // perform one action
31
+ execute(action: CuaAction, signal?: AbortSignal): Promise<void>; // perform one action
32
32
  }
33
33
 
34
34
  interface CuaObservation {
@@ -38,6 +38,15 @@ interface CuaObservation {
38
38
  }
39
39
  ```
40
40
 
41
+ The loop aborts an action's optional signal when its wait ends, including cancellation or a
42
+ deadline. An executor that prepares asynchronously should check the signal before dispatching
43
+ an input; wrappers must forward it (`execute: (...args) => inner.execute(...args)`). Existing
44
+ one-argument executors remain compatible. This does not cancel already-dispatched substrate
45
+ operations. The desktop executor uses this boundary while reading the current cursor before
46
+ left/double clicks: an exact integer match avoids redundant movement; uncertain or fractional
47
+ coordinates retain the original SDK path. The cursor read adds at most 500 ms of preparation
48
+ wait and does not cache a previous action's position.
49
+
41
50
  - **`screenshot` is optional.** A non-vision (state) executor omits it. The loop
42
51
  persists no screenshot that turn; `counts.screenshots` stays 0, so the trace's
43
52
  `redaction.screenshots` resolves to `"n/a"`. No fabricated `Buffer.alloc(0)`
@@ -1,6 +1,6 @@
1
1
  # Terminal-product real-agent lane (issue #154)
2
2
 
3
- Date: 2026-06-16 (current-state note updated 2026-07-14)
3
+ Date: 2026-06-16 (runtime-auth contract updated 2026-09-05)
4
4
 
5
5
  Status: live terminal-product route shipped in `0.8.0`. The in-sandbox backend,
6
6
  command-scoped credential placement, exact-id cleanup proof, an interventions ledger,
@@ -16,8 +16,8 @@ for the full slice plan and the safety contract.
16
16
 
17
17
  A lab lane for **terminal-product real-agent studies**: a real autonomous coding
18
18
  agent (Codex) discovering and using a CLI/product from its **public surfaces
19
- only**, running **inside an E2B shell** with command-scoped runtime auth, capped
20
- at no-spend, emitting durable terminal/substrate/cost/no-spend/cleanup/
19
+ only**, running **inside an E2B shell** with declared runtime-auth placement and
20
+ spend/time caps, emitting durable terminal/substrate/cost/no-spend/cleanup/
21
21
  intervention proof that verifies fail-closed. This is distinct from the browser
22
22
  lanes: it is not testing whether a browser can click a local web app — it tests
23
23
  whether an autonomous agent can discover and use a CLI/product surface from
@@ -36,7 +36,9 @@ fail-closed cross-validation, and forward-declared warnings.
36
36
  | `subject.product` | `{ name, publicSurfaces[] }` — the only world the agent sees |
37
37
  | `execution.target` | `e2b-terminal` (or absent → implied) |
38
38
  | `execution.terminal` | `{ transport: exec-stream, stdin: disabled }` |
39
- | `execution.runtimeAuth` | `openai-env` (names-only durable evidence) |
39
+ | `execution.runtimeAuth` | `openai-env` (default) or opt-in `openai-egress`; names-only durable evidence |
40
+ | `execution.runtime.version` | Optional exact `@openai/codex` version; observed before keyed execution |
41
+ | `actors[0].model` / `reasoningEffort` | Forwarded to Codex; retained as declarations, not observed provider identity |
40
42
  | `scenario.caps` | `{ maxUsd, maxJobs, maxMinutes }` — the blast-radius budget |
41
43
  | `policies` | `allowPrivateRepoAccess` / `allowProviderCredentials` / `allowPaymentCredentials` / `allowGitHubMutation`, all DEFAULT FALSE |
42
44
  | `actors[0].type` | `codex-exec` — a registered terminal actor (`keyPlacement: in-sandbox-command-scoped`) |
@@ -46,9 +48,143 @@ Routing is `routesToTerminalProduct(config)` — the single source of truth that
46
48
  both `selectLabBackend` and the forward-declared-warning logic consume, mirroring
47
49
  `routesToComputerUse` / `routesToScriptedBrowser`.
48
50
 
49
- ## The safety contract (the lane's reason to exist)
51
+ ## Repeating a terminal study with the same runtime
50
52
 
51
- This lane **inverts** the credential-placement default of every other E2B route.
53
+ ```yaml
54
+ actors:
55
+ - type: codex-exec
56
+ model: gpt-5.6-sol
57
+ reasoningEffort: low
58
+ execution:
59
+ target: e2b-terminal
60
+ runtime:
61
+ version: 0.153.3
62
+ ```
63
+
64
+ An explicit version must be exact semver; tags, ranges, URLs, and extra runtime
65
+ fields fail at parsing. Before the keyed actor command, Humanish runs an unkeyed
66
+ `npx @openai/codex@<selector> --version` command with a 60-second deadline.
67
+ A malformed result, nonzero exit, or requested/observed mismatch fails the lane
68
+ and reclaims its owned sandbox. When the version is omitted, the probe resolves
69
+ `latest` once and execution uses the exact version it reported.
70
+
71
+ `actor.json`, the bundle's terminal actor, and `terminal-ledgers.json` retain
72
+ `humanish.actor-runtime.v1`: requested and observed versions, verification status,
73
+ declared model/effort, and the usage granularity. The observed executable version
74
+ also appears as `providerVersion`. An undeclared model stays explicitly
75
+ `runtime_default_unobserved`; Humanish does not label the runtime name as a model.
76
+ Dry runs record declarations only, in a `terminal-lab.runtime.declared` event.
77
+
78
+ These settings make a study's request reproducible, but do not attest the actual
79
+ provider model or add a provider spending limit. Codex `turn.completed` usage can
80
+ aggregate several model requests, so Humanish does not use a declared model to
81
+ infer per-request pricing tiers or fill in unknown costs. Runtime-token costs
82
+ and the studied product's no-spend boundary still need separate interpretation.
83
+
84
+ ## Runtime auth: raw-key placement and remaining provider access
85
+
86
+ `execution.runtimeAuth: openai-env` remains the compatible default. It supplies
87
+ `CODEX_API_KEY` command-scoped to Codex, with `OPENAI_API_KEY` also supplied when
88
+ that was the host source. Child processes can read and use the raw key.
89
+
90
+ Opt in to keeping the raw key outside the sandbox:
91
+
92
+ ```yaml
93
+ execution:
94
+ target: e2b-terminal
95
+ runtimeAuth: openai-egress
96
+ terminal:
97
+ transport: exec-stream
98
+ stdin: disabled
99
+ ```
100
+
101
+ `openai-egress` resolves the same host key (`CODEX_API_KEY` first, otherwise
102
+ `OPENAI_API_KEY`) and supplies it only to E2B's host-side network rule for
103
+ `api.openai.com`. The rule sets the HTTPS `Authorization` header. The sandbox's
104
+ Codex command receives the nonsecret value `humanish-egress-auth-placeholder`
105
+ under `CODEX_API_KEY`, plus `CODEX_CA_CERTIFICATE` pointing at E2B's existing
106
+ system CA bundle (`/etc/ssl/certs/ca-certificates.crt`) so TLS verification trusts
107
+ the platform's proxy CA. Humanish does not disable TLS verification or download
108
+ an unauthenticated CA. The sandbox receives no raw runtime key in command env, sandbox env,
109
+ files, metadata, or captured evidence. The host still scrubs the actual key from
110
+ output and errors, including errors during sandbox creation.
111
+
112
+ This mode supports the default OpenAI endpoint only. Humanish explicitly sets
113
+ Codex's built-in `openai` provider and `openai_base_url` to
114
+ `https://api.openai.com/v1` for that invocation. It does not support a custom
115
+ provider, proxy base URL, or regional endpoint under this mode. `openai-env`
116
+ retains its existing command behavior. E2B header rules are a public-beta
117
+ capability; the local contract is checked against the installed Desktop SDK
118
+ (`@e2b/desktop` 2.3.3, resolving `e2b` 2.46.1).
119
+
120
+ **The sandbox still has a spendable OpenAI proxy capability.** Every process can
121
+ make authenticated requests to that host from sandbox creation until teardown,
122
+ including bootstrap/setup commands and commands launched outside Codex. Calls
123
+ made outside Codex may be absent from its usage ledger. This mode does not impose
124
+ a provider-side spending limit, restrict models/API paths, or make
125
+ `scenario.caps.maxUsd` a preventive provider budget. A hard provider budget needs
126
+ a separately enforced control; do not infer zero spend from an unmeasured ledger
127
+ line.
128
+
129
+ Public internet discovery stays unrestricted unless the lab already declares
130
+ `execution.egressAllow`. The mode preserves that allowlist and its deny-all
131
+ fallback without adding hosts. If an allowlist omits `api.openai.com`, provider
132
+ requests can fail. E2B domain allowlists are routing controls rather than strict
133
+ destination isolation on shared infrastructure. An existing exact OpenAI host
134
+ rule is rejected instead of silently overwritten.
135
+
136
+ Evidence records the selected auth mode and the residual proxy capability.
137
+ Resolved live actor traces use `keyPlacement: external` in `openai-egress`; the
138
+ actor registry continues to describe the default `in-sandbox-command-scoped`
139
+ placement. Dry runs record declarations and prove no live proxy behavior.
140
+
141
+ The upstream contracts are documented in [E2B internet access and network
142
+ rules](https://docs.e2b.dev/network/internet-access) and [Codex advanced
143
+ configuration](https://developers.openai.com/codex/config-advanced),
144
+ [Codex custom CA bundles](https://developers.openai.com/codex/auth#custom-ca-bundles),
145
+ and [E2B's CA installer](https://github.com/e2b-dev/infra/blob/main/packages/envd/internal/host/cacerts.go).
146
+ E2B's installed
147
+ SDK documents that transformed headers override request headers. Deterministic
148
+ request/redaction tests do not establish live wire behavior. The [2026-09-05
149
+ transport receipt](../goals/terminal-product-lane/receipts/2026-09-05-runtime-egress-auth.md)
150
+ records the controlled live header/auth checks and their scope.
151
+
152
+ ## Runtime prerequisite
153
+
154
+ The terminal route reuses a working Node >=20 and npm from the calling shell.
155
+ Otherwise it installs the pinned official Node 22.23.2 Linux x64 or arm64 archive,
156
+ downloaded over verified HTTPS and checked against an architecture-specific
157
+ SHA256 committed in the bootstrap. It does not refresh apt repositories or fetch
158
+ an unpinned checksum beside the archive. Node 22 is a supported LTS line on the
159
+ [official release schedule](https://nodejs.org/en/about/previous-releases); the
160
+ trusted hashes come from its [release manifest](https://nodejs.org/dist/v22.23.2/SHASUMS256.txt).
161
+
162
+ Installation requires `curl`, `sha256sum`, `tar`, `gzip`, `mktemp`, and passwordless
163
+ `sudo`. Only a verified archive is extracted into a root-owned versioned directory
164
+ under `/opt/humanish`; `/usr/local/bin` links make Node/npm/npx available to later
165
+ shells. In that new distribution only, a missing built-in npm `prefix` defaults
166
+ to `/usr/local`, so global product executables use the existing PATH. Existing
167
+ distribution settings and higher-priority npm overrides are preserved; an adopter
168
+ override can still choose a bin directory outside PATH. The installer changes no
169
+ user/global npm configuration, global permissions, or shell startup files. It
170
+ checks Node/npm in both ordinary and sudo shells after installation. The existing
171
+ runtime fast path preserves user-specific installations; a later sudo product
172
+ install can still fail if that installation is absent from sudo's PATH.
173
+
174
+ The [global executable receipt](../goals/terminal-product-lane/receipts/2026-09-05-global-npm-prefix.md)
175
+ records the regression found after the initial runtime-only proof and its stock
176
+ desktop checks. npm documents [global executable locations](https://docs.npmjs.com/cli/v10/configuring-npm/folders#executables)
177
+ and the [distribution built-in configuration](https://docs.npmjs.com/cli/v10/configuring-npm/npmrc#built-in-config-file).
178
+
179
+ An egress allowlist must permit `nodejs.org` if the runtime needs installation,
180
+ as well as the registries and product surfaces the study uses. Missing tools,
181
+ unsupported architectures, a failed download or checksum, and a failed runtime
182
+ check stop the lane before Codex. Downloads have finite connection, transfer, and
183
+ retry bounds within the existing five-minute bootstrap deadline.
184
+
185
+ ## The original command-scoped safety contract
186
+
187
+ The default mode **inverts** the credential-placement default of every other E2B route.
52
188
  On the computer-use route the model's key stays *outside* the sandbox; here the
53
189
  agent-under-test runs *inside* with a real `OPENAI_API_KEY`/`CODEX_API_KEY` and
54
190
  is **presumed exfiltratable**. The doctrine (invariants-and-defaults.md, the
@@ -52,6 +52,20 @@ setup-quality gap, ceremonial/absent study quality, target-app blocker, or
52
52
  Humanish CLI blocker. The OSS
53
53
  meta-lab is one maintainer dogfood example, not the only candidate source.
54
54
 
55
+ For completed computer-use sessions, participant-reported friction comes from
56
+ the redacted closing report and observed-report clauses in earlier messages.
57
+ Matching `stopWhen` or ending a dwell window does not discard an earlier
58
+ report or change the successful completion verdict. Exact repeated reports
59
+ appear once in the lane's candidate. Harness notices, reasoning, and observed
60
+ page content do not become participant findings; quoted app copy and negated
61
+ reports retain the existing exclusions. Code excerpts are treated as quoted
62
+ material. Interim matching requires an observation-shaped clause and filters
63
+ common plans, questions, and hypotheticals; a report followed by a retry plan
64
+ can still qualify. This text
65
+ heuristic does not establish that a reported defect is real or independently
66
+ confirmed. A custom session's closing `reason` remains eligible even when it
67
+ was not repeated in the message trace.
68
+
55
69
  ### `draft`
56
70
 
57
71
  Builds structured feedback from the strongest run candidate first. If no
@@ -126,7 +126,11 @@ enters this field; identity is digests, a sha, a boolean, and counts.
126
126
 
127
127
  `cost` is optional and additive (`humanish.run-cost-summary.v1`): the
128
128
  computer-use lane's run-level cost ESTIMATE — the sum of each lane's
129
- token-derived model cost plus one aggregate E2B desktop-minute figure. It is an
129
+ token-derived model cost plus E2B desktop compute lines. New independent CUA runs
130
+ emit one line per owned desktop, keyed by public lane ID and carrying observed CPU/memory,
131
+ resource source, host-measured minutes, and the derived per-second rate. Older
132
+ single aggregate desktop lines remain valid. Missing resource metadata stays
133
+ unpriced; unconfirmed cleanup adds an unknown remaining-lifetime line. It is an
130
134
  ESTIMATE, never authoritative: every dollar is a rate-table multiply from the
131
135
  operator-editable `src/pricing.ts`, carries the pricing `ratesAsOf` date and
132
136
  `source`, and is surfaced with the "estimated (rates as of `<date>`)" label —
@@ -3,7 +3,7 @@
3
3
  Date: 2026-06-02 (current-state note updated 2026-07-14)
4
4
 
5
5
  Status: reference map for the major contracts shipped through source version
6
- `0.80.0`; it is not an exhaustive inventory of command/result envelopes. Exported types,
6
+ `0.82.0`; it is not an exhaustive inventory of command/result envelopes. Exported types,
7
7
  schema constants, parsers, and validators in `src/` are authoritative. Rows
8
8
  marked "reserved" name layering intent only — no code emits or validates them
9
9
  yet. Do not emit a reserved schema.
@@ -978,16 +978,16 @@ never authoritative: every dollar figure is a rate-table multiply, labeled
978
978
  `humanish.run-bundle.v1` stays v1 and every pre-existing bundle is byte-stable:
979
979
 
980
980
  - `humanish.pricing.v1` — the OPERATOR-EDITABLE rate table in `src/pricing.ts`:
981
- dated per-model input/output USD-per-token rates and an E2B desktop
982
- USD-per-minute rate, each with a public pricing-page `source` and an `asOf`
981
+ dated per-model input/output USD-per-token rates and E2B CPU/GiB-second
982
+ rates, each with a public pricing-page `source` and an `asOf`
983
983
  date. A prominent banner says these are estimates to update when providers
984
984
  change pricing. Model rates may carry a `cacheWriteUsdPerToken` (OpenAI 5.6+
985
985
  bills cache writes at 1.25x input as the total rate for written tokens) and a
986
986
  `longContext` tier (a per-request input threshold that re-prices the whole
987
987
  request; priced exactly only from the trace's per-request `turns` ledger —
988
988
  totals alone never re-tier, which is the under-estimate direction). Some
989
- entries are `placeholder: true` stand-ins (the E2B desktop rate, pending a
990
- live RAM-spec confirmation) — an operator MUST confirm them before trusting
989
+ entries are `placeholder: true` stand-ins (the legacy desktop helper's
990
+ 8-vCPU/8-GiB planning assumption) — an operator MUST confirm them before trusting
991
991
  the magnitude; the flag propagates into every estimate so a stand-in is never
992
992
  mistaken for a live rate. An UNKNOWN model/desktop rate is DECLARED ABSENT
993
993
  (`estimatedCostUsd: null` + a `reason`), never guessed.
@@ -996,7 +996,14 @@ never authoritative: every dollar figure is a rate-table multiply, labeled
996
996
  `no_rate_for_model`/`no_token_usage`), `ratesAsOf`, `source`, `modelId`,
997
997
  optional `placeholder`, and a `breakdown`.
998
998
  - `humanish.run-cost-summary.v1` — `RunBundle.cost`: the sum of every lane's
999
- `model-tokens` line PLUS one aggregate `desktop-minutes` line.
999
+ `model-tokens` lines PLUS `desktop-minutes` lines. New independent CUA runs
1000
+ price each owned desktop separately; older single aggregate lines remain readable.
1001
+ A desktop line's optional `desktop` object records `minutes`,
1002
+ `durationBasis: host-acquired-to-cleanup`, observed `resources` (`cpuCount`,
1003
+ `memoryMiB`), `resourceSource: e2b.getInfo`, and `usdPerSecond` when priceable.
1004
+ Failed metadata reads record `resourceUnavailableReason`; no resource guess is used.
1005
+ Resource metadata and missing/unsupported rates produce a null line. A kept or
1006
+ unconfirmed allocation adds `desktop_lifetime_incomplete` as a second null line.
1000
1007
 
1001
1008
  The summary follows the SAME null discipline as the terminal cost ledger above.
1002
1009
  `estimatedTotalUsd` sums ONLY the non-null `breakdown` lines and is `null` iff
@@ -1008,8 +1015,10 @@ tried and could not price it) and contributes nothing. `fullyEstimated` is
1008
1015
  the MIN (oldest) `asOf` across contributing rates — an aggregate is only as fresh
1009
1016
  as its stalest input, so MAX would overclaim freshness (each `breakdown` line
1010
1017
  keeps its own true `asOf`). `desktopMinutes` is a HOST-SIDE
1011
- createteardown span an approximation of E2B's server-side billed lifetime, so
1012
- the desktop dollar figure is doubly an estimate.
1018
+ acquired-handlecleanup span, excluding allocation/startup before handle acquisition.
1019
+ It approximates E2B's server-side billed lifetime. Plan fees, credits, and negotiated
1020
+ prices are excluded; unknown remaining lifetime makes `fullyEstimated` false.
1021
+ Shared-world, scripted-browser, and terminal routes do not yet emit these desktop lines.
1013
1022
 
1014
1023
  ```yaml
1015
1024
  schema: humanish.run-cost-summary.v1