humanish 0.0.1 → 0.15.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 (196) hide show
  1. package/AGENTS.md +77 -0
  2. package/CONTRIBUTING.md +39 -0
  3. package/LICENSE +21 -0
  4. package/README.md +435 -8
  5. package/SECURITY.md +34 -0
  6. package/dist/actor-contract.d.ts +107 -0
  7. package/dist/actor-contract.js +286 -0
  8. package/dist/actor-contract.js.map +1 -0
  9. package/dist/actor-registry.d.ts +73 -0
  10. package/dist/actor-registry.js +93 -0
  11. package/dist/actor-registry.js.map +1 -0
  12. package/dist/adapter-extension.d.ts +54 -0
  13. package/dist/adapter-extension.js +214 -0
  14. package/dist/adapter-extension.js.map +1 -0
  15. package/dist/argv.d.ts +1 -0
  16. package/dist/argv.js +8 -0
  17. package/dist/argv.js.map +1 -0
  18. package/dist/artifact-reference.d.ts +22 -0
  19. package/dist/artifact-reference.js +47 -0
  20. package/dist/artifact-reference.js.map +1 -0
  21. package/dist/browser-evidence-hygiene.d.ts +7 -0
  22. package/dist/browser-evidence-hygiene.js +40 -0
  23. package/dist/browser-evidence-hygiene.js.map +1 -0
  24. package/dist/claude-agent-sdk.d.ts +94 -0
  25. package/dist/claude-agent-sdk.js +356 -0
  26. package/dist/claude-agent-sdk.js.map +1 -0
  27. package/dist/cli.d.ts +2 -0
  28. package/dist/cli.js +5 -0
  29. package/dist/cli.js.map +1 -0
  30. package/dist/codex-app-server-ui.d.ts +39 -0
  31. package/dist/codex-app-server-ui.js +327 -0
  32. package/dist/codex-app-server-ui.js.map +1 -0
  33. package/dist/codex-app-server.d.ts +141 -0
  34. package/dist/codex-app-server.js +747 -0
  35. package/dist/codex-app-server.js.map +1 -0
  36. package/dist/command-failure.d.ts +29 -0
  37. package/dist/command-failure.js +54 -0
  38. package/dist/command-failure.js.map +1 -0
  39. package/dist/computer-use-actor.d.ts +52 -0
  40. package/dist/computer-use-actor.js +48 -0
  41. package/dist/computer-use-actor.js.map +1 -0
  42. package/dist/computer-use.d.ts +232 -0
  43. package/dist/computer-use.js +615 -0
  44. package/dist/computer-use.js.map +1 -0
  45. package/dist/concurrency.d.ts +1 -0
  46. package/dist/concurrency.js +27 -0
  47. package/dist/concurrency.js.map +1 -0
  48. package/dist/concurrent-shared-world-lab.d.ts +115 -0
  49. package/dist/concurrent-shared-world-lab.js +1092 -0
  50. package/dist/concurrent-shared-world-lab.js.map +1 -0
  51. package/dist/core/git-state.d.ts +31 -0
  52. package/dist/core/git-state.js +142 -0
  53. package/dist/core/git-state.js.map +1 -0
  54. package/dist/core/index.d.ts +4 -0
  55. package/dist/core/index.js +3 -0
  56. package/dist/core/index.js.map +1 -0
  57. package/dist/core/run-primitives.d.ts +66 -0
  58. package/dist/core/run-primitives.js +120 -0
  59. package/dist/core/run-primitives.js.map +1 -0
  60. package/dist/cua-actor-lab.d.ts +620 -0
  61. package/dist/cua-actor-lab.js +2834 -0
  62. package/dist/cua-actor-lab.js.map +1 -0
  63. package/dist/device-presets.d.ts +67 -0
  64. package/dist/device-presets.js +50 -0
  65. package/dist/device-presets.js.map +1 -0
  66. package/dist/e2b-desktop-executor.d.ts +101 -0
  67. package/dist/e2b-desktop-executor.js +309 -0
  68. package/dist/e2b-desktop-executor.js.map +1 -0
  69. package/dist/e2b-desktop-launch.d.ts +144 -0
  70. package/dist/e2b-desktop-launch.js +59 -0
  71. package/dist/e2b-desktop-launch.js.map +1 -0
  72. package/dist/e2b-detached.d.ts +53 -0
  73. package/dist/e2b-detached.js +130 -0
  74. package/dist/e2b-detached.js.map +1 -0
  75. package/dist/e2b-terminal-lab.d.ts +345 -0
  76. package/dist/e2b-terminal-lab.js +1467 -0
  77. package/dist/e2b-terminal-lab.js.map +1 -0
  78. package/dist/env-file.d.ts +14 -0
  79. package/dist/env-file.js +108 -0
  80. package/dist/env-file.js.map +1 -0
  81. package/dist/feedback.d.ts +51 -0
  82. package/dist/feedback.js +313 -0
  83. package/dist/feedback.js.map +1 -0
  84. package/dist/image-evidence.d.ts +2 -0
  85. package/dist/image-evidence.js +33 -0
  86. package/dist/image-evidence.js.map +1 -0
  87. package/dist/index.d.ts +63 -0
  88. package/dist/index.js +33 -0
  89. package/dist/index.js.map +1 -0
  90. package/dist/init-templates.d.ts +12 -0
  91. package/dist/init-templates.js +369 -0
  92. package/dist/init-templates.js.map +1 -0
  93. package/dist/init.d.ts +26 -0
  94. package/dist/init.js +343 -0
  95. package/dist/init.js.map +1 -0
  96. package/dist/lab-app-runner.d.ts +78 -0
  97. package/dist/lab-app-runner.js +403 -0
  98. package/dist/lab-app-runner.js.map +1 -0
  99. package/dist/lab-config.d.ts +578 -0
  100. package/dist/lab-config.js +1751 -0
  101. package/dist/lab-config.js.map +1 -0
  102. package/dist/lab-engine.d.ts +77 -0
  103. package/dist/lab-engine.js +216 -0
  104. package/dist/lab-engine.js.map +1 -0
  105. package/dist/lab-preflight.d.ts +67 -0
  106. package/dist/lab-preflight.js +385 -0
  107. package/dist/lab-preflight.js.map +1 -0
  108. package/dist/labs.d.ts +53 -0
  109. package/dist/labs.js +176 -0
  110. package/dist/labs.js.map +1 -0
  111. package/dist/observer-assets.d.ts +2 -0
  112. package/dist/observer-assets.js +2630 -0
  113. package/dist/observer-assets.js.map +1 -0
  114. package/dist/observer-data.d.ts +63 -0
  115. package/dist/observer-data.js +145 -0
  116. package/dist/observer-data.js.map +1 -0
  117. package/dist/observer-static.d.ts +39 -0
  118. package/dist/observer-static.js +192 -0
  119. package/dist/observer-static.js.map +1 -0
  120. package/dist/observer.d.ts +46 -0
  121. package/dist/observer.js +353 -0
  122. package/dist/observer.js.map +1 -0
  123. package/dist/openai-responses-cu.d.ts +113 -0
  124. package/dist/openai-responses-cu.js +531 -0
  125. package/dist/openai-responses-cu.js.map +1 -0
  126. package/dist/oss-lab.d.ts +52 -0
  127. package/dist/oss-lab.js +299 -0
  128. package/dist/oss-lab.js.map +1 -0
  129. package/dist/oss-meta-lab-scoring.d.ts +15 -0
  130. package/dist/oss-meta-lab-scoring.js +145 -0
  131. package/dist/oss-meta-lab-scoring.js.map +1 -0
  132. package/dist/oss-meta-lab.d.ts +279 -0
  133. package/dist/oss-meta-lab.js +4889 -0
  134. package/dist/oss-meta-lab.js.map +1 -0
  135. package/dist/oss-remote-telemetry.d.ts +77 -0
  136. package/dist/oss-remote-telemetry.js +403 -0
  137. package/dist/oss-remote-telemetry.js.map +1 -0
  138. package/dist/persona.d.ts +35 -0
  139. package/dist/persona.js +133 -0
  140. package/dist/persona.js.map +1 -0
  141. package/dist/pi-agent-core.d.ts +77 -0
  142. package/dist/pi-agent-core.js +203 -0
  143. package/dist/pi-agent-core.js.map +1 -0
  144. package/dist/program.d.ts +54 -0
  145. package/dist/program.js +2347 -0
  146. package/dist/program.js.map +1 -0
  147. package/dist/redaction.d.ts +90 -0
  148. package/dist/redaction.js +292 -0
  149. package/dist/redaction.js.map +1 -0
  150. package/dist/run.d.ts +784 -0
  151. package/dist/run.js +4555 -0
  152. package/dist/run.js.map +1 -0
  153. package/dist/scripted-browser-actor.d.ts +294 -0
  154. package/dist/scripted-browser-actor.js +1286 -0
  155. package/dist/scripted-browser-actor.js.map +1 -0
  156. package/dist/scripted-browser-lab.d.ts +107 -0
  157. package/dist/scripted-browser-lab.js +738 -0
  158. package/dist/scripted-browser-lab.js.map +1 -0
  159. package/dist/shared-world-lab.d.ts +208 -0
  160. package/dist/shared-world-lab.js +1048 -0
  161. package/dist/shared-world-lab.js.map +1 -0
  162. package/dist/source-archive.d.ts +82 -0
  163. package/dist/source-archive.js +365 -0
  164. package/dist/source-archive.js.map +1 -0
  165. package/dist/stop-conditions.d.ts +26 -0
  166. package/dist/stop-conditions.js +66 -0
  167. package/dist/stop-conditions.js.map +1 -0
  168. package/dist/terminal-agent-actor.d.ts +36 -0
  169. package/dist/terminal-agent-actor.js +23 -0
  170. package/dist/terminal-agent-actor.js.map +1 -0
  171. package/docs/architecture/actor-contract.md +431 -0
  172. package/docs/architecture/github-feedback-loop.md +189 -0
  173. package/docs/architecture/local-codex-tui-actor.md +213 -0
  174. package/docs/architecture/observer.md +118 -0
  175. package/docs/architecture/oss-lab-poc.md +242 -0
  176. package/docs/architecture/project-layout.md +166 -0
  177. package/docs/architecture/state-driven-executor.md +203 -0
  178. package/docs/architecture/terminal-product-lane.md +148 -0
  179. package/docs/contracts/adapter-fixtures.md +80 -0
  180. package/docs/contracts/core.md +71 -0
  181. package/docs/contracts/feedback.md +145 -0
  182. package/docs/contracts/policy.md +305 -0
  183. package/docs/contracts/run-bundle.md +358 -0
  184. package/docs/contracts/schemas.md +984 -0
  185. package/docs/goals/current.md +409 -0
  186. package/docs/principles/invariants-and-defaults.md +135 -0
  187. package/docs/principles/self-driving-harness.md +129 -0
  188. package/docs/product/open-source-install-experience.md +229 -0
  189. package/docs/ramp/README.md +200 -0
  190. package/docs/release/open-source-readiness.md +208 -0
  191. package/docs/release/public-readiness-standard.md +205 -0
  192. package/docs/roadmap/world-class-open-source-v0.md +311 -0
  193. package/package.json +102 -8
  194. package/skills/humanish/SKILL.md +244 -0
  195. package/skills/humanish/agents/openai.yaml +7 -0
  196. package/index.js +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stop-conditions.js","sourceRoot":"","sources":["../src/stop-conditions.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ import type { ActorPersonaRef, ActorStatus, ActorCompletionReason, ActorTrace } from "./actor-contract.js";
2
+ /** The fail-closed marker a SLICE-1 live invocation surfaces (never a raw crash). */
3
+ export declare const TERMINAL_AGENT_NOT_IMPLEMENTED_CODE: "HUMANISH_TERMINAL_AGENT_NOT_IMPLEMENTED";
4
+ /**
5
+ * Options the engine hands the terminal agent session. The transport is the captured
6
+ * non-interactive exec stream (stdin disabled), NOT an interactive PTY — see ActorProtocol
7
+ * "terminal-exec" and the goal packet's PTY ruling. Fields here are the SLICE-1 contract; SLICE 2
8
+ * consumes them to drive the in-sandbox `codex exec` and capture its redacted output.
9
+ */
10
+ export interface TerminalAgentSessionOptions {
11
+ /** Where the session writes its native trace/transcript/event-stream artifacts. */
12
+ artifactRoot: string;
13
+ /** The composed prompt the agent runs (mission + persona + public-surface manifest). */
14
+ prompt: string;
15
+ /** Public-safe persona reference (id + composed-prompt digest); no plaintext beyond the mission. */
16
+ persona: ActorPersonaRef;
17
+ /** The product's declared PUBLIC surfaces (http(s) URLs / refs) — the only world the agent sees. */
18
+ publicSurfaces: string[];
19
+ /** Session wall-clock budget (also bounded by scenario.caps.maxMinutes in SLICE 2). */
20
+ timeoutMs: number;
21
+ /** Per-run verdict nonce: the agent must echo it so replayed text can't forge the verdict. */
22
+ verdictNonce: string;
23
+ }
24
+ export interface TerminalAgentSessionResult {
25
+ status: ActorStatus;
26
+ completionReason: ActorCompletionReason;
27
+ reason: string;
28
+ /** The provider-neutral evidence projection (humanish.actor-trace.v1, lane "terminal"). */
29
+ trace: ActorTrace;
30
+ }
31
+ /**
32
+ * Registry-facing session entry. SLICE 1: NOT yet live — the dry-run lab path never calls this,
33
+ * and a live engine call fails closed with a structured error before reaching here. Calling it
34
+ * directly throws the same fail-closed marker rather than pretending to drive a real agent.
35
+ */
36
+ export declare function runTerminalAgentSession(_options: TerminalAgentSessionOptions): Promise<TerminalAgentSessionResult>;
@@ -0,0 +1,23 @@
1
+ // The terminal agent actor: a real autonomous coding agent (Codex) discovering and using a
2
+ // CLI/product from PUBLIC SURFACES ONLY, running INSIDE an E2B shell with command-scoped runtime
3
+ // auth, capturing its non-interactive exec output (stdin disabled) as a redacted event stream +
4
+ // normalized transcript. This is the registry seam for the terminal-product lane.
5
+ //
6
+ // SLICE 1 SCOPE (honest): this module declares the session CONTRACT — the option/result shapes
7
+ // and the registry-facing `runTerminalAgentSession` entry — but the LIVE session is NOT
8
+ // implemented here yet. The dry-run lab path (src/e2b-terminal-lab.ts) never invokes runSession;
9
+ // it builds a contract-only bundle. A live (non-dry-run) call into the engine returns a
10
+ // structured "not yet implemented in this slice" failure, and calling runSession directly throws
11
+ // the same fail-closed marker. SLICE 2 implements the real create -> inject (command-scoped) ->
12
+ // run `codex exec --json` -> capture -> teardown session on the @e2b/desktop commands.run surface.
13
+ /** The fail-closed marker a SLICE-1 live invocation surfaces (never a raw crash). */
14
+ export const TERMINAL_AGENT_NOT_IMPLEMENTED_CODE = "HUMANISH_TERMINAL_AGENT_NOT_IMPLEMENTED";
15
+ /**
16
+ * Registry-facing session entry. SLICE 1: NOT yet live — the dry-run lab path never calls this,
17
+ * and a live engine call fails closed with a structured error before reaching here. Calling it
18
+ * directly throws the same fail-closed marker rather than pretending to drive a real agent.
19
+ */
20
+ export async function runTerminalAgentSession(_options) {
21
+ throw new Error(`${TERMINAL_AGENT_NOT_IMPLEMENTED_CODE}: the live terminal-agent session (in-sandbox Codex exec + command-scoped runtime auth) is implemented in SLICE 2. SLICE 1 only produces the dry-run contract bundle.`);
22
+ }
23
+ //# sourceMappingURL=terminal-agent-actor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"terminal-agent-actor.js","sourceRoot":"","sources":["../src/terminal-agent-actor.ts"],"names":[],"mappings":"AAAA,2FAA2F;AAC3F,iGAAiG;AACjG,gGAAgG;AAChG,kFAAkF;AAClF,EAAE;AACF,+FAA+F;AAC/F,wFAAwF;AACxF,iGAAiG;AACjG,wFAAwF;AACxF,iGAAiG;AACjG,gGAAgG;AAChG,mGAAmG;AAInG,qFAAqF;AACrF,MAAM,CAAC,MAAM,mCAAmC,GAAG,yCAAkD,CAAC;AA+BtG;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAC3C,QAAqC;IAErC,MAAM,IAAI,KAAK,CACb,GAAG,mCAAmC,uKAAuK,CAC9M,CAAC;AACJ,CAAC"}
@@ -0,0 +1,431 @@
1
+ # Actor Contract
2
+
3
+ Date: 2026-06-06 (updated 2026-06-11)
4
+
5
+ Status: accepted design, partially implemented. Shipped: the evidence schema
6
+ `humanish.actor-trace.v1` (`src/actor-contract.ts`) and a registry of five
7
+ actors (`src/actor-registry.ts`: `codex-app-server`, `pi-agent-core`,
8
+ `claude-agent-sdk`, `openai-computer-use`, `scripted-browser`), with
9
+ `actors[0].type` a real dispatch key on the computer-use and scripted-browser
10
+ lab routes. Not yet shipped (roadmap, not near-term claims): the full
11
+ `Actor.run(input)` interface, `RedactionHooks` injection, `ApprovalPolicy`,
12
+ `StagehandCuaActor`, and the `persona-fidelity` verify check. Decision 6's
13
+ capture-time screenshot stance was recanted in 0.6.0; see the inline notes and
14
+ the capture-vs-publish rule in
15
+ [`docs/principles/invariants-and-defaults.md`](../principles/invariants-and-defaults.md).
16
+
17
+ ## Context
18
+
19
+ > 2026-06-11: this section describes the world as it stood when the design was
20
+ > accepted (one real actor, hardcoded dispatch). That ceiling has since been
21
+ > removed — the registry now holds the four actors listed in the status note.
22
+
23
+ An actor is the thing that drives a persona scenario and produces evidence. Today
24
+ Humanish has exactly one real actor: the local Codex integration in
25
+ `src/codex-app-server.ts` (plus the `codex-exec` and `codex-tui` variants in
26
+ `src/run.ts`). The actor selection is a hardcoded `if (actor === ...)` dispatch,
27
+ `RunStream.codex` is Codex-shaped, and the evidence schema is
28
+ `humanish.codex-app-server-trace.v1`.
29
+
30
+ That is a ceiling. Humanish's value is being a public-safe harness for persona and
31
+ agent user-studies, and the agent harnesses our users actually run are plural:
32
+ OpenAI Codex, the pi stack (`@earendil-works/pi-agent-core`, `pi-coding-agent`,
33
+ OpenClaw), Claude Code and the Claude Agent SDK, and computer-use models that
34
+ drive a real screen. A neutral, public-safe way to run the same persona scenario
35
+ across these harnesses, and to compare how each fares against a product, is an
36
+ unoccupied position. To get there, the actor must become a pluggable contract,
37
+ not a hardcoded branch.
38
+
39
+ This document defines that contract and the decisions behind it. It is the API
40
+ surface the adapters depend on, so it is treated as durable: proof artifacts are
41
+ API surface.
42
+
43
+ ## Decisions
44
+
45
+ 1. **Transport-agnostic contract; Codex stays the reference implementation.** The
46
+ contract describes lifecycle and evidence, not transport. An adapter may be a
47
+ subprocess protocol (Codex stdio JSON-RPC, `pi --mode rpc`, `claude -p
48
+ --output-format stream-json`) or an in-process SDK (`pi-agent-core`, Claude
49
+ Agent SDK, Stagehand). The existing Codex app-server integration is the
50
+ reference adapter; `pi-agent-core` is the first in-process-SDK adapter, chosen
51
+ to prove both shapes early.
52
+
53
+ 2. **One normalized evidence schema: `humanish.actor-trace.v1`.** Codex `item/*`
54
+ events, Claude `ToolUse`/`ToolResult` blocks, pi `tool_execution_*` events,
55
+ and computer-use `computer_call` cycles all map onto one `ActorTrace` with a
56
+ typed `items[]`. `humanish.codex-app-server-trace.v1` remains a back-compat
57
+ alias during migration.
58
+
59
+ 3. **A run is multi-turn within one trace; it stops on goal, abandonment,
60
+ unrecoverable failure, or a wall-clock safety timeout, never on a turn cap.**
61
+ Turn count is explicitly rejected as a stop signal: many turns usually means
62
+ legitimate complex progress, so a turn budget truncates real work and rewards
63
+ early quitting (it is a proxy for "too complex," not for "this user would
64
+ quit"). Patience is modeled as **friction tolerance**, not a budget (see
65
+ Persona section). The only hard runaway guard is the existing `timeoutMs`.
66
+ One `ActorRunResult` covers a multi-step scenario.
67
+
68
+ 4. **Redaction is injected once, never re-implemented per adapter.** Every
69
+ adapter receives `RedactionHooks` (the shared secret/path/prompt-digest
70
+ redaction, plus screenshot redaction) and must route all persisted evidence
71
+ through it. This is also where the remaining consolidation of #107 lands.
72
+
73
+ 5. **The registry refuses capability mismatches.** Each adapter declares
74
+ `ActorCapabilities`. A scenario that needs `producesScreenshots` (a GUI
75
+ journey) will not be dispatched to a code-only actor that would fake success
76
+ via the shell. Coverage honesty over green-by-construction.
77
+
78
+ 6. **Computer-use is one lane behind one adapter.** The shipped computer-use
79
+ actor is `openai-computer-use` (fronting the OpenAI Responses adapter); a
80
+ `StagehandCuaActor` fronting the other raw-pixel providers (Anthropic
81
+ computer-use, Gemini) is not-yet-shipped roadmap. Screenshots are the
82
+ largest new public-safety surface, and the original stance here
83
+ (field-blurred plus OCR-scrubbed before any public artifact, fail-closed)
84
+ enforced a default at capture-time; 0.6.0 recanted it. Current policy:
85
+ frames are retained raw and full-fidelity by default in the gitignored
86
+ `.humanish/` tree (never emitted by a publish command; this repo's CI
87
+ binary-asset scan additionally blocks them from commit), and
88
+ `policies.redactScreenshots: true` blurs at capture for share-as-is
89
+ bundles. See the capture-vs-publish rule in
90
+ [`docs/principles/invariants-and-defaults.md`](../principles/invariants-and-defaults.md).
91
+
92
+ ## The contract
93
+
94
+ ```ts
95
+ export const ACTOR_TRACE_SCHEMA = "humanish.actor-trace.v1";
96
+
97
+ export type ActorStatus = "passed" | "failed" | "blocked" | "timed_out";
98
+
99
+ export type ActorCompletionReason =
100
+ | "goal_satisfied" // scenario success predicate met
101
+ | "turn_completed" // harness saw an explicit done signal, no predicate
102
+ | "gave_up" // persona abandoned in character: friction exceeded its tolerance
103
+ | "blocked_approval" // an action was auto-declined and the actor could not proceed
104
+ | "timed_out"
105
+ | "actor_error"
106
+ | "step_failed" // a deterministic scripted step/expectation evaluated false: the
107
+ // SUBJECT failed the script; the harness executed faithfully
108
+ // (distinct from actor_error/harness_error)
109
+ | "harness_error";
110
+
111
+ // One normalized evidence row. Codex item/*, Claude ToolUse/ToolResult,
112
+ // pi tool_execution_*, and computer_call cycles all collapse onto this.
113
+ export interface ActorTraceItem {
114
+ id: string;
115
+ kind:
116
+ | "message" | "reasoning" | "tool_call" | "command" | "file_change"
117
+ | "approval" | "screenshot" | "ui_action" | "plan" | "notice";
118
+ lifecycle: "started" | "completed";
119
+ status?: string;
120
+ title: string; // redacted, <= 120 chars
121
+ tool?: { server?: string; name?: string };
122
+ command?: { text?: string; cwd?: string; exitCode?: number; outputTail?: string };
123
+ screenshotRef?: { path: string; redaction: "blurred" | "ocr_scrubbed" | "none" };
124
+ text?: string; // redacted
125
+ }
126
+
127
+ export interface ActorCapabilities {
128
+ headless: boolean;
129
+ structuredTrace: boolean;
130
+ lanes: Array<"code" | "app" | "computer-use" | "scripted-browser">;
131
+ producesScreenshots: boolean;
132
+ byoModel: boolean;
133
+ preGrantableApprovals: boolean; // can run unattended without a human prompt
134
+ inProcessTools: boolean; // can inject product tools without a subprocess
135
+ license: "open" | "source-available" | "proprietary";
136
+ }
137
+
138
+ export interface ActorTrace {
139
+ schema: typeof ACTOR_TRACE_SCHEMA;
140
+ provider: string; // "codex-app-server" | "pi-agent-core" | "claude-agent-sdk" | "openai-responses-cu" | "browser-persona"
141
+ providerVersion?: string;
142
+ protocol: "json-rpc" | "json-stream" | "in-process-sdk" | "cua-loop" | "scripted-steps";
143
+ lane: "code" | "app" | "computer-use" | "scripted-browser";
144
+ persona: { id: string; traitsApplied: string[]; promptDigest: string }; // proves traits were threaded
145
+ // "raw" = full-fidelity frames retained (valid for LOCAL use; redact before
146
+ // publishing); "blurred"/"ocr_scrubbed" = publish-safe; "n/a" = none captured.
147
+ redaction: { status: "passed"; screenshots: "n/a" | "raw" | "blurred" | "ocr_scrubbed"; notes: string };
148
+ startedAt: string; completedAt: string; durationMs: number;
149
+ status: ActorStatus; completionReason: ActorCompletionReason; reason: string;
150
+ ids: { sessionId?: string; threadId?: string; turnId?: string; model?: string };
151
+ counts: Record<string, number>;
152
+ items: ActorTraceItem[];
153
+ tokenUsage?: { input?: number; output?: number; total?: number; costUsd?: number };
154
+ capabilities: ActorCapabilities;
155
+ }
156
+
157
+ export interface ApprovalPolicy {
158
+ mode: "auto-decline" | "pre-grant-allowlist" | "deny-all";
159
+ allow?: string[]; // e.g. ["read:*", "bash:git diff *", "mcp__browser__*"]
160
+ onRequest(req: ApprovalRequest): ApprovalDecision; // adapter calls; harness records every call
161
+ }
162
+
163
+ export interface RedactionHooks {
164
+ redactText(s: string): string;
165
+ publicPath(p: string, root: string): string;
166
+ redactScreenshot(buf: Buffer, meta: ScreenshotMeta): Promise<{ buf: Buffer; method: "blurred" | "ocr_scrubbed" }>;
167
+ promptForLog(raw: string): { placeholder: string; digest: string; length: number };
168
+ }
169
+
170
+ export interface ActorRunInput {
171
+ cwd: string;
172
+ runRoot: string; // where the adapter writes events/summary/transcript
173
+ timeoutMs: number;
174
+ persona: ResolvedPersona; // FULL traits, not just {id, name}
175
+ scenario: { id: string; title: string; goal: string; successText?: string[] };
176
+ laneFocus?: { id: string; label: string; instruction: string };
177
+ approval: ApprovalPolicy;
178
+ redaction: RedactionHooks;
179
+ model?: string;
180
+ actorCommand?: string[]; // override binary / transport
181
+ signal: AbortSignal;
182
+ }
183
+
184
+ export interface ActorRunResult {
185
+ status: ActorStatus;
186
+ completionReason: ActorCompletionReason;
187
+ reason: string;
188
+ durationMs: number;
189
+ trace: ActorTrace;
190
+ transcriptPath: string; tracePath: string; eventsPath: string;
191
+ tail: string;
192
+ }
193
+
194
+ export interface Actor {
195
+ readonly id: string; // "codex-app-server"
196
+ capabilities(): ActorCapabilities;
197
+ run(input: ActorRunInput): Promise<ActorRunResult>;
198
+ }
199
+ ```
200
+
201
+ ### Contract semantics (every adapter must guarantee)
202
+
203
+ - **Lifecycle.** connect/spawn, initialize, apply persona + scenario as system or
204
+ turn input, drive a bounded turn loop honoring `timeoutMs` and `signal`, emit a
205
+ single explicit `completionReason`, tear down. No adapter may block waiting on a
206
+ human.
207
+ - **Evidence.** Write the same three artifacts (`events.ndjson` redacted
208
+ envelopes, `summary.json` = `ActorTrace`, `transcript.txt` human view) so the
209
+ run-bundle wiring is provider-agnostic.
210
+ - **Approvals.** Call `approval.onRequest`; never embed adapter-local decline
211
+ strings. Every call is recorded as an `items[kind=approval]`.
212
+ - **Redaction.** Use the injected `RedactionHooks`. Never re-implement redaction
213
+ per adapter.
214
+ - **Diagnostics.** Unexpected actor-loop failures are recorded as
215
+ `items[kind=notice,status=error]`, not raw crash dumps. Keep diagnostic notices
216
+ public-safe: redacted message, coarse loop phase, last normalized UI action,
217
+ and last screenshot reference only. Do not persist raw stacks, env values,
218
+ target URLs, or unredacted provider payloads in the trace.
219
+ - **Capabilities.** Declare them honestly; the registry uses them to refuse
220
+ unsuitable dispatch.
221
+
222
+ ## The scripted-browser lane (shipped)
223
+
224
+ `scripted-browser` is the deterministic, model-free browser-actuation lane — distinct from
225
+ `computer-use` (raw pixels + a model deciding actions) and `app`. The registered
226
+ `scripted-browser` actor (`src/scripted-browser-actor.ts`) replays a committed scenario's
227
+ browser steps with playwright against a loopback app; the steps ARE the behavior, so
228
+ `byoModel: false` means there is NO model, and `tokenUsage` records zeros as an affirmative
229
+ $0 declaration that is true by mechanism (no provider client is importable from that code
230
+ path). Its trace keeps the concrete driver name `provider: "browser-persona"` (matching the
231
+ native `humanish.browser-persona-trace.v1` it also emits) with `protocol: "scripted-steps"`.
232
+
233
+ Completion semantics: `goal_satisfied` means the scenario's `expect` blocks — the success
234
+ predicate — all held ("the app still affords this exact journey", nothing about user
235
+ behavior); `step_failed` means a deterministic step or expectation evaluated false (the
236
+ subject failed the script; the harness ran faithfully); `timed_out` is the journey wall-clock
237
+ budget; `harness_error` is a browser that could not launch. `gave_up` and `blocked_approval`
238
+ are unreachable — no persona patience, no approvals exist on a deterministic replay.
239
+
240
+ Actuation-vs-spend gate: on the scripted lab route `scenario.mode: live` is still required
241
+ even though provider spend is $0 by mechanism. The gate's justification there is ACTUATION,
242
+ not cost — a live scripted run drives a real browser against a real running app
243
+ (state-mutating effects on the operator's app), which deserves the same affirmative
244
+ declaration as spend. "Live" on this route must never silently come to mean "costs money";
245
+ this paragraph is the record of that decision.
246
+
247
+ ## The state-driven executor seam (shipped — the transport-agnostic intent, made real)
248
+
249
+ The `CuaExecutor` / `CuaProvider` ports are the concrete realization of the "plural harnesses /
250
+ transport-agnostic" intent above: the computer-use loop does not require a screen or a vision
251
+ model. A library caller can drive an **already-running local app** through its in-process JS
252
+ contract (`window.app.getState()` etc.) with a custom `CuaExecutor` (screenshot optional,
253
+ `appState` as the progress signal) paired with a **non-vision** `CuaProvider` (`requiresFrame`
254
+ falsey), keeping the whole lab composition with NO E2B desktop and NO clone. See
255
+ [`state-driven-executor.md`](./state-driven-executor.md) for the port, both entry points
256
+ (`runComputerUseLoop` and `runLab` + `buildExecutor`/`buildProvider`), the `subject.source:
257
+ local-app` config surface, the `requiresFrame` provider-authoring contract, and the
258
+ appState-is-runtime-only stance.
259
+
260
+ ## The product-adapter extension seam (shipped — terminal-product lane, layer 6)
261
+
262
+ The terminal-product lane carries the proof-roadmap layer-6 deliverable: a product
263
+ adopter attaches product-specific scoring + feedback as a THIN in-repo extension
264
+ WITHOUT forking core. The seam is exported contract types (`RunBundle`,
265
+ `RunFeedbackCandidate`, `RunAdapterScore`, `RunMeaningfulUseScore`, `ActorTrace`,
266
+ the terminal-lane `TerminalProductScoringContext` / `TerminalLedgers` / ...) plus a
267
+ registrable `score` / `deriveFeedback` DI hook on `TerminalProductLabHooks` (mirror
268
+ of the `CuaActorLabHooks` DI seam). The adapter records its product nouns ONLY under
269
+ an adapter-NAMESPACED block (`RunFeedbackCandidate.adapter` /
270
+ `RunAdapterScore.{namespace,data}`), so core's enums stay product-agnostic — no
271
+ adopter noun is hardcoded into a core enum. Default (no hook) behavior is unchanged.
272
+ See [`terminal-product-lane.md`](./terminal-product-lane.md#slice-4--the-product-adapter-extension-seam-layer-6)
273
+ for the full seam and the thin-adapter conformance proof.
274
+
275
+ ## Making personas load-bearing
276
+
277
+ The bug, grounded in code: `loadDryRunSelection` (`src/run.ts`) parses persona
278
+ YAML down to `{ id, name, source, sourceDigest }` and discards `summary`,
279
+ `traits.{patience, technical_confidence, accessibility_needs}`, and `constraints`.
280
+ The prompt builders then inject one line: `Persona: ${name}`. The persona is a
281
+ label.
282
+
283
+ Plan:
284
+
285
+ 1. **Parse the whole persona** into a `ResolvedPersona`:
286
+ `{ id, name, summary, goals[], traits: { patience, skill, accessibilityNeeds? }, constraints[], sourceDigest }`
287
+ (map `technical_confidence` to `skill`).
288
+ 2. **Compile traits into actor-neutral directives**, not prose, via a pure
289
+ `personaToDirectives(p)`:
290
+ - patience -> `frictionTolerance`: how much failure, dead-end, or
291
+ no-forward-progress the persona absorbs before abandoning the task in
292
+ character. Expressed as an instruction the actor embodies ("you are
293
+ impatient: if you hit repeated friction or stop making progress toward your
294
+ goal, stop and report exactly what blocked you"), not a turn or tool-call
295
+ count.
296
+ - skill -> tool/strategy bias (low-skill avoids CLI/flags and narrates
297
+ confusion at ambiguous UI; high-skill uses shortcuts and recovery paths).
298
+ - accessibilityNeeds -> concrete behavior (keyboard_first navigates by
299
+ keyboard and fails a step that is mouse-only; clear_terminal_output flags
300
+ noisy output as a defect).
301
+ - goals + constraints become explicit success / forbidden lists for the
302
+ scenario predicate.
303
+ 3. **Abandonment is persona-judged, harness-corroborated, never a counter.** The
304
+ persona-actor (an LLM embodying that user) decides in character when the
305
+ friction is no longer worth it and stops with `completionReason: "gave_up"`,
306
+ citing the specific friction. The harness corroborates with objective signals
307
+ it already sees in the stream (consecutive failed/blocked actions,
308
+ repeated-identical-action looping, no progress toward the success predicate)
309
+ and annotates the abandonment friction as a feedback candidate. The harness
310
+ imposes no turn cap; its only hard stop is the wall-clock `timeoutMs`.
311
+ 4. **Bind the same directives per harness**: pi (`systemPrompt` +
312
+ `beforeToolCall` allow rules), Claude (`system_prompt`/`--append-system-prompt`
313
+ + `allowedTools`), Codex (prepend to `turn/start` input), Stagehand (agent
314
+ context + action policy). Each binds the friction-tolerance, skill, and
315
+ accessibility directives identically; none uses a `max_turns`-style cap as the
316
+ persona stop condition.
317
+ 5. **Prove it.** `ActorTrace.persona.traitsApplied` lists the injected
318
+ directives; a `persona-fidelity` verify check asserts that the friction and
319
+ accessibility directives reached the actor input and that a `gave_up` run
320
+ cites a concrete friction reason (not a turn count). "Did the persona drive
321
+ the run" becomes a verifiable artifact, not an assertion.
322
+ (Status 2026-06-11: `personaToDirectives` shipped in `src/persona.ts` and
323
+ `traitsApplied` is threaded on the codex routes, but the `persona-fidelity`
324
+ verify check is not-yet-shipped roadmap, and the computer-use route stubs
325
+ `persona.traitsApplied` to `[]` today — see `src/cua-actor-lab.ts`.)
326
+
327
+ ## Decision: how abandonment is adjudicated
328
+
329
+ "When does a synthetic persona give up?" has no obvious best answer, so the
330
+ choice is recorded here rather than left implicit and silently re-litigated.
331
+
332
+ Options considered:
333
+
334
+ 1. **Persona-judged only** (the LLM decides in character, uncorroborated).
335
+ Truest embodiment, but LLM stop behavior is erratic (often too stubborn,
336
+ sometimes too eager), non-reproducible, and hard to verify. A purely
337
+ model-judged stop can also run uselessly to the wall-clock timeout.
338
+ 2. **Harness-adjudicated from objective signals only** (no-progress,
339
+ repeated-failure, looping); the persona just sets a numeric threshold.
340
+ Deterministic and reproducible, but mechanical, misses the subjective "this
341
+ is not worth it" judgment that is the whole point of a persona, and a fixed
342
+ threshold quietly drifts back toward a disguised counter.
343
+ 3. **Persona-judged primary, harness-corroborated backstop.** The actor decides
344
+ in character and emits `gave_up` with the friction; the harness independently
345
+ tracks objective signals and (a) annotates the friction as a feedback
346
+ candidate, and (b) force-ends only on unambiguous pathology (e.g. repeated
347
+ identical failed actions = a loop, or no progress past a wall-clock
348
+ checkpoint) so a too-stubborn model cannot waste the entire timeout.
349
+
350
+ **Decision: option 3.** It keeps the behavior emergent and persona-faithful (the
351
+ value) while the objective backstop adds reproducibility and bounds a stubborn
352
+ model. Every backstop signal is progress- or friction-based, never a turn or
353
+ tool-call count.
354
+
355
+ Non-obvious tradeoffs to revisit with real-run data:
356
+
357
+ - The backstop thresholds (what counts as "looping" or "no progress") are
358
+ themselves judgment calls. Start conservative (fire only on unambiguous
359
+ pathology) and tune against real runs, logging when the backstop fires versus
360
+ when the persona self-abandons, so the split stays mostly persona-judged.
361
+ - Friction tolerance per patience level is qualitative in the prompt, not a
362
+ number. If "impatient" proves too soft, escalate by feeding the actor explicit
363
+ running friction context ("you have hit 3 dead-ends"), still never a turn
364
+ count.
365
+ - `persona-fidelity` treats a `gave_up` with no cited friction as a fidelity
366
+ failure, not an accepted stop, so the model cannot quietly quit for no reason.
367
+
368
+ This keeps patience load-bearing and reproducible without ever using elapsed
369
+ turns as a stop signal.
370
+
371
+ ## Capability matrix (target adapters)
372
+
373
+ | Adapter | headless | structured trace | sandbox | BYO model | license | actor fit |
374
+ | --- | --- | --- | --- | --- | --- | --- |
375
+ | codex-app-server (reference) | yes (stdio JSON-RPC) | typed item/* | OS Seatbelt/seccomp + approvalPolicy | OpenAI-first | Apache-2.0 | code |
376
+ | pi-agent-core (first new) | yes (SDK + rpc/json) | event stream + session JSONL + token/cost | BYO container + hook gating | 15+ providers, local | MIT | code, app |
377
+ | claude-agent-sdk | yes (SDK + `-p` stream-json) | typed ToolUse/ToolResult + cost | OS sandbox + dontAsk/allowedTools | Anthropic-centric | SDK MIT (CLI proprietary) | code, app |
378
+ | stagehand-cua (roadmap, not shipped; `openai-computer-use` is the shipped computer-use actor) | yes (SDK, mode:'cua') | structured results + replay | Playwright/Browserbase isolation | OpenAI/Anthropic/Google | MIT | computer-use |
379
+
380
+ ## Sequencing
381
+
382
+ 1. This document.
383
+ 2. Shared `RedactionHooks` module (completes the remaining #107 criterion) plus
384
+ the `Actor` contract types, with the Codex integration refactored to implement
385
+ `Actor` and emit `ActorTrace` behind the back-compat alias. Add an
386
+ `actorRegistry`; generalize `RunStream.codex` to `RunStream.actor`.
387
+ 3. Personas load-bearing: `ResolvedPersona`, `personaToDirectives`, harness turn
388
+ budget, and the `persona-fidelity` verify check.
389
+ 4. `pi-agent-core` adapter (proves the contract against a non-Codex protocol;
390
+ local-model dogfood for ~$0). Landed in two slices: first the pure
391
+ `piSessionToActorTrace` mapper + registry generalization (discriminated
392
+ `ActorDescriptor` union + `getActor` overloads) + a fixture conformance test,
393
+ with no pi dependency and no model key required (proves the evidence contract
394
+ is provider-neutral); then a follow-up live SDK shim behind a DI seam, deferred
395
+ until the package identity (`@earendil-works/pi-agent-core` vs
396
+ `@mariozechner/pi-coding-agent`) and the Node `>=22.19` vs engines `>=20` gap
397
+ are pinned against an installed build.
398
+ 5. `claude-agent-sdk` adapter (the `app` lane).
399
+ 6. Computer-use lane. (Shipped as `openai-computer-use` — registered 0.3.0,
400
+ lab-dispatched 0.4.0; `stagehand-cua` as a multi-provider front remains
401
+ not-yet-shipped roadmap.)
402
+ 7. Cross-harness conformance test: one persona x scenario through every adapter,
403
+ asserting identical trace shape, completion vocabulary, and redaction status.
404
+ 8. The proof point: run the harness-plural loop against popular OSS repos and turn
405
+ real, merged issues into the receipt.
406
+
407
+ ## Risks
408
+
409
+ - Protocol/version drift across four moving harnesses. Pin every binary/SDK and
410
+ assert the init handshake; the registry refuses an actor whose declared
411
+ capabilities do not satisfy the scenario.
412
+ - Screenshot PII in the computer-use lane. Redaction binds the PUBLISH
413
+ boundary, not capture (0.6.0): raw frames stay local in gitignored
414
+ `.humanish/` and are never emitted by a publish command (this repo's CI
415
+ binary-asset scan additionally blocks them from commit);
416
+ `policies.redactScreenshots: true` blurs at capture for share-as-is
417
+ bundles. The earlier fail-closed redacted-thumbnail default was recanted —
418
+ see the capture-vs-publish rule in
419
+ [`docs/principles/invariants-and-defaults.md`](../principles/invariants-and-defaults.md).
420
+ - Persona directives regressing into decoration. Friction tolerance and
421
+ accessibility must demonstrably reach the actor input and change step pass/fail,
422
+ enforced by the `persona-fidelity` check; a `gave_up` run must cite a concrete
423
+ friction, never an elapsed-turn count.
424
+ - License contamination. Proprietary harnesses (Claude CLI, Cursor) sit behind
425
+ adapters; only their open SDKs are depended on directly.
426
+
427
+ ## References
428
+
429
+ - Self-driving harness principles: `docs/principles/self-driving-harness.md`.
430
+ - Observer architecture: `docs/architecture/observer.md`.
431
+ - Related issues: shared redaction module (#107), PII/PHI detector (#108).