humanish 0.19.1 → 0.20.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.
package/dist/index.d.ts CHANGED
@@ -49,13 +49,13 @@ export { TERMINAL_PRODUCT_LAB_SCHEMA, buildTerminalProductBundle, runTerminalPro
49
49
  export type { CommandLogRecord, CostCategory, CostLine, InterventionRecord, LifecycleRecord, NoSpendProof, RunTerminalProductLabOptions, TerminalCostLedger, TerminalLedgers, TerminalProductLabHooks, TerminalProductLabResult, TerminalProductScoringContext } from "./e2b-terminal-lab.js";
50
50
  export { SHARED_WORLD_LAB_PROVIDER_METADATA, SHARED_WORLD_LAB_SCHEMA, buildSharedWorldBundle, runSharedWorldLab } from "./shared-world-lab.js";
51
51
  export type { RunSharedWorldLabOptions, SharedWorldLabErrorCode, SharedWorldLabHooks, SharedWorldLabResult, SharedWorldRoleResult } from "./shared-world-lab.js";
52
- export { CONCURRENT_ATTRIBUTION_LIMITS, CONCURRENT_SHARED_WORLD_LAB_SCHEMA, CONCURRENT_SHARED_WORLD_PROVIDER_METADATA, buildConcurrentSharedWorldBundle, runConcurrentSharedWorld } from "./concurrent-shared-world-lab.js";
53
- export type { ConcurrentSharedWorldLabErrorCode, ConcurrentSharedWorldLabResult, ConcurrentSharedWorldRoleResult, RunConcurrentSharedWorldLabOptions } from "./concurrent-shared-world-lab.js";
52
+ export { CONCURRENT_ATTRIBUTION_LIMITS, CONCURRENT_SHARED_WORLD_LAB_SCHEMA, CONCURRENT_SHARED_WORLD_PROVIDER_METADATA, EXTERNAL_PUBLIC_ATTRIBUTION_LIMITS, LOBBY_CODE_PATTERN, buildConcurrentSharedWorldBundle, extractLobbyCode, runConcurrentSharedWorld } from "./concurrent-shared-world-lab.js";
53
+ export type { ConcurrentSharedWorldLabErrorCode, ConcurrentSharedWorldLabResult, ConcurrentSharedWorldPlaneClass, ConcurrentSharedWorldRoleResult, RunConcurrentSharedWorldLabOptions } from "./concurrent-shared-world-lab.js";
54
54
  export { probeUrl, readDetachedLog, runDetachedStep, startDetachedProcess } from "./e2b-detached.js";
55
55
  export type { DetachedStepOptions, DetachedStepResult, DetachedTimers } from "./e2b-detached.js";
56
56
  export { DEFAULT_DEVICE_PRESET, DEVICE_PRESETS, DEVICE_PRESET_NAMES, isDevicePresetName, resolveDevicePreset } from "./device-presets.js";
57
57
  export type { DevicePreset, DevicePresetName } from "./device-presets.js";
58
- export { actorResolvesToTerminal, cuaLaneCount, cuaLaneValidationReason, isHttpUrl, isLoopbackUrl, LAB_CONFIG_SCHEMA, MAX_CUA_LANES, concurrentSharedWorldValidationReason, parseLabConfig, resolveSeatUrl, routesToComputerUse, routesToConcurrentSharedWorld, routesToScriptedBrowser, routesToSharedWorld, routesToTerminalProduct, sharedWorldValidationReason, subjectStateInvalidReason } from "./lab-config.js";
58
+ export { actorResolvesToTerminal, cuaLaneCount, cuaLaneValidationReason, isHttpUrl, isLoopbackUrl, LAB_CONFIG_SCHEMA, MAX_CUA_LANES, concurrentSharedWorldValidationReason, externalPublicSharedWorldValidationReason, parseLabConfig, resolveSeatUrl, routesToComputerUse, routesToConcurrentSharedWorld, routesToExternalPublicSharedWorld, routesToProvisionedSharedWorld, routesToScriptedBrowser, routesToSharedWorld, routesToTerminalProduct, sharedWorldValidationReason, subjectStateInvalidReason } from "./lab-config.js";
59
59
  export type { LabActor, LabActorLane, LabConfig, LabConfigParseResult, LabExecutionTerminal, LabRuntimeAuth, LabScenarioCaps, LabStateStepWhen, LabSubject, LabSubjectProduct, LabSubjectServe, LabSubjectSource, LabSubjectState, LabSubjectStateCheckpoint, LabSubjectStateStep, LabSubjectTopology, LabTerminalStdin, LabTerminalTransport } from "./lab-config.js";
60
60
  export { resolveLabDryRun, runLab, selectLabBackend } from "./lab-engine.js";
61
61
  export type { LabBackend, LabOutcome, RunLabOptions } from "./lab-engine.js";
package/dist/index.js CHANGED
@@ -24,10 +24,10 @@ export { SCRIPTED_BROWSER_PROVIDER, runScriptedBrowserSession } from "./scripted
24
24
  export { SCRIPTED_BROWSER_LAB_SCHEMA, buildScriptedLabBundle, runScriptedBrowserLab } from "./scripted-browser-lab.js";
25
25
  export { TERMINAL_PRODUCT_LAB_SCHEMA, buildTerminalProductBundle, runTerminalProductLab } from "./e2b-terminal-lab.js";
26
26
  export { SHARED_WORLD_LAB_PROVIDER_METADATA, SHARED_WORLD_LAB_SCHEMA, buildSharedWorldBundle, runSharedWorldLab } from "./shared-world-lab.js";
27
- export { CONCURRENT_ATTRIBUTION_LIMITS, CONCURRENT_SHARED_WORLD_LAB_SCHEMA, CONCURRENT_SHARED_WORLD_PROVIDER_METADATA, buildConcurrentSharedWorldBundle, runConcurrentSharedWorld } from "./concurrent-shared-world-lab.js";
27
+ export { CONCURRENT_ATTRIBUTION_LIMITS, CONCURRENT_SHARED_WORLD_LAB_SCHEMA, CONCURRENT_SHARED_WORLD_PROVIDER_METADATA, EXTERNAL_PUBLIC_ATTRIBUTION_LIMITS, LOBBY_CODE_PATTERN, buildConcurrentSharedWorldBundle, extractLobbyCode, runConcurrentSharedWorld } from "./concurrent-shared-world-lab.js";
28
28
  export { probeUrl, readDetachedLog, runDetachedStep, startDetachedProcess } from "./e2b-detached.js";
29
29
  export { DEFAULT_DEVICE_PRESET, DEVICE_PRESETS, DEVICE_PRESET_NAMES, isDevicePresetName, resolveDevicePreset } from "./device-presets.js";
30
- export { actorResolvesToTerminal, cuaLaneCount, cuaLaneValidationReason, isHttpUrl, isLoopbackUrl, LAB_CONFIG_SCHEMA, MAX_CUA_LANES, concurrentSharedWorldValidationReason, parseLabConfig, resolveSeatUrl, routesToComputerUse, routesToConcurrentSharedWorld, routesToScriptedBrowser, routesToSharedWorld, routesToTerminalProduct, sharedWorldValidationReason, subjectStateInvalidReason } from "./lab-config.js";
30
+ export { actorResolvesToTerminal, cuaLaneCount, cuaLaneValidationReason, isHttpUrl, isLoopbackUrl, LAB_CONFIG_SCHEMA, MAX_CUA_LANES, concurrentSharedWorldValidationReason, externalPublicSharedWorldValidationReason, parseLabConfig, resolveSeatUrl, routesToComputerUse, routesToConcurrentSharedWorld, routesToExternalPublicSharedWorld, routesToProvisionedSharedWorld, routesToScriptedBrowser, routesToSharedWorld, routesToTerminalProduct, sharedWorldValidationReason, subjectStateInvalidReason } from "./lab-config.js";
31
31
  export { resolveLabDryRun, runLab, selectLabBackend } from "./lab-engine.js";
32
32
  export { LAB_PREFLIGHT_SCHEMA, runLabPreflight } from "./lab-preflight.js";
33
33
  export { CLI_RESPONSE_SCHEMA, createProgram } from "./program.js";
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,EAClB,6BAA6B,EAC7B,6BAA6B,EAC7B,2BAA2B,EAC3B,uBAAuB,EACvB,6BAA6B,EAC9B,MAAM,qBAAqB,CAAC;AAa7B,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,oBAAoB,EAAE,gCAAgC,EAAE,yBAAyB,EAAE,MAAM,qBAAqB,CAAC;AAEjJ,OAAO,EACL,mCAAmC,EACnC,uBAAuB,EACxB,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EACL,iBAAiB,EACjB,kBAAkB,EACnB,MAAM,mBAAmB,CAAC;AAY3B,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAE7D,OAAO,EAAE,wBAAwB,EAAE,MAAM,2BAA2B,CAAC;AAErE,OAAO,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AAE/D,OAAO,EACL,uBAAuB,EACvB,gCAAgC,EAChC,6BAA6B,EAC9B,MAAM,0BAA0B,CAAC;AAElC,OAAO,EACL,0BAA0B,EAC1B,gCAAgC,EAChC,wBAAwB,EACzB,MAAM,wBAAwB,CAAC;AAOhC,OAAO,EACL,YAAY,EACZ,WAAW,EACX,cAAc,EACd,iBAAiB,EACjB,mBAAmB,EACpB,MAAM,cAAc,CAAC;AAOtB,OAAO,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EACL,0BAA0B,EAC1B,qBAAqB,EACtB,MAAM,0BAA0B,CAAC;AAElC,OAAO,EACL,6BAA6B,EAC7B,wBAAwB,EACzB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EACL,sBAAsB,EACtB,eAAe,EACf,aAAa,EACb,YAAY,EACZ,mBAAmB,EACnB,cAAc,EACd,cAAc,EACf,MAAM,eAAe,CAAC;AAEvB,OAAO,EAAE,oBAAoB,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAE1D,OAAO,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAExF,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAE3F,OAAO,EACL,oBAAoB,EACpB,2BAA2B,EAC3B,yBAAyB,EACzB,8BAA8B,EAC9B,mBAAmB,EACpB,MAAM,sBAAsB,CAAC;AAM9B,OAAO,EACL,iBAAiB,EACjB,cAAc,EACd,qBAAqB,EACrB,SAAS,EACT,mBAAmB,EACpB,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,cAAc,EACd,aAAa,EACb,aAAa,EACb,WAAW,EACX,iBAAiB,EACjB,aAAa,EACb,UAAU,EACV,MAAM,EACN,wBAAwB,EACxB,QAAQ,EACR,6BAA6B,EAC7B,UAAU,EACV,SAAS,EACT,SAAS,EACV,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAsC/C,OAAO,EACL,+BAA+B,EAC/B,oBAAoB,EACpB,mBAAmB,EACnB,cAAc,EACd,oBAAoB,EACpB,kBAAkB,EAClB,cAAc,EACf,MAAM,oBAAoB,CAAC;AAa5B,OAAO,EACL,yBAAyB,EACzB,yBAAyB,EAC1B,MAAM,6BAA6B,CAAC;AAWrC,OAAO,EACL,2BAA2B,EAC3B,sBAAsB,EACtB,qBAAqB,EACtB,MAAM,2BAA2B,CAAC;AAOnC,OAAO,EACL,2BAA2B,EAC3B,0BAA0B,EAC1B,qBAAqB,EACtB,MAAM,uBAAuB,CAAC;AAe/B,OAAO,EACL,kCAAkC,EAClC,uBAAuB,EACvB,sBAAsB,EACtB,iBAAiB,EAClB,MAAM,uBAAuB,CAAC;AAQ/B,OAAO,EACL,6BAA6B,EAC7B,kCAAkC,EAClC,yCAAyC,EACzC,kCAAkC,EAClC,kBAAkB,EAClB,gCAAgC,EAChC,gBAAgB,EAChB,wBAAwB,EACzB,MAAM,kCAAkC,CAAC;AAQ1C,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAE,eAAe,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAErG,OAAO,EACL,qBAAqB,EACrB,cAAc,EACd,mBAAmB,EACnB,kBAAkB,EAClB,mBAAmB,EACpB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EACL,uBAAuB,EACvB,YAAY,EACZ,uBAAuB,EACvB,SAAS,EACT,aAAa,EACb,iBAAiB,EACjB,aAAa,EACb,qCAAqC,EACrC,yCAAyC,EACzC,cAAc,EACd,cAAc,EACd,mBAAmB,EACnB,6BAA6B,EAC7B,iCAAiC,EACjC,8BAA8B,EAC9B,uBAAuB,EACvB,mBAAmB,EACnB,uBAAuB,EACvB,2BAA2B,EAC3B,yBAAyB,EAC1B,MAAM,iBAAiB,CAAC;AAqBzB,OAAO,EAAE,gBAAgB,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAE7E,OAAO,EAAE,oBAAoB,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAU3E,OAAO,EACL,mBAAmB,EACnB,aAAa,EACd,MAAM,cAAc,CAAC"}
@@ -197,6 +197,58 @@ scenario:
197
197
  # policies:
198
198
  # redactScreenshots: true # blur persisted frames (default off — full fidelity for local use)
199
199
  # allowPublicTargets: true # required to drive a non-loopback app-url (a deployment you own)
200
+ defaults:
201
+ open: true
202
+ `
203
+ },
204
+ {
205
+ path: "humanish/labs/cineguessr-3player.yaml",
206
+ plane: "source",
207
+ contents: `schema: humanish.lab.v2
208
+ id: cineguessr-3player
209
+ title: External-public shared world (N mobile personas share ONE public app at once)
210
+ description: >-
211
+ A worked example of the EXTERNAL-PUBLIC shared-world route (#164 phase 2): N mobile-LAYOUT
212
+ personas play the SAME multiplayer lobby on a REAL public deployment AT ONCE. The public site is
213
+ the shared plane DIRECTLY — NO clone, NO getHost, NO subject sandbox, NO seed. A host-first barrier
214
+ extracts the shared-session code from the host seat's CDP-observed URL and threads it into the
215
+ follower join missions (followers go through the real Join flow). Dry-run (the default) emits ONE
216
+ external-public evidence bundle at $0. A live run needs OPENAI_API_KEY + E2B_API_KEY and
217
+ scenario.mode: live. Point appUrl + publicTarget at a public deployment YOU own/operate.
218
+ MOBILE FIDELITY: on the E2B-desktop route only width/height render (real mobile LAYOUT); there is
219
+ no touch input and DPR/isMobile are prompt-signal + metadata. HONESTY: attribution stays
220
+ shared-world, but every strength claim degrades honestly — provenance external-public (not seeded),
221
+ NO synthetic attestation (you cannot claim synthetic on a real site), NO authoritative shared-state
222
+ proof, concurrency by temporal co-occupancy + observed lobby convergence.
223
+ subject:
224
+ source: app-url # the real public deployment IS the shared plane (no clone, no getHost)
225
+ topology: shared-world # the declared override of the per-lane-worlds default
226
+ appUrl: https://your-public-app.example/ # a deployment YOU own/operate
227
+ publicTarget: # REQUIRED ownership attestation (the honest analog of exposure: synthetic)
228
+ owner: your-org/your-app
229
+ authorized: true
230
+ policies:
231
+ allowPublicTargets: true # REQUIRED: the shared plane is a real non-loopback public deployment
232
+ actors:
233
+ - type: openai-computer-use
234
+ mission: You are one of several users on the same shared session at once. Play your role, then stop.
235
+ lanes: # the roster (>=2) with EXACTLY ONE host: true seat
236
+ - id: host
237
+ host: true # the designated host seat that creates the shared session
238
+ device: mobile # 414x896 mobile LAYOUT (no touch; DPR is prompt-signal on this route)
239
+ instruction: Create a shared lobby, wait for the others to join, then start and play.
240
+ - id: player-2
241
+ device: mobile
242
+ instruction: Join the lobby you're told the code for, then play.
243
+ - id: player-3
244
+ device: small-mobile
245
+ instruction: Join the lobby you're told the code for, then play.
246
+ execution:
247
+ target: e2b-desktop
248
+ timeoutMs: 120000 # PER-SEAT session budget; also caps the host-first handoff deadline
249
+ concurrency: 3 # > 1 selects the CONCURRENT substrate
250
+ scenario:
251
+ mode: dry-run # default; the live path opens N real mobile-layout seats against the public app
200
252
  defaults:
201
253
  open: true
202
254
  `
@@ -1 +1 @@
1
- {"version":3,"file":"init-templates.js","sourceRoot":"","sources":["../src/init-templates.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"init-templates.js","sourceRoot":"","sources":["../src/init-templates.ts"],"names":[],"mappings":"AAWA,MAAM,CAAC,MAAM,YAAY,GAAkB;IACzC;QACE,IAAI,EAAE,oBAAoB;QAC1B,KAAK,EAAE,QAAQ;QACf,QAAQ,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;CAyBb;KACE;IACD;QACE,IAAI,EAAE,oBAAoB;QAC1B,KAAK,EAAE,QAAQ;QACf,QAAQ,EAAE;;;;;;;;;;;;CAYb;KACE;IACD;QACE,IAAI,EAAE,2CAA2C;QACjD,KAAK,EAAE,QAAQ;QACf,QAAQ,EAAE;;;;;;;;;;;;CAYb;KACE;IACD;QACE,IAAI,EAAE,6CAA6C;QACnD,KAAK,EAAE,QAAQ;QACf,QAAQ,EAAE;;;;;;;;;;;;CAYb;KACE;IACD;QACE,IAAI,EAAE,yCAAyC;QAC/C,KAAK,EAAE,QAAQ;QACf,QAAQ,EAAE;;;;;;;;;;;;;CAab;KACE;IACD;QACE,IAAI,EAAE,+CAA+C;QACrD,KAAK,EAAE,QAAQ;QACf,QAAQ,EAAE;;;;;;;;;;;;;CAab;KACE;IACD;QACE,IAAI,EAAE,8BAA8B;QACpC,KAAK,EAAE,QAAQ;QACf,QAAQ,EAAE;;;;;;;;;;;;;CAab;KACE;IACD;QACE,IAAI,EAAE,gCAAgC;QACtC,KAAK,EAAE,QAAQ;QACf,QAAQ,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8Db;KACE;IACD;QACE,IAAI,EAAE,uCAAuC;QAC7C,KAAK,EAAE,QAAQ;QACf,QAAQ,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA+Cb;KACE;IACD;QACE,IAAI,EAAE,kCAAkC;QACxC,KAAK,EAAE,QAAQ;QACf,QAAQ,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgCb;KACE;IACD;QACE,IAAI,EAAE,gCAAgC;QACtC,KAAK,EAAE,QAAQ;QACf,QAAQ,EAAE;;;;;;;CAOb;KACE;IACD;QACE,IAAI,EAAE,4CAA4C;QAClD,KAAK,EAAE,QAAQ;QACf,QAAQ,EAAE;;;;;;;;CAQb;KACE;IACD;QACE,IAAI,EAAE,0BAA0B;QAChC,KAAK,EAAE,QAAQ;QACf,QAAQ,EAAE;;;;;;;;;;;;CAYb;KACE;IACD;QACE,IAAI,EAAE,iCAAiC;QACvC,KAAK,EAAE,QAAQ;QACf,QAAQ,EAAE;;;;;;;;;;CAUb;KACE;IACD;QACE,IAAI,EAAE,iCAAiC;QACvC,KAAK,EAAE,QAAQ;QACf,QAAQ,EAAE;;;;;;;;CAQb;KACE;IACD;QACE,IAAI,EAAE,0BAA0B;QAChC,KAAK,EAAE,QAAQ;QACf,QAAQ,EAAE;;;;;CAKb;KACE;IACD;QACE,IAAI,EAAE,6BAA6B;QACnC,KAAK,EAAE,QAAQ;QACf,QAAQ,EAAE;;;;;;CAMb;KACE;IACD;QACE,IAAI,EAAE,8CAA8C;QACpD,KAAK,EAAE,QAAQ;QACf,QAAQ,EAAE;;;;;;;;;;;;CAYb;KACE;CACF,CAAC;AAEF,MAAM,CAAC,MAAM,kBAAkB,GAAuB;IACpD,EAAE,IAAI,EAAE,gBAAgB,EAAE,KAAK,EAAE,SAAS,EAAE;IAC5C,EAAE,IAAI,EAAE,iBAAiB,EAAE,KAAK,EAAE,SAAS,EAAE;IAC7C,EAAE,IAAI,EAAE,eAAe,EAAE,KAAK,EAAE,SAAS,EAAE;IAC3C,EAAE,IAAI,EAAE,gBAAgB,EAAE,KAAK,EAAE,SAAS,EAAE;IAC5C,EAAE,IAAI,EAAE,gBAAgB,EAAE,KAAK,EAAE,SAAS,EAAE;IAC5C,EAAE,IAAI,EAAE,sBAAsB,EAAE,KAAK,EAAE,SAAS,EAAE;IAClD,EAAE,IAAI,EAAE,0BAA0B,EAAE,KAAK,EAAE,SAAS,EAAE;IACtD,EAAE,IAAI,EAAE,0BAA0B,EAAE,KAAK,EAAE,SAAS,EAAE;IACtD,EAAE,IAAI,EAAE,mBAAmB,EAAE,KAAK,EAAE,SAAS,EAAE;CAChD,CAAC;AAEF,MAAM,CAAC,MAAM,eAAe,GAA2B;IACrD,QAAQ,EAAE,UAAU;IACpB,iBAAiB,EAAE,iBAAiB;IACpC,cAAc,EAAE,wBAAwB;IACxC,gBAAgB,EAAE,gBAAgB;IAClC,mBAAmB,EAAE,iCAAiC;IACtD,iBAAiB,EAAE,iBAAiB;CACrC,CAAC"}
@@ -162,6 +162,22 @@ export interface LabSubject {
162
162
  * `topology: shared-world` + `execution.concurrency > 1`; inert/warned elsewhere.
163
163
  */
164
164
  exposure?: "synthetic";
165
+ /**
166
+ * EXTERNAL-PUBLIC shared-world route ONLY (#164 phase 2): the author's REQUIRED ownership
167
+ * attestation when a real PUBLIC deployment (`source: app-url` + `topology: shared-world` +
168
+ * `concurrency > 1` + `policies.allowPublicTargets: true`) is used directly as the shared plane.
169
+ * The harness neither provisions nor exposes this target (no getHost, no clone, no seed), so it
170
+ * cannot attest the data is synthetic; instead the operator MUST attest they own/operate it.
171
+ * `owner` is a public-safe operator/repo label; `authorized` must be true. This is author-trust —
172
+ * the harness cannot verify ownership — surfaced honestly in the evidence class. Required on the
173
+ * external-public branch. On a non-`app-url` subject it is REJECTED (parse error); on any OTHER
174
+ * `app-url` config that does not route to external-public shared-world it is IGNORED-WITH-A-WARNING
175
+ * (forwardDeclaredWarnings), never silently consumed — it is meaningless without that plane.
176
+ */
177
+ publicTarget?: {
178
+ owner: string;
179
+ authorized: boolean;
180
+ };
165
181
  /** `clone`: one or more owner/repo slugs (public or authorized-private). */
166
182
  repos?: string[];
167
183
  clone?: LabSubjectClone;
@@ -257,6 +273,14 @@ export interface LabActorLane {
257
273
  * non-shared-world route (the per-lane-worlds fan-out roster has no per-lane entry).
258
274
  */
259
275
  entry?: string;
276
+ /**
277
+ * EXTERNAL-PUBLIC shared-world ONLY (#164 phase 2): marks this lane the DESIGNATED HOST seat — it
278
+ * creates the shared session (e.g. a multiplayer lobby) that the follower seats then join. Exactly
279
+ * ONE lane in the roster may carry `host: true` (validated in externalPublicSharedWorldValidationReason).
280
+ * The orchestrator watches the host seat's observed URL for the shared-session code and threads it
281
+ * into the follower missions at a host-first barrier. Inert/warned on every other route.
282
+ */
283
+ host?: boolean;
260
284
  }
261
285
  /**
262
286
  * Compact authoring sugar for repeated lane groups. The parser expands each group into concrete
@@ -544,11 +568,26 @@ export declare function routesToComputerUse(config: LabConfig): boolean;
544
568
  * (the cua route) — the topology declaration is the override switch.
545
569
  */
546
570
  export declare function routesToSharedWorld(config: LabConfig): boolean;
571
+ /** The getHost provisioned-subject shared-world shape (clone/local-tree served + exposed in-sandbox). */
572
+ export declare function routesToProvisionedSharedWorld(config: LabConfig): boolean;
573
+ /**
574
+ * The EXTERNAL-PUBLIC shared-world shape (#164 phase 2): a real PUBLIC deployment used DIRECTLY as
575
+ * the shared plane — `source: app-url` + `topology: shared-world` + a computer-use actor on
576
+ * e2b-desktop + `policies.allowPublicTargets: true`. NO getHost, NO clone, NO subject sandbox, NO
577
+ * seed. The operator-ownership attestation `subject.publicTarget` is required (validated in
578
+ * externalPublicSharedWorldValidationReason, not here — this predicate is the router only, so a
579
+ * half-declared external-public config still routes here to get its precise fail-closed reason
580
+ * rather than silently downgrading to the per-lane cua route). Always concurrent (concurrency > 1 is
581
+ * enforced by the validation reason).
582
+ */
583
+ export declare function routesToExternalPublicSharedWorld(config: LabConfig): boolean;
547
584
  /**
548
585
  * True when this config routes to the CONCURRENT shared-world backend (#164 phase 2): a shared-world
549
- * config with `execution.concurrency > 1` (N actor seats driving ONE getHost-exposed plane AT ONCE).
550
- * `concurrency` absent or 1 stays the sequential PoC (`routesToSharedWorld` → the sequential
551
- * backend). selectLabBackend checks this BEFORE routesToSharedWorld.
586
+ * config with `execution.concurrency > 1` (N actor seats driving ONE plane AT ONCE). Two plane
587
+ * classes: the getHost provisioned-subject shape (clone/local-tree) AND the external-public shape (a
588
+ * real public deployment used directly as the plane — `source: app-url` + `allowPublicTargets`, no
589
+ * getHost/clone/seed). `concurrency` absent or 1 stays the sequential PoC (getHost only).
590
+ * selectLabBackend checks this BEFORE routesToSharedWorld.
552
591
  */
553
592
  export declare function routesToConcurrentSharedWorld(config: LabConfig): boolean;
554
593
  /**
@@ -560,6 +599,18 @@ export declare function routesToConcurrentSharedWorld(config: LabConfig): boolea
560
599
  * is exported npm surface).
561
600
  */
562
601
  export declare function concurrentSharedWorldValidationReason(config: LabConfig): string | null;
602
+ /**
603
+ * Cross-validate the EXTERNAL-PUBLIC shared-world declaration (#164 phase 2): a real PUBLIC
604
+ * deployment used DIRECTLY as the shared plane (no getHost, no clone, no subject sandbox, no seed).
605
+ * The honest analog of concurrentSharedWorldValidationReason for a plane the harness does NOT own:
606
+ * it FORBIDS every provisioned-subject field (serve/state.seed/state.checkpoint/exposure/clone/repos
607
+ * are inert with no sandbox — fail closed, never silently ignored, per invariant 6), and REQUIRES a
608
+ * non-loopback appUrl + allowPublicTargets + the operator-ownership attestation subject.publicTarget +
609
+ * concurrency > 1 + an actors[0].lanes roster of ≥2 with EXACTLY ONE host lane. The getHost synthetic
610
+ * gate is deliberately unreachable here (there is no internet-reachable harness-owned URL to attest).
611
+ * Enforced at parse AND re-enforced in the engine (runConcurrentSharedWorld is exported npm surface).
612
+ */
613
+ export declare function externalPublicSharedWorldValidationReason(config: LabConfig): string | null;
563
614
  /**
564
615
  * Resolve a shared-world seat's entry URL from `serve.url` + a role's `entry` (relative path or
565
616
  * same-origin absolute URL). Returns null when the combination is not a same-origin loopback URL
@@ -284,9 +284,14 @@ export function parseLabConfig(raw) {
284
284
  // silently downgrading to a per-lane-worlds cua run. With `execution.concurrency > 1` the
285
285
  // concurrent extras (synthetic-subject attestation, 0.0.0.0 serve bind, no clone.keep) also apply.
286
286
  if (config.subject.topology === "shared-world") {
287
- const reason = (config.execution?.concurrency ?? 1) > 1
288
- ? concurrentSharedWorldValidationReason(config)
289
- : sharedWorldValidationReason(config);
287
+ const reason = config.subject.source === "app-url"
288
+ // The external-public plane (a real public deployment as the shared plane): NEVER the getHost
289
+ // synthetic gate — that gate exists because getHost is internet-reachable AND harness-owned; a
290
+ // public site the harness neither provisioned nor exposed has neither property.
291
+ ? externalPublicSharedWorldValidationReason(config)
292
+ : (config.execution?.concurrency ?? 1) > 1
293
+ ? concurrentSharedWorldValidationReason(config)
294
+ : sharedWorldValidationReason(config);
290
295
  if (reason) {
291
296
  return invalid(reason);
292
297
  }
@@ -434,9 +439,13 @@ export function cuaLaneValidationReason(config) {
434
439
  }
435
440
  // Public targets fan out into N independent worlds driving the SAME public app — that is an
436
441
  // ambiguous shared-world-ish shape, not a per-lane target swarm. Permit N>1 public runs only when
437
- // every roster lane declares its own target, making the adapter-owned topology explicit.
438
- if (laneCount > 1 && config.policies?.allowPublicTargets === true && declaredLaneTargets(config).length === 0) {
439
- return "policies.allowPublicTargets cannot be combined with multi-lane fan-out (N>1) — N lanes against one declared public target is the SHARED-WORLD topology (layer 7, #164), not per-lane worlds. Fan out against a loopback/provisioned subject, or run a single public-target lane.";
442
+ // every roster lane declares its own target, making the adapter-owned topology explicit. But when
443
+ // `subject.topology: shared-world` is ALSO declared, N lanes against one public target is the
444
+ // EXTERNAL-PUBLIC shared-world topology (#164 phase 2) ROUTE it there (a real public deployment
445
+ // as the shared plane) instead of refusing; externalPublicSharedWorldValidationReason then applies.
446
+ if (laneCount > 1 && config.policies?.allowPublicTargets === true && declaredLaneTargets(config).length === 0
447
+ && config.subject.topology !== "shared-world") {
448
+ return "policies.allowPublicTargets cannot be combined with multi-lane fan-out (N>1) — N lanes against one declared public target is the SHARED-WORLD topology (layer 7, #164), not per-lane worlds. Declare `subject.topology: shared-world` to run the external-public shared-world route, fan out against a loopback/provisioned subject, or run a single public-target lane.";
440
449
  }
441
450
  return null;
442
451
  }
@@ -537,16 +546,39 @@ export function routesToComputerUse(config) {
537
546
  * (the cua route) — the topology declaration is the override switch.
538
547
  */
539
548
  export function routesToSharedWorld(config) {
549
+ return routesToProvisionedSharedWorld(config) || routesToExternalPublicSharedWorld(config);
550
+ }
551
+ /** The getHost provisioned-subject shared-world shape (clone/local-tree served + exposed in-sandbox). */
552
+ export function routesToProvisionedSharedWorld(config) {
540
553
  return (config.subject.source === "clone" || config.subject.source === "local-tree")
541
554
  && config.subject.topology === "shared-world"
542
555
  && config.execution?.target === "e2b-desktop"
543
556
  && actorResolvesToComputerUse(config.actors[0]?.type);
544
557
  }
558
+ /**
559
+ * The EXTERNAL-PUBLIC shared-world shape (#164 phase 2): a real PUBLIC deployment used DIRECTLY as
560
+ * the shared plane — `source: app-url` + `topology: shared-world` + a computer-use actor on
561
+ * e2b-desktop + `policies.allowPublicTargets: true`. NO getHost, NO clone, NO subject sandbox, NO
562
+ * seed. The operator-ownership attestation `subject.publicTarget` is required (validated in
563
+ * externalPublicSharedWorldValidationReason, not here — this predicate is the router only, so a
564
+ * half-declared external-public config still routes here to get its precise fail-closed reason
565
+ * rather than silently downgrading to the per-lane cua route). Always concurrent (concurrency > 1 is
566
+ * enforced by the validation reason).
567
+ */
568
+ export function routesToExternalPublicSharedWorld(config) {
569
+ return config.subject.source === "app-url"
570
+ && config.subject.topology === "shared-world"
571
+ && config.execution?.target === "e2b-desktop"
572
+ && actorResolvesToComputerUse(config.actors[0]?.type)
573
+ && config.policies?.allowPublicTargets === true;
574
+ }
545
575
  /**
546
576
  * True when this config routes to the CONCURRENT shared-world backend (#164 phase 2): a shared-world
547
- * config with `execution.concurrency > 1` (N actor seats driving ONE getHost-exposed plane AT ONCE).
548
- * `concurrency` absent or 1 stays the sequential PoC (`routesToSharedWorld` → the sequential
549
- * backend). selectLabBackend checks this BEFORE routesToSharedWorld.
577
+ * config with `execution.concurrency > 1` (N actor seats driving ONE plane AT ONCE). Two plane
578
+ * classes: the getHost provisioned-subject shape (clone/local-tree) AND the external-public shape (a
579
+ * real public deployment used directly as the plane — `source: app-url` + `allowPublicTargets`, no
580
+ * getHost/clone/seed). `concurrency` absent or 1 stays the sequential PoC (getHost only).
581
+ * selectLabBackend checks this BEFORE routesToSharedWorld.
550
582
  */
551
583
  export function routesToConcurrentSharedWorld(config) {
552
584
  return routesToSharedWorld(config) && (config.execution?.concurrency ?? 1) > 1;
@@ -580,6 +612,74 @@ export function concurrentSharedWorldValidationReason(config) {
580
612
  }
581
613
  return null;
582
614
  }
615
+ /**
616
+ * Cross-validate the EXTERNAL-PUBLIC shared-world declaration (#164 phase 2): a real PUBLIC
617
+ * deployment used DIRECTLY as the shared plane (no getHost, no clone, no subject sandbox, no seed).
618
+ * The honest analog of concurrentSharedWorldValidationReason for a plane the harness does NOT own:
619
+ * it FORBIDS every provisioned-subject field (serve/state.seed/state.checkpoint/exposure/clone/repos
620
+ * are inert with no sandbox — fail closed, never silently ignored, per invariant 6), and REQUIRES a
621
+ * non-loopback appUrl + allowPublicTargets + the operator-ownership attestation subject.publicTarget +
622
+ * concurrency > 1 + an actors[0].lanes roster of ≥2 with EXACTLY ONE host lane. The getHost synthetic
623
+ * gate is deliberately unreachable here (there is no internet-reachable harness-owned URL to attest).
624
+ * Enforced at parse AND re-enforced in the engine (runConcurrentSharedWorld is exported npm surface).
625
+ */
626
+ export function externalPublicSharedWorldValidationReason(config) {
627
+ const structuralReason = laneRosterStructuralValidationReason(config);
628
+ if (structuralReason) {
629
+ return structuralReason;
630
+ }
631
+ if (config.subject.source !== "app-url") {
632
+ return "the external-public shared-world route requires `subject.source: app-url` — a real public deployment is used directly as the shared plane (no clone, no provisioned subject).";
633
+ }
634
+ if (config.execution?.target !== "e2b-desktop") {
635
+ return "the external-public shared-world route requires `execution.target: e2b-desktop` — the role seats drive hosted desktop browsers against the one public deployment.";
636
+ }
637
+ if (!actorResolvesToComputerUse(config.actors[0]?.type)) {
638
+ return `the external-public shared-world route requires a registered computer-use actor (one of: ${registeredComputerUseActors().join(", ")}) — each role seat runs a computer-use session.`;
639
+ }
640
+ if ((config.execution?.concurrency ?? 1) <= 1) {
641
+ return "the external-public shared-world route requires `execution.concurrency > 1` (N concurrent seats sharing ONE public plane); concurrency 1 proves no shared world.";
642
+ }
643
+ if (config.policies?.allowPublicTargets !== true) {
644
+ return "the external-public shared-world route requires `policies.allowPublicTargets: true` — the shared plane is a real non-loopback public deployment.";
645
+ }
646
+ const appUrl = config.subject.appUrl ?? "";
647
+ if (!isHttpUrl(appUrl) || isLoopbackUrl(appUrl)) {
648
+ return "the external-public shared-world route requires a non-loopback http(s) `subject.appUrl` — a loopback URL is not a shared public plane (use the getHost provisioned route for a local subject).";
649
+ }
650
+ // The operator-ownership attestation (the honest analog of exposure: synthetic — you cannot claim
651
+ // synthetic on a real site, but you MUST attest you own/operate it). Author-trust; unverifiable.
652
+ if (config.subject.publicTarget?.authorized !== true) {
653
+ return "the external-public shared-world route requires `subject.publicTarget: { owner, authorized: true }` — you must attest you own/operate the public deployment used as the shared plane (author-trust; the harness cannot verify ownership).";
654
+ }
655
+ // FORBID every provisioned-subject field: with no sandbox they cannot act, so they are rejected
656
+ // with a precise reason, never silently ignored (invariant 6). exposure: synthetic in particular
657
+ // would be a LIE on a real site (the harness neither provisioned nor exposed it).
658
+ if (config.subject.exposure !== undefined) {
659
+ return "`subject.exposure: synthetic` is forbidden on the external-public shared-world route — you cannot attest a real public deployment is synthetic seeded data; use `subject.publicTarget` to attest ownership instead.";
660
+ }
661
+ if (config.subject.serve !== undefined) {
662
+ return "`subject.serve` is forbidden on the external-public shared-world route — the harness does not serve the plane (it is an already-deployed public app); there is no in-sandbox serve to run.";
663
+ }
664
+ if (config.subject.state?.seed !== undefined || config.subject.state?.checkpoint !== undefined || config.subject.state !== undefined) {
665
+ return "`subject.state` (seed/checkpoint/external) is forbidden on the external-public shared-world route — the harness neither seeds nor snapshots the plane (no in-sandbox filesystem to digest); no authoritative shared-state proof is possible on this class.";
666
+ }
667
+ if (config.subject.clone !== undefined || config.subject.repos !== undefined) {
668
+ return "`subject.clone`/`subject.repos` are forbidden on the external-public shared-world route — nothing is cloned; the public deployment IS the plane.";
669
+ }
670
+ const lanes = config.actors[0]?.lanes;
671
+ if (!lanes || lanes.length < 2) {
672
+ return "the external-public shared-world route requires an `actors[0].lanes` roster of at least 2 roles (a single-seat shared world proves no shared session).";
673
+ }
674
+ if (lanes.some((lane) => lane.entry !== undefined)) {
675
+ return "`actors[0].lanes[].entry` (the loopback same-origin seat path) is forbidden on the external-public shared-world route — there is no harness-served serve.url to resolve it against; seats open the public appUrl and reach the shared session through the real UI.";
676
+ }
677
+ const hostLanes = lanes.filter((lane) => lane.host === true);
678
+ if (hostLanes.length !== 1) {
679
+ return `the external-public shared-world route requires EXACTLY ONE \`host: true\` lane (the designated host seat that creates the shared session; got ${hostLanes.length}). The other ≥1 lanes are followers that join it.`;
680
+ }
681
+ return null;
682
+ }
583
683
  /**
584
684
  * Resolve a shared-world seat's entry URL from `serve.url` + a role's `entry` (relative path or
585
685
  * same-origin absolute URL). Returns null when the combination is not a same-origin loopback URL
@@ -646,6 +746,9 @@ function forwardDeclaredWarnings(config) {
646
746
  const routesToTerminal = routesToTerminalProduct(config);
647
747
  const routesToShared = routesToSharedWorld(config);
648
748
  const routesToConcurrent = routesToConcurrentSharedWorld(config);
749
+ // The external-public plane (a real public deployment as the shared plane) consumes host lanes +
750
+ // subject.publicTarget; it is only ever the concurrent app-url shape.
751
+ const routesToExternalPublic = routesToConcurrent && config.subject.source === "app-url";
649
752
  const routesToHostedCuaBrowser = config.execution?.target === "e2b-desktop"
650
753
  && routesToCua;
651
754
  for (const [index, actor] of config.actors.entries()) {
@@ -653,6 +756,10 @@ function forwardDeclaredWarnings(config) {
653
756
  if (actor.lanes?.some((lane) => lane.entry !== undefined) && !routesToShared) {
654
757
  inert.push(`actors[${index}].lanes[].entry (the per-role loopback entry is a shared-world capability; needs subject.topology: shared-world)`);
655
758
  }
759
+ // The host-seat marker acts ONLY on the external-public shared-world route; inert elsewhere.
760
+ if (actor.lanes?.some((lane) => lane.host === true) && !routesToExternalPublic) {
761
+ inert.push(`actors[${index}].lanes[].host (the designated host-seat marker; needs the external-public shared-world route: app-url × topology shared-world × allowPublicTargets × concurrency > 1)`);
762
+ }
656
763
  if (routesToCua || routesToTerminal) {
657
764
  // The cua + terminal routes consume mission/persona/model + laneFocus.instruction (they
658
765
  // compose the agent prompt + bundle provenance); laneFocus.id/label remain inert. On the
@@ -705,6 +812,11 @@ function forwardDeclaredWarnings(config) {
705
812
  if (config.subject.state?.checkpoint !== undefined && !routesToShared) {
706
813
  inert.push("subject.state.checkpoint (the shared-world state-checkpoint probe; needs subject.topology: shared-world)");
707
814
  }
815
+ // publicTarget (the external-public ownership attestation) acts ONLY on the external-public
816
+ // shared-world route; inert elsewhere. (It is already parse-rejected on non-app-url sources.)
817
+ if (config.subject.publicTarget !== undefined && !routesToExternalPublic) {
818
+ inert.push("subject.publicTarget (the external-public ownership attestation; needs the external-public shared-world route: app-url × topology shared-world × allowPublicTargets × concurrency > 1)");
819
+ }
708
820
  // exposure (the synthetic-subject attestation) acts ONLY on the CONCURRENT shared-world route
709
821
  // (the getHost-exposed plane); inert on the sequential shared-world route (loopback) and elsewhere.
710
822
  if (config.subject.exposure !== undefined && !routesToConcurrent && !(routesToScripted && config.subject.source === "clone")) {
@@ -848,6 +960,11 @@ function parseSubject(raw) {
848
960
  if (source !== "local-tree" && raw.localTree !== undefined) {
849
961
  return invalid("`subject.localTree` applies only to local-tree subjects (keep/exclude/maxArchiveBytes for packing the working tree).");
850
962
  }
963
+ // publicTarget is app-url-ONLY (the external-public shared-world ownership attestation). It is
964
+ // meaningless without a real public deployment as the plane — reject it elsewhere (invariant 6).
965
+ if (source !== "app-url" && raw.publicTarget !== undefined) {
966
+ return invalid("`subject.publicTarget` applies only to app-url subjects on the external-public shared-world route (the operator's ownership attestation for a real public deployment used directly as the shared plane).");
967
+ }
851
968
  if (source === "clone") {
852
969
  const repos = strList(raw.repos);
853
970
  if (!repos || repos.length === 0) {
@@ -950,6 +1067,15 @@ function parseSubject(raw) {
950
1067
  return invalid("`subject.appUrl` must be an http(s) URL.");
951
1068
  }
952
1069
  subject.appUrl = appUrl;
1070
+ // publicTarget (external-public shared-world ownership attestation) is app-url-only. Shape it
1071
+ // here; its REQUIRED-on-that-route semantics live in externalPublicSharedWorldValidationReason.
1072
+ if (source === "app-url" && raw.publicTarget !== undefined) {
1073
+ const publicTargetResult = parsePublicTarget(raw.publicTarget);
1074
+ if (!publicTargetResult.ok) {
1075
+ return publicTargetResult;
1076
+ }
1077
+ subject.publicTarget = publicTargetResult.value;
1078
+ }
953
1079
  }
954
1080
  if (source === "terminal-product") {
955
1081
  const productResult = parseProduct(raw.product);
@@ -960,6 +1086,24 @@ function parseSubject(raw) {
960
1086
  }
961
1087
  return { ok: true, value: subject };
962
1088
  }
1089
+ // The publicTarget.owner is a public-safe operator/repo label surfaced in evidence (e.g.
1090
+ // "danielgwilson/cineguessr" or a bare org name). Slash allowed for the owner/repo convention.
1091
+ const PUBLIC_TARGET_OWNER_PATTERN = /^[A-Za-z0-9][A-Za-z0-9_./-]*$/;
1092
+ /** Parse the external-public shared-world ownership attestation ({ owner, authorized: true }). The
1093
+ * harness cannot verify ownership — this is author-trust, surfaced honestly in the evidence class. */
1094
+ function parsePublicTarget(raw) {
1095
+ if (!isRecord(raw)) {
1096
+ return invalid("`subject.publicTarget` must be an object ({ owner, authorized: true }) — the operator's ownership attestation for the external-public shared plane.");
1097
+ }
1098
+ const owner = str(raw.owner);
1099
+ if (!owner || !PUBLIC_TARGET_OWNER_PATTERN.test(owner)) {
1100
+ return invalid("`subject.publicTarget.owner` must be a public-safe operator/repo label (e.g. owner/repo); it is recorded in evidence, so it must carry no secret.");
1101
+ }
1102
+ if (raw.authorized !== true) {
1103
+ return invalid("`subject.publicTarget.authorized` must be true — you must attest you own/operate the public deployment used as the shared plane (author-trust; the harness cannot verify ownership).");
1104
+ }
1105
+ return { ok: true, value: { owner, authorized: true } };
1106
+ }
963
1107
  // The product name interpolates into evidence labels and the composed prompt; the public-safe
964
1108
  // token shape is the same load-bearing constraint as a lab id.
965
1109
  const PRODUCT_NAME_PATTERN = /^[A-Za-z0-9][A-Za-z0-9_.-]*$/;
@@ -1438,6 +1582,15 @@ function parseLanes(raw, actorIndex) {
1438
1582
  const laneEntry = str(entry.entry);
1439
1583
  if (laneEntry !== undefined)
1440
1584
  lane.entry = laneEntry;
1585
+ // `host` marks the designated host seat on the external-public shared-world route; the
1586
+ // exactly-one-host check runs in externalPublicSharedWorldValidationReason (route context).
1587
+ if (entry.host !== undefined) {
1588
+ if (typeof entry.host !== "boolean") {
1589
+ return invalid(`actors[${actorIndex}].lanes[${laneIndex}].host must be a boolean (marks the designated host seat on the external-public shared-world route).`);
1590
+ }
1591
+ if (entry.host)
1592
+ lane.host = true;
1593
+ }
1441
1594
  lanes.push(lane);
1442
1595
  }
1443
1596
  return { ok: true, value: lanes };