humanish 0.0.1 → 0.15.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (205) hide show
  1. package/AGENTS.md +77 -0
  2. package/CONTRIBUTING.md +39 -0
  3. package/LICENSE +21 -0
  4. package/README.md +437 -8
  5. package/SECURITY.md +34 -0
  6. package/dist/actor-contract.d.ts +107 -0
  7. package/dist/actor-contract.js +286 -0
  8. package/dist/actor-contract.js.map +1 -0
  9. package/dist/actor-registry.d.ts +73 -0
  10. package/dist/actor-registry.js +93 -0
  11. package/dist/actor-registry.js.map +1 -0
  12. package/dist/adapter-extension.d.ts +54 -0
  13. package/dist/adapter-extension.js +214 -0
  14. package/dist/adapter-extension.js.map +1 -0
  15. package/dist/argv.d.ts +1 -0
  16. package/dist/argv.js +8 -0
  17. package/dist/argv.js.map +1 -0
  18. package/dist/artifact-reference.d.ts +22 -0
  19. package/dist/artifact-reference.js +47 -0
  20. package/dist/artifact-reference.js.map +1 -0
  21. package/dist/browser-evidence-hygiene.d.ts +7 -0
  22. package/dist/browser-evidence-hygiene.js +40 -0
  23. package/dist/browser-evidence-hygiene.js.map +1 -0
  24. package/dist/claude-agent-sdk.d.ts +94 -0
  25. package/dist/claude-agent-sdk.js +363 -0
  26. package/dist/claude-agent-sdk.js.map +1 -0
  27. package/dist/cli.d.ts +2 -0
  28. package/dist/cli.js +5 -0
  29. package/dist/cli.js.map +1 -0
  30. package/dist/codex-app-server-ui.d.ts +39 -0
  31. package/dist/codex-app-server-ui.js +357 -0
  32. package/dist/codex-app-server-ui.js.map +1 -0
  33. package/dist/codex-app-server.d.ts +144 -0
  34. package/dist/codex-app-server.js +754 -0
  35. package/dist/codex-app-server.js.map +1 -0
  36. package/dist/command-failure.d.ts +29 -0
  37. package/dist/command-failure.js +54 -0
  38. package/dist/command-failure.js.map +1 -0
  39. package/dist/computer-use-actor.d.ts +52 -0
  40. package/dist/computer-use-actor.js +48 -0
  41. package/dist/computer-use-actor.js.map +1 -0
  42. package/dist/computer-use.d.ts +232 -0
  43. package/dist/computer-use.js +615 -0
  44. package/dist/computer-use.js.map +1 -0
  45. package/dist/concurrency.d.ts +1 -0
  46. package/dist/concurrency.js +27 -0
  47. package/dist/concurrency.js.map +1 -0
  48. package/dist/concurrent-shared-world-lab.d.ts +115 -0
  49. package/dist/concurrent-shared-world-lab.js +1094 -0
  50. package/dist/concurrent-shared-world-lab.js.map +1 -0
  51. package/dist/core/git-state.d.ts +33 -0
  52. package/dist/core/git-state.js +277 -0
  53. package/dist/core/git-state.js.map +1 -0
  54. package/dist/core/git-workspace.d.ts +31 -0
  55. package/dist/core/git-workspace.js +447 -0
  56. package/dist/core/git-workspace.js.map +1 -0
  57. package/dist/core/index.d.ts +4 -0
  58. package/dist/core/index.js +3 -0
  59. package/dist/core/index.js.map +1 -0
  60. package/dist/core/run-primitives.d.ts +66 -0
  61. package/dist/core/run-primitives.js +120 -0
  62. package/dist/core/run-primitives.js.map +1 -0
  63. package/dist/cua-actor-lab.d.ts +621 -0
  64. package/dist/cua-actor-lab.js +2845 -0
  65. package/dist/cua-actor-lab.js.map +1 -0
  66. package/dist/device-presets.d.ts +67 -0
  67. package/dist/device-presets.js +50 -0
  68. package/dist/device-presets.js.map +1 -0
  69. package/dist/e2b-desktop-executor.d.ts +101 -0
  70. package/dist/e2b-desktop-executor.js +309 -0
  71. package/dist/e2b-desktop-executor.js.map +1 -0
  72. package/dist/e2b-desktop-launch.d.ts +144 -0
  73. package/dist/e2b-desktop-launch.js +59 -0
  74. package/dist/e2b-desktop-launch.js.map +1 -0
  75. package/dist/e2b-detached.d.ts +53 -0
  76. package/dist/e2b-detached.js +130 -0
  77. package/dist/e2b-detached.js.map +1 -0
  78. package/dist/e2b-terminal-lab.d.ts +345 -0
  79. package/dist/e2b-terminal-lab.js +1532 -0
  80. package/dist/e2b-terminal-lab.js.map +1 -0
  81. package/dist/env-file.d.ts +14 -0
  82. package/dist/env-file.js +108 -0
  83. package/dist/env-file.js.map +1 -0
  84. package/dist/feedback.d.ts +51 -0
  85. package/dist/feedback.js +351 -0
  86. package/dist/feedback.js.map +1 -0
  87. package/dist/image-evidence.d.ts +2 -0
  88. package/dist/image-evidence.js +33 -0
  89. package/dist/image-evidence.js.map +1 -0
  90. package/dist/index.d.ts +63 -0
  91. package/dist/index.js +33 -0
  92. package/dist/index.js.map +1 -0
  93. package/dist/init-templates.d.ts +12 -0
  94. package/dist/init-templates.js +369 -0
  95. package/dist/init-templates.js.map +1 -0
  96. package/dist/init.d.ts +26 -0
  97. package/dist/init.js +429 -0
  98. package/dist/init.js.map +1 -0
  99. package/dist/lab-app-runner.d.ts +78 -0
  100. package/dist/lab-app-runner.js +403 -0
  101. package/dist/lab-app-runner.js.map +1 -0
  102. package/dist/lab-config.d.ts +583 -0
  103. package/dist/lab-config.js +1789 -0
  104. package/dist/lab-config.js.map +1 -0
  105. package/dist/lab-engine.d.ts +77 -0
  106. package/dist/lab-engine.js +216 -0
  107. package/dist/lab-engine.js.map +1 -0
  108. package/dist/lab-preflight.d.ts +67 -0
  109. package/dist/lab-preflight.js +385 -0
  110. package/dist/lab-preflight.js.map +1 -0
  111. package/dist/labs.d.ts +53 -0
  112. package/dist/labs.js +382 -0
  113. package/dist/labs.js.map +1 -0
  114. package/dist/observer-assets.d.ts +2 -0
  115. package/dist/observer-assets.js +2630 -0
  116. package/dist/observer-assets.js.map +1 -0
  117. package/dist/observer-data.d.ts +63 -0
  118. package/dist/observer-data.js +145 -0
  119. package/dist/observer-data.js.map +1 -0
  120. package/dist/observer-static.d.ts +39 -0
  121. package/dist/observer-static.js +306 -0
  122. package/dist/observer-static.js.map +1 -0
  123. package/dist/observer.d.ts +46 -0
  124. package/dist/observer.js +550 -0
  125. package/dist/observer.js.map +1 -0
  126. package/dist/openai-responses-cu.d.ts +113 -0
  127. package/dist/openai-responses-cu.js +545 -0
  128. package/dist/openai-responses-cu.js.map +1 -0
  129. package/dist/oss-lab.d.ts +52 -0
  130. package/dist/oss-lab.js +344 -0
  131. package/dist/oss-lab.js.map +1 -0
  132. package/dist/oss-meta-lab-scoring.d.ts +15 -0
  133. package/dist/oss-meta-lab-scoring.js +145 -0
  134. package/dist/oss-meta-lab-scoring.js.map +1 -0
  135. package/dist/oss-meta-lab.d.ts +277 -0
  136. package/dist/oss-meta-lab.js +4529 -0
  137. package/dist/oss-meta-lab.js.map +1 -0
  138. package/dist/oss-remote-telemetry.d.ts +77 -0
  139. package/dist/oss-remote-telemetry.js +403 -0
  140. package/dist/oss-remote-telemetry.js.map +1 -0
  141. package/dist/persona.d.ts +35 -0
  142. package/dist/persona.js +133 -0
  143. package/dist/persona.js.map +1 -0
  144. package/dist/pi-agent-core.d.ts +77 -0
  145. package/dist/pi-agent-core.js +203 -0
  146. package/dist/pi-agent-core.js.map +1 -0
  147. package/dist/program.d.ts +54 -0
  148. package/dist/program.js +2349 -0
  149. package/dist/program.js.map +1 -0
  150. package/dist/redaction.d.ts +90 -0
  151. package/dist/redaction.js +292 -0
  152. package/dist/redaction.js.map +1 -0
  153. package/dist/run-paths.d.ts +56 -0
  154. package/dist/run-paths.js +340 -0
  155. package/dist/run-paths.js.map +1 -0
  156. package/dist/run.d.ts +794 -0
  157. package/dist/run.js +4844 -0
  158. package/dist/run.js.map +1 -0
  159. package/dist/scripted-browser-actor.d.ts +297 -0
  160. package/dist/scripted-browser-actor.js +1399 -0
  161. package/dist/scripted-browser-actor.js.map +1 -0
  162. package/dist/scripted-browser-lab.d.ts +107 -0
  163. package/dist/scripted-browser-lab.js +788 -0
  164. package/dist/scripted-browser-lab.js.map +1 -0
  165. package/dist/selected-output-paths.d.ts +49 -0
  166. package/dist/selected-output-paths.js +354 -0
  167. package/dist/selected-output-paths.js.map +1 -0
  168. package/dist/shared-world-lab.d.ts +208 -0
  169. package/dist/shared-world-lab.js +1049 -0
  170. package/dist/shared-world-lab.js.map +1 -0
  171. package/dist/source-archive.d.ts +82 -0
  172. package/dist/source-archive.js +408 -0
  173. package/dist/source-archive.js.map +1 -0
  174. package/dist/stop-conditions.d.ts +26 -0
  175. package/dist/stop-conditions.js +66 -0
  176. package/dist/stop-conditions.js.map +1 -0
  177. package/dist/terminal-agent-actor.d.ts +36 -0
  178. package/dist/terminal-agent-actor.js +23 -0
  179. package/dist/terminal-agent-actor.js.map +1 -0
  180. package/docs/architecture/actor-contract.md +431 -0
  181. package/docs/architecture/github-feedback-loop.md +189 -0
  182. package/docs/architecture/local-codex-tui-actor.md +213 -0
  183. package/docs/architecture/observer.md +118 -0
  184. package/docs/architecture/oss-lab-poc.md +250 -0
  185. package/docs/architecture/project-layout.md +166 -0
  186. package/docs/architecture/state-driven-executor.md +203 -0
  187. package/docs/architecture/terminal-product-lane.md +148 -0
  188. package/docs/contracts/adapter-fixtures.md +80 -0
  189. package/docs/contracts/core.md +71 -0
  190. package/docs/contracts/feedback.md +145 -0
  191. package/docs/contracts/policy.md +311 -0
  192. package/docs/contracts/run-bundle.md +358 -0
  193. package/docs/contracts/schemas.md +984 -0
  194. package/docs/goals/current.md +428 -0
  195. package/docs/principles/invariants-and-defaults.md +135 -0
  196. package/docs/principles/self-driving-harness.md +129 -0
  197. package/docs/product/open-source-install-experience.md +237 -0
  198. package/docs/ramp/README.md +207 -0
  199. package/docs/release/open-source-readiness.md +208 -0
  200. package/docs/release/public-readiness-standard.md +205 -0
  201. package/docs/roadmap/world-class-open-source-v0.md +311 -0
  202. package/package.json +102 -8
  203. package/skills/humanish/SKILL.md +244 -0
  204. package/skills/humanish/agents/openai.yaml +7 -0
  205. package/index.js +0 -1
@@ -0,0 +1,250 @@
1
+ # Maintainer OSS Meta-Lab
2
+
3
+ Date: 2026-06-01
4
+
5
+ Status: implemented as an experimental repo-owned lab manifest plus
6
+ compatibility aliases.
7
+
8
+ Safety amendment (2026-07-14): beginning with `0.15.1`, the bundled `oss`
9
+ manifest is a contract-only dry-run. A direct live OSS meta-lab request fails
10
+ with `HUMANISH_OSS_META_LIVE_ISOLATION_REQUIRED` before callbacks, filesystem
11
+ or network side effects, credential forwarding, or provider launch. The
12
+ historical design and evidence description below is preserved as a record; it
13
+ is not current execution guidance. The separate `oss-smoke` clone/discard lane
14
+ remains available for public repositories.
15
+
16
+ ## Decision
17
+
18
+ `humanish/labs/oss.yaml` is this repo's authorized-repo meta-simulation
19
+ dogfood loop. It is intentionally a lab manifest, not the canonical consumer
20
+ shape. Consumer projects should author their own `humanish/labs/*.yaml` files
21
+ and run them with `humanish watch <lab>` or `humanish lab run <lab>`.
22
+
23
+ The lab should feel like `humanish watch`: it opens the Observer and, for human
24
+ output, keeps the shell attached. Its top-level Observer is an
25
+ Observer-of-Observers: each lane represents a headed E2B desktop assigned to a
26
+ GitHub `owner/repo` slug. Inside each desktop, the bootstrap clones the repo,
27
+ gets it into local dev mode where feasible, installs and initializes Humanish,
28
+ runs nested Humanish proof commands, starts the target app when a runnable
29
+ script is present, opens desktop/mobile app windows plus the nested Observer in
30
+ the E2B browser, and starts a nonblocking Codex actor attempt.
31
+
32
+ The previous clone/discard proof loop remains useful, but it is now explicitly
33
+ named `humanish/labs/oss-smoke.yaml`.
34
+
35
+ ## Commands
36
+
37
+ Main operator path:
38
+
39
+ ```bash
40
+ humanish watch oss
41
+ humanish lab run oss
42
+ humanish lab run oss --repos CorentinTh/it-tools,drawdb-io/drawdb,maciekt07/TodoApp,lissy93/dashy
43
+ humanish lab run oss --repo CorentinTh/it-tools --repo drawdb-io/drawdb --count 4
44
+ ```
45
+
46
+ Agent/CI contract path:
47
+
48
+ ```bash
49
+ humanish lab run oss --dry-run --json --no-open
50
+ ```
51
+
52
+ Disposable clone smoke path:
53
+
54
+ ```bash
55
+ humanish lab run oss-smoke
56
+ humanish lab run oss-smoke --limit 1 --keep
57
+ ```
58
+
59
+ Local dogfood shortcuts:
60
+
61
+ ```bash
62
+ pnpm humanish:lab:oss
63
+ pnpm humanish:lab:oss:ci
64
+ pnpm humanish:lab:oss:smoke
65
+ ```
66
+
67
+ ## Repo Selection
68
+
69
+ The default public targets are product-like, locally runnable apps/tools:
70
+
71
+ - `CorentinTh/it-tools`
72
+ - `drawdb-io/drawdb`
73
+ - `maciekt07/TodoApp`
74
+ - `lissy93/dashy`
75
+
76
+ Target selection should minimize distance from a real user-facing journey.
77
+ Good defaults expose an app, CLI, or agent-facing tool that can be tried out of
78
+ the box, preferably with a local dev script and no account setup. Libraries,
79
+ frameworks, starters, and infrastructure packages belong only in scenarios that
80
+ explicitly test developer experience. They are poor defaults for proving
81
+ Humanish as a user-simulation harness because they add another abstract setup
82
+ layer before any product behavior is visible.
83
+
84
+ `--repos` accepts a comma-separated list. Repeated `--repo` is also supported.
85
+ If `--count` is larger than the repo list, assignments cycle through the repo
86
+ pool. Inputs must be GitHub `owner/repo` slugs. Arbitrary URLs, local paths,
87
+ tokens, and SSH remotes are rejected. Private repos are maintainer-only and
88
+ require an authorized `GH_TOKEN` or `GITHUB_TOKEN` at runtime; no token value is
89
+ written to committed source or public issue text.
90
+
91
+ ## Private Product Labs
92
+
93
+ Private products can be used for local maintainer dogfood, but they must stay
94
+ out of the public package surface. Do not commit private repo names as defaults,
95
+ fixtures, screenshots, README examples, npm assets, skill examples, or issue
96
+ draft text.
97
+
98
+ The safe local shape is:
99
+
100
+ ```bash
101
+ humanish watch .humanish/labs/private-app.yaml --env-file .humanish/local/provider.env
102
+ ```
103
+
104
+ with an authorized runtime GitHub token and default repo redaction enabled.
105
+ Do not pass `--no-redact-repos` for a private target. Public receipts for
106
+ those runs should say `authorized private app target` and point only to ignored
107
+ local artifact paths, redacted statuses, and verifier results. Never publish
108
+ private screenshots, logs, app URLs, source snippets, branch names, issue
109
+ names, stream URLs, or operational details.
110
+
111
+ ## Runtime Shape
112
+
113
+ The meta-lab writes ignored local Observer evidence:
114
+
115
+ ```text
116
+ .humanish/
117
+ runs/<oss-meta-run-id>/
118
+ run.json
119
+ review.json
120
+ review.md
121
+ events.ndjson
122
+ observer/
123
+ index.html
124
+ observer-data.json
125
+ ```
126
+
127
+ Each stream lane records:
128
+
129
+ - assigned repo slug for public runs, or a redacted lane label for token-backed
130
+ maintainer/private runs;
131
+ - whether a live E2B desktop stream exists; auth-bearing stream URLs are
132
+ runtime-only for the attached Observer server and are not persisted;
133
+ - target app URL/status when a runnable script becomes HTTP-ready inside the
134
+ sandbox;
135
+ - nested Observer presence and nested verification status;
136
+ - headed desktop visual-window status and browser window count;
137
+ - Codex actor status, optionally moved before deterministic setup with
138
+ `HUMANISH_OSS_META_ACTOR_FIRST=1` and required through
139
+ `HUMANISH_OSS_META_REQUIRE_ACTOR=1`;
140
+ - setup-quality filesystem evidence: shallow tree, Humanish setup checks,
141
+ package scripts, study-quality rating, and allowlisted previews for public
142
+ runs;
143
+ - public-safe remote bootstrap log tail;
144
+ - public-safe gaps and events.
145
+
146
+ The current implementation launches live E2B desktop streams when
147
+ `E2B_API_KEY` and `OPENAI_API_KEY` are present, overlays those stream URLs only
148
+ in the attached Observer server, and marks missing key or launch failures
149
+ in-lane. It also packs the local Humanish package, uploads it into each sandbox,
150
+ raises a visible bootstrap terminal, clones the assigned repo, runs nested
151
+ Humanish setup and proof commands, starts the target app, opens desktop/mobile
152
+ app windows, opens the nested Observer in the sandbox browser, arranges visible
153
+ browser windows for screenshot proof, and starts the Codex actor attempt. By
154
+ default the actor remains nonblocking and runs after deterministic readback; with
155
+ `HUMANISH_OSS_META_ACTOR_FIRST=1`, the actor attempts setup/use before
156
+ deterministic validation; with `HUMANISH_OSS_META_REQUIRE_ACTOR=1`, the bootstrap
157
+ waits up to `HUMANISH_OSS_META_ACTOR_TIMEOUT_MS` for terminal actor readback and
158
+ will not mark the lane passed unless the actor exits cleanly.
159
+
160
+ When the remote bootstrap completes, the host persists a local
161
+ `setup-quality/<stream>-setup-quality.json` artifact. The Observer Files tab can
162
+ render it inline from the served Observer. Static `file://` observers keep the
163
+ artifact link openable but do not hydrate it inline. Token-backed/private runs
164
+ suppress raw file previews by default while preserving the setup checks and
165
+ tree shape. They also preserve `studyQuality` structural signals so an actor
166
+ that merely installs Humanish receives a `ceremonial` rating instead of being
167
+ treated as successful user-study leverage.
168
+
169
+ The live desktop substrate is an optional peer dependency. Install it in the
170
+ project that runs live labs:
171
+
172
+ ```bash
173
+ npm i -D @e2b/desktop
174
+ ```
175
+
176
+ Remaining substrate work: upgrade the nested `--app-url` browser proof into
177
+ provider-backed personas that actually drive multi-step target-app journeys,
178
+ and live-prove actor-first setup/use, not only deterministic bootstrap readback.
179
+
180
+ ## Smoke Harness Runtime
181
+
182
+ `humanish lab run oss-smoke` shallow clones lightweight public GitHub repos into
183
+ ignored runtime state, applies Humanish setup inside each throwaway clone, runs
184
+ the synthetic four-lane proof path, verifies the generated bundle, records
185
+ git-status evidence, writes an ignored report, and removes cloned repos by
186
+ default.
187
+
188
+ ```text
189
+ .humanish/
190
+ lab/oss/<run-id>/
191
+ report.json
192
+ report.md
193
+ tmp/oss-lab/<run-id>/
194
+ repos... # removed by default
195
+ ```
196
+
197
+ Each cloned repo receives disposable uncommitted changes:
198
+
199
+ - `humanish/` source starter files;
200
+ - `.humanish/` runtime state;
201
+ - `.gitignore` updates;
202
+ - `package.json` script updates;
203
+ - synthetic run/Observer evidence under the clone's ignored `.humanish/`.
204
+
205
+ The clone is removed unless `--keep` is passed. The host report remains under
206
+ ignored `.humanish/lab/oss/<run-id>/`.
207
+
208
+ ## Safety Rules
209
+
210
+ - GitHub `owner/repo` slugs only.
211
+ - Private repos require an authorized runtime token. Token-backed runs redact
212
+ repo labels in durable artifacts by default and must not appear in committed
213
+ fixtures, docs examples, public issue text, or published media.
214
+ - pnpm dependency build scripts may be allowed only inside the disposable E2B
215
+ lab so target app surfaces can start. Never use this as a host install
216
+ default.
217
+ - No credential prompts; smoke clone calls set `GIT_TERMINAL_PROMPT=0`.
218
+ - No commits, pushes, branches, tags, GitHub API mutation, deploys, or issue
219
+ filing.
220
+ - Do not write key values into committed `humanish/` source.
221
+ - Do not emit PII, PHI, raw private transcripts, private screenshots, secrets,
222
+ keys, or private upstream artifacts.
223
+
224
+ ## What This Proves
225
+
226
+ The meta-lab proves the operator control surface and artifact contract for
227
+ watching multiple Codex/E2B setup attempts at once. The live path now detects
228
+ E2B desktop fanout, visible bootstrap terminals, local-package upload,
229
+ disposable authorized-repo setup, target app HTTP readiness when a runnable
230
+ script is present, headed browser-window layout, nested live `--app-url`
231
+ desktop/mobile browser proof when the target app runs, nested Observer opening,
232
+ and top-level lane completion from remote evidence. It does not yet prove a
233
+ general provider-backed multi-step target-app persona runtime.
234
+
235
+ The smoke harness proves first-run Humanish package compatibility against
236
+ arbitrary public JavaScript repositories:
237
+
238
+ - setup can patch real package.json files without committing;
239
+ - generated `humanish/` source can coexist with external repo layout;
240
+ - ignored `.humanish/` runtime proof can be generated;
241
+ - the Observer can render from those disposable proofs;
242
+ - verification passes before the clone is discarded.
243
+
244
+ Neither path may claim private product behavior proof without live, redacted,
245
+ public-safe evidence.
246
+
247
+ Feedback candidates are derived from this evidence when a lane records concrete
248
+ setup-quality gaps, ceremonial/absent study quality, or actor-reported Humanish
249
+ CLI blockers. `humanish feedback` uses those candidates before falling back to
250
+ generic dry-run follow-up drafts.
@@ -0,0 +1,166 @@
1
+ # Project Layout Contract
2
+
3
+ Date: 2026-06-01
4
+
5
+ Status: target layout for apps that install `humanish`.
6
+
7
+ ## Decision
8
+
9
+ Use two roots:
10
+
11
+ ```text
12
+ humanish/ # committed source of simulation intent
13
+ .humanish/ # ignored runtime state, evidence, local overlays, and secrets
14
+ ```
15
+
16
+ Do not gitignore all Humanish state. Labs, personas, scenarios, policies,
17
+ adapters, coverage maps, and review vocabulary are the harness. They must be
18
+ versioned, reviewed, and reproducible from a clean clone.
19
+
20
+ Do not commit run bundles, raw screenshots, browser traces, transcripts,
21
+ draft issue bodies before verification, local auth, local overrides, or secrets.
22
+
23
+ ## Committed Source Plane
24
+
25
+ `humanish/` is the project-owned simulation contract:
26
+
27
+ ```text
28
+ humanish/
29
+ README.md
30
+ config.ts
31
+ personas/
32
+ synthetic-new-user.yaml
33
+ skeptical-power-user.yaml
34
+ scenarios/
35
+ first-run-smoke.yaml
36
+ onboarding-regression.yaml
37
+ labs/
38
+ first-run.yaml
39
+ policies/
40
+ redaction.yaml
41
+ network.yaml
42
+ credentials.example.yaml
43
+ adapters/
44
+ app.ts
45
+ review/
46
+ vocabulary.yaml
47
+ milestones.yaml
48
+ coverage-map.md
49
+ coverage-matrix.md
50
+ fixtures/
51
+ synthetic-login-state.json
52
+ ```
53
+
54
+ ## Humanish Format Stack
55
+
56
+ Use formats based on who edits the file and how it is consumed:
57
+
58
+ - `.yaml` for human-authored Humanish source: personas, scenarios, policies,
59
+ labs, review vocabulary, and review milestones. Prefer `.yaml` over `.yml`
60
+ for Humanish-owned source files.
61
+ - `.ts` for executable project integration: `humanish/config.ts`, adapters,
62
+ route catalogs, app launch plans, and logic that benefits from imports or
63
+ type checking.
64
+ - `.json` for generated machine artifacts and synthetic fixtures: run bundles,
65
+ observer data, review JSON, latest/history pointers, and fixture records.
66
+ - `.ndjson` for appendable event or transcript streams.
67
+ - `.yml` is acceptable for ecosystem files that conventionally use it, such as
68
+ `.github/workflows/*.yml`; do not use `.yml` for Humanish-owned authored
69
+ source.
70
+
71
+ Do not convert personas or scenarios to JSON because parser implementation is
72
+ easier. Keep authored simulation intent readable, then validate it through
73
+ schemas and CLI checks. TOML is not part of the current Humanish stack; add it
74
+ only if a concrete scalar global-config need appears that is better served by
75
+ TOML than YAML, TypeScript, or JSON.
76
+
77
+ Committed files must be public-safe:
78
+
79
+ - synthetic personas only;
80
+ - synthetic or redacted fixtures only;
81
+ - env var names only, never values;
82
+ - no PHI, PII, keys, tokens, raw private transcripts, real screenshots, real
83
+ customer data, or real patient data.
84
+
85
+ Changing a scenario or persona is equivalent to changing a test. It should be
86
+ visible in PR review.
87
+
88
+ ## Ignored Runtime Plane
89
+
90
+ `.humanish/` is local/generated state:
91
+
92
+ ```text
93
+ .humanish/
94
+ runs/
95
+ <run-id>/
96
+ run.json
97
+ review.md
98
+ review.json
99
+ observer/
100
+ screenshots/
101
+ traces/
102
+ terminal/
103
+ feedback/
104
+ cache/
105
+ tmp/
106
+ logs/
107
+ labs/
108
+ local/
109
+ labs/
110
+ personas/
111
+ policies/
112
+ secrets/
113
+ ```
114
+
115
+ Default `.gitignore` entry:
116
+
117
+ ```gitignore
118
+ .humanish/
119
+ .env*
120
+ ```
121
+
122
+ If a target repo already uses `.env.example`, preserve its existing exception.
123
+
124
+ ## Local Overrides
125
+
126
+ When a team needs private local personas or credentials, use ignored overlays:
127
+
128
+ ```text
129
+ .humanish/local/personas/*.yaml
130
+ .humanish/local/policies/*.yaml
131
+ .humanish/local/labs/*.yaml
132
+ .humanish/labs/*.yaml
133
+ .humanish/secrets/*
134
+ ```
135
+
136
+ Committed `humanish/labs/*.yaml` should be useful to anyone with a clean clone.
137
+ Ignored `.humanish/labs/*.yaml` and `.humanish/local/labs/*.yaml` are for
138
+ machine-specific or private dogfood labs. The CLI should warn that local
139
+ overlays cannot be used for reproducible CI or public issue drafts unless
140
+ redacted into committed synthetic equivalents.
141
+
142
+ ## CI And Reproducibility
143
+
144
+ CI should reproduce proof from committed inputs:
145
+
146
+ - app commit;
147
+ - `humanish` version;
148
+ - `humanish/config.ts`;
149
+ - scenario and persona catalog;
150
+ - lab manifest;
151
+ - policy files;
152
+ - synthetic fixtures;
153
+ - declared env var names.
154
+
155
+ CI should store generated run bundles as artifacts, not commit them.
156
+
157
+ ## Why Not `.humanish/` For Everything?
158
+
159
+ A fully ignored `.humanish/` makes setup feel tidy, but it hides the product
160
+ contract. Future agents and contributors cannot see what the harness is meant
161
+ to prove, CI cannot validate it from a clean clone, and PR review cannot catch
162
+ weakened personas or dropped hard paths.
163
+
164
+ A partially tracked dotdir is possible but worse UX. Dotdirs read as local,
165
+ editors hide them, and negated gitignore rules are easy to break. A visible
166
+ `humanish/` source root plus ignored `.humanish/` runtime root is clearer.
@@ -0,0 +1,203 @@
1
+ # State-driven executor (the `CuaExecutor` port)
2
+
3
+ Date: 2026-06-15
4
+
5
+ Status: shipped (PR1 of issue #148). The library path (a custom executor + a
6
+ non-vision provider, driven through the lab with NO E2B and NO vision) is
7
+ implemented and proven. A config-only deterministic lane and a
8
+ `subject.contract.ref` JS-module loader are deferred (see "Deferred", below).
9
+
10
+ ## What this is
11
+
12
+ The computer-use (CUA) loop in [`src/computer-use.ts`](../../src/computer-use.ts)
13
+ is provider- and substrate-agnostic by design: the model lives behind a
14
+ `CuaProvider` port and the thing being driven lives behind a `CuaExecutor` port.
15
+ You do not have to drive a screen with a vision model. You can point the loop at
16
+ **an already-running local app** and drive it through that app's **in-process
17
+ JavaScript automation contract** (e.g. `window.app.getState()`,
18
+ `sendChat(text)`, `dispatch(action)`, `navigate(target)`), using `getState()` as
19
+ the progress signal instead of a quantized screenshot — keeping humanish's
20
+ composition (personas, the Observer, the normalized `ActorTrace` evidence
21
+ bundle, redaction, and the friction / no-progress loop).
22
+
23
+ This is the "plural harnesses / transport-agnostic" intent of
24
+ [`actor-contract.md`](./actor-contract.md), made into a supported seam.
25
+
26
+ ## The port
27
+
28
+ ```ts
29
+ interface CuaExecutor {
30
+ observe(): Promise<CuaObservation>; // capture current state
31
+ execute(action: CuaAction): Promise<void>; // perform one action
32
+ }
33
+
34
+ interface CuaObservation {
35
+ screenshot?: Buffer; // OPTIONAL — a state executor omits it
36
+ stateSignature: string; // REQUIRED — the fallback progress key
37
+ appState?: Record<string, unknown>; // structured state; preferred for progress
38
+ }
39
+ ```
40
+
41
+ - **`screenshot` is optional.** A non-vision (state) executor omits it. The loop
42
+ persists no screenshot that turn; `counts.screenshots` stays 0, so the trace's
43
+ `redaction.screenshots` resolves to `"n/a"`. No fabricated `Buffer.alloc(0)`
44
+ ever reaches disk. (A vision executor still returns a frame, exactly as before.)
45
+ - **`stateSignature` is still required.** Derive it from your own state — e.g.
46
+ `JSON.stringify({ route, turn, modal })`. It is the canonical fallback progress
47
+ key when `appState` is absent. It is never written to the trace as text.
48
+ - **`appState` is the preferred progress input.** When present, the loop's
49
+ friction / no-progress detection keys off a deterministic, sorted-key,
50
+ depth/length-capped projection of it (`stableProgressKey`) rather than the
51
+ signature. So a route change drives "progress" even when a quantized signature
52
+ would not, and key insertion order can never fabricate a delta.
53
+
54
+ ### `stateSignature` vs `appState`
55
+
56
+ Either is enough on its own. `appState` is preferred because `getState()` is a
57
+ far more reliable progress signal than a quantized screenshot signature on a
58
+ graphically dense (pixel-art) UI. `stableProgressKey(appState)`:
59
+
60
+ - sorts object keys (order-independent — shuffled keys ARE NOT progress);
61
+ - caps depth, key count, array length, string length, and total output;
62
+ - never throws on a cyclic or huge `appState` — it degrades to a bounded value
63
+ (cycles become `"[Circular]"`, over-cap nodes become markers). This is
64
+ correctness-load-bearing: a hostile or merely large state blob cannot crash the
65
+ loop.
66
+
67
+ ## You need a NON-vision provider too
68
+
69
+ Swapping the executor is not enough. The default OpenAI computer-use provider is
70
+ **vision-based**: it sends the screenshot as the `computer_call_output`, so a
71
+ screenshot-less observation would crash it. A genuinely non-vision flow swaps
72
+ **both** the executor (the state contract) **and** the provider (a brain that
73
+ reasons over app state).
74
+
75
+ **Provider-authoring contract:**
76
+
77
+ - A **vision** provider MUST set `requiresFrame: true` (the OpenAI provider does).
78
+ When a `requiresFrame: true` provider is handed a screenshot-less observation,
79
+ the loop fails closed with a structured `harness_error` per turn — not a silent
80
+ crash, not a false pass.
81
+ - A **state-reasoning** provider omits `requiresFrame` (defaults falsey) and reads
82
+ `req.observation.appState`.
83
+
84
+ `requiresFrame` defaulting to falsey is a known third-party-author footgun (a
85
+ vision provider that forgets to set it would get a blank-frame crash instead of a
86
+ clean verdict). This slice accepts it because only one vision provider exists
87
+ today, and records it here.
88
+
89
+ ## `appState` is RUNTIME-ONLY (not evidence, in this slice)
90
+
91
+ `appState` is an in-memory progress-comparison input, exactly like
92
+ `stateSignature` (which is itself never written as text). It is **never** copied
93
+ into any `ActorTraceItem`, reason, id, or count, and is **never** persisted to the
94
+ trace. Only the derived progress key is computed in-memory and discarded.
95
+
96
+ Why: the published-evidence scan catches only secret-*shaped* patterns. A
97
+ structured app blob (ids, free-form state, possibly user chat or shapeless
98
+ tokens) is exactly the "value has no shape" gap that pattern redaction cannot
99
+ close. So this slice does not treat `appState` as an evidence surface at all.
100
+
101
+ The bundle is self-describing about it (invariant 6): when a state executor
102
+ surfaces `appState`, the trace's `redaction.notes` declares that app state was
103
+ observed each turn to drive progress detection and was NOT written to the trace.
104
+
105
+ A future "appState in evidence" slice MUST route a stringified projection through
106
+ `redaction.redactText` (and the lab's `scrubText`) AND cap / whitelist fields
107
+ before persisting — pattern + literal redaction alone cannot sanitize an
108
+ arbitrary blob.
109
+
110
+ ## Provenance is honestly UNPINNED (invariant 5)
111
+
112
+ An already-running local dev server cannot be commit-pinned. The bundle does not
113
+ silently omit a subject block — it DECLARES the absence: `subject.source:
114
+ app-url` with `state.provenance: "undeclared"` (the app-url "absence declared"
115
+ marker), and a `cua-lab.subject.declared` event that states the entry is a local
116
+ dev server driven in-process, caller-provisioned and UNPINNED, with no E2B
117
+ desktop created.
118
+
119
+ ## Two entry points
120
+
121
+ ### 1. Bare `runComputerUseLoop` (lowest level)
122
+
123
+ Implement `CuaExecutor.observe()/execute()`, derive `stateSignature`/`appState`
124
+ from your own state, pair it with a non-vision `CuaProvider`, and call
125
+ `runComputerUseLoop`. You own the bundle/redaction wiring.
126
+
127
+ ### 2. `runLab` + `buildExecutor` / `buildProvider` (keeps the composition)
128
+
129
+ The supported library path. It keeps personas, the Observer, the evidence
130
+ bundle, redaction, and the friction loop, while skipping E2B entirely:
131
+
132
+ ```ts
133
+ import { runLab, parseLabConfig, type CuaExecutor, type CuaProvider } from "humanish";
134
+
135
+ // local-app YAML (shareable; fails closed without hooks):
136
+ // schema: humanish.lab.v2
137
+ // id: pixel-bae-state
138
+ // subject: { source: local-app, appUrl: http://localhost:5173 }
139
+ // actors: [{ type: openai-computer-use, persona: pixel-pat, mission: "…" }]
140
+ // scenario: { mode: live }
141
+ const parsed = parseLabConfig(yaml);
142
+ if (!parsed.ok) throw new Error(parsed.error.message);
143
+
144
+ const outcome = await runLab(parsed.config, {
145
+ cwd: process.cwd(),
146
+ dryRun: false,
147
+ cuaHooks: {
148
+ buildExecutor: async ({ appUrl }) => createAppContractExecutor(bridge, appUrl),
149
+ buildProvider: async () => createStateBrain(),
150
+ },
151
+ });
152
+ // outcome.backend === "cua"; outcome.result.sandbox === undefined (NO E2B); the
153
+ // trace's provider id is the injected brain's id.
154
+ ```
155
+
156
+ When `cuaHooks.buildExecutor` is set, `runCuaActorLab` takes a branch that NEVER
157
+ loads the E2B module, creates a sandbox, runs `prepareDesktop`, provisions a
158
+ clone, opens a browser, or starts a stream. `sandboxId`/`streamUrl` stay
159
+ undefined, so `result.sandbox` is omitted — the verifiable "no E2B SDK call"
160
+ proof.
161
+
162
+ Two boot-time fail-closed guards (both BEFORE any key check, so a CLI invocation
163
+ never sees a misleading `KEYS_MISSING` first):
164
+
165
+ - `HUMANISH_CUA_LAB_EXECUTOR_NO_PROVIDER` — `buildExecutor` without `buildProvider`
166
+ (a state executor MUST be paired with a non-vision provider). `buildProvider`
167
+ ALONE is allowed — that is just a model swap on the normal E2B route.
168
+ - `HUMANISH_CUA_LAB_LOCAL_APP_NO_EXECUTOR` — a `subject.source: local-app` config
169
+ run with no `buildExecutor` hook (there is no built-in in-process driver yet).
170
+ A structured error, never a desktop attempt.
171
+
172
+ Key gating is route-aware: the in-process route uses the caller's OWN model and
173
+ executor, so no `OPENAI_API_KEY`/`E2B_API_KEY` is required.
174
+
175
+ ## A note on `appState` typing
176
+
177
+ `CuaObservation.appState` is `Record<string, unknown> | undefined` under the
178
+ repo's strict flags (`exactOptionalPropertyTypes`, `noUncheckedIndexedAccess`).
179
+ A value typed as an `interface` does NOT satisfy `Record<string, unknown>`
180
+ (interfaces have no implicit index signature), and a direct `as Record<...>` cast
181
+ on an interface-typed value is rejected. Use one of:
182
+
183
+ - a `type` alias for your state shape (type aliases get the index signature);
184
+ - a spread into a fresh object literal (`{ ...state }`); or
185
+ - `as unknown as Record<string, unknown>`.
186
+
187
+ Read every optional field defensively, and spread-omit optional fields
188
+ (`...(x === undefined ? {} : { x })`) rather than assigning `undefined`.
189
+
190
+ ## Deferred (tracked, not shipped here)
191
+
192
+ - **PR2 — a config-only deterministic `state-contract` lane.** A registered,
193
+ model-free lane driving a built-in `window.app.*` bridge over the existing
194
+ `ScriptedPageLike.evaluate` primitive + a YAML step program, `scenario.mode:
195
+ live` gating actuation. It would be deterministic step replay, NOT
196
+ `runComputerUseLoop`, and must not overclaim friction-loop reuse.
197
+ - **PR3 — a `subject.contract.ref` JS-module loader.** A config-referenced module
198
+ loaded and run in-process with full harness privileges is a genuinely NEW trust
199
+ surface with no precedent in this repo (the scripted lane loads only declarative
200
+ YAML; serve commands run isolated inside the disposable E2B sandbox). It earns
201
+ its place only behind its own clamping / trust / digest-pinning design.
202
+ pixel-bae does not need it: a library caller builds the bridge in their own
203
+ trusted code (entry point 2 above).