@kheleshikhi/studio-agent 0.6.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.
package/README.md ADDED
@@ -0,0 +1,225 @@
1
+ # NerdBro Studio Agent
2
+
3
+ Provider-neutral external authoring for KheleShikhi Interactive Studio. It
4
+ requires Node 20+ and Chrome, Edge, or explicitly installed Playwright Chromium.
5
+ It does not require the NerdBro repository, Flutter, Dart, Python, Docker, an
6
+ OpenAI API key, or an Anthropic API key.
7
+
8
+ ```sh
9
+ npm install -g @kheleshikhi/studio-agent
10
+ nerdbro setup
11
+ nerdbro connect
12
+
13
+ mkdir my-kheleshikhi-work
14
+ cd my-kheleshikhi-work
15
+ nerdbro agent init --provider codex # or claude / generic
16
+ nerdbro studio create --title "Ball Lab" --target combined --concept "Projectile range"
17
+ nerdbro studio checkout <asset-id>
18
+ ```
19
+
20
+ The browser pairing page uses the normal NerdBro login. The daemon stores the
21
+ scoped production credential only in memory. It binds to `127.0.0.1` on a
22
+ random port and protects local RPC with a random discovery secret. The
23
+ persistent Playwright profile is dedicated to NerdBro and never reads personal
24
+ browser cookies, extensions, passwords, or history.
25
+
26
+ The normal headless Agent browser uses the real installed
27
+ Chrome or Edge where available, keeps the browser sandbox enabled, and leaves
28
+ display scaling to the operating system. Desktop and mobile viewport options
29
+ are CSS layout sizes; they do not simulate a low-density display. Headless mode
30
+ uses the same explicit responsive viewports for automated inspection, but its
31
+ screenshot pixels are not promised to be byte-identical across hardware.
32
+ Chrome's device-metrics API applies the CSS size while leaving the host device
33
+ scale native; NerdBro does not force a DPR value. Version 0.6.1 keeps the
34
+ Chrome DevTools viewport session alive for the browser tab, so a requested
35
+ desktop, mobile, or custom CSS size remains in effect through inspection and
36
+ capture.
37
+ `browser.json` records the actual CSS viewport, `window.devicePixelRatio`, safe
38
+ screen dimensions, browser version, headed/headless mode, and PNG pixel size.
39
+
40
+ ## Benchmark Attempt
41
+
42
+ From a fresh Git workspace, connect once, then run:
43
+
44
+ ```sh
45
+ nerdbro attempt start --label ball-launcher-v3 --mode benchmark
46
+ nerdbro agent init --provider claude # or codex / generic
47
+ ```
48
+
49
+ The generated provider instructions tell the Agent to write `REPORT.md` and
50
+ `EVALUATION.md` before `nerdbro attempt finish --outcome completed|blocked|incomplete|abandoned`. The workspace pointer
51
+ `.nerdbro-attempt.json` holds only non-secret metadata. Commands automatically
52
+ join the same Attempt after a daemon restart or a new Agent authorization
53
+ session under the same Maker account. The client ZIP is written under
54
+ `~/NerdBroAttempts/<attempt-id>/client/` (override with
55
+ `NERDBRO_ATTEMPT_ROOT`). An operator with backend database access later runs
56
+ `just studio-agent-attempt-export <attempt-id>` from the repository to create
57
+ one outer ZIP. Ordinary Studio authoring remains available without an Attempt.
58
+
59
+ Use `--json` on every command for stable envelopes. Exit code `0` is success,
60
+ `1` is an operation/server failure, `2` is local input, `3` is a stale draft or
61
+ source conflict, `4` means authentication/daemon state is required, and `5`
62
+ means setup/browser compatibility failed.
63
+
64
+ The repository's `agent_capability_proof` and `ball_launcher_stress` journeys
65
+ are **scripted platform regressions**: repository code authors `source.json`,
66
+ then this public CLI drives and certifies the real browser runtime. They do not
67
+ show that Claude or another model independently discovered or authored a Scene.
68
+ In particular, the scripted Ball Launcher does not perform a vernier-caliper
69
+ diameter reading and choose a ball from that measurement. The original, harder
70
+ brief is reserved for a fresh external-AI run without repository access.
71
+
72
+ Read the current live Authoring Playbook with `nerdbro guide` before substantial
73
+ authoring. It is the same canonical page served at
74
+ `/docs/studio-language/agent-authoring-playbook`, not a bundled instruction copy.
75
+
76
+ Use `nerdbro docs`, `nerdbro docs search <query>`, and
77
+ `nerdbro docs open <slug>` to read the same current Language Reference hosted
78
+ for Makers. Schema, capabilities, Components, Behaviors, and examples are
79
+ always fetched live from NerdBro; none is bundled in this npm package.
80
+ Use `nerdbro schema experience` for the final learner content contract and
81
+ `nerdbro docs open experiences` for one-Scene, narrated, mixed, and multi-Band
82
+ examples. Use `nerdbro schema scene.variables`, `scene.events`, `scene.gestures`, `scene.diagram`,
83
+ `scene.motions`, `scene.captions`, `scene.audio`, `scene.stateMachines`,
84
+ `expressions`, `stateMachine.actions`, `mathGraph.behaviorProjection`,
85
+ `assessment`, or the `runtime.*` topics to request a narrow projection of
86
+ the parser/runtime contract. `nerdbro schema` returns the topic index and
87
+ summary contracts.
88
+ Search Components and Behaviors narrowly. Example search rejects an empty
89
+ query, returns ranked summaries with a small default limit, and leaves full
90
+ source to `example inspect <id>`; normally inspect no more than three relevant
91
+ examples.
92
+
93
+ The daemon reuses the authenticated context response for at most 30 seconds,
94
+ then revalidates it; connecting or disconnecting clears the cache. This cuts
95
+ repeated compatibility reads without caching mutable drafts.
96
+
97
+ Check out once, edit only `source.json`, and use `studio status` for later
98
+ synchronization. Validate, compile, preview when useful, save, then inspect the
99
+ real Studio. The daemon reuses its short-lived read-only browser credential in
100
+ memory and clears it on disconnect, stop, reconnect, or origin change. A graph
101
+ requirement without a teaching purpose or axis/synchronization meaning is a
102
+ clarification request, not permission to invent filler.
103
+
104
+ `studio create` needs the `STUDIO_ASSET_CREATE` pairing scope and returns a
105
+ Maker-owned draft identity and its server starter-source hash. `--scratch`
106
+ only labels the intent to use an ordinary new, unapproved asset as a disposable
107
+ spike. It has no separate lifecycle, automatic expiry, promotion, or garbage
108
+ collection. Save its exact ID for explicit cleanup after success. Use
109
+ `studio brief <asset>` and `studio brief update <asset> --graph-purpose ...`
110
+ to inspect or update authoring intent with a concurrency check.
111
+
112
+ `studio save` creates a draft. `studio certify` records technical evidence.
113
+ Neither approves or publishes. `studio export --revision ...` accepts only the
114
+ server-built canonical V2 package for an approved immutable revision; the CLI
115
+ does not assemble package formats, and draft export is not a command.
116
+
117
+ For visual evolution, use `nerdbro studio capture-preview <asset-id> --mode
118
+ semantic --viewport 480x320 --duration 8 --out preview-capture`. It captures
119
+ the real Studio preview into local JPEG checkpoints, a labelled contact sheet,
120
+ and a source/revision-bound runtime timeline. `--mode fixed --fps 2` samples
121
+ motion at regular intervals. The local output path is accessible to a local
122
+ agent only; no frames are uploaded or silently persisted by the backend.
123
+
124
+ `nerdbro studio inspect <asset-id> --view learner --viewport mobile` opens the
125
+ Studio's production part surface at mobile CSS size. `--view final` is an alias.
126
+
127
+ Use `nerdbro studio drive <asset-id> --scenario journey.json --viewport mobile`
128
+ to exercise an unsaved local source in that same production preview. This
129
+ does not save a draft or write learner progress. The paired browser opens the
130
+ current server source, transfers the bounded local candidate into Studio's
131
+ explicit authoring/test mode, compiles it through the normal preview API, and
132
+ rejects a source-hash mismatch before the first step. The scenario is versioned
133
+ and has at most 64 steps and a two-minute run budget:
134
+
135
+ ```json
136
+ {
137
+ "version": 1,
138
+ "steps": [
139
+ {"op": "set", "target": "launchSpeed", "value": 8},
140
+ {"op": "drag", "target": "angleGesture", "value": 42},
141
+ {"op": "press", "target": "launch"},
142
+ {"op": "wait", "path": "recent_scene_event", "equals": "projectileLanded", "timeoutMs": 10000},
143
+ {"op": "assert", "path": "active_states.attempt", "equals": "result"},
144
+ {"op": "assert", "path": "motion_outputs.projectile.range", "between": [6, 6.5]},
145
+ {"op": "capture", "name": "result"}
146
+ ]
147
+ }
148
+ ```
149
+
150
+ `set`, `fire`, `press`, `tap`, `drag`, `play`, `pause`, `seek`, and `reset`
151
+ address only declared variables, events, instances, and gestures. `press`
152
+ honours conditional availability; `drag` derives a Scene point from the
153
+ authored gesture range. `wait` and `assert` read allowlisted observation paths,
154
+ never arbitrary code. `capture` records the actual browser pixels. The result
155
+ directory contains the initial and step captures plus bounded runtime state.
156
+ For a gesture with an interaction handle, assert
157
+ `gestures.<id>.affordance_visible` before `drag`; the driver also refuses a
158
+ handle that the mounted preview has not presented.
159
+
160
+ ## Library authoring
161
+
162
+ The Studio Library's semantic kinds — `experience`, `audio`, `component`
163
+ (including Characters) and `behavior` — are authored with the same
164
+ draft-first flow. The CLI is a thin client of `/v1/agent/authoring`: templates,
165
+ validation, certification profiles, preview resolution and package building all
166
+ run on the server, and schemas and documentation are fetched live (use
167
+ `nerdbro docs` and `nerdbro schema`); none is bundled here.
168
+
169
+ ```sh
170
+ nerdbro audio upload take.m4a # prints sha256 and duration
171
+ nerdbro audio create narration.sunrise.v1 --name "Sunrise narration" --media <sha256> [--category c] [--language bn]
172
+ nerdbro experience create poem.sunrise.v1 --name "Sunrise" --kind poem \
173
+ --scene-asset <interactive-asset-id> # or --scene-package scene:<id> --scene <scene-id>
174
+ [--master-audio narration.sunrise.v1] [--duration-ms 42000]
175
+ nerdbro component create card.star.v1 --name "Star card" [--category c] [--visual v] [--palette p]
176
+ nerdbro behavior create tap.counter.v1 --name "Tap counter" [--declarative]
177
+
178
+ nerdbro <kind> checkout <stable-id> [--workspace dir] [--force]
179
+ nerdbro <kind> status <stable-id>
180
+ nerdbro <kind> save <stable-id>
181
+ nerdbro <kind> validate <stable-id> [--revision <revision>]
182
+ nerdbro <kind> certify <stable-id> [--revision <revision>]
183
+ nerdbro experience|behavior preview <stable-id>
184
+ nerdbro experience|behavior export <stable-id> --revision <revision> [--out file.kspkg]
185
+ nerdbro experience|audio search <query> [--limit n]
186
+ nerdbro experience|audio inspect <stable-id>
187
+ nerdbro <kind> inspect-browser <stable-id> [--viewport desktop|mobile|WxH]
188
+ ```
189
+
190
+ `component` and `behavior` keep their catalogue `search` and `inspect`
191
+ commands; the authoring verbs above sit beside them.
192
+
193
+ Checkout writes `<workspace>/<kind>/<stable-id>/source.json` and
194
+ `library-lock.json` (`kheleshikhi.library-lock.v1`: kind, stable id, head
195
+ revision, canonical source hash, schema version, checkout time). The default
196
+ workspace is `./nerdbro-studio`. Checkout refuses to overwrite unsaved local
197
+ edits without `--force`. `status` reports `local_changed`, `remote_changed` and
198
+ `conflict`. `save` sends the whole local document with the lock's base revision;
199
+ saving unchanged content creates no draft but still refreshes the lock, and a
200
+ stale base exits `3` (`VOCABULARY_DRAFT_CONFLICT`) with `source.json` untouched.
201
+ `validate` and `certify` default to the lock's head revision. `preview` sends
202
+ the local, unsaved `source.json` and writes `preview.json` beside it.
203
+ `inspect-browser` captures the Studio Library workspace to `screenshot.png` and
204
+ `browser.json` in the checkout directory; its bounded `browser.capture` activity
205
+ record is reported locally in the command result only.
206
+
207
+ The flow is create or checkout → edit `source.json` → `status` → `save` (an
208
+ immutable draft) → `validate` → `certify`. Certification is technical evidence;
209
+ approval and publication are human actions in the Studio, and the CLI has no
210
+ approve or publish command. `export` downloads only an approved revision's
211
+ server-built `.kspkg`.
212
+
213
+ Audio must be AAC in an MP4 container (`.m4a` or `.mp4`) of at most 24 MiB.
214
+ `audio upload` checks the extension and size locally, and the server probes the
215
+ bytes authoritatively. The upload carries only the bytes under the neutral name
216
+ `upload.m4a`/`upload.mp4`; the local path and filename are never sent, printed
217
+ or stored. Uploading requires the `AUDIO_UPLOAD` scope, which new pairings
218
+ receive by default. A session paired by an older CLI gets
219
+ `AGENT_SCOPE_REQUIRED` (exit `4`): run `nerdbro disconnect` and then
220
+ `nerdbro connect`.
221
+
222
+ Screenshot SHA-256 values identify the exact local artifact inspected in the
223
+ activity stream; screenshot bytes, pixel size, and DPR are audit metadata, not
224
+ semantic certification identity. Certification remains bound to source and
225
+ runtime evidence rather than pixel-identical rendering.
@@ -0,0 +1,25 @@
1
+ declare const POINTER_SCHEMA = "kheleshikhi.studio-agent-attempt-pointer.v1";
2
+ export interface AttemptPointer {
3
+ schema: typeof POINTER_SCHEMA;
4
+ attempt_id: string;
5
+ label: string;
6
+ mode: string;
7
+ attempt_root: string;
8
+ server_origin: string;
9
+ started_at: string;
10
+ }
11
+ export declare function attemptHome(): string;
12
+ export declare function attemptDirectory(attemptId: string): string;
13
+ export declare function readAttemptPointer(start?: string): Promise<{
14
+ pointer: AttemptPointer;
15
+ workspace: string;
16
+ } | null>;
17
+ export declare function createAttemptPointer(server: Record<string, unknown>, serverOrigin: string, workspace?: string): Promise<AttemptPointer>;
18
+ export declare function saveCommandEvidence(pointer: AttemptPointer, commandId: string, command: string, result: unknown): Promise<void>;
19
+ export declare function finalizeClient(pointer: AttemptPointer, workspace: string, server: Record<string, unknown>): Promise<{
20
+ path: string;
21
+ sha256: string;
22
+ included: number;
23
+ excluded: string[];
24
+ }>;
25
+ export {};
@@ -0,0 +1,197 @@
1
+ /** Local Attempt pointer and privacy-bounded client evidence finalization. */
2
+ import { randomBytes } from "node:crypto";
3
+ import { spawnSync } from "node:child_process";
4
+ import { lstat, mkdir, readFile, readdir, realpath, stat, writeFile } from "node:fs/promises";
5
+ import os from "node:os";
6
+ import path from "node:path";
7
+ import { CliError } from "./types.js";
8
+ import { makeZip, sha256, verifyZip } from "./zip.js";
9
+ const POINTER_NAME = ".nerdbro-attempt.json";
10
+ const POINTER_SCHEMA = "kheleshikhi.studio-agent-attempt-pointer.v1";
11
+ const MAX_FILE = 32 * 1024 * 1024;
12
+ const MAX_TOTAL = 256 * 1024 * 1024;
13
+ const BLOCKED_DIRS = new Set([".git", "node_modules", ".cache", ".venv", "venv", "__pycache__", ".playwright", "browser-profile", "dist", "build"]);
14
+ const BLOCKED_FILES = /(^\.env(?:\..*)?$|\.pem$|\.key$|^id_rsa|^id_ed25519|cookie|credential|secret|token|\.p12$|\.pfx$|\.sqlite$)/i;
15
+ const SECRET_CONTENT = /nbs_[A-Za-z0-9_-]{12,}|nbp_[A-Za-z0-9_-]{12,}|Bearer\s+[A-Za-z0-9_.-]{20,}|eyJ[A-Za-z0-9_-]{30,}\.[A-Za-z0-9_-]{20,}/i;
16
+ export function attemptHome() {
17
+ return path.resolve(process.env.NERDBRO_ATTEMPT_ROOT || path.join(os.homedir(), "NerdBroAttempts"));
18
+ }
19
+ export function attemptDirectory(attemptId) {
20
+ if (!/^att_[A-Za-z0-9_-]{16,36}$/.test(attemptId))
21
+ throw new CliError("Invalid Attempt ID.", "AGENT_ATTEMPT_ID_INVALID", 2);
22
+ return path.join(attemptHome(), attemptId);
23
+ }
24
+ export async function readAttemptPointer(start = process.cwd()) {
25
+ let current = path.resolve(start);
26
+ while (true) {
27
+ try {
28
+ const candidate = JSON.parse(await readFile(path.join(current, POINTER_NAME), "utf8"));
29
+ if (candidate.schema !== POINTER_SCHEMA || !/^att_[A-Za-z0-9_-]{16,36}$/.test(candidate.attempt_id)) {
30
+ throw new CliError("Attempt pointer is malformed.", "AGENT_ATTEMPT_POINTER_INVALID", 2);
31
+ }
32
+ return { pointer: candidate, workspace: current };
33
+ }
34
+ catch (error) {
35
+ if (error instanceof CliError)
36
+ throw error;
37
+ if (error.code !== "ENOENT")
38
+ throw new CliError("Cannot read Attempt pointer.", "AGENT_ATTEMPT_POINTER_INVALID", 2);
39
+ }
40
+ if (current === path.dirname(current) || current === os.homedir())
41
+ return null;
42
+ current = path.dirname(current);
43
+ }
44
+ }
45
+ export async function createAttemptPointer(server, serverOrigin, workspace = process.cwd()) {
46
+ if (await readAttemptPointer(workspace))
47
+ throw new CliError("This workspace already has an Attempt pointer.", "AGENT_ATTEMPT_ALREADY_ACTIVE", 3);
48
+ const attemptId = String(server.attempt_id);
49
+ const root = attemptDirectory(attemptId);
50
+ await mkdir(attemptHome(), { recursive: true });
51
+ await mkdir(root, { recursive: false });
52
+ const pointer = {
53
+ schema: POINTER_SCHEMA, attempt_id: attemptId,
54
+ label: String(server.label), mode: String(server.mode),
55
+ attempt_root: root, server_origin: serverOrigin,
56
+ started_at: String(server.started_at),
57
+ };
58
+ await writeFile(path.join(workspace, POINTER_NAME), `${JSON.stringify(pointer, null, 2)}\n`, { flag: "wx", mode: 0o600 });
59
+ await writeFile(path.join(root, "README.md"), `# NerdBro Attempt ${attemptId}\n\nLabel: ${pointer.label}\nMode: ${pointer.mode}\n\nRun \`nerdbro attempt finish --outcome completed|blocked|incomplete|abandoned\` in the original workspace with the accurate outcome, then run \`just studio-agent-attempt-export ${attemptId}\` from the NerdBro repository.\n`);
60
+ await writeFile(path.join(root, "RUN_MANIFEST.json"), `${JSON.stringify({ schema: "kheleshikhi.studio-agent-attempt-run.v1", ...server }, null, 2)}\n`);
61
+ return pointer;
62
+ }
63
+ export async function saveCommandEvidence(pointer, commandId, command, result) {
64
+ const root = attemptDirectory(pointer.attempt_id);
65
+ const destination = path.join(root, "client", "evidence", command.startsWith("studio.drive") ? "drives" : command.startsWith("studio.inspect") ? "captures" : command.startsWith("studio.certify") ? "certification" : "other");
66
+ const serialized = JSON.stringify({ command_id: commandId, command, at: new Date().toISOString(), result }, null, 2);
67
+ if (serialized.length > 1024 * 1024)
68
+ return;
69
+ const sanitized = serialized.replace(/nbs_[A-Za-z0-9_-]+|nbp_[A-Za-z0-9_-]+|Bearer\s+[A-Za-z0-9_.-]+|eyJ[A-Za-z0-9_-]{30,}\.[A-Za-z0-9_-]+/g, "[REDACTED]");
70
+ await mkdir(destination, { recursive: true });
71
+ await writeFile(path.join(destination, `${commandId}.json`), `${sanitized}\n`, { mode: 0o600 });
72
+ }
73
+ function git(workspace, args, code = "ATTEMPT_GIT_INVALID") {
74
+ const result = spawnSync("git", args, { cwd: workspace, encoding: "utf8", maxBuffer: 4 * 1024 * 1024 });
75
+ if (result.status !== 0)
76
+ throw new CliError(`Cannot read workspace Git state: ${result.stderr?.trim() || result.error?.message || args.join(" ")}`, code, 2);
77
+ return result.stdout.trim();
78
+ }
79
+ async function snapshot(workspace, relative = "", entries = [], excluded = [], total = { bytes: 0 }) {
80
+ for (const item of await readdir(path.join(workspace, relative), { withFileTypes: true })) {
81
+ const name = path.join(relative, item.name);
82
+ const portable = name.split(path.sep).join("/");
83
+ if (item.isSymbolicLink() || BLOCKED_FILES.test(item.name) || (item.isDirectory() && BLOCKED_DIRS.has(item.name))) {
84
+ excluded.push(portable);
85
+ continue;
86
+ }
87
+ if (item.isDirectory()) {
88
+ await snapshot(workspace, name, entries, excluded, total);
89
+ continue;
90
+ }
91
+ if (!item.isFile()) {
92
+ excluded.push(portable);
93
+ continue;
94
+ }
95
+ const info = await stat(path.join(workspace, name));
96
+ if (info.size > MAX_FILE || total.bytes + info.size > MAX_TOTAL) {
97
+ excluded.push(portable);
98
+ continue;
99
+ }
100
+ const data = await readFile(path.join(workspace, name));
101
+ if (SECRET_CONTENT.test(data.toString("utf8"))) {
102
+ excluded.push(portable);
103
+ continue;
104
+ }
105
+ entries.push({ name: `workspace/${portable}`, data });
106
+ total.bytes += data.length;
107
+ }
108
+ return { entries, excluded };
109
+ }
110
+ export async function finalizeClient(pointer, workspace, server) {
111
+ const root = attemptDirectory(pointer.attempt_id);
112
+ const client = path.join(root, "client");
113
+ const reports = path.join(client, "reports");
114
+ const gitDir = path.join(client, "git");
115
+ await mkdir(reports, { recursive: true });
116
+ await mkdir(gitDir, { recursive: true });
117
+ const reportFiles = [];
118
+ for (const name of ["REPORT.md", "EVALUATION.md"]) {
119
+ let data;
120
+ try {
121
+ data = await readFile(path.join(workspace, name));
122
+ }
123
+ catch {
124
+ if (pointer.mode === "benchmark")
125
+ throw new CliError(`${name} is required before finishing this benchmark Attempt.`, name === "REPORT.md" ? "ATTEMPT_REPORT_MISSING" : "ATTEMPT_EVALUATION_MISSING", 2);
126
+ else
127
+ continue;
128
+ }
129
+ if (!data.length)
130
+ throw new CliError(`${name} is empty.`, "ATTEMPT_REPORT_EMPTY", 2);
131
+ if (SECRET_CONTENT.test(data.toString("utf8")))
132
+ throw new CliError(`${name} contains a credential-like value.`, "ATTEMPT_REPORT_SECRET", 2);
133
+ await writeFile(path.join(reports, name), data);
134
+ reportFiles.push({ name: `reports/${name}`, data });
135
+ }
136
+ const top = git(workspace, ["rev-parse", "--show-toplevel"]);
137
+ if (await realpath(top) !== await realpath(workspace))
138
+ throw new CliError("Finish from the Attempt workspace Git root.", "ATTEMPT_GIT_ROOT_REQUIRED", 2);
139
+ const statusText = git(workspace, ["status", "--porcelain=v1", "--untracked-files=all"]);
140
+ const history = `${git(workspace, ["log", "--graph", "--decorate", "--oneline", "--all"])}\n`;
141
+ const bundlePath = path.join(gitDir, "attempt.gitbundle");
142
+ git(workspace, ["bundle", "create", bundlePath, "--all"]);
143
+ const bundle = await readFile(bundlePath);
144
+ const snap = await snapshot(workspace);
145
+ const existing = await collectExistingEvidence(path.join(client, "evidence"));
146
+ const fileEntries = [
147
+ ...reportFiles, ...snap.entries, ...existing,
148
+ { name: "git/attempt.gitbundle", data: bundle },
149
+ { name: "git/history.txt", data: Buffer.from(history) },
150
+ { name: "git/working-tree-status.txt", data: Buffer.from(`${statusText}\n`) },
151
+ ];
152
+ const manifest = {
153
+ schema: "kheleshikhi.studio-agent-client-manifest.v1",
154
+ attempt_id: pointer.attempt_id, label: pointer.label, mode: pointer.mode,
155
+ status: "finished", outcome: server.outcome,
156
+ started_at: pointer.started_at, finished_at: new Date().toISOString(),
157
+ cli_version: String(server.initial_cli_version || "unknown"), node_version: process.versions.node,
158
+ platform: `${process.platform} ${process.arch}`, server_origin: pointer.server_origin,
159
+ documentation_fingerprint: server.documentation_fingerprint,
160
+ language_contract_fingerprint: server.language_contract_fingerprint,
161
+ scene_schema: server.scene_schema, compiler_version: server.compiler_version,
162
+ runtime_fingerprint: server.runtime_fingerprint,
163
+ git_head: git(workspace, ["rev-parse", "HEAD"]),
164
+ git_status: statusText,
165
+ studio_asset_ids: server.studio_asset_ids || [],
166
+ command_counts: server.command_counts || {},
167
+ paths_included: fileEntries.map((entry) => entry.name),
168
+ paths_excluded: snap.excluded,
169
+ };
170
+ const manifestData = Buffer.from(`${JSON.stringify(manifest, null, 2)}\n`);
171
+ await writeFile(path.join(client, "CLIENT_MANIFEST.json"), manifestData);
172
+ fileEntries.push({ name: "CLIENT_MANIFEST.json", data: manifestData });
173
+ const sums = Buffer.from(fileEntries.map((entry) => `${sha256(entry.data)} ${entry.name}`).join("\n") + "\n");
174
+ await writeFile(path.join(client, "CLIENT_SHA256SUMS"), sums);
175
+ fileEntries.push({ name: "CLIENT_SHA256SUMS", data: sums });
176
+ const bytes = makeZip(fileEntries);
177
+ if (!verifyZip(bytes))
178
+ throw new CliError("Client ZIP failed integrity verification.", "ATTEMPT_CLIENT_ZIP_INVALID");
179
+ const zipPath = path.join(client, "client-attempt.zip");
180
+ const temporary = `${zipPath}.${randomBytes(4).toString("hex")}.tmp`;
181
+ await writeFile(temporary, bytes);
182
+ await (await import("node:fs/promises")).rename(temporary, zipPath);
183
+ const digest = sha256(bytes);
184
+ await writeFile(path.join(client, "CLIENT_COMPLETE"), `${JSON.stringify({ attempt_id: pointer.attempt_id, zip_sha256: digest, finished_at: manifest.finished_at })}\n`);
185
+ return { path: zipPath, sha256: digest, included: fileEntries.length, excluded: snap.excluded };
186
+ }
187
+ async function collectExistingEvidence(directory) {
188
+ try {
189
+ await lstat(directory);
190
+ }
191
+ catch {
192
+ return [];
193
+ }
194
+ const found = await snapshot(directory);
195
+ return found.entries.map((entry) => ({ name: `evidence/${entry.name.slice("workspace/".length)}`, data: entry.data }));
196
+ }
197
+ //# sourceMappingURL=attempt.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attempt.js","sourceRoot":"","sources":["../src/attempt.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,102 @@
1
+ import { type Page } from "playwright";
2
+ import { AgentApi } from "./protocol.js";
3
+ import { type DriveScenario } from "./scenario.js";
4
+ export declare function studioBrowserLaunchOptions(headless: boolean): {
5
+ readonly headless: boolean;
6
+ readonly viewport: null;
7
+ readonly chromiumSandbox: true;
8
+ };
9
+ export declare function browserProbeLaunchOptions(channel?: "chrome" | "msedge"): {
10
+ readonly channel: "chrome" | "msedge" | undefined;
11
+ readonly headless: true;
12
+ readonly chromiumSandbox: true;
13
+ };
14
+ export declare function cssViewport(value: string): {
15
+ width: number;
16
+ height: number;
17
+ };
18
+ export declare function waitForCapturePaint(page: Page): Promise<void>;
19
+ export declare function applyCssViewport(page: Page, dimensions: {
20
+ width: number;
21
+ height: number;
22
+ }): Promise<void>;
23
+ export declare function renderingDiagnostics(page: Page, screenshot: Buffer): Promise<Record<string, unknown>>;
24
+ export declare const VIEW_QUERY: Record<string, Record<string, string>>;
25
+ export declare function viewRequiresRuntimeObservation(view: string): boolean;
26
+ interface BrowserCredential {
27
+ token: string;
28
+ expiresAt: number;
29
+ webOrigin: string;
30
+ }
31
+ interface BrowserAuthApi {
32
+ readonly origin: string;
33
+ request<T>(method: string, route: string, body?: unknown, authenticated?: boolean): Promise<T>;
34
+ }
35
+ /** Memory-only reuse of the short-lived browser JWT; bootstrap tickets stay one-use. */
36
+ export declare class BrowserCredentialManager {
37
+ private api;
38
+ private now;
39
+ private credential?;
40
+ private bootstrapCount;
41
+ private readonly apiOrigin;
42
+ constructor(api: BrowserAuthApi, now?: () => number);
43
+ clear(): void;
44
+ diagnostics(): {
45
+ browser_credential_cached: boolean;
46
+ browser_bootstrap_count: number;
47
+ };
48
+ tokenForRequest(requestUrl: string): string | undefined;
49
+ get(forceRefresh?: boolean): Promise<BrowserCredential>;
50
+ }
51
+ export interface BrowserCaptureRequest {
52
+ asset: string;
53
+ sourceHash: string;
54
+ view?: string;
55
+ viewport?: string;
56
+ fullPage?: boolean;
57
+ out: string;
58
+ timeoutMs?: number;
59
+ observation?: boolean;
60
+ drive?: boolean;
61
+ /** Library workspace route (`?workspace=<name>&selection=<stable id>`) instead of a Scene asset. */
62
+ library?: {
63
+ workspace: string;
64
+ selection: string;
65
+ revision?: string;
66
+ definitionHash?: string;
67
+ };
68
+ }
69
+ export interface PreviewObservationRequest extends BrowserCaptureRequest {
70
+ mode?: string;
71
+ duration?: number;
72
+ fps?: number;
73
+ }
74
+ export declare class StudioBrowser {
75
+ private api;
76
+ private context?;
77
+ private page?;
78
+ private headless;
79
+ private browserEngine;
80
+ private apiOrigin;
81
+ private credentials;
82
+ constructor(api: AgentApi);
83
+ start(headless: boolean): Promise<void>;
84
+ stop(): Promise<void>;
85
+ clearAuthentication(): void;
86
+ authenticationDiagnostics(): {
87
+ browser_credential_cached: boolean;
88
+ browser_bootstrap_count: number;
89
+ browser_engine: string;
90
+ };
91
+ private authenticatedPage;
92
+ open(asset: string, view?: string, allowRefresh?: boolean): Promise<Record<string, unknown>>;
93
+ capture(request: BrowserCaptureRequest, allowRefresh?: boolean): Promise<Record<string, unknown>>;
94
+ drive(request: BrowserCaptureRequest & {
95
+ scenario: DriveScenario;
96
+ candidate: Record<string, unknown>;
97
+ candidateHash: string;
98
+ }): Promise<Record<string, unknown>>;
99
+ /** Local-agent capture from the same browser page and renderer the Maker sees. */
100
+ capturePreview(request: PreviewObservationRequest): Promise<Record<string, unknown>>;
101
+ }
102
+ export {};