diff --git a/.agent/critical-audit/2026-08-12T02-10-41Z/findings.jsonl b/.agent/critical-audit/2026-08-12T02-10-41Z/findings.jsonl new file mode 100644 index 0000000..4a15aa9 --- /dev/null +++ b/.agent/critical-audit/2026-08-12T02-10-41Z/findings.jsonl @@ -0,0 +1,4 @@ +{"id":1,"severity":"HIGH","file":"scripts/live-required/tangle-sandbox-worker.mjs:212","defect":"Replay projection discarded execution identity from events.","failureScenario":"A replay stream contains the cursor event for execution-1 and a result event for execution-2; the stress proof accepts the foreign result when event identity is dropped.","status":"measured; resolved","evidence":"Independent read-only probe reproduced acceptance before the fix; final `node --test scripts/live-required.test.mjs` reports 19/19, including the foreign-event rejection at scripts/live-required.test.mjs:1259.","fix":"Preserve top-level or data.executionId on replay records and reject any present identity that differs from the requested execution.","verification":"`node --test scripts/live-required.test.mjs` → 19 passed, 0 failed.","costIfShipped":"1 false proof pass per foreign replay occurrence.","savedIfFixed":"1 rejected false proof pass per foreign replay occurrence."} +{"id":2,"severity":"HIGH","file":"scripts/live-required/tangle-sandbox-stress.mjs:86","defect":"Cancellation replay detection accepted acknowledgements without a terminal session state.","failureScenario":"The target execution returns first cancelled=true and second cancelled=false while sessionStatus remains running; the helper reports cancellation replay success.","status":"measured; resolved","evidence":"Independent read-only probe reproduced acceptance before the fix; final `node --test scripts/live-required.test.mjs` covers running-state rejection at scripts/live-required.test.mjs:973.","fix":"Require the exact target execution ID, the acknowledgement transition, and sessionStatus=cancelled.","verification":"`node --test scripts/live-required.test.mjs` → 19 passed, 0 failed.","costIfShipped":"1 false cancellation proof pass per non-terminal acknowledgement pair.","savedIfFixed":"1 rejected false cancellation proof pass per non-terminal acknowledgement pair."} +{"id":3,"severity":"MEDIUM","file":"scripts/live-required/tangle-sandbox-stress.mjs:243","defect":"Cleanup inspected only the first sandbox page.","failureScenario":"An exact proof tag appears at offset 100; cleanup reports success after checking a shorter first page and leaves the tagged sandbox undeleted.","status":"measured; resolved","evidence":"Independent read-only probe reproduced first-page confirmation before the fix; final `node --test scripts/live-required.test.mjs` covers page-two deletion at scripts/live-required.test.mjs:1129.","fix":"Page through all sandbox results and recheck all pages after deletion, rejecting repeated IDs.","verification":"`node --test scripts/live-required.test.mjs` → 19 passed, 0 failed.","costIfShipped":"1 leaked proof sandbox and its associated lifetime cost per hidden exact tag.","savedIfFixed":"1 prevented leaked proof sandbox per hidden exact tag."} +{"id":4,"severity":"MEDIUM","file":"scripts/live-required/tangle-sandbox-stress.mjs:384","defect":"Concurrent admission and cursor waits could omit an emitted admission from failure evidence.","failureScenario":"The dispatcher emits admission, then the watcher emits an error before its cursor; Promise.all rejects before state.admitted is assigned, so the partial artifact loses the admission receipt.","status":"measured; resolved","evidence":"Independent read-only probe reproduced a null admission before the fix; final `node --test scripts/live-required.test.mjs` covers the persisted receipt at scripts/live-required.test.mjs:1073.","fix":"Capture each successful wait into state before joining the two promises.","verification":"`node --test scripts/live-required.test.mjs` → 19 passed, 0 failed.","costIfShipped":"1 missing admission receipt per mid-dispatch failure artifact.","savedIfFixed":"1 retained admission receipt per mid-dispatch failure artifact."} diff --git a/.agent/critical-audit/2026-08-12T02-10-41Z/manifest.json b/.agent/critical-audit/2026-08-12T02-10-41Z/manifest.json new file mode 100644 index 0000000..b8a0f5d --- /dev/null +++ b/.agent/critical-audit/2026-08-12T02-10-41Z/manifest.json @@ -0,0 +1,26 @@ +{ + "schemaVersion": 1, + "scope": "Tangle sandbox stress and worker scripts with deterministic fake seams", + "files": 3, + "base": "8cce4c6e8aade3b1fc51f558d65a823ac12a5e1a", + "head": "8cce4c6e8aade3b1fc51f558d65a823ac12a5e1a", + "workingTree": true, + "diffSha256": "b8d943482ed98ce1cbf6c0792da8d4de043ea383c2a0de001af57b20a94423d5", + "projectType": "Node.js ESM package", + "reviewers": [ + "A: correctness, process lifecycle, cleanup, and failure persistence", + "B: architecture, SDK contract, and boundary coverage", + "C: independent read-only adversarial review and focused test pass" + ], + "mode": "current working-tree review with serial local checks", + "verdict": "APPROVE", + "counts": { + "critical": 0, + "high": 2, + "medium": 2, + "low": 0, + "resolved": 4, + "unresolved": 0, + "dropped": 1 + } +} diff --git a/.agent/critical-audit/2026-08-12T02-10-41Z/summary.md b/.agent/critical-audit/2026-08-12T02-10-41Z/summary.md new file mode 100644 index 0000000..693b39c --- /dev/null +++ b/.agent/critical-audit/2026-08-12T02-10-41Z/summary.md @@ -0,0 +1,38 @@ +# Audit: Tangle sandbox stress and worker scripts — 8cce4c6e8aade3b1fc51f558d65a823ac12a5e1a..8cce4c6e8aade3b1fc51f558d65a823ac12a5e1a — n=3 files, 4 findings + +**Verdict:** APPROVE — 4 measured defects resolved · 0 CRITICAL / 2 HIGH / 2 MEDIUM / 0 LOW +**Worst:** #1 `scripts/live-required/tangle-sandbox-worker.mjs:212` — foreign replay identity could pass · cost if shipped 1 false proof pass per occurrence +**Next:** stop + +## Scope + +| Field | Value | +|---|---| +| Files | n=3 via explicit target scope | +| Base..head | `8cce4c6e8aade3b1fc51f558d65a823ac12a5e1a..8cce4c6e8aade3b1fc51f558d65a823ac12a5e1a` | +| Project type | Node.js ESM package | +| Reviewers | A,B,C · serial | +| Not inspected | Runtime adapters and live network behavior; both were outside the requested scope and tests used fakes | + +## Findings — 4 of 5, ranked + +| # | Sev | file:line | Defect | Failure scenario (input/state → wrong result) | Status | Evidence | Fix | Verification | Cost if shipped | Saved if fixed | +|---:|---|---|---|---|---|---|---|---|---:|---:| +| 1 | HIGH | `scripts/live-required/tangle-sandbox-worker.mjs:212` | Replay projection discarded event execution identity. | Cursor event for execution-1 plus result event for execution-2 → foreign result accepted. | measured; resolved | Independent probe; final regression `scripts/live-required.test.mjs:1259` | Preserve event identity and reject foreign IDs. | `node --test scripts/live-required.test.mjs` → 19/19 | 1 false pass/occurrence | 1 false pass rejected/occurrence | +| 2 | HIGH | `scripts/live-required/tangle-sandbox-stress.mjs:86` | Cancellation proof ignored terminal state. | Matching acknowledgements with `sessionStatus=running` → cancellation proof accepted. | measured; resolved | Independent probe; final regression `scripts/live-required.test.mjs:973` | Require `sessionStatus=cancelled`. | `node --test scripts/live-required.test.mjs` → 19/19 | 1 false pass/occurrence | 1 false pass rejected/occurrence | +| 3 | MEDIUM | `scripts/live-required/tangle-sandbox-stress.mjs:243` | Cleanup stopped at page one. | Exact tag at offset 100 → sandbox remains while cleanup reports success. | measured; resolved | Independent probe; final regression `scripts/live-required.test.mjs:1129` | Page every result and verify every page after deletion. | `node --test scripts/live-required.test.mjs` → 19/19 | 1 leaked sandbox/hidden tag | 1 leak prevented/hidden tag | +| 4 | MEDIUM | `scripts/live-required/tangle-sandbox-stress.mjs:384` | Concurrent failure could omit a received admission. | Admission emitted before cursor error → partial artifact has no admission receipt. | measured; resolved | Independent probe; final regression `scripts/live-required.test.mjs:1073` | Store each successful wait before `Promise.all`. | `node --test scripts/live-required.test.mjs` → 19/19 | 1 missing receipt/failure | 1 receipt retained/failure | + +1 dropped (an external fake counter changed despite a receipt saying `dispatched=false`; it did not describe a production-visible failure). + +## Assumptions & unverified + +| Assumption | Finding it would flip | Check that settles it | +|---|---|---| +| The SDK keeps `SessionStatus` values aligned with `completed` and `cancelled`. | Terminal-state checks could reject a valid live result. | SDK declaration `types-C7mxmAil.d.ts:3094` and live proof run. | +| The account list API honors `limit` and `offset`. | Pagination could miss or repeat cleanup records. | SDK declaration `client-BCwkh2kj.d.ts:382` and live cleanup run. | + +## Self-gate + +9/9 passed — failed: none. +1 verdict = decision + 1 number · 2 every finding has file:line · 3 concrete failure scenario · 4 status label · 5 evidence is a pointer · 6 cost both sides · 7 fix + verification per row · 8 zero adjectives standing in for counts · 9 185 words outside tables ≤600. diff --git a/.agent/critical-audit/2026-08-12T03-41-05Z/findings.jsonl b/.agent/critical-audit/2026-08-12T03-41-05Z/findings.jsonl new file mode 100644 index 0000000..5ee0c1c --- /dev/null +++ b/.agent/critical-audit/2026-08-12T03-41-05Z/findings.jsonl @@ -0,0 +1 @@ +{"findings":[],"dropped":0} diff --git a/.agent/critical-audit/2026-08-12T03-41-05Z/manifest.json b/.agent/critical-audit/2026-08-12T03-41-05Z/manifest.json new file mode 100644 index 0000000..c8207d2 --- /dev/null +++ b/.agent/critical-audit/2026-08-12T03-41-05Z/manifest.json @@ -0,0 +1,26 @@ +{ + "schemaVersion": 1, + "scope": "provider-neutral retained execution lifecycle and provider-owned identity binding", + "files": 11, + "base": "8cce4c6e8aade3b1fc51f558d65a823ac12a5e1a", + "head": "8cce4c6e8aade3b1fc51f558d65a823ac12a5e1a", + "workingTree": true, + "diffSha256": "f62722d6b99529f797363a13035430b5a2307e29e1a771b5c34764f46dc21006", + "projectType": "Node.js ESM package", + "reviewers": [ + "A: lifecycle correctness, exact identity, cancellation, and resource cleanup", + "B: provider-neutral contract, module boundaries, and recovery architecture", + "C: independent callsite, fixture, and restart coverage review" + ], + "mode": "current working-tree review with serial local checks", + "verdict": "APPROVE", + "counts": { + "critical": 0, + "high": 0, + "medium": 0, + "low": 0, + "resolved": 0, + "unresolved": 0, + "dropped": 0 + } +} diff --git a/.agent/critical-audit/2026-08-12T03-41-05Z/summary.md b/.agent/critical-audit/2026-08-12T03-41-05Z/summary.md new file mode 100644 index 0000000..b8e6234 --- /dev/null +++ b/.agent/critical-audit/2026-08-12T03-41-05Z/summary.md @@ -0,0 +1,34 @@ +# Audit: provider-neutral retained execution — 8cce4c6e8aade3b1fc51f558d65a823ac12a5e1a..8cce4c6e8aade3b1fc51f558d65a823ac12a5e1a — n=11 files, 0 findings + +**Verdict:** APPROVE — no reproducible lifecycle or identity defect · 0 CRITICAL / 0 HIGH / 0 MEDIUM / 0 LOW +**Worst:** none — no reproducible failure scenario · cost if shipped 0 measured incidents +**Next:** stop + +## Scope + +| Field | Value | +|---|---| +| Files | n=11 via explicit retained-runtime scope | +| Base..head | `8cce4c6e8aade3b1fc51f558d65a823ac12a5e1a..8cce4c6e8aade3b1fc51f558d65a823ac12a5e1a` | +| Project type | Node.js ESM package | +| Reviewers | A,B,C · serial | +| Not inspected | Tangle backend and production composition; explicitly outside this change | + +## Findings — 0 of 0, ranked + +| # | Sev | file:line | Defect | Failure scenario (input/state → wrong result) | Status | Evidence | Fix | Verification | Cost if shipped | Saved if fixed | +|---:|---|---|---|---|---|---|---|---|---:|---:| + +0 dropped (no reproducible failure scenario / no actionable fix). + +## Assumptions & unverified + +| Assumption | Finding it would flip | Check that settles it | +|---|---|---| +| CLI Bridge discovery maps its provider execution ID to the server run lookup key. | A provider implementation could require another discovery coordinate. | The local retained fixture with Braid ID `run/retained-crash-window` passes restart discovery; inspect any future provider adapter contract before adoption. | +| Tangle recovery will implement `RetainedExecutionDriver.recover` using its persisted exact reference. | A future adapter could still derive cloud environment identity from the local run ID. | Add the Tangle adapter and run the exact-reference recovery test against its provider. | + +## Self-gate + +9/9 passed — failed: none. +1 verdict = decision + 1 number · 2 every finding has file:line · 3 concrete failure scenario · 4 status label · 5 evidence is a pointer · 6 cost both sides · 7 fix + verification per row · 8 zero adjectives standing in for counts · 9 words ≤600 outside tables. diff --git a/.agent/skill-runs.jsonl b/.agent/skill-runs.jsonl index 86fb877..968b6b7 100644 --- a/.agent/skill-runs.jsonl +++ b/.agent/skill-runs.jsonl @@ -36,3 +36,8 @@ {"skill":"/ui-test","ts":"2026-08-11T03:04:27Z","project":"braid-current-ux-wt","target":"Braid packed terminal shell and focused workflows","operatorPrompt":"","durationMin":null,"verdict":"PASS","dispatchedTo":"/verify","operatorOverride":null,"transcriptPath":null,"traceDir":null} {"skill":"/critical-audit","ts":"2026-08-11T03:04:27Z","project":"braid-current-ux-wt","target":"Braid current UX diff n=32 files","operatorPrompt":"","durationMin":null,"verdict":"APPROVE","dispatchedTo":"/verify","operatorOverride":null,"transcriptPath":null,"traceDir":null} {"skill":"/verify","ts":"2026-08-11T03:04:27Z","project":"braid-current-ux-wt","target":"Braid current packed terminal UX and CLI Bridge checks","operatorPrompt":"","durationMin":null,"verdict":"PASS","dispatchedTo":"/ship","operatorOverride":null,"transcriptPath":null,"traceDir":null} +{"skill":"/harden","ts":"2026-08-12T02:12:32Z","project":"braid-transcript-first-wt","target":"Tangle sandbox stress and worker lifecycle, replay, cancellation, artifact, and cleanup paths n=3 files","operatorPrompt":"","durationMin":null,"verdict":"APPROVE","dispatchedTo":"/critical-audit","operatorOverride":null,"transcriptPath":null,"traceDir":null} +{"skill":"/critical-audit","ts":"2026-08-12T02:12:33Z","project":"braid-transcript-first-wt","target":"Tangle sandbox stress and worker scripts with deterministic fake seams n=3 files","operatorPrompt":"","durationMin":null,"verdict":"APPROVE","dispatchedTo":"/stop","operatorOverride":null,"transcriptPath":null,"traceDir":null} +{"skill":"/harden","ts":"2026-08-12T03:42:22Z","project":"braid-transcript-first-wt","target":"provider-neutral retained execution lifecycle, exact provider identity, recovery, cancellation, and event binding n=11 files","operatorPrompt":"","durationMin":null,"verdict":"APPROVE","dispatchedTo":"/critical-audit","operatorOverride":null,"transcriptPath":null,"traceDir":null} +{"skill":"/critical-audit","ts":"2026-08-12T03:42:23Z","project":"braid-transcript-first-wt","target":"provider-neutral retained execution lifecycle and provider-owned identity binding n=11 files","operatorPrompt":"","durationMin":null,"verdict":"APPROVE","dispatchedTo":"/stop","operatorOverride":null,"transcriptPath":null,"traceDir":null} +{"skill":"/converge","ts":"2026-08-12T12:53:14Z","project":"braid-transcript-first-wt","target":"Braid full release check performance failure","operatorPrompt":"","durationMin":null,"verdict":"PASS","dispatchedTo":"/review-to-green","operatorOverride":null,"transcriptPath":null,"traceDir":null} diff --git a/README.md b/README.md index 23ea23c..afa955a 100644 --- a/README.md +++ b/README.md @@ -7,45 +7,52 @@

-Braid is a terminal client over [`agent-runtime`](https://github.com/tangle-network/agent-runtime). +Braid is one durable terminal for coding agents. -It lets one portable [`AgentProfile`](https://github.com/tangle-network/agent-sdk/tree/main/packages/agent-interface) drive different coding runners through local CLI Bridge connections, Tangle inference, or Tangle sandboxes. +A portable [`AgentProfile`](https://github.com/tangle-network/agent-sdk/tree/main/packages/agent-interface) selects the runner, model, instructions, tools, and permissions. -Braid owns the conversation, transcript, branches, approvals, activity, graph, and trace-analysis experience. +Braid sends every turn through [`agent-runtime`](https://github.com/tangle-network/agent-runtime), then keeps the transcript, branches, approvals, activity, graph, and trace analysis together. -The selected runner owns its native process and session, while `agent-runtime` owns admission, lifecycle, normalized events, and runtime control. +![Braid sending a Product engineer AgentProfile through agent-runtime, Local CLI Bridge, Claude Code, and Opus, then asking a cited question about the retained run](artifacts/demo/braid-live.gif) -![Braid using a GLM reviewer AgentProfile through agent-runtime, Local CLI Bridge, Pi, and GLM-5.2, then opening the exact retained-run activity](artifacts/demo/braid-live-cli-bridge.gif) +This recording uses a packed Braid artifact and a real `Product engineer` AgentProfile. -This 13-second recording comes from a clean install of a freshly packed `@tangle-network/braid@0.1.0` artifact. +It routes one coding task through Local CLI Bridge to Claude Code and `opus`. -The installed artifact resolves `agent-runtime@0.132.0` and the CLI Bridge provider at `0.6.0` from npm. +It verifies the edited workspace, then switches to a `Trace analyst` AgentProfile on `sonnet`. -It restores the `Braid live GLM reviewer` AgentProfile and its retained Pi session through Local CLI Bridge. +It runs `/ask` over the frozen trace and renders cited findings, model calls, tokens, cost provenance, and latency. -The user asks Braid to make a generated JavaScript CLI independent of an incompatible parent package configuration. +The [capture manifest](artifacts/demo/braid-live.json) records the package hash, route, profile, limits, usage, latency, workspace proof, analysis evidence, and artifact hashes. -Braid fixes the project, runs `node --test`, and reports 11 tests passed with zero failures. +## Install -The user then types `/activity` and opens the retained run receipt. +Braid requires Node.js 22.19 or newer. -The receipt shows the AgentProfile, Pi runner, Local CLI Bridge connection, execution environment, provider session, exact GLM-5.2 route, thinking level, tokens, model calls, and measurement completeness. +Current validated release targets are Linux x64 and macOS arm64. -The run used 28,949 input tokens, 1,943 output tokens, and five model calls. +The package rejects Windows installation until encrypted state meets the required path-race boundary there. -Cost and model latency remain `unknown` because this route did not report them. +```bash +npm install --global @tangle-network/braid +braid +``` + +The first-run flow selects an AgentProfile and a connection. -An independent Node process reran all 11 tests successfully. +No runner-specific Braid configuration is required. -A fresh Braid process then restored the same run and transcript from encrypted state and exited with code zero. +For an offline terminal walkthrough, use the deterministic fixture. -The [capture summary](artifacts/demo/braid-live-cli-bridge.json) records the package hash, route, retained identifiers, independent test output, restart result, and artifact hashes. +```bash +braid --fixture deterministic +``` -The [Pi and `/ask` recording](artifacts/demo/braid-live-pi.gif) shows a second real route followed by cited trace analysis. +The fixture proves rendering and state transitions only. -Its [capture manifest](artifacts/demo/braid-live-pi.json) records the exact route, profile, limits, usage, cost, latency, workspace checks, and artifact hashes. +It does not prove a live runner, model, connection, inference route, or sandbox. -## Product path +## How it works The core path is deliberately small. @@ -66,46 +73,10 @@ AgentProfile + user turn normalized events, receipts, activity, and final output ``` -Braid does not implement another agent loop, spawn runner processes directly, parse private runner output, or invent a second profile format. +Braid does not implement an agent loop, spawn runner processes directly, parse private runner output, or invent another profile format. A concrete local route is `AgentProfile` with `harness: 'pi'` → Braid admission → `agent-runtime` → a CLI Bridge connection → Pi → normalized events back to Braid. -## Install - -Braid requires Node.js 22.19 or newer. - -Current validated release targets are Linux x64 and macOS arm64. - -npm rejects Windows installation because encrypted state cannot yet meet Braid's path-race boundary there. - -```bash -npm install --global @tangle-network/braid -braid -``` - -To build Braid from source, use pnpm 11.18. - -```bash -git clone https://github.com/tangle-network/braid.git -cd braid -corepack enable -pnpm install --frozen-lockfile -pnpm build -pnpm exec braid -``` - -The first-run flow selects an `AgentProfile` and a connection without requiring a hand-written runner configuration file. - -For an offline deterministic terminal walkthrough, use the explicit fixture mode. - -```bash -braid --fixture deterministic -``` - -The fixture proves Braid rendering and state transitions only. - -It does not prove a live runner, model, CLI Bridge, inference, or sandbox integration. - ## AgentProfile is the configuration unit `AgentProfile` is the canonical portable definition of one agent. @@ -256,8 +227,18 @@ A Tangle sandbox connection can provide an isolated remote workspace, environmen Braid shows those capabilities and their receipts through the same activity and graph surfaces. +The current published Tangle path runs one isolated cloud turn and deletes its environment after the turn. + +Cloud restart, retained-session continuation, checkpoint, and fork remain unavailable until the shared provider reports exact recovery support. + The user can inspect the requested and verified execution location, but provider-private machine details remain unavailable when they are not reported. +The latest production stress proof completed 20 of 20 Braid turns through OpenCode, GLM 5.2, and Tangle Sandbox at four-way concurrency. + +All 20 remote environments were unique and confirmed deleted, while the account's active Sandbox count returned from four to four. + +See the [secret-free proof artifact](artifacts/verification/live/tangle-sandbox-braid-execution-stress-production-20260812.json) for every run, token receipt, latency, environment observation, and cleanup result. + ## Commands users reach for first | Need | Command or key | diff --git a/artifacts/demo/braid-live.cast b/artifacts/demo/braid-live.cast new file mode 100644 index 0000000..0b85219 --- /dev/null +++ b/artifacts/demo/braid-live.cast @@ -0,0 +1,906 @@ +{"version":2,"width":120,"height":30,"timestamp":1786538523,"duration":121.616066,"idle_time_limit":1,"command":"braid --profile Product-engineer --connection Local-CLI-Bridge","title":"Braid · AgentProfile coding run, then trace analysis","env":{"TERM":"xterm-256color"},"stdin":true} +[0.01,"o","\u001b[?2004h"] +[0.01,"o","\u001b[>7u\u001b[?u\u001b[c\u001b[?1049h\u001b[2J\u001b[H\u001b[?25l"] +[0.01,"o","\u001b[?2026h\u001b[2J\u001b[H\u001b[3J \u001b[0m\u001b]8;;\u0007\r\r\n Ask Product engineer anything. \u001b[0m\u001b]8;;\u0007\r\r\n \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[38;2;119;166;255m›\u001b[39m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[38;2;225;230;240mprofile Product engineer\u001b[39m · \u001b[38;2;225;230;240mclaude-code / opus\u001b[39m · \u001b[38;2;225;230;240mLocal CLI Bridge\u001b[39m · \u001b[38;2;225;230;240mhigh\u001b[39m \u001b[38;2;112;211;151mready\u001b[39m · \u001b[38;2;225;230;240mCtrl+P commands\u001b[39m\u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[0.01,"o","\u001b[3A\u001b[2G\u001b[?25l"] +[0.01,"o","\u001b]0;Braid — /tmp/braid-live-demo-8KYbTI/braid-demo\u0007"] +[0.01,"o","\u001b[?2026h\u001b[25A\r\u001b[2K \u001b[38;2;135;145;165mAsk Product engineer anything.\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;2;225;230;240mprofile Product engineer\u001b[39m · \u001b[38;2;225;230;240mclaude-code / opus\u001b[39m · \u001b[38;2;225;230;240mLocal CLI Bridge\u001b[39m · \u001b[38;2;225;230;240mhigh\u001b[39m \u001b[38;2;225;230;240mCtrl+P commands\u001b[39m\u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[0.01,"o","\u001b[3A\u001b[2G\u001b[?25l"] +[1.01,"i","/"] +[1.013599,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m/\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[3G\u001b[?25l"] +[1.029801,"o","\u001b[?2026h\u001b[7A\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m/\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;2;135;145;165m──────────────────────────────────────────────\u001b[39m\u001b[38;2;119;166;255m alt+enter newline · paste \u001b[39m\u001b[38;2;135;145;165m───────────────────────────────────────────────\u001b[39m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[1m\u001b[38;2;119;166;255m→ new Create an empty conversation\u001b[39m\u001b[22m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K open\u001b[38;2;135;145;165m Search and open conversations\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K profile\u001b[38;2;135;145;165m Inspect, select, import, or edit a profile\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K connection\u001b[38;2;135;145;165m Inspect, create, test, select, or remove a connection\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K runner\u001b[38;2;135;145;165m unavailable — Runner overrides require the current profile compatibility helpers\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m (1/29)\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[1.029917,"o","\u001b[9A\u001b[3G\u001b[?25l"] +[1.034692,"i","p"] +[1.036254,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m/p\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[1.036285,"o","\u001b[4G\u001b[?25l"] +[1.05267,"o","\u001b[?2026h\u001b[4B\r\u001b[2K \u001b[1m\u001b[38;2;119;166;255m→ profile Inspect, select, import, or edit a profile\u001b[39m\u001b[22m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K open\u001b[38;2;135;145;165m Search and open conversations\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K approve\u001b[38;2;135;145;165m unavailable — Interaction response is not exposed by the current runtime adapter\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K export\u001b[38;2;135;145;165m Export selected redacted data\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K import\u001b[38;2;135;145;165m Import a redacted Braid conversation file\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m (1/8)\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[9A\u001b[4G"] +[1.052754,"o","\u001b[?25l"] +[1.059351,"i","r"] +[1.061345,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m/pr\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[1.061412,"o","\u001b[5G\u001b[?25l"] +[1.076855,"o","\u001b[?2026h\r\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m/pr\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;2;135;145;165m──────────────────────────────────────────────\u001b[39m\u001b[38;2;119;166;255m alt+enter newline · paste \u001b[39m\u001b[38;2;135;145;165m───────────────────────────────────────────────\u001b[39m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[1m\u001b[38;2;119;166;255m→ profile Inspect, select, import, or edit a profile\u001b[39m\u001b[22m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K approve\u001b[38;2;135;145;165m unavailable — Interaction response is not exposed by the current runtime adapter\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K export\u001b[38;2;135;145;165m Export selected redacted data\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K import\u001b[38;2;135;145;165m Import a redacted Braid conversation file\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K compare\u001b[38;2;135;145;165m unavailable — Two completed or failed runs are required for comparison\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[8A\u001b[5G\u001b[?25l"] +[1.083559,"i","o"] +[1.085117,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m/pro\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[6G"] +[1.085189,"o","\u001b[?25l"] +[1.101158,"o","\u001b[?2026h\r\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m/pro\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;2;135;145;165m──────────────────────────────────────────────\u001b[39m\u001b[38;2;119;166;255m alt+enter newline · paste \u001b[39m\u001b[38;2;135;145;165m───────────────────────────────────────────────\u001b[39m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[1m\u001b[38;2;119;166;255m→ profile Inspect, select, import, or edit a profile\u001b[39m\u001b[22m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K approve\u001b[38;2;135;145;165m unavailable — Interaction response is not exposed by the current runtime adapter\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[5A\u001b[6G\u001b[?25l"] +[1.108759,"i","f"] +[1.110058,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m/prof\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[7G\u001b[?25l"] +[1.12553,"o","\u001b[?2026h\r\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m/prof\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;2;135;145;165m──────────────────────────────────────────────\u001b[39m\u001b[38;2;119;166;255m alt+enter newline · paste \u001b[39m\u001b[38;2;135;145;165m───────────────────────────────────────────────\u001b[39m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[1m\u001b[38;2;119;166;255m→ profile Inspect, select, import, or edit a profile\u001b[39m\u001b[22m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[4A\u001b[7G\u001b[?25l"] +[1.133052,"i","i"] +[1.134259,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m/profi\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[8G"] +[1.134332,"o","\u001b[?25l"] +[1.149974,"o","\u001b[8G\u001b[?25l"] +[1.157318,"i","l"] +[1.158603,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m/profil\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[9G"] +[1.158862,"o","\u001b[?25l"] +[1.174266,"o","\u001b[9G\u001b[?25l"] +[1.181716,"i","e"] +[1.183043,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m/profile\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[10G\u001b[?25l"] +[1.198311,"o","\u001b[10G\u001b[?25l"] +[1.206745,"i","\r"] +[1.209021,"o","\u001b[?2026h\r\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[2A\u001b[2G\u001b[?25l"] +[1.212952,"o","\u001b[?25l"] +[1.239429,"o","\u001b[?2026h\u001b[18A\r\u001b[2K \u001b[0m\u001b]8;;\u0007 Active profile · Product engineer \u001b[0m\u001b]8;;\u0007 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007 \u001b[38;2;135;145;165msource Configured AgentProfile · trusted · read-only\u001b[39m \u001b[0m\u001b]8;;\u0007 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007 \u001b[38;2;135;145;165mrunner claude-code · model opus\u001b[39m \u001b[0m\u001b]8;;\u0007 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007 \u001b[38;2;135;145;165mthinking high · max output 32,768 tokens\u001b[39m \u001b[0m\u001b]8;;\u0007 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007 \u001b[38;2;135;145;165mdigest sha256:751f2a0f…b8cba6c0\u001b[39m \u001b[0m\u001b]8;;\u0007 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007 \u001b[1m\u001b[38;2;151;190;255mprofiles\u001b[39m\u001b[22m \u001b[0m\u001b]8;;\u0007 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\u001b[38;2;135;145;165m─────────────────────────────────────────────────────────────────────────────────────────────────────────\u001b[0m\u001b]8;;\u0007 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007> \u001b[7m \u001b[27m \u001b[0m\u001b]8;;\u0007 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\u001b[1m\u001b[38;2;119;166;255m→ CLI Bridge · claude-code · hai\u001b[0m trusted · read-only · Advertised CLI Bridge model · claude-code · hai\u001b[0m\u001b[39m\u001b[22m \u001b[0m\u001b]8;;\u0007 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007 ✓ Product engineer\u001b[38;2;135;145;165m trusted · read-only · Configured AgentProfile · claude-code · opus\u001b[39m \u001b[0m\u001b]8;;\u0007 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007 Trace analyst\u001b[38;2;135;145;165m trusted · read-only · profile.json · claude-code · sonnet\u001b[39m \u001b[0m\u001b]8;;\u0007 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\u001b[38;2;135;145;165m─────────────────────────────────────────────────────────────────────────────────────────────────────────\u001b[0m\u001b]8;;\u0007 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007 \u001b[38;2;135;145;165menter · ^V valid · ^S save · esc\u001b[39m \u001b[0m\u001b]8;;\u0007 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[11A\u001b[10G\u001b[?25l"] +[2.166086,"i","\u001b"] +[2.177255,"o","\u001b[?25l"] +[2.178892,"o","\u001b[?2026h\u001b[7A\r\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n"] +[2.17891,"o","\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[2G\u001b[?25l"] +[2.516763,"i","F"] +[2.518735,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mF\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[3G\u001b[?25l"] +[2.526098,"i","i"] +[2.527353,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFi\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[4G"] +[2.527396,"o","\u001b[?25l"] +[2.53501,"i","n"] +[2.536008,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFin\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[5G"] +[2.536031,"o","\u001b[?25l"] +[2.54426,"i","i"] +[2.545222,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFini\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[6G\u001b[?25l"] +[2.553454,"i","s"] +[2.554384,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinis\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[7G\u001b[?25l"] +[2.562663,"i","h"] +[2.563654,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[8G\u001b[?25l"] +[2.571938,"i"," "] +[2.573121,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish \u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[2.573152,"o","\u001b[9G\u001b[?25l"] +[2.58137,"i","t"] +[2.582254,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish t\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[10G\u001b[?25l"] +[2.590497,"i","h"] +[2.591612,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish th\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[2.59174,"o","\u001b[11G\u001b[?25l"] +[2.598984,"i","e"] +[2.600108,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[12G\u001b[?25l"] +[2.608558,"i"," "] +[2.609957,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the \u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[13G\u001b[?25l"] +[2.617396,"i","s"] +[2.618557,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the s\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[14G\u001b[?25l"] +[2.625904,"i","l"] +[2.626545,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the sl\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[15G\u001b[?25l"] +[2.635057,"i","u"] +[2.635812,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slu\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[2.635952,"o","\u001b[16G\u001b[?25l"] +[2.644303,"i","g"] +[2.644922,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slug\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[17G"] +[2.644984,"o","\u001b[?25l"] +[2.653467,"i","i"] +[2.654477,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugi\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[18G\u001b[?25l"] +[2.661878,"i","f"] +[2.66308,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugif\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[19G"] +[2.663124,"o","\u001b[?25l"] +[2.671436,"i","y"] +[2.672227,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugify\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[20G\u001b[?25l"] +[2.680615,"i"," "] +[2.681927,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugify \u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[2.681992,"o","\u001b[21G\u001b[?25l"] +[2.690399,"i","f"] +[2.691489,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugify f\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[22G\u001b[?25l"] +[2.698864,"i","u"] +[2.699981,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugify fu\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[23G\u001b[?25l"] +[2.708366,"i","n"] +[2.709503,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugify fun\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[24G"] +[2.709546,"o","\u001b[?25l"] +[2.716856,"i","c"] +[2.718198,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugify func\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[2.718268,"o","\u001b[25G\u001b[?25l"] +[2.726667,"i","t"] +[2.727915,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugify funct\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[2.72798,"o","\u001b[26G\u001b[?25l"] +[2.736369,"i","i"] +[2.737691,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugify functi\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[2.737761,"o","\u001b[27G\u001b[?25l"] +[2.745129,"i","o"] +[2.746206,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugify functio\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[28G\u001b[?25l"] +[2.754597,"i","n"] +[2.755771,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugify function\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[29G"] +[2.755826,"o","\u001b[?25l"] +[2.76429,"i","."] +[2.765451,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugify function.\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[2.765502,"o","\u001b[30G\u001b[?25l"] +[2.77283,"i"," "] +[2.773862,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugify function. \u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[31G\u001b[?25l"] +[2.782234,"i","N"] +[2.783226,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugify function. N\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[32G\u001b[?25l"] +[2.791734,"i","o"] +[2.792679,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugify function. No\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[33G\u001b[?25l"] +[2.801107,"i","r"] +[2.802074,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugify function. Nor\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[34G\u001b[?25l"] +[2.81056,"i","m"] +[2.811584,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugify function. Norm\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[35G\u001b[?25l"] +[2.818953,"i","a"] +[2.819955,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugify function. Norma\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[36G\u001b[?25l"] +[2.828298,"i","l"] +[2.829467,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugify function. Normal\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[37G"] +[2.829532,"o","\u001b[?25l"] +[2.837078,"i","i"] +[2.83814,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugify function. Normali\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[2.838194,"o","\u001b[38G\u001b[?25l"] +[2.846436,"i","z"] +[2.847509,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugify function. Normaliz\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[39G\u001b[?25l"] +[2.854767,"i","e"] +[2.85592,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugify function. Normalize\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[40G"] +[2.855953,"o","\u001b[?25l"] +[2.864369,"i"," "] +[2.866005,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugify function. Normalize \u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[41G\u001b[?25l"] +[2.873411,"i","U"] +[2.874465,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugify function. Normalize U\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[42G\u001b[?25l"] +[2.881801,"i","n"] +[2.882868,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugify function. Normalize Un\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[43G\u001b[?25l"] +[2.891311,"i","i"] +[2.892305,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugify function. Normalize Uni\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[44G\u001b[?25l"] +[2.899817,"i","c"] +[2.900942,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugify function. Normalize Unic\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[45G\u001b[?25l"] +[2.909398,"i","o"] +[2.91071,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugify function. Normalize Unico\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[46G\u001b[?25l"] +[2.918205,"i","d"] +[2.919465,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugify function. Normalize Unicod\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[47G\u001b[?25l"] +[2.92687,"i","e"] +[2.927558,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugify function. Normalize Unicode\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[48G\u001b[?25l"] +[2.936025,"i"," "] +[2.93669,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugify function. Normalize Unicode \u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[49G\u001b[?25l"] +[2.945187,"i","a"] +[2.945883,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugify function. Normalize Unicode a\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[50G\u001b[?25l"] +[2.954308,"i","c"] +[2.955113,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugify function. Normalize Unicode ac\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[51G"] +[2.955152,"o","\u001b[?25l"] +[2.963402,"i","c"] +[2.964141,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugify function. Normalize Unicode acc\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[52G"] +[2.964189,"o","\u001b[?25l"] +[2.972466,"i","e"] +[2.9731,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugify function. Normalize Unicode acce\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[53G"] +[2.973128,"o","\u001b[?25l"] +[2.981345,"i","n"] +[2.982406,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugify function. Normalize Unicode accen\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[2.982433,"o","\u001b[54G\u001b[?25l"] +[2.990686,"i","t"] +[2.991439,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugify function. Normalize Unicode accent\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[2.99146,"o","\u001b[55G\u001b[?25l"] +[3.000691,"i","s"] +[3.001483,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugify function. Normalize Unicode accents\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[3.001517,"o","\u001b[56G\u001b[?25l"] +[3.009772,"i",","] +[3.01101,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugify function. Normalize Unicode accents,\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[57G\u001b[?25l"] +[3.019263,"i"," "] +[3.020195,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugify function. Normalize Unicode accents, \u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[58G\u001b[?25l"] +[3.028511,"i","t"] +[3.029421,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugify function. Normalize Unicode accents, t\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[59G"] +[3.029463,"o","\u001b[?25l"] +[3.036737,"i","r"] +[3.038019,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugify function. Normalize Unicode accents, tr\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[3.038071,"o","\u001b[60G\u001b[?25l"] +[3.046607,"i","e"] +[3.04766,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugify function. Normalize Unicode accents, tre\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[61G\u001b[?25l"] +[3.055938,"i","a"] +[3.05692,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugify function. Normalize Unicode accents, trea\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[62G"] +[3.056963,"o","\u001b[?25l"] +[3.065232,"i","t"] +[3.066429,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugify function. Normalize Unicode accents, treat\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[63G"] +[3.066474,"o","\u001b[?25l"] +[3.073735,"i"," "] +[3.074999,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugify function. Normalize Unicode accents, treat \u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[3.07503,"o","\u001b[64G\u001b[?25l"] +[3.083241,"i","u"] +[3.084437,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugify function. Normalize Unicode accents, treat u\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[65G\u001b[?25l"] +[3.091796,"i","n"] +[3.092982,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugify function. Normalize Unicode accents, treat un\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[3.093033,"o","\u001b[66G\u001b[?25l"] +[3.101376,"i","d"] +[3.102611,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugify function. Normalize Unicode accents, treat und\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[3.10276,"o","\u001b[67G\u001b[?25l"] +[3.109942,"i","e"] +[3.110984,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugify function. Normalize Unicode accents, treat unde\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[68G\u001b[?25l"] +[3.119242,"i","r"] +[3.12017,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugify function. Normalize Unicode accents, treat under\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[69G\u001b[?25l"] +[3.128357,"i","s"] +[3.129589,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugify function. Normalize Unicode accents, treat unders\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[3.129602,"o","\u001b[70G\u001b[?25l"] +[3.136954,"i","c"] +[3.137984,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugify function. Normalize Unicode accents, treat undersc\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[71G"] +[3.138002,"o","\u001b[?25l"] +[3.146207,"i","o"] +[3.147241,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugify function. Normalize Unicode accents, treat undersco\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[72G\u001b[?25l"] +[3.155491,"i","r"] +[3.156468,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugify function. Normalize Unicode accents, treat underscor\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[73G\u001b[?25l"] +[3.163762,"i","e"] +[3.164724,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugify function. Normalize Unicode accents, treat underscore\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[3.164758,"o","\u001b[74G\u001b[?25l"] +[3.172997,"i","s"] +[3.173979,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugify function. Normalize Unicode accents, treat underscores\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[3.174008,"o","\u001b[75G\u001b[?25l"] +[3.18236,"i"," "] +[3.183408,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugify function. Normalize Unicode accents, treat underscores \u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[76G\u001b[?25l"] +[3.190772,"i","a"] +[3.191828,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugify function. Normalize Unicode accents, treat underscores a\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[77G\u001b[?25l"] +[3.200393,"i","s"] +[3.201618,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugify function. Normalize Unicode accents, treat underscores as\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[78G\u001b[?25l"] +[3.208939,"i"," "] +[3.210039,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugify function. Normalize Unicode accents, treat underscores as \u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[79G\u001b[?25l"] +[3.218363,"i","p"] +[3.219619,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugify function. Normalize Unicode accents, treat underscores as p\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[80G"] +[3.219747,"o","\u001b[?25l"] +[3.22694,"i","u"] +[3.228045,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugify function. Normalize Unicode accents, treat underscores as pu\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[81G\u001b[?25l"] +[3.236364,"i","n"] +[3.237433,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugify function. Normalize Unicode accents, treat underscores as pun\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[82G"] +[3.237478,"o","\u001b[?25l"] +[3.244826,"i","c"] +[3.245944,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugify function. Normalize Unicode accents, treat underscores as punc\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[83G"] +[3.246005,"o","\u001b[?25l"] +[3.254297,"i","t"] +[3.255434,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugify function. Normalize Unicode accents, treat underscores as punct\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[84G\u001b[?25l"] +[3.262782,"i","u"] +[3.263938,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugify function. Normalize Unicode accents, treat underscores as punctu\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[85G\u001b[?25l"] +[3.272308,"i","a"] +[3.273332,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugify function. Normalize Unicode accents, treat underscores as punctua\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[86G\u001b[?25l"] +[3.281725,"i","t"] +[3.282861,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugify function. Normalize Unicode accents, treat underscores as punctuat\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[87G\u001b[?25l"] +[3.291296,"i","i"] +[3.292446,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugify function. Normalize Unicode accents, treat underscores as punctuati\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[88G"] +[3.292493,"o","\u001b[?25l"] +[3.299758,"i","o"] +[3.300653,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugify function. Normalize Unicode accents, treat underscores as punctuatio\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[89G"] +[3.300686,"o","\u001b[?25l"] +[3.308972,"i","n"] +[3.309791,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugify function. Normalize Unicode accents, treat underscores as punctuation\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[90G\u001b[?25l"] +[3.318015,"i",","] +[3.318972,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugify function. Normalize Unicode accents, treat underscores as punctuation,\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[91G\u001b[?25l"] +[3.327208,"i"," "] +[3.328332,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugify function. Normalize Unicode accents, treat underscores as punctuation, \u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[3.328372,"o","\u001b[92G\u001b[?25l"] +[3.336618,"i","c"] +[3.337525,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugify function. Normalize Unicode accents, treat underscores as punctuation, c\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[3.337565,"o","\u001b[93G\u001b[?25l"] +[3.345816,"i","o"] +[3.346527,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugify function. Normalize Unicode accents, treat underscores as punctuation, co\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[94G"] +[3.346556,"o","\u001b[?25l"] +[3.354818,"i","l"] +[3.355888,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugify function. Normalize Unicode accents, treat underscores as punctuation, col\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[95G"] +[3.355939,"o","\u001b[?25l"] +[3.364438,"i","l"] +[3.365564,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugify function. Normalize Unicode accents, treat underscores as punctuation, coll\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[96G\u001b[?25l"] +[3.372881,"i","a"] +[3.373722,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugify function. Normalize Unicode accents, treat underscores as punctuation, colla\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[97G"] +[3.373772,"o","\u001b[?25l"] +[3.382088,"i","p"] +[3.382905,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugify function. Normalize Unicode accents, treat underscores as punctuation, collap\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[3.382948,"o","\u001b[98G\u001b[?25l"] +[3.391209,"i","s"] +[3.392237,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugify function. Normalize Unicode accents, treat underscores as punctuation, collaps\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[99G\u001b[?25l"] +[3.400447,"i","e"] +[3.401258,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugify function. Normalize Unicode accents, treat underscores as punctuation, collapse\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[3.401317,"o","\u001b[100G\u001b[?25l"] +[3.409606,"i"," "] +[3.410786,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugify function. Normalize Unicode accents, treat underscores as punctuation, collapse \u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[101G"] +[3.410821,"o","\u001b[?25l"] +[3.419081,"i","p"] +[3.42004,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugify function. Normalize Unicode accents, treat underscores as punctuation, collapse p\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[3.420079,"o","\u001b[102G\u001b[?25l"] +[3.428419,"i","u"] +[3.429529,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugify function. Normalize Unicode accents, treat underscores as punctuation, collapse pu\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[3.429578,"o","\u001b[103G\u001b[?25l"] +[3.43688,"i","n"] +[3.437872,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugify function. Normalize Unicode accents, treat underscores as punctuation, collapse pun\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[3.437917,"o","\u001b[104G\u001b[?25l"] +[3.446197,"i","c"] +[3.447145,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugify function. Normalize Unicode accents, treat underscores as punctuation, collapse punc\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[105G"] +[3.447228,"o","\u001b[?25l"] +[3.455565,"i","t"] +[3.456531,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugify function. Normalize Unicode accents, treat underscores as punctuation, collapse punct\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[3.456573,"o","\u001b[106G\u001b[?25l"] +[3.463862,"i","u"] +[3.464984,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugify function. Normalize Unicode accents, treat underscores as punctuation, collapse punctu\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[107G"] +[3.465029,"o","\u001b[?25l"] +[3.473313,"i","a"] +[3.474108,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugify function. Normalize Unicode accents, treat underscores as punctuation, collapse punctua\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[3.474154,"o","\u001b[108G\u001b[?25l"] +[3.482466,"i","t"] +[3.483549,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugify function. Normalize Unicode accents, treat underscores as punctuation, collapse punctuat\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[109G\u001b[?25l"] +[3.490874,"i","i"] +[3.492,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugify function. Normalize Unicode accents, treat underscores as punctuation, collapse punctuati\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[110G\u001b[?25l"] +[3.500628,"i","o"] +[3.501896,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugify function. Normalize Unicode accents, treat underscores as punctuation, collapse punctuatio\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[111G\u001b[?25l"] +[3.510253,"i","n"] +[3.511325,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugify function. Normalize Unicode accents, treat underscores as punctuation, collapse punctuation\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[112G"] +[3.511372,"o","\u001b[?25l"] +[3.518774,"i"," "] +[3.520002,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugify function. Normalize Unicode accents, treat underscores as punctuation, collapse punctuation \u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[113G\u001b[?25l"] +[3.528366,"i","a"] +[3.529591,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugify function. Normalize Unicode accents, treat underscores as punctuation, collapse punctuation a\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[114G"] +[3.529643,"o","\u001b[?25l"] +[3.53684,"i","n"] +[3.538011,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugify function. Normalize Unicode accents, treat underscores as punctuation, collapse punctuation an\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[3.538029,"o","\u001b[115G\u001b[?25l"] +[3.546241,"i","d"] +[3.547157,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugify function. Normalize Unicode accents, treat underscores as punctuation, collapse punctuation and\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[3.547184,"o","\u001b[116G\u001b[?25l"] +[3.555406,"i"," "] +[3.556098,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugify function. Normalize Unicode accents, treat underscores as punctuation, collapse punctuation and \u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[117G"] +[3.556143,"o","\u001b[?25l"] +[3.564435,"i","s"] +[3.565357,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugify function. Normalize Unicode accents, treat underscores as punctuation, collapse punctuation and s\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[118G\u001b[?25l"] +[3.57362,"i","p"] +[3.574571,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugify function. Normalize Unicode accents, treat underscores as punctuation, collapse punctuation and sp\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[3.57461,"o","\u001b[119G\u001b[?25l"] +[3.582817,"i","a"] +[3.583735,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugify function. Normalize Unicode accents, treat underscores as punctuation, collapse punctuation and spa\u001b[7m \u001b[0m\u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[3.58376,"o","\u001b[120G\u001b[?25l"] +[3.591944,"i","c"] +[3.59383,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39mFinish the slugify function. Normalize Unicode accents, treat underscores as punctuation, collapse punctuation and \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K spac\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[3.593876,"o","\u001b[6G\u001b[?25l"] +[3.601172,"i","e"] +[3.602233,"o","\u001b[?2026h\r\u001b[2K space\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[3.602273,"o","\u001b[7G\u001b[?25l"] +[3.610547,"i","s"] +[3.611711,"o","\u001b[?2026h\r\u001b[2K spaces\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[8G"] +[3.61175,"o","\u001b[?25l"] +[3.619004,"i"," "] +[3.620044,"o","\u001b[?2026h\r\u001b[2K spaces \u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[9G"] +[3.620086,"o","\u001b[?25l"] +[3.628375,"i","t"] +[3.629355,"o","\u001b[?2026h\r\u001b[2K spaces t\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[10G"] +[3.629395,"o","\u001b[?25l"] +[3.636909,"i","o"] +[3.637942,"o","\u001b[?2026h\r\u001b[2K spaces to\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[11G\u001b[?25l"] +[3.646276,"i"," "] +[3.647219,"o","\u001b[?2026h\r\u001b[2K spaces to \u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[3.647261,"o","\u001b[12G\u001b[?25l"] +[3.655556,"i","o"] +[3.656456,"o","\u001b[?2026h\r\u001b[2K spaces to o\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[3.656495,"o","\u001b[13G\u001b[?25l"] +[3.663795,"i","n"] +[3.664772,"o","\u001b[?2026h\r\u001b[2K spaces to on\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[14G\u001b[?25l"] +[3.673079,"i","e"] +[3.674191,"o","\u001b[?2026h\r\u001b[2K spaces to one\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[15G\u001b[?25l"] +[3.682549,"i"," "] +[3.683582,"o","\u001b[?2026h\r\u001b[2K spaces to one \u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[16G\u001b[?25l"] +[3.690908,"i","d"] +[3.691892,"o","\u001b[?2026h\r\u001b[2K spaces to one d\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[17G"] +[3.691951,"o","\u001b[?25l"] +[3.700314,"i","a"] +[3.701268,"o","\u001b[?2026h\r\u001b[2K spaces to one da\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[18G\u001b[?25l"] +[3.709587,"i","s"] +[3.710516,"o","\u001b[?2026h\r\u001b[2K spaces to one das\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[19G\u001b[?25l"] +[3.718894,"i","h"] +[3.720036,"o","\u001b[?2026h\r\u001b[2K spaces to one dash\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[20G\u001b[?25l"] +[3.728451,"i",","] +[3.729435,"o","\u001b[?2026h\r\u001b[2K spaces to one dash,\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[21G\u001b[?25l"] +[3.736814,"i"," "] +[3.738262,"o","\u001b[?2026h\r\u001b[2K spaces to one dash, \u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[22G\u001b[?25l"] +[3.746633,"i","t"] +[3.748,"o","\u001b[?2026h\r\u001b[2K spaces to one dash, t\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[3.748066,"o","\u001b[23G\u001b[?25l"] +[3.756413,"i","r"] +[3.757374,"o","\u001b[?2026h\r\u001b[2K spaces to one dash, tr\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[3.757444,"o","\u001b[24G\u001b[?25l"] +[3.764996,"i","i"] +[3.76611,"o","\u001b[?2026h\r\u001b[2K spaces to one dash, tri\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[25G\u001b[?25l"] +[3.774453,"i","m"] +[3.775363,"o","\u001b[?2026h\r\u001b[2K spaces to one dash, trim\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[3.775412,"o","\u001b[26G\u001b[?25l"] +[3.783687,"i"," "] +[3.784681,"o","\u001b[?2026h\r\u001b[2K spaces to one dash, trim \u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[3.784729,"o","\u001b[27G\u001b[?25l"] +[3.793044,"i","d"] +[3.794118,"o","\u001b[?2026h\r\u001b[2K spaces to one dash, trim d\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[28G"] +[3.794174,"o","\u001b[?25l"] +[3.802674,"i","a"] +[3.803716,"o","\u001b[?2026h\r\u001b[2K spaces to one dash, trim da\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[29G\u001b[?25l"] +[3.812112,"i","s"] +[3.813118,"o","\u001b[?2026h\r\u001b[2K spaces to one dash, trim das\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[30G\u001b[?25l"] +[3.821459,"i","h"] +[3.822313,"o","\u001b[?2026h\r\u001b[2K spaces to one dash, trim dash\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[3.822353,"o","\u001b[31G\u001b[?25l"] +[3.830721,"i","e"] +[3.831708,"o","\u001b[?2026h\r\u001b[2K spaces to one dash, trim dashe\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[32G\u001b[?25l"] +[3.840094,"i","s"] +[3.840995,"o","\u001b[?2026h\r\u001b[2K spaces to one dash, trim dashes\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[33G"] +[3.841055,"o","\u001b[?25l"] +[3.849353,"i",","] +[3.850565,"o","\u001b[?2026h\r\u001b[2K spaces to one dash, trim dashes,\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[34G"] +[3.850622,"o","\u001b[?25l"] +[3.857865,"i"," "] +[3.859324,"o","\u001b[?2026h\r\u001b[2K spaces to one dash, trim dashes, \u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[35G\u001b[?25l"] +[3.867621,"i","a"] +[3.86908,"o","\u001b[?2026h\r\u001b[2K spaces to one dash, trim dashes, a\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[36G\u001b[?25l"] +[3.877446,"i","n"] +[3.878502,"o","\u001b[?2026h\r\u001b[2K spaces to one dash, trim dashes, an\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[37G\u001b[?25l"] +[3.885802,"i","d"] +[3.886713,"o","\u001b[?2026h\r\u001b[2K spaces to one dash, trim dashes, and\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[38G\u001b[?25l"] +[3.895095,"i"," "] +[3.896331,"o","\u001b[?2026h\r\u001b[2K spaces to one dash, trim dashes, and \u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[39G\u001b[?25l"] +[3.904665,"i","l"] +[3.905715,"o","\u001b[?2026h\r\u001b[2K spaces to one dash, trim dashes, and l\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[40G\u001b[?25l"] +[3.914086,"i","o"] +[3.915138,"o","\u001b[?2026h\r\u001b[2K spaces to one dash, trim dashes, and lo\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[3.915201,"o","\u001b[41G\u001b[?25l"] +[3.923536,"i","w"] +[3.924823,"o","\u001b[?2026h\r\u001b[2K spaces to one dash, trim dashes, and low\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[42G\u001b[?25l"] +[3.932164,"i","e"] +[3.933096,"o","\u001b[?2026h\r\u001b[2K spaces to one dash, trim dashes, and lowe\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[43G\u001b[?25l"] +[3.941399,"i","r"] +[3.942299,"o","\u001b[?2026h\r\u001b[2K spaces to one dash, trim dashes, and lower\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[44G\u001b[?25l"] +[3.950603,"i","c"] +[3.951544,"o","\u001b[?2026h\r\u001b[2K spaces to one dash, trim dashes, and lowerc\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[45G\u001b[?25l"] +[3.959947,"i","a"] +[3.96078,"o","\u001b[?2026h\r\u001b[2K spaces to one dash, trim dashes, and lowerca\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[46G\u001b[?25l"] +[3.96906,"i","s"] +[3.969697,"o","\u001b[?2026h\r\u001b[2K spaces to one dash, trim dashes, and lowercas\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[47G\u001b[?25l"] +[3.977939,"i","e"] +[3.978743,"o","\u001b[?2026h\r\u001b[2K spaces to one dash, trim dashes, and lowercase\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[48G\u001b[?25l"] +[3.987014,"i","."] +[3.987718,"o","\u001b[?2026h\r\u001b[2K spaces to one dash, trim dashes, and lowercase.\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[49G\u001b[?25l"] +[3.995975,"i"," "] +[3.997045,"o","\u001b[?2026h\r\u001b[2K spaces to one dash, trim dashes, and lowercase. \u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[50G\u001b[?25l"] +[4.005566,"i","A"] +[4.00648,"o","\u001b[?2026h\r\u001b[2K spaces to one dash, trim dashes, and lowercase. A\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[51G\u001b[?25l"] +[4.01466,"i","d"] +[4.015546,"o","\u001b[?2026h\r\u001b[2K spaces to one dash, trim dashes, and lowercase. Ad\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[4.015583,"o","\u001b[52G\u001b[?25l"] +[4.023829,"i","d"] +[4.024916,"o","\u001b[?2026h\r\u001b[2K spaces to one dash, trim dashes, and lowercase. Add\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[4.024951,"o","\u001b[53G\u001b[?25l"] +[4.033187,"i"," "] +[4.034376,"o","\u001b[?2026h\r\u001b[2K spaces to one dash, trim dashes, and lowercase. Add \u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[4.034395,"o","\u001b[54G\u001b[?25l"] +[4.042599,"i","e"] +[4.043478,"o","\u001b[?2026h\r\u001b[2K spaces to one dash, trim dashes, and lowercase. Add e\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[55G\u001b[?25l"] +[4.050798,"i","d"] +[4.05167,"o","\u001b[?2026h\r\u001b[2K spaces to one dash, trim dashes, and lowercase. Add ed\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[56G"] +[4.051709,"o","\u001b[?25l"] +[4.059987,"i","g"] +[4.060805,"o","\u001b[?2026h\r\u001b[2K spaces to one dash, trim dashes, and lowercase. Add edg\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[57G\u001b[?25l"] +[4.069064,"i","e"] +[4.069958,"o","\u001b[?2026h\r\u001b[2K spaces to one dash, trim dashes, and lowercase. Add edge\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[4.069984,"o","\u001b[58G\u001b[?25l"] +[4.078247,"i","-"] +[4.079452,"o","\u001b[?2026h\r\u001b[2K spaces to one dash, trim dashes, and lowercase. Add edge-\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[59G\u001b[?25l"] +[4.087663,"i","c"] +[4.088886,"o","\u001b[?2026h\r\u001b[2K spaces to one dash, trim dashes, and lowercase. Add edge-c\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[60G\u001b[?25l"] +[4.097129,"i","a"] +[4.098237,"o","\u001b[?2026h\r\u001b[2K spaces to one dash, trim dashes, and lowercase. Add edge-ca\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[61G\u001b[?25l"] +[4.106438,"i","s"] +[4.107055,"o","\u001b[?2026h\r\u001b[2K spaces to one dash, trim dashes, and lowercase. Add edge-cas\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[62G\u001b[?25l"] +[4.115272,"i","e"] +[4.116006,"o","\u001b[?2026h\r\u001b[2K spaces to one dash, trim dashes, and lowercase. Add edge-case\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[63G\u001b[?25l"] +[4.124238,"i"," "] +[4.124718,"o","\u001b[?2026h\r\u001b[2K spaces to one dash, trim dashes, and lowercase. Add edge-case \u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[64G\u001b[?25l"] +[4.132906,"i","t"] +[4.133403,"o","\u001b[?2026h\r\u001b[2K spaces to one dash, trim dashes, and lowercase. Add edge-case t\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[65G"] +[4.133431,"o","\u001b[?25l"] +[4.14266,"i","e"] +[4.143463,"o","\u001b[?2026h\r\u001b[2K spaces to one dash, trim dashes, and lowercase. Add edge-case te\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[66G\u001b[?25l"] +[4.152686,"i","s"] +[4.153754,"o","\u001b[?2026h\r\u001b[2K spaces to one dash, trim dashes, and lowercase. Add edge-case tes\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[67G\u001b[?25l"] +[4.16207,"i","t"] +[4.16327,"o","\u001b[?2026h\r\u001b[2K spaces to one dash, trim dashes, and lowercase. Add edge-case test\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[68G\u001b[?25l"] +[4.171543,"i","s"] +[4.172302,"o","\u001b[?2026h\r\u001b[2K spaces to one dash, trim dashes, and lowercase. Add edge-case tests\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[4.172339,"o","\u001b[69G\u001b[?25l"] +[4.179784,"i",","] +[4.180373,"o","\u001b[?2026h\r\u001b[2K spaces to one dash, trim dashes, and lowercase. Add edge-case tests,\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[70G\u001b[?25l"] +[4.189613,"i"," "] +[4.190613,"o","\u001b[?2026h\r\u001b[2K spaces to one dash, trim dashes, and lowercase. Add edge-case tests, \u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[4.190781,"o","\u001b[71G\u001b[?25l"] +[4.198993,"i","r"] +[4.200124,"o","\u001b[?2026h\r\u001b[2K spaces to one dash, trim dashes, and lowercase. Add edge-case tests, r\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[72G\u001b[?25l"] +[4.208451,"i","u"] +[4.20937,"o","\u001b[?2026h\r\u001b[2K spaces to one dash, trim dashes, and lowercase. Add edge-case tests, ru\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[73G\u001b[?25l"] +[4.217656,"i","n"] +[4.218697,"o","\u001b[?2026h\r\u001b[2K spaces to one dash, trim dashes, and lowercase. Add edge-case tests, run\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[74G\u001b[?25l"] +[4.226939,"i"," "] +[4.227991,"o","\u001b[?2026h\r\u001b[2K spaces to one dash, trim dashes, and lowercase. Add edge-case tests, run \u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[4.228022,"o","\u001b[75G\u001b[?25l"] +[4.236295,"i","t"] +[4.237351,"o","\u001b[?2026h\r\u001b[2K spaces to one dash, trim dashes, and lowercase. Add edge-case tests, run t\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[76G\u001b[?25l"] +[4.245678,"i","h"] +[4.246613,"o","\u001b[?2026h\r\u001b[2K spaces to one dash, trim dashes, and lowercase. Add edge-case tests, run th\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[77G\u001b[?25l"] +[4.254912,"i","e"] +[4.255832,"o","\u001b[?2026h\r\u001b[2K spaces to one dash, trim dashes, and lowercase. Add edge-case tests, run the\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[78G\u001b[?25l"] +[4.264203,"i","m"] +[4.265116,"o","\u001b[?2026h\r\u001b[2K spaces to one dash, trim dashes, and lowercase. Add edge-case tests, run them\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[4.265156,"o","\u001b[79G\u001b[?25l"] +[4.273412,"i",","] +[4.274343,"o","\u001b[?2026h\r\u001b[2K spaces to one dash, trim dashes, and lowercase. Add edge-case tests, run them,\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[80G\u001b[?25l"] +[4.28263,"i"," "] +[4.283544,"o","\u001b[?2026h\r\u001b[2K spaces to one dash, trim dashes, and lowercase. Add edge-case tests, run them, \u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[4.283574,"o","\u001b[81G\u001b[?25l"] +[4.291804,"i","a"] +[4.292845,"o","\u001b[?2026h\r\u001b[2K spaces to one dash, trim dashes, and lowercase. Add edge-case tests, run them, a\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[82G"] +[4.29287,"o","\u001b[?25l"] +[4.301076,"i","n"] +[4.301947,"o","\u001b[?2026h\r\u001b[2K spaces to one dash, trim dashes, and lowercase. Add edge-case tests, run them, an\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[83G\u001b[?25l"] +[4.310138,"i","d"] +[4.311242,"o","\u001b[?2026h\r\u001b[2K spaces to one dash, trim dashes, and lowercase. Add edge-case tests, run them, and\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[84G\u001b[?25l"] +[4.319456,"i"," "] +[4.320341,"o","\u001b[?2026h\r\u001b[2K spaces to one dash, trim dashes, and lowercase. Add edge-case tests, run them, and \u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[85G\u001b[?25l"] +[4.328569,"i","s"] +[4.329411,"o","\u001b[?2026h\r\u001b[2K spaces to one dash, trim dashes, and lowercase. Add edge-case tests, run them, and s\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[86G\u001b[?25l"] +[4.337606,"i","u"] +[4.338515,"o","\u001b[?2026h\r\u001b[2K spaces to one dash, trim dashes, and lowercase. Add edge-case tests, run them, and su\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[87G"] +[4.338547,"o","\u001b[?25l"] +[4.345821,"i","m"] +[4.34678,"o","\u001b[?2026h\r\u001b[2K spaces to one dash, trim dashes, and lowercase. Add edge-case tests, run them, and sum\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[88G\u001b[?25l"] +[4.355126,"i","m"] +[4.356125,"o","\u001b[?2026h\r\u001b[2K spaces to one dash, trim dashes, and lowercase. Add edge-case tests, run them, and summ\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[89G\u001b[?25l"] +[4.363784,"i","a"] +[4.364596,"o","\u001b[?2026h\r\u001b[2K spaces to one dash, trim dashes, and lowercase. Add edge-case tests, run them, and summa\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[90G\u001b[?25l"] +[4.372861,"i","r"] +[4.373652,"o","\u001b[?2026h\r\u001b[2K spaces to one dash, trim dashes, and lowercase. Add edge-case tests, run them, and summar\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[4.373705,"o","\u001b[91G\u001b[?25l"] +[4.382044,"i","i"] +[4.383218,"o","\u001b[?2026h\r\u001b[2K spaces to one dash, trim dashes, and lowercase. Add edge-case tests, run them, and summari\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[92G\u001b[?25l"] +[4.391527,"i","z"] +[4.392513,"o","\u001b[?2026h\r\u001b[2K spaces to one dash, trim dashes, and lowercase. Add edge-case tests, run them, and summariz\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[93G\u001b[?25l"] +[4.399867,"i","e"] +[4.401041,"o","\u001b[?2026h\r\u001b[2K spaces to one dash, trim dashes, and lowercase. Add edge-case tests, run them, and summarize\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[94G"] +[4.401087,"o","\u001b[?25l"] +[4.409393,"i"," "] +[4.41057,"o","\u001b[?2026h\r\u001b[2K spaces to one dash, trim dashes, and lowercase. Add edge-case tests, run them, and summarize \u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[4.410617,"o","\u001b[95G\u001b[?25l"] +[4.417955,"i","t"] +[4.419102,"o","\u001b[?2026h\r\u001b[2K spaces to one dash, trim dashes, and lowercase. Add edge-case tests, run them, and summarize t\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[96G\u001b[?25l"] +[4.42748,"i","h"] +[4.428632,"o","\u001b[?2026h\r\u001b[2K spaces to one dash, trim dashes, and lowercase. Add edge-case tests, run them, and summarize th\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[97G\u001b[?25l"] +[4.436054,"i","e"] +[4.437167,"o","\u001b[?2026h\r\u001b[2K spaces to one dash, trim dashes, and lowercase. Add edge-case tests, run them, and summarize the\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[98G\u001b[?25l"] +[4.445524,"i"," "] +[4.446676,"o","\u001b[?2026h\r\u001b[2K spaces to one dash, trim dashes, and lowercase. Add edge-case tests, run them, and summarize the \u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[4.446718,"o","\u001b[99G\u001b[?25l"] +[4.454002,"i","p"] +[4.45513,"o","\u001b[?2026h\r\u001b[2K spaces to one dash, trim dashes, and lowercase. Add edge-case tests, run them, and summarize the p\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[4.455185,"o","\u001b[100G\u001b[?25l"] +[4.463478,"i","r"] +[4.464596,"o","\u001b[?2026h\r\u001b[2K spaces to one dash, trim dashes, and lowercase. Add edge-case tests, run them, and summarize the pr\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[101G\u001b[?25l"] +[4.471879,"i","o"] +[4.474891,"o","\u001b[?2026h\r\u001b[2K spaces to one dash, trim dashes, and lowercase. Add edge-case tests, run them, and summarize the pro\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[102G\u001b[?25l"] +[4.48132,"i","o"] +[4.482466,"o","\u001b[?2026h\r\u001b[2K spaces to one dash, trim dashes, and lowercase. Add edge-case tests, run them, and summarize the proo\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[103G\u001b[?25l"] +[4.489816,"i","f"] +[4.490941,"o","\u001b[?2026h\r\u001b[2K spaces to one dash, trim dashes, and lowercase. Add edge-case tests, run them, and summarize the proof\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[104G\u001b[?25l"] +[4.499327,"i","."] +[4.500398,"o","\u001b[?2026h\r\u001b[2K spaces to one dash, trim dashes, and lowercase. Add edge-case tests, run them, and summarize the proof.\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[105G\u001b[?25l"] +[4.507751,"i","\r"] +[4.509419,"o","\u001b[?2026h\u001b[1A\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[1A\u001b[2G\u001b[?25l"] +[4.618367,"o","\u001b[?2026h\u001b[26A\r\u001b[2K \u001b[38;2;119;166;255m› \u001b[39mFinish the slugify function. Normalize Unicode accents, treat underscores as punctuation, collapse punctuation and \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K spaces to one dash, trim dashes, and lowercase. Add edge-case tests, run them, and summarize the proof. \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;255;194;92mworking…\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;2;255;194;92m› queue #1\u001b[39m \u001b[7m \u001b[0m \u001b[0m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;2;225;230;2"] +[4.618454,"o","40mprofile Product engineer\u001b[39m · \u001b[38;2;225;230;240mclaude-code / opus\u001b[39m · \u001b[38;2;225;230;240mLocal CLI Bridge\u001b[39m · \u001b[38;2;225;230;240mhigh\u001b[39m \u001b[38;2;255;194;92mworking · Ctrl+C cancel\u001b[39m · \u001b[38;2;225;230;240mEnter queues\u001b[39m\u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[3A\u001b[12G\u001b[?25l"] +[4.652793,"o","\u001b[12G\u001b[?25l"] +[4.658198,"i","\u001bOQ"] +[4.668821,"o","\u001b[?2026h\u001b[26A\r\u001b[2K \u001b[38;2;119;166;255m› \u001b[39mFinish the slugify function. Normalize Unicode accents, treat underscores as \u001b[38;2;135;145;165m│\u001b[39m \u001b[1m\u001b[38;2;151;190;255mlive work\u001b[39m\u001b[22m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K punctuation, collapse punctuation and spaces to one dash, trim dashes, and lowercase. Add \u001b[38;2;135;145;165m│\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K edge-case tests, run them, and summarize the proof. \u001b[38;2;135;145;165m│\u001b[39m \u001b[38;2;255;194;92m> running\u001b[39m run run-b413c\u001b[0m...\u001b[0m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m│\u001b[39m \u001b[38;2;255;194;92m> running\u001b[39m admitted · pr\u001b[0m...\u001b[0m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;255;194;92mworking…\u001b[39m \u001b[38;2;135;145;165m│\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m│\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m│\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m│\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m│\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m│\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m│\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m│\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m│\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m│\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m│\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m│\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m│\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m│\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m│\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m│\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m│\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m│\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m│\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m│\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K "] +[4.668915,"o"," \u001b[38;2;135;145;165m│\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m│\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[1B\u001b[12G\u001b[?25l"] +[5.60973,"i","\u001bOQ"] +[5.612981,"o","\u001b[?2026h\u001b[26A\r\u001b[2K \u001b[38;2;119;166;255m› \u001b[39mFinish the slugify function. Normalize Unicode accents, treat underscores as punctuation, collapse punctuation and \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K spaces to one dash, trim dashes, and lowercase. Add edge-case tests, run them, and summarize the proof. \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;255;194;92mworking…\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[5.613046,"o","\u001b[1B\u001b[12G\u001b[?25l"] +[7.889089,"o","\u001b[?2026h\u001b[23A\r\u001b[2K I'll start by inspecting the repository. \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;255;194;92mworking…\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[22B\u001b[12G\u001b[?25l"] +[9.31446,"o","\u001b[?2026h\u001b[22A\r\u001b[2K \u001b[38;2;135;145;165m· Bash\u001b[39m\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;255;194;92mworking…\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[21B\u001b[12G\u001b[?25l"] +[9.68202,"o","\u001b[?2026h\u001b[22A\r\u001b[2K \u001b[38;2;135;145;165m· Bash\u001b[39m 1/2\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 2/2\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;255;194;92mworking…\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[20B\u001b[12G\u001b[?25l"] +[11.465654,"o","\u001b[?2026h\u001b[22A\r\u001b[2K \u001b[38;2;135;145;165m· Bash\u001b[39m 1/3\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 2/3\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 3/3\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;255;194;92mworking…\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[19B\u001b[12G\u001b[?25l"] +[12.147084,"o","\u001b[?2026h\u001b[22A\r\u001b[2K \u001b[38;2;135;145;165m· Bash\u001b[39m 1/4\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 2/4\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 3/4\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 4/4\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;255;194;92mworking…\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[18B\u001b[12G\u001b[?25l"] +[12.305996,"o","\u001b[?2026h\u001b[22A\r\u001b[2K \u001b[38;2;135;145;165m· Bash\u001b[39m 1/5\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 2/5\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 3/5\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 4/5\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 5/5\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;255;194;92mworking…\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[17B\u001b[12G\u001b[?25l"] +[18.345077,"o","\u001b[?2026h\u001b[22A\r\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Now implementing: \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Bash\u001b[39m 1/5\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 2/5\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 3/5\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 4/5\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 5/5\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;255;194;92mworking…\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[15B\u001b[12G\u001b[?25l"] +[19.594184,"o","\u001b[?2026h\u001b[20A\r\u001b[2K \u001b[38;2;135;145;165m· Bash\u001b[39m 1/6\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 2/6\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 3/6\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 4/6\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 5/6\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Write\u001b[39m 6/6\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;255;194;92mworking…\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[14B\u001b[12G\u001b[?25l"] +[22.515022,"o","\u001b[?2026h\u001b[20A\r\u001b[2K \u001b[38;2;135;145;165m· Bash\u001b[39m 1/7\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 2/7\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 3/7\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 4/7\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 5/7\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Write\u001b[39m 6/7\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Edit\u001b[39m 7/7\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;255;194;92mworking…\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[13B\u001b[12G\u001b[?25l"] +[25.221367,"o","\u001b[?2026h\u001b[20A\r\u001b[2K \u001b[38;2;135;145;165m· Bash\u001b[39m 1/8\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 2/8\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 3/8\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 4/8\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 5/8\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Write\u001b[39m 6/8\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Edit\u001b[39m 7/8\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Write\u001b[39m 8/8\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;255;194;92mworking…\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[12B\u001b[12G\u001b[?25l"] +[33.722873,"o","\u001b[?2026h\u001b[20A\r\u001b[2K \u001b[38;2;135;145;165m· Bash\u001b[39m 1/9\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 2/9\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 3/9\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 4/9\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 5/9\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Write\u001b[39m 6/9\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Edit\u001b[39m 7/9\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Write\u001b[39m 8/9\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Write\u001b[39m 9/9\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;255;194;92mworking…\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[11B\u001b[12G\u001b[?25l"] +[35.545267,"o","\u001b[?2026h\u001b[20A\r\u001b[2K \u001b[38;2;135;145;165m· Bash\u001b[39m 1/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 2/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 3/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 4/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 5/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Write\u001b[39m 6/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Edit\u001b[39m 7/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Write\u001b[39m 8/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Write\u001b[39m 9/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Bash\u001b[39m 10/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;255;194;92mworking…\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[10B\u001b[12G\u001b[?25l"] +[46.608454,"o","\u001b[?2026h\u001b[26A\r\u001b[2K ├─────────────────────────────┼──────────────────────────────────────┼───────────────────────────────────────────────┤ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K │ Trim dashes │ \u001b[38;2;255;205;117m--Hello, World!--\u001b[39m → \u001b[38;2;255;205;117mhello-world\u001b[39m │ both ends │ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K ├─────────────────────────────┼──────────────────────────────────────┼───────────────────────────────────────────────┤ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K │ Lowercase + digits survive │ \u001b[38;2;255;205;117mTop 10 Songs (2024)\u001b[39m → │ digits are not punctuation │ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K │ │ \u001b[38;2;255;205;117mtop-10-songs-2024\u001b[39m │ │ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K ├─────────────────────────────┼──────────────────────────────────────┼───────────────────────────────────────────────┤ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K │ Degenerate input │ \u001b[38;2;255;205;117m!!! ___ ---\u001b[39m → \u001b[38;2;255;205;117m''\u001b[39m, \u001b[38;2;255;205;117m''\u001b[39m → \u001b[38;2;255;205;117m''\u001b[39m │ trim can empty the string without throwing │ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K ├─────────────────────────────┼──────────────────────────────────────┼───────────────────────────────────────────────┤ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K │ Idempotence │ \u001b[38;2;255;205;117mslugify(slugify(x)) === slugify(x)\u001b[39m │ \u001b[38;2;255;205;117m Ünïcödé__Slug!! \u001b[39m → \u001b[38;2;255;205;117municode-slug\u001b[39m, stable on │ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K │ │ │ re-run │ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K └─────────────────────────────┴──────────────────────────────────────┴───────────────────────────────────────────────┘ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K One behavior worth flagging: characters with no ASCII decomposition (CJK, Cyrillic, emoji) are treated as punctuation \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K and become dashes — \u001b[38;2;255;205;117mslugify('日本語')\u001b[39m is \u001b[38;2;255;205;117m''\u001b[39m. That's the standard ASCII-slug tradeoff and matches the \"collapse \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K punctuation\" rule as written, but if you need transliteration instead, that's a different (larger) change. \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Bash\u001b[39m 1/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 2/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 3/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m "] +[46.608537,"o"," \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 4/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 5/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Write\u001b[39m 6/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Edit\u001b[39m 7/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Write\u001b[39m 8/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Write\u001b[39m 9/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Bash\u001b[39m 10/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;255;194;92mworking…\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[1B\u001b[12G\u001b[?25l"] +[46.658299,"o","\u001b[?2026h\u001b[26A\r\u001b[2K │ Collapse runs │ \u001b[38;2;255;205;117ma -- _ b\u001b[39m → \u001b[38;2;255;205;117ma-b\u001b[39m │ mixed space/dash/underscore run → one dash │ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K ├─────────────────────────────┼──────────────────────────────────────┼───────────────────────────────────────────────┤ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K │ Trim dashes │ \u001b[38;2;255;205;117m--Hello, World!--\u001b[39m → \u001b[38;2;255;205;117mhello-world\u001b[39m │ both ends │ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K ├─────────────────────────────┼──────────────────────────────────────┼───────────────────────────────────────────────┤ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K │ Lowercase + digits survive │ \u001b[38;2;255;205;117mTop 10 Songs (2024)\u001b[39m → │ digits are not punctuation │ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K │ │ \u001b[38;2;255;205;117mtop-10-songs-2024\u001b[39m │ │ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K ├─────────────────────────────┼──────────────────────────────────────┼───────────────────────────────────────────────┤ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K │ Degenerate input │ \u001b[38;2;255;205;117m!!! ___ ---\u001b[39m → \u001b[38;2;255;205;117m''\u001b[39m, \u001b[38;2;255;205;117m''\u001b[39m → \u001b[38;2;255;205;117m''\u001b[39m │ trim can empty the string without throwing │ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K ├─────────────────────────────┼──────────────────────────────────────┼───────────────────────────────────────────────┤ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K │ Idempotence │ \u001b[38;2;255;205;117mslugify(slugify(x)) === slugify(x)\u001b[39m │ \u001b[38;2;255;205;117m Ünïcödé__Slug!! \u001b[39m → \u001b[38;2;255;205;117municode-slug\u001b[39m, stable on │ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K │ │ │ re-run │ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K └─────────────────────────────┴──────────────────────────────────────┴───────────────────────────────────────────────┘ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K One behavior worth flagging: characters with no ASCII decomposition (CJK, Cyrillic, emoji) are treated as punctuation \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K and become dashes — \u001b[38;2;255;205;117mslugify('日本語')\u001b[39m is \u001b[38;2;255;205;117m''\u001b[39m. That's the standard ASCII-slug tradeoff and matches the \"collapse \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K punctuation\" rule as written, but if you need transliteration instead, that's a different (larger) change. \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Bash\u001b[39m 1/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 2/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m"] +[46.658372,"o"," \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 3/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 4/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 5/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Write\u001b[39m 6/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Edit\u001b[39m 7/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Write\u001b[39m 8/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Write\u001b[39m 9/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Bash\u001b[39m 10/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;2;225;230;240mprofile Product engineer\u001b[39m · \u001b[38;2;225;230;240mclaude-code / opus\u001b[39m · \u001b[38;2;225;230;240mLocal CLI Bridge\u001b[39m · \u001b[38;2;225;230;240mhigh\u001b[39m \u001b[38;2;225;230;240min 15\u001b[39m · \u001b[38;2;225;230;240mout 2.9k\u001b[39m\u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[3A\u001b[2G\u001b[?25l"] +[47.907787,"i","/"] +[47.911157,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m/\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[47.911215,"o","\u001b[3G\u001b[?25l"] +[47.928297,"o","\u001b[?2026h\u001b[26A\r\u001b[2K │ Degenerate input │ \u001b[38;2;255;205;117m!!! ___ ---\u001b[39m → \u001b[38;2;255;205;117m''\u001b[39m, \u001b[38;2;255;205;117m''\u001b[39m → \u001b[38;2;255;205;117m''\u001b[39m │ trim can empty the string without throwing │ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K ├─────────────────────────────┼──────────────────────────────────────┼───────────────────────────────────────────────┤ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K │ Idempotence │ \u001b[38;2;255;205;117mslugify(slugify(x)) === slugify(x)\u001b[39m │ \u001b[38;2;255;205;117m Ünïcödé__Slug!! \u001b[39m → \u001b[38;2;255;205;117municode-slug\u001b[39m, stable on │ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K │ │ │ re-run │ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K └─────────────────────────────┴──────────────────────────────────────┴───────────────────────────────────────────────┘ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K One behavior worth flagging: characters with no ASCII decomposition (CJK, Cyrillic, emoji) are treated as punctuation \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K and become dashes — \u001b[38;2;255;205;117mslugify('日本語')\u001b[39m is \u001b[38;2;255;205;117m''\u001b[39m. That's the standard ASCII-slug tradeoff and matches the \"collapse \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K punctuation\" rule as written, but if you need transliteration instead, that's a different (larger) change. \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Bash\u001b[39m 1/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 2/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 3/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 4/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 5/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Write\u001b[39m 6/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Edit\u001b[39m 7/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Write\u001b[39m 8/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Write\u001b[39m 9/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Bash\u001b[39m 10/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m/\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K "] +[47.928388,"o"," \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;2;135;145;165m──────────────────────────────────────────────\u001b[39m\u001b[38;2;119;166;255m alt+enter newline · paste \u001b[39m\u001b[38;2;135;145;165m───────────────────────────────────────────────\u001b[39m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[1m\u001b[38;2;119;166;255m→ new Create an empty conversation\u001b[39m\u001b[22m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K open\u001b[38;2;135;145;165m Search and open conversations\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K profile\u001b[38;2;135;145;165m Inspect, select, import, or edit a profile\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K connection\u001b[38;2;135;145;165m Inspect, create, test, select, or remove a connection\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K runner\u001b[38;2;135;145;165m unavailable — Runner overrides require the current profile compatibility helpers\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m (1/29)\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[9A\u001b[3G\u001b[?25l"] +[47.931818,"i","a"] +[47.934692,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m/a\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[4G"] +[47.934736,"o","\u001b[?25l"] +[47.950941,"o","\u001b[?2026h\u001b[4B\r\u001b[2K \u001b[1m\u001b[38;2;119;166;255m→ ask Analyze a frozen run with citations\u001b[39m\u001b[22m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K analyze\u001b[38;2;135;145;165m Run a named trace-analysis recipe\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K approve\u001b[38;2;135;145;165m unavailable — Interaction response is not exposed by the current runtime adapter\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K automate\u001b[38;2;135;145;165m unavailable — Interaction automation requires the shared response contract\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K activity\u001b[38;2;135;145;165m Open run, tool, worker, and usage activity\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m (1/10)\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[9A\u001b[4G\u001b[?25l"] +[47.956292,"i","c"] +[47.958828,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m/ac\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[47.958863,"o","\u001b[5G\u001b[?25l"] +[47.97468,"o","\u001b[?2026h\u001b[19A\r\u001b[2K │ │ \u001b[38;2;255;205;117mtop-10-songs-2024\u001b[39m │ │ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K ├─────────────────────────────┼──────────────────────────────────────┼───────────────────────────────────────────────┤ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K │ Degenerate input │ \u001b[38;2;255;205;117m!!! ___ ---\u001b[39m → \u001b[38;2;255;205;117m''\u001b[39m, \u001b[38;2;255;205;117m''\u001b[39m → \u001b[38;2;255;205;117m''\u001b[39m │ trim can empty the string without throwing │ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K ├─────────────────────────────┼──────────────────────────────────────┼───────────────────────────────────────────────┤ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K │ Idempotence │ \u001b[38;2;255;205;117mslugify(slugify(x)) === slugify(x)\u001b[39m │ \u001b[38;2;255;205;117m Ünïcödé__Slug!! \u001b[39m → \u001b[38;2;255;205;117municode-slug\u001b[39m, stable on │ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K │ │ │ re-run │ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K └─────────────────────────────┴──────────────────────────────────────┴───────────────────────────────────────────────┘ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K One behavior worth flagging: characters with no ASCII decomposition (CJK, Cyrillic, emoji) are treated as punctuation \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K and become dashes — \u001b[38;2;255;205;117mslugify('日本語')\u001b[39m is \u001b[38;2;255;205;117m''\u001b[39m. That's the standard ASCII-slug tradeoff and matches the \"collapse \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K punctuation\" rule as written, but if you need transliteration instead, that's a different (larger) change. \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Bash\u001b[39m 1/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 2/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 3/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 4/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 5/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Write\u001b[39m 6/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Edit\u001b[39m 7/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Write\u001b[39m 8/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Write\u001b[39m 9/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39"] +[47.974752,"o","m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Bash\u001b[39m 10/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m/ac\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;2;135;145;165m──────────────────────────────────────────────\u001b[39m\u001b[38;2;119;166;255m alt+enter newline · paste \u001b[39m\u001b[38;2;135;145;165m───────────────────────────────────────────────\u001b[39m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[1m\u001b[38;2;119;166;255m→ activity Open run, tool, worker, and usage activity\u001b[39m\u001b[22m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K detach\u001b[38;2;135;145;165m unavailable — There is no active run\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K cancel\u001b[38;2;135;145;165m unavailable — There is no active run to cancel\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K branch\u001b[38;2;135;145;165m Create a branch at a message boundary\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[7A\u001b[5G\u001b[?25l"] +[47.980198,"i","t"] +[47.982609,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m/act\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[47.982734,"o","\u001b[6G\u001b[?25l"] +[47.998491,"o","\u001b[?2026h\u001b[21A\r\u001b[2K │ Trim dashes │ \u001b[38;2;255;205;117m--Hello, World!--\u001b[39m → \u001b[38;2;255;205;117mhello-world\u001b[39m │ both ends │ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K ├─────────────────────────────┼──────────────────────────────────────┼───────────────────────────────────────────────┤ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K │ Lowercase + digits survive │ \u001b[38;2;255;205;117mTop 10 Songs (2024)\u001b[39m → │ digits are not punctuation │ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K │ │ \u001b[38;2;255;205;117mtop-10-songs-2024\u001b[39m │ │ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K ├─────────────────────────────┼──────────────────────────────────────┼───────────────────────────────────────────────┤ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K │ Degenerate input │ \u001b[38;2;255;205;117m!!! ___ ---\u001b[39m → \u001b[38;2;255;205;117m''\u001b[39m, \u001b[38;2;255;205;117m''\u001b[39m → \u001b[38;2;255;205;117m''\u001b[39m │ trim can empty the string without throwing │ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K ├─────────────────────────────┼──────────────────────────────────────┼───────────────────────────────────────────────┤ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K │ Idempotence │ \u001b[38;2;255;205;117mslugify(slugify(x)) === slugify(x)\u001b[39m │ \u001b[38;2;255;205;117m Ünïcödé__Slug!! \u001b[39m → \u001b[38;2;255;205;117municode-slug\u001b[39m, stable on │ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K │ │ │ re-run │ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K └─────────────────────────────┴──────────────────────────────────────┴───────────────────────────────────────────────┘ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K One behavior worth flagging: characters with no ASCII decomposition (CJK, Cyrillic, emoji) are treated as punctuation \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K and become dashes — \u001b[38;2;255;205;117mslugify('日本語')\u001b[39m is \u001b[38;2;255;205;117m''\u001b[39m. That's the standard ASCII-slug tradeoff and matches the \"collapse \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K punctuation\" rule as written, but if you need transliteration instead, that's a different (larger) change. \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Bash\u001b[39m 1/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 2/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 3/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 4/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 5/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m "] +[47.998547,"o"," \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Write\u001b[39m 6/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Edit\u001b[39m 7/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Write\u001b[39m 8/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Write\u001b[39m 9/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Bash\u001b[39m 10/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m/act\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;2;135;145;165m──────────────────────────────────────────────\u001b[39m\u001b[38;2;119;166;255m alt+enter newline · paste \u001b[39m\u001b[38;2;135;145;165m───────────────────────────────────────────────\u001b[39m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[1m\u001b[38;2;119;166;255m→ activity Open run, tool, worker, and usage activity\u001b[39m\u001b[22m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[4A\u001b[6G\u001b[?25l"] +[48.004053,"i","i"] +[48.006472,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m/acti\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[7G"] +[48.006495,"o","\u001b[?25l"] +[48.022534,"o","\u001b[7G\u001b[?25l"] +[48.027777,"i","v"] +[48.030199,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m/activ\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[8G"] +[48.030225,"o","\u001b[?25l"] +[48.047117,"o","\u001b[8G\u001b[?25l"] +[48.052601,"i","i"] +[48.055206,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m/activi\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[9G\u001b[?25l"] +[48.071865,"o","\u001b[9G\u001b[?25l"] +[48.077153,"i","t"] +[48.079758,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m/activit\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[10G\u001b[?25l"] +[48.095962,"o","\u001b[10G\u001b[?25l"] +[48.101322,"i","y"] +[48.104127,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m/activity\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[11G"] +[48.10418,"o","\u001b[?25l"] +[48.120329,"o","\u001b[11G\u001b[?25l"] +[48.125624,"i","\r"] +[48.129327,"o","\u001b[?2026h\u001b[24A\r\u001b[2K │ Collapse runs │ \u001b[38;2;255;205;117ma -- _ b\u001b[39m → \u001b[38;2;255;205;117ma-b\u001b[39m │ mixed space/dash/underscore run → one dash │ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K ├─────────────────────────────┼──────────────────────────────────────┼───────────────────────────────────────────────┤ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K │ Trim dashes │ \u001b[38;2;255;205;117m--Hello, World!--\u001b[39m → \u001b[38;2;255;205;117mhello-world\u001b[39m │ both ends │ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K ├─────────────────────────────┼──────────────────────────────────────┼───────────────────────────────────────────────┤ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K │ Lowercase + digits survive │ \u001b[38;2;255;205;117mTop 10 Songs (2024)\u001b[39m → │ digits are not punctuation │ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K │ │ \u001b[38;2;255;205;117mtop-10-songs-2024\u001b[39m │ │ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K ├─────────────────────────────┼──────────────────────────────────────┼───────────────────────────────────────────────┤ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K │ Degenerate input │ \u001b[38;2;255;205;117m!!! ___ ---\u001b[39m → \u001b[38;2;255;205;117m''\u001b[39m, \u001b[38;2;255;205;117m''\u001b[39m → \u001b[38;2;255;205;117m''\u001b[39m │ trim can empty the string without throwing │ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K ├─────────────────────────────┼──────────────────────────────────────┼───────────────────────────────────────────────┤ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K │ Idempotence │ \u001b[38;2;255;205;117mslugify(slugify(x)) === slugify(x)\u001b[39m │ \u001b[38;2;255;205;117m Ünïcödé__Slug!! \u001b[39m → \u001b[38;2;255;205;117municode-slug\u001b[39m, stable on │ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K │ │ │ re-run │ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K └─────────────────────────────┴──────────────────────────────────────┴───────────────────────────────────────────────┘ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K One behavior worth flagging: characters with no ASCII decomposition (CJK, Cyrillic, emoji) are treated as punctuation \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K and become dashes — \u001b[38;2;255;205;117mslugify('日本語')\u001b[39m is \u001b[38;2;255;205;117m''\u001b[39m. That's the standard ASCII-slug tradeoff and matches the \"collapse \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K punctuation\" rule as written, but if you need transliteration instead, that's a different (larger) change. \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Bash\u001b[39m 1/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 2/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 3/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 4/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 5/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Write\u001b[39m 6/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Edit\u001b[39m 7/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Write\u001b[39m 8/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Write\u001b[39m 9/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Bash\u001b[39m 10/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[48.129371,"o","\u001b[2A\u001b[2G\u001b[?25l"] +[48.144752,"o","\u001b[?25l"] +[48.156672,"o","\u001b[?2026h\u001b[26A\r\u001b[2K\u001b[0m\u001b]8;;\u0007 \u001b[1m\u001b[38;2;151;190;255mactivity\u001b[39m\u001b[22m \u001b[38;2;135;145;165m›\u001b[39m \u001b[38;2;119;166;255madmitted\u001b[39m \u001b[38;2;135;145;165m· completed · 1/3\u001b[39m \u001b[38;2;135;145;165mProduct engineer · claude-code · opus · in 15 · out 2.9k\u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007\u001b[38;2;135;145;165m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────\u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 \u001b[38;2;119;166;255m›\u001b[39m \u001b[38;2;112;211;151m✓\u001b[39m \u001b[38;2;119;166;255madmitted\u001b[39m\u001b[38;2;135;145;165m · system · profile 751f2a\u001b[0m…\u001b[0m\u001b[38;2;135;145;165m│\u001b[39m time: 2026-08-12T12:40:04.891Z \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 \u001b[38;2;112;211;151m✓\u001b[39m \u001b[38;2;225;230;240mcli-bridge execution\u001b[39m\u001b[38;2;135;145;165m · environment\u001b[0m…\u001b[0m\u001b[38;2;135;145;165m│\u001b[39m run: run-b413c4d4-820d-453f-93d2-ff52f9751b83 \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 \u001b[38;2;112;211;151m✓\u001b[39m \u001b[38;2;225;230;240mrun run-b413c4d4-820d-453f-93d2-ff\u001b[0m…\u001b[0m\u001b[38;2;135;145;165m│\u001b[39m profile 751f2a0fca472cff39aba489cad8b639b8324680873a3fe41029a7c0b8cba6c0 \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 \u001b[38;2;135;145;165m│\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 \u001b[38;2;135;145;165m│\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 \u001b[38;2;135;145;165m│\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 \u001b[38;2;135;145;165m│\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 \u001b[38;2;135;145;165m│\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 \u001b[38;2;135;145;165m│\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 \u001b[38;2;135;145;165m│\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 \u001b[38;2;135;145;165m│\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 \u001b[38;2;135;145;165m│\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 \u001b[38;2;135;145;165m│\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 \u001b[38;2;135;145;165m│\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 \u001b[38;2;135;145;165m│\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 \u001b[38;2;135;145;165m│\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 \u001b[38;2;135;145;165m│\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 "] +[48.156753,"o"," \u001b[38;2;135;145;165m│\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 \u001b[38;2;135;145;165m│\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 \u001b[38;2;135;145;165m│\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 \u001b[38;2;135;145;165m│\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 \u001b[38;2;135;145;165m│\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 \u001b[38;2;135;145;165m│\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 \u001b[38;2;135;145;165m│\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 \u001b[38;2;135;145;165m│\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 \u001b[38;2;135;145;165m│\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007\u001b[38;2;135;145;165m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────\u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 \u001b[38;2;135;145;165m↑↓ select · PgUp/PgDn detail · home/end jump · ←/esc close · tab filter: all\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[?25l"] +[49.025979,"i","\u001b"] +[49.037585,"o","\u001b[?25l"] +[49.040997,"o","\u001b[?2026h\u001b[29A\r\u001b[2K │ Collapse runs │ \u001b[38;2;255;205;117ma -- _ b\u001b[39m → \u001b[38;2;255;205;117ma-b\u001b[39m │ mixed space/dash/underscore run → one dash │ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K ├─────────────────────────────┼──────────────────────────────────────┼───────────────────────────────────────────────┤ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K │ Trim dashes │ \u001b[38;2;255;205;117m--Hello, World!--\u001b[39m → \u001b[38;2;255;205;117mhello-world\u001b[39m │ both ends │ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K ├─────────────────────────────┼──────────────────────────────────────┼───────────────────────────────────────────────┤ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K │ Lowercase + digits survive │ \u001b[38;2;255;205;117mTop 10 Songs (2024)\u001b[39m → │ digits are not punctuation │ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K │ │ \u001b[38;2;255;205;117mtop-10-songs-2024\u001b[39m │ │ \u001b[0m\u001b]8;;\u0007\r\r\n"] +[49.041019,"o","\u001b[2K ├─────────────────────────────┼──────────────────────────────────────┼───────────────────────────────────────────────┤ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K │ Degenerate input │ \u001b[38;2;255;205;117m!!! ___ ---\u001b[39m → \u001b[38;2;255;205;117m''\u001b[39m, \u001b[38;2;255;205;117m''\u001b[39m → \u001b[38;2;255;205;117m''\u001b[39m │ trim can empty the string without throwing │ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K ├─────────────────────────────┼──────────────────────────────────────┼───────────────────────────────────────────────┤ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K │ Idempotence │ \u001b[38;2;255;205;117mslugify(slugify(x)) === slugify(x)\u001b[39m │ \u001b[38;2;255;205;117m Ünïcödé__Slug!! \u001b[39m → \u001b[38;2;255;205;117municode-slug\u001b[39m, stable on │ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K │ │ │ re-run │ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K └─────────────────────────────┴──────────────────────────────────────┴───────────────────────────────────────────────┘ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K One behavior worth flagging: characters with no ASCII decomposition (CJK, Cyrillic, emoji) are treated as punctuation \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K and become dashes — \u001b[38;2;255;205;117mslugify('日本語')\u001b[39m is \u001b[38;2;255;205;117m''\u001b[39m. That's the standard ASCII-slug tradeoff and matches the \"collapse \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K punctuation\" rule as written, but if you need transliteration instead, that's a different (larger) change. \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Bash\u001b[39m 1/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 2/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 3/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 4/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 5/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Write\u001b[39m 6/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Edit\u001b[39m 7/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Write\u001b[39m 8/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Write\u001b[39m 9/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Bash\u001b[39m 10/10\u001b[38;2;135;145;165m · Ctrl+E details"] +[49.041025,"o","\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;2;225;230;240mprofile Product engineer\u001b[39m · \u001b[38;2;225;230;240mclaude-code / opus\u001b[39m · \u001b[38;2;225;230;240mLocal CLI Bridge\u001b[39m · \u001b[38;2;225;230;240mhigh\u001b[39m \u001b[38;2;225;230;240min 15\u001b[39m · \u001b[38;2;225;230;240mout 2.9k\u001b[39m\u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[3A\u001b[2G\u001b[?25l"] +[49.326741,"i","/"] +[49.329864,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m/\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[3G\u001b[?25l"] +[49.344493,"o","\u001b[?2026h\u001b[26A\r\u001b[2K │ Degenerate input │ \u001b[38;2;255;205;117m!!! ___ ---\u001b[39m → \u001b[38;2;255;205;117m''\u001b[39m, \u001b[38;2;255;205;117m''\u001b[39m → \u001b[38;2;255;205;117m''\u001b[39m │ trim can empty the string without throwing │ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K ├─────────────────────────────┼──────────────────────────────────────┼───────────────────────────────────────────────┤ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K │ Idempotence │ \u001b[38;2;255;205;117mslugify(slugify(x)) === slugify(x)\u001b[39m │ \u001b[38;2;255;205;117m Ünïcödé__Slug!! \u001b[39m → \u001b[38;2;255;205;117municode-slug\u001b[39m, stable on │ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K │ │ │ re-run │ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K └─────────────────────────────┴──────────────────────────────────────┴───────────────────────────────────────────────┘ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K One behavior worth flagging: characters with no ASCII decomposition (CJK, Cyrillic, emoji) are treated as punctuation \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K and become dashes — \u001b[38;2;255;205;117mslugify('日本語')\u001b[39m is \u001b[38;2;255;205;117m''\u001b[39m. That's the standard ASCII-slug tradeoff and matches the \"collapse \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K punctuation\" rule as written, but if you need transliteration instead, that's a different (larger) change. \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Bash\u001b[39m 1/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 2/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 3/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 4/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 5/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Write\u001b[39m 6/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Edit\u001b[39m 7/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Write\u001b[39m 8/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Write\u001b[39m 9/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Bash\u001b[39m 10/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m/\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;2;135;145;165m──────────────────────────────────────────────\u001b[39m\u001b[38;2;119;166;255m alt+enter newline · paste \u001b[39m\u001b[38;2;135;145;165m───────────────────────────────────────────────\u001b[39m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[1m\u001b[38;2;119;166;255m→ new Create an empty conversation\u001b[39m\u001b[22m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K open\u001b[38;2;135;145;165m Search and open conversations\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K profile\u001b[38;2;135;145;165m Inspect, select, import, or edit a profile\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K connection\u001b[38;2;135;145;165m Inspect, create, test, select, or remove a connection\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K runner\u001b[38;2;135;145;165m unavailable — Runner overrides require the current profile compatibility helpers\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m (1/29)\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[9A\u001b[3G\u001b[?25l"] +[49.351234,"i","p"] +[49.353679,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m/p\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[4G\u001b[?25l"] +[49.369919,"o","\u001b[?2026h\u001b[4B\r\u001b[2K \u001b[1m\u001b[38;2;119;166;255m→ profile Inspect, select, import, or edit a profile\u001b[39m\u001b[22m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K open\u001b[38;2;135;145;165m Search and open conversations\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K approve\u001b[38;2;135;145;165m unavailable — Interaction response is not exposed by the current runtime adapter\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K export\u001b[38;2;135;145;165m Export selected redacted data\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K import\u001b[38;2;135;145;165m Import a redacted Braid conversation file\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m (1/8)\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[9A\u001b[4G\u001b[?25l"] +[49.375354,"i","r"] +[49.377951,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m/pr\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[5G\u001b[?25l"] +[49.393786,"o","\u001b[?2026h\u001b[19A\r\u001b[2K ├─────────────────────────────┼──────────────────────────────────────┼───────────────────────────────────────────────┤ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K │ Degenerate input │ \u001b[38;2;255;205;117m!!! ___ ---\u001b[39m → \u001b[38;2;255;205;117m''\u001b[39m, \u001b[38;2;255;205;117m''\u001b[39m → \u001b[38;2;255;205;117m''\u001b[39m │ trim can empty the string without throwing │ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K ├─────────────────────────────┼──────────────────────────────────────┼───────────────────────────────────────────────┤ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K │ Idempotence │ \u001b[38;2;255;205;117mslugify(slugify(x)) === slugify(x)\u001b[39m │ \u001b[38;2;255;205;117m Ünïcödé__Slug!! \u001b[39m → \u001b[38;2;255;205;117municode-slug\u001b[39m, stable on │ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K │ │ │ re-run │ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K └─────────────────────────────┴──────────────────────────────────────┴───────────────────────────────────────────────┘ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K One behavior worth flagging: characters with no ASCII decomposition (CJK, Cyrillic, emoji) are treated as punctuation \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K and become dashes — \u001b[38;2;255;205;117mslugify('日本語')\u001b[39m is \u001b[38;2;255;205;117m''\u001b[39m. That's the standard ASCII-slug tradeoff and matches the \"collapse \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K punctuation\" rule as written, but if you need transliteration instead, that's a different (larger) change. \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Bash\u001b[39m 1/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 2/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 3/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 4/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 5/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Write\u001b[39m 6/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Edit\u001b[39m 7/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Write\u001b[39m 8/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Write\u001b[39m 9/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Bash\u001b[39m 10/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m/pr\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;2;135;145;165m──────────────────────────────────────────────\u001b[39m\u001b[38;2;119;166;255m alt+enter newline · paste \u001b[39m\u001b[38;2;135;145;165m───────────────────────────────────────────────\u001b[39m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[1m\u001b[38;2;119;166;255m→ profile Inspect, select, import, or edit a profile\u001b[39m\u001b[22m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K approve\u001b[38;2;135;145;165m unavailable — Interaction response is not exposed by the current runtime adapter\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K export\u001b[38;2;135;145;165m Export selected redacted data\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K import\u001b[38;2;135;145;165m Import a redacted Braid conversation file\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K compare\u001b[38;2;135;145;165m unavailable — Two completed or failed runs are required for comparison\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[8A\u001b[5G\u001b[?25l"] +[49.399157,"i","o"] +[49.401147,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m/pro\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[49.401188,"o","\u001b[6G\u001b[?25l"] +[49.417193,"o","\u001b[?2026h\u001b[19A\r\u001b[2K │ Lowercase + digits survive │ \u001b[38;2;255;205;117mTop 10 Songs (2024)\u001b[39m → │ digits are not punctuation │ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K │ │ \u001b[38;2;255;205;117mtop-10-songs-2024\u001b[39m │ │ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K ├─────────────────────────────┼──────────────────────────────────────┼───────────────────────────────────────────────┤ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K │ Degenerate input │ \u001b[38;2;255;205;117m!!! ___ ---\u001b[39m → \u001b[38;2;255;205;117m''\u001b[39m, \u001b[38;2;255;205;117m''\u001b[39m → \u001b[38;2;255;205;117m''\u001b[39m │ trim can empty the string without throwing │ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K ├─────────────────────────────┼──────────────────────────────────────┼───────────────────────────────────────────────┤ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K │ Idempotence │ \u001b[38;2;255;205;117mslugify(slugify(x)) === slugify(x)\u001b[39m │ \u001b[38;2;255;205;117m Ünïcödé__Slug!! \u001b[39m → \u001b[38;2;255;205;117municode-slug\u001b[39m, stable on │ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K │ │ │ re-run │ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K └─────────────────────────────┴──────────────────────────────────────┴───────────────────────────────────────────────┘ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K One behavior worth flagging: characters with no ASCII decomposition (CJK, Cyrillic, emoji) are treated as punctuation \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K and become dashes — \u001b[38;2;255;205;117mslugify('日本語')\u001b[39m is \u001b[38;2;255;205;117m''\u001b[39m. That's the standard ASCII-slug tradeoff and matches the \"collapse \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K punctuation\" rule as written, but if you need transliteration instead, that's a different (larger) change. \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Bash\u001b[39m 1/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 2/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 3/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 4/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 5/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Write\u001b[39m 6/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Edit\u001b[39m 7/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Write\u001b[39m 8/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m "] +[49.417256,"o"," \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Write\u001b[39m 9/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Bash\u001b[39m 10/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m/pro\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;2;135;145;165m──────────────────────────────────────────────\u001b[39m\u001b[38;2;119;166;255m alt+enter newline · paste \u001b[39m\u001b[38;2;135;145;165m───────────────────────────────────────────────\u001b[39m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[1m\u001b[38;2;119;166;255m→ profile Inspect, select, import, or edit a profile\u001b[39m\u001b[22m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K approve\u001b[38;2;135;145;165m unavailable — Interaction response is not exposed by the current runtime adapter\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[5A\u001b[6G\u001b[?25l"] +[49.423574,"i","f"] +[49.425856,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m/prof\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[7G\u001b[?25l"] +[49.442233,"o","\u001b[?2026h\u001b[23A\r\u001b[2K │ Trim dashes │ \u001b[38;2;255;205;117m--Hello, World!--\u001b[39m → \u001b[38;2;255;205;117mhello-world\u001b[39m │ both ends │ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K ├─────────────────────────────┼──────────────────────────────────────┼───────────────────────────────────────────────┤ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K │ Lowercase + digits survive │ \u001b[38;2;255;205;117mTop 10 Songs (2024)\u001b[39m → │ digits are not punctuation │ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K │ │ \u001b[38;2;255;205;117mtop-10-songs-2024\u001b[39m │ │ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K ├─────────────────────────────┼──────────────────────────────────────┼───────────────────────────────────────────────┤ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K │ Degenerate input │ \u001b[38;2;255;205;117m!!! ___ ---\u001b[39m → \u001b[38;2;255;205;117m''\u001b[39m, \u001b[38;2;255;205;117m''\u001b[39m → \u001b[38;2;255;205;117m''\u001b[39m │ trim can empty the string without throwing │ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K ├─────────────────────────────┼──────────────────────────────────────┼───────────────────────────────────────────────┤ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K │ Idempotence │ \u001b[38;2;255;205;117mslugify(slugify(x)) === slugify(x)\u001b[39m │ \u001b[38;2;255;205;117m Ünïcödé__Slug!! \u001b[39m → \u001b[38;2;255;205;117municode-slug\u001b[39m, stable on │ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K │ │ │ re-run │ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K └─────────────────────────────┴──────────────────────────────────────┴───────────────────────────────────────────────┘ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K One behavior worth flagging: characters with no ASCII decomposition (CJK, Cyrillic, emoji) are treated as punctuation \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K and become dashes — \u001b[38;2;255;205;117mslugify('日本語')\u001b[39m is \u001b[38;2;255;205;117m''\u001b[39m. That's the standard ASCII-slug tradeoff and matches the \"collapse \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K punctuation\" rule as written, but if you need transliteration instead, that's a different (larger) change. \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Bash\u001b[39m 1/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 2/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 3/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 4/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 5/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Write\u001b[39m 6/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Edit\u001b[39m 7/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Write\u001b[39m 8/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Write\u001b[39m 9/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Bash\u001b[39m 10/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m/prof\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;2;135;145;165m──────────────────────────────────────────────\u001b[39m\u001b[38;2;119;166;255m alt+enter newline · paste \u001b[39m\u001b[38;2;135;145;165m───────────────────────────────────────────────\u001b[39m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[1m\u001b[38;2;119;166;255m→ profile Inspect, select, import, or edit a profile\u001b[39m\u001b[22m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[4A\u001b[7G\u001b[?25l"] +[49.4476,"i","i"] +[49.449772,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m/profi\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[49.449801,"o","\u001b[8G\u001b[?25l"] +[49.466114,"o","\u001b[8G\u001b[?25l"] +[49.4724,"i","l"] +[49.475526,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m/profil\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[9G"] +[49.475579,"o","\u001b[?25l"] +[49.491577,"o","\u001b[9G\u001b[?25l"] +[49.4959,"i","e"] +[49.498329,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m/profile\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[10G"] +[49.498388,"o","\u001b[?25l"] +[49.51446,"o","\u001b[10G\u001b[?25l"] +[49.51975,"i","\r"] +[49.523229,"o","\u001b[?2026h\u001b[24A\r\u001b[2K │ Collapse runs │ \u001b[38;2;255;205;117ma -- _ b\u001b[39m → \u001b[38;2;255;205;117ma-b\u001b[39m │ mixed space/dash/underscore run → one dash │ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K ├─────────────────────────────┼──────────────────────────────────────┼───────────────────────────────────────────────┤ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K │ Trim dashes │ \u001b[38;2;255;205;117m--Hello, World!--\u001b[39m → \u001b[38;2;255;205;117mhello-world\u001b[39m │ both ends │ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K ├─────────────────────────────┼──────────────────────────────────────┼───────────────────────────────────────────────┤ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K │ Lowercase + digits survive │ \u001b[38;2;255;205;117mTop 10 Songs (2024)\u001b[39m → │ digits are not punctuation │ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K │ │ \u001b[38;2;255;205;117mtop-10-songs-2024\u001b[39m │ │ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K ├─────────────────────────────┼──────────────────────────────────────┼───────────────────────────────────────────────┤ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K │ Degenerate input │ \u001b[38;2;255;205;117m!!! ___ ---\u001b[39m → \u001b[38;2;255;205;117m''\u001b[39m, \u001b[38;2;255;205;117m''\u001b[39m → \u001b[38;2;255;205;117m''\u001b[39m │ trim can empty the string without throwing │ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K ├─────────────────────────────┼──────────────────────────────────────┼───────────────────────────────────────────────┤ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K │ Idempotence │ \u001b[38;2;255;205;117mslugify(slugify(x)) === slugify(x)\u001b[39m │ \u001b[38;2;255;205;117m Ünïcödé__Slug!! \u001b[39m → \u001b[38;2;255;205;117municode-slug\u001b[39m, stable on │ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K │ │ │ re-run │ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K └─────────────────────────────┴──────────────────────────────────────┴───────────────────────────────────────────────┘ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K One behavior worth flagging: characters with no ASCII decomposition (CJK, Cyrillic, emoji) are treated as punctuation \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K and become dashes — \u001b[38;2;255;205;117mslugify('日本語')\u001b[39m is \u001b[38;2;255;205;117m''\u001b[39m. That's the standard ASCII-slug tradeoff and matches the \"collapse \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K punctuation\" rule as written, but if you need transliteration instead, that's a different (larger) change. \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Bash\u001b[39m 1/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 2/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 3/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 4/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 5/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Write\u001b[39m 6/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Edit\u001b[39m 7/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Write\u001b[39m 8/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Write\u001b[39m 9/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Bash\u001b[39m 10/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[49.523271,"o","\u001b[2A\u001b[2G\u001b[?25l"] +[49.523468,"o","\u001b[?25l"] +[49.54139,"o","\u001b[?2026h\u001b[26A\r\u001b[2K ├─────────────────────────────┼──────────────────────────────────────┼───────────────────────────────────────────────┤ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K │ Underscores as punctuation │ \u001b[38;2;255;205;117mhello_world\u001b[39m → \u001b[38;2;255;205;117mhello-world\u001b[39m │ \u001b[38;2;255;205;117m_\u001b[39m excluded from the keep-class │ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K ├─────────────────────────────┼──────────────────────────────────────┼───────────────────────────────────────────────┤ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K │ Collapse runs │ \u001b[38;2;255;205;117ma -- _ b\u001b[39m → \u001b[38;2;255;205;117ma-b\u001b[39m │ mixed space/dash/underscore run → one dash │ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K ├─────────────────────────────┼──────────────────────────────────────┼───────────────────────────────────────────────┤ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K │ Trim dashes │ \u001b[38;2;255;205;117m--Hello, World!--\u001b[39m → \u001b[38;2;255;205;117mhello-world\u001b[39m │ both ends │ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K ├─────────────────────────────┼──────────────────────────────────────┼───────────────────────────────────────────────┤ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K │ Lowercase + digits survive │ \u001b[38;2;255;205;117mTop 10 Songs (2024)\u001b[39m → │ digits are not punctuation │ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K │ \u001b[0m\u001b]8;;\u0007 Active profile · Product engineer \u001b[0m\u001b]8;;\u0007 │ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K ├─────\u001b[0m\u001b]8;;\u0007 \u001b[38;2;135;145;165msource Configured AgentProfile · trusted · read-only\u001b[39m \u001b[0m\u001b]8;;\u0007──────┤ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K │ Dege\u001b[0m\u001b]8;;\u0007 \u001b[38;2;135;145;165mrunner claude-code · model opus\u001b[39m \u001b[0m\u001b]8;;\u0007ng │ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K ├─────\u001b[0m\u001b]8;;\u0007 \u001b[38;2;135;145;165mthinking high · max output 32,768 tokens\u001b[39m \u001b[0m\u001b]8;;\u0007──────┤ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K │ Idem\u001b[0m\u001b]8;;\u0007 \u001b[38;2;135;145;165mdigest sha256:751f2a0f…b8cba6c0\u001b[39m \u001b[0m\u001b]8;;\u0007 on │ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K │ \u001b[0m\u001b]8;;\u0007 \u001b[1m\u001b[38;2;151;190;255mprofiles\u001b[39m\u001b[22m \u001b[0m\u001b]8;;\u0007 │ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K └─────\u001b[0m\u001b]8;;\u0007\u001b[38;2;135;145;165m─────────────────────────────────────────────────────────────────────────────────────────────────────────\u001b[0m\u001b]8;;\u0007──────┘ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007> \u001b[7m \u001b[27m \u001b[0m\u001b]8;;\u0007 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K One be\u001b[0m\u001b]8;;\u0007\u001b[1m\u001b[38;2;119;166;255m→ CLI Bridge · claude-code · hai\u001b[0"] +[49.54147,"o","m trusted · read-only · Advertised CLI Bridge model · claude-code · hai\u001b[0m\u001b[39m\u001b[22m \u001b[0m\u001b]8;;\u0007uation \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K and be\u001b[0m\u001b]8;;\u0007 ✓ Product engineer\u001b[38;2;135;145;165m trusted · read-only · Configured AgentProfile · claude-code · opus\u001b[39m \u001b[0m\u001b]8;;\u0007 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K punctu\u001b[0m\u001b]8;;\u0007 Trace analyst\u001b[38;2;135;145;165m trusted · read-only · profile.json · claude-code · sonnet\u001b[39m \u001b[0m\u001b]8;;\u0007 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Bash\u001b[0m\u001b]8;;\u0007\u001b[38;2;135;145;165m─────────────────────────────────────────────────────────────────────────────────────────────────────────\u001b[0m\u001b]8;;\u0007 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[0m\u001b]8;;\u0007 \u001b[38;2;135;145;165menter · ^V valid · ^S save · esc\u001b[39m \u001b[0m\u001b]8;;\u0007 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 3/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 4/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 5/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Write\u001b[39m 6/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Edit\u001b[39m 7/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Write\u001b[39m 8/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Write\u001b[39m 9/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Bash\u001b[39m 10/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[13A\u001b[10G\u001b[?25l"] +[49.570112,"i","T"] +[49.574529,"o","\u001b[?2026h\r\u001b[2K \u001b[0m\u001b]8;;\u0007> T\u001b[7m \u001b[27m \u001b[0m\u001b]8;;\u0007 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K One be\u001b[0m\u001b]8;;\u0007\u001b[1m\u001b[38;2;119;166;255m→ Trace analyst trusted · read-only · profile.json · claude-code · sonnet\u001b[39m\u001b[22m \u001b[0m\u001b]8;;\u0007uation \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K and be\u001b[0m\u001b]8;;\u0007 CLI Bridge · claude-code · hai\u001b[0m\u001b[38;2;135;145;165m trusted · read-only · Advertised CLI Bridge model · claude-code · hai\u001b[0m\u001b[39m \u001b[0m\u001b]8;;\u0007 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K punctu\u001b[0m\u001b]8;;\u0007 ✓ Product engineer\u001b[38;2;135;145;165m trusted · read-only · Configured AgentProfile · claude-code · opus\u001b[39m \u001b[0m\u001b]8;;\u0007 \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[3A\u001b[11G\u001b[?25l"] +[49.593873,"i","r"] +[49.597723,"o","\u001b[?2026h\r\u001b[2K \u001b[0m\u001b]8;;\u0007> Tr\u001b[7m \u001b[27m \u001b[0m\u001b]8;;\u0007 \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[49.597771,"o","\u001b[12G"] +[49.59778,"o","\u001b[?25l"] +[49.61813,"i","a"] +[49.621996,"o","\u001b[?2026h\r\u001b[2K \u001b[0m\u001b]8;;\u0007> Tra\u001b[7m \u001b[27m \u001b[0m\u001b]8;;\u0007 \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[13G\u001b[?25l"] +[49.642392,"i","c"] +[49.646525,"o","\u001b[?2026h\r\u001b[2K \u001b[0m\u001b]8;;\u0007> Trac\u001b[7m \u001b[27m \u001b[0m\u001b]8;;\u0007 \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[14G\u001b[?25l"] +[49.666123,"i","e"] +[49.669777,"o","\u001b[?2026h\r\u001b[2K \u001b[0m\u001b]8;;\u0007> Trace\u001b[7m \u001b[27m \u001b[0m\u001b]8;;\u0007 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K One be\u001b[0m\u001b]8;;\u0007\u001b[1m\u001b[38;2;119;166;255m→ Trace analyst trusted · read-only · profile.json · claude-code · sonnet\u001b[39m\u001b[22m \u001b[0m\u001b]8;;\u0007uation \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K and be\u001b[0m\u001b]8;;\u0007 ✓ Product engineer\u001b[38;2;135;145;165m trusted · read-only · Configured AgentProfile · claude-code · opus\u001b[39m \u001b[0m\u001b]8;;\u0007 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K punctu\u001b[0m\u001b]8;;\u0007 CLI Bridge · claude-code · hai\u001b[0m\u001b[38;2;135;145;165m trusted · read-only · Advertised CLI Bridge model · claude-code · hai\u001b[0m\u001b[39m \u001b[0m\u001b]8;;\u0007 \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[3A\u001b[15G\u001b[?25l"] +[49.690208,"i"," "] +[49.694187,"o","\u001b[?2026h\r\u001b[2K \u001b[0m\u001b]8;;\u0007> Trace \u001b[7m \u001b[27m \u001b[0m\u001b]8;;\u0007 \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[16G\u001b[?25l"] +[49.714539,"i","a"] +[49.719927,"o","\u001b[?2026h\r\u001b[2K \u001b[0m\u001b]8;;\u0007> Trace a\u001b[7m \u001b[27m \u001b[0m\u001b]8;;\u0007 \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[17G"] +[49.71998,"o","\u001b[?25l"] +[49.738289,"i","n"] +[49.741692,"o","\u001b[?2026h\r\u001b[2K \u001b[0m\u001b]8;;\u0007> Trace an\u001b[7m \u001b[27m \u001b[0m\u001b]8;;\u0007 \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[49.741735,"o","\u001b[18G\u001b[?25l"] +[49.762197,"i","a"] +[49.766128,"o","\u001b[?2026h\r\u001b[2K \u001b[0m\u001b]8;;\u0007> Trace ana\u001b[7m \u001b[27m \u001b[0m\u001b]8;;\u0007 \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[19G\u001b[?25l"] +[49.786442,"i","l"] +[49.790415,"o","\u001b[?2026h\r\u001b[2K \u001b[0m\u001b]8;;\u0007> Trace anal\u001b[7m \u001b[27m \u001b[0m\u001b]8;;\u0007 \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[49.790451,"o","\u001b[20G\u001b[?25l"] +[49.809778,"i","y"] +[49.815917,"o","\u001b[?2026h\u001b[7A\r\u001b[2K │ │ \u001b[38;2;255;205;117mtop-10-songs-2024\u001b[39m │ │ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K ├─────\u001b[0m\u001b]8;;\u0007 Active profile · Product engineer \u001b[0m\u001b]8;;\u0007──────┤ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K │ Dege\u001b[0m\u001b]8;;\u0007 \u001b[38;2;135;145;165msource Configured AgentProfile · trusted · read-only\u001b[39m \u001b[0m\u001b]8;;\u0007ng │ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K ├─────\u001b[0m\u001b]8;;\u0007 \u001b[38;2;135;145;165mrunner claude-code · model opus\u001b[39m \u001b[0m\u001b]8;;\u0007──────┤ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K │ Idem\u001b[0m\u001b]8;;\u0007 \u001b[38;2;135;145;165mthinking high · max output 32,768 tokens\u001b[39m \u001b[0m\u001b]8;;\u0007 on │ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K │ \u001b[0m\u001b]8;;\u0007 \u001b[38;2;135;145;165mdigest sha256:751f2a0f…b8cba6c0\u001b[39m \u001b[0m\u001b]8;;\u0007 │ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K └─────\u001b[0m\u001b]8;;\u0007 \u001b[1m\u001b[38;2;151;190;255mprofiles\u001b[39m\u001b[22m \u001b[0m\u001b]8;;\u0007──────┘ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\u001b[38;2;135;145;165m─────────────────────────────────────────────────────────────────────────────────────────────────────────\u001b[0m\u001b]8;;\u0007 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K One be\u001b[0m\u001b]8;;\u0007> Trace analy\u001b[7m \u001b[27m \u001b[0m\u001b]8;;\u0007uation \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K and be\u001b[0m\u001b]8;;\u0007\u001b[1m\u001b[38;2;119;166;255m→ Trace analyst trusted · read-only · profile.json · claude-code · sonnet\u001b[39m\u001b[22m \u001b[0m\u001b]8;;\u0007 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K punctu\u001b[0m\u001b]8;;\u0007\u001b[38;2;135;145;165m─────────────────────────────────────────────────────────────────────────────────────────────────────────\u001b[0m\u001b]8;;\u0007 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Bash\u001b[0m\u001b]8;;\u0007 \u001b[38;2;135;145;165menter · ^V valid · ^S save · esc\u001b[39m \u001b[0m\u001b]8;;\u0007 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 2/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[49.815966,"o","\u001b[4A\u001b[21G\u001b[?25l"] +[49.834389,"i","s"] +[49.838226,"o","\u001b[?2026h\r\u001b[2K One be\u001b[0m\u001b]8;;\u0007> Trace analys\u001b[7m \u001b[27m \u001b[0m\u001b]8;;\u0007uation \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[49.83826,"o","\u001b[22G\u001b[?25l"] +[49.858658,"i","t"] +[49.861978,"o","\u001b[?2026h\r\u001b[2K One be\u001b[0m\u001b]8;;\u0007> Trace analyst\u001b[7m \u001b[27m \u001b[0m\u001b]8;;\u0007uation \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[23G\u001b[?25l"] +[49.883364,"i","\r"] +[49.887144,"o","\u001b[?2026h\u001b[7A\r\u001b[2K ├─────\u001b[0m\u001b]8;;\u0007 Selecting Trace analyst… \u001b[0m\u001b]8;;\u0007──────┤ \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[49.887182,"o","\u001b[7B\u001b[23G\u001b[?25l"] +[49.930696,"o","\u001b[?2026h\u001b[7A\r\u001b[2K ├─────\u001b[0m\u001b]8;;\u0007 Selected Trace analyst · next runs use it \u001b[0m\u001b]8;;\u0007──────┤ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K │ Dege\u001b[0m\u001b]8;;\u0007 \u001b[38;2;135;145;165msource profile.json · trusted · read-only\u001b[39m \u001b[0m\u001b]8;;\u0007ng │ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K ├─────\u001b[0m\u001b]8;;\u0007 \u001b[38;2;135;145;165mrunner claude-code · model sonnet\u001b[39m \u001b[0m\u001b]8;;\u0007──────┤ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K │ Idem\u001b[0m\u001b]8;;\u0007 \u001b[38;2;135;145;165mthinking high · max output 16,384 tokens\u001b[39m \u001b[0m\u001b]8;;\u0007 on │ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K │ \u001b[0m\u001b]8;;\u0007 \u001b[38;2;135;145;165mdigest sha256:61f7efab…7fcf69a3\u001b[39m \u001b[0m\u001b]8;;\u0007 │ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K └─────\u001b[0m\u001b]8;;\u0007 \u001b[1m\u001b[38;2;151;190;255mprofiles\u001b[39m\u001b[22m \u001b[0m\u001b]8;;\u0007──────┘ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\u001b[38;2;135;145;165m─────────────────────────────────────────────────────────────────────────────────────────────────────────\u001b[0m\u001b]8;;\u0007 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K One be\u001b[0m\u001b]8;;\u0007> Trace analyst\u001b[7m \u001b[27m \u001b[0m\u001b]8;;\u0007uation \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K and be\u001b[0m\u001b]8;;\u0007\u001b[1m\u001b[38;2;119;166;255m→ ✓ Trace analyst trusted · read-only · profile.json · claude-code · sonnet\u001b[39m\u001b[22m \u001b[0m\u001b]8;;\u0007 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K punctu\u001b[0m\u001b]8;;\u0007\u001b[38;2;135;145;165m─────────────────────────────────────────────────────────────────────────────────────────────────────────\u001b[0m\u001b]8;;\u0007 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Bash\u001b[0m\u001b]8;;\u0007 \u001b[38;2;135;145;165menter · ^V valid · ^S save · esc\u001b[39m \u001b[0m\u001b]8;;\u0007 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 2/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 3/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 4/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 5/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Write\u001b[39m 6/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Edit\u001b[39m 7/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Write\u001b[39m 8/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Write\u001b[39m 9/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Bash\u001b[39m 10/10\u001b[38;2;135;145;165m · Ctrl+E detail"] +[49.930778,"o","s\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;2;225;230;240mprofile Trace analyst\u001b[39m · \u001b[38;2;225;230;240mclaude-code / sonnet\u001b[39m · \u001b[38;2;225;230;240mLocal CLI Bridge\u001b[39m · \u001b[38;2;225;230;240mhigh\u001b[39m \u001b[38;2;225;230;240min 15\u001b[39m · \u001b[38;2;225;230;240mout 2.9k\u001b[39m\u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[13A\u001b[23G\u001b[?25l"] +[49.933249,"i","\u001b"] +[49.943763,"o","\u001b[?25l"] +[49.947454,"o","\u001b[?2026h\u001b[16A\r\u001b[2K │ Collapse runs │ \u001b[38;2;255;205;117ma -- _ b\u001b[39m → \u001b[38;2;255;205;117ma-b\u001b[39m │ mixed space/dash/underscore run → one dash │ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K ├─────────────────────────────┼──────────────────────────────────────┼───────────────────────────────────────────────┤ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K │ Trim dashes │ \u001b[38;2;255;205;117m--Hello, World!--\u001b[39m → \u001b[38;2;255;205;117mhello-world\u001b[39m │ both ends │ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K ├─────────────────────────────┼──────────────────────────────────────┼───────────────────────────────────────────────┤ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K │ Lowercase + digits survive │ \u001b[38;2;255;205;117mTop 10 Songs (2024)\u001b[39m → │ digits are not punctuation │ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K │ │ \u001b[38;2;255;205;117mtop-10-songs-2024\u001b[39m │ │ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K ├─────────────────────────────┼──────────────────────────────────────┼───────────────────────────────────────────────┤ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K │ Degenerate input │ \u001b[38;2;255;205;117m!!! ___ ---\u001b[39m → \u001b[38;2;255;205;117m''\u001b[39m, \u001b[38;2;255;205;117m''\u001b[39m → \u001b[38;2;255;205;117m''\u001b[39m │ trim can empty the string without throwing │ \u001b[0m\u001b]8;;\u0007\r\r\n"] +[49.947507,"o","\u001b[2K ├─────────────────────────────┼──────────────────────────────────────┼───────────────────────────────────────────────┤ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K │ Idempotence │ \u001b[38;2;255;205;117mslugify(slugify(x)) === slugify(x)\u001b[39m │ \u001b[38;2;255;205;117m Ünïcödé__Slug!! \u001b[39m → \u001b[38;2;255;205;117municode-slug\u001b[39m, stable on │ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K │ │ │ re-run │ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K └─────────────────────────────┴──────────────────────────────────────┴───────────────────────────────────────────────┘ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K One behavior worth flagging: characters with no ASCII decomposition (CJK, Cyrillic, emoji) are treated as punctuation \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K and become dashes — \u001b[38;2;255;205;117mslugify('日本語')\u001b[39m is \u001b[38;2;255;205;117m''\u001b[39m. That's the standard ASCII-slug tradeoff and matches the \"collapse \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K punctuation\" rule as written, but if you need transliteration instead, that's a different (larger) change. \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Bash\u001b[39m 1/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 2/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 3/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 4/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 5/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Write\u001b[39m 6/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Edit\u001b[39m 7/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Write\u001b[39m 8/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Write\u001b[39m 9/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Bash\u001b[39m 10/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[2A\u001b[2G\u001b[?25l"] +[50.483535,"i","/"] +[50.48628,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m/\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[3G"] +[50.486328,"o","\u001b[?25l"] +[50.492633,"i","a"] +[50.495182,"o","\u001b[?2026h\u001b[26A\r\u001b[2K │ Degenerate input │ \u001b[38;2;255;205;117m!!! ___ ---\u001b[39m → \u001b[38;2;255;205;117m''\u001b[39m, \u001b[38;2;255;205;117m''\u001b[39m → \u001b[38;2;255;205;117m''\u001b[39m │ trim can empty the string without throwing │ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K ├─────────────────────────────┼──────────────────────────────────────┼───────────────────────────────────────────────┤ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K │ Idempotence │ \u001b[38;2;255;205;117mslugify(slugify(x)) === slugify(x)\u001b[39m │ \u001b[38;2;255;205;117m Ünïcödé__Slug!! \u001b[39m → \u001b[38;2;255;205;117municode-slug\u001b[39m, stable on │ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K │ │ │ re-run │ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K └─────────────────────────────┴──────────────────────────────────────┴───────────────────────────────────────────────┘ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K One behavior worth flagging: characters with no ASCII decomposition (CJK, Cyrillic, emoji) are treated as punctuation \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K and become dashes — \u001b[38;2;255;205;117mslugify('日本語')\u001b[39m is \u001b[38;2;255;205;117m''\u001b[39m. That's the standard ASCII-slug tradeoff and matches the \"collapse \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K punctuation\" rule as written, but if you need transliteration instead, that's a different (larger) change. \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Bash\u001b[39m 1/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 2/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 3/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 4/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 5/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Write\u001b[39m 6/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n"] +[50.495216,"o","\u001b[2K \u001b[38;2;135;145;165m· Edit\u001b[39m 7/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Write\u001b[39m 8/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Write\u001b[39m 9/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Bash\u001b[39m 10/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m/a\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;2;135;145;165m──────────────────────────────────────────────\u001b[39m\u001b[38;2;119;166;255m alt+enter newline · paste \u001b[39m\u001b[38;2;135;145;165m───────────────────────────────────────────────\u001b[39m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[1m\u001b[38;2;119;166;255m→ new Create an empty conversation\u001b[39m\u001b[22m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K open\u001b[38;2;135;145;165m Search and open conversations\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K profile\u001b[38;2;135;145;165m Inspect, select, import, or edit a profile\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K connection\u001b[38;2;135;145;165m Inspect, create, test, select, or remove a connection\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K runner\u001b[38;2;135;145;165m unavailable — Runner overrides require the current profile compatibility helpers\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m (1/29)\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[9A\u001b[4G\u001b[?25l"] +[50.501878,"i","s"] +[50.50386,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m/as\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;2;135;145;165m──────────────────────────────────────────────\u001b[39m\u001b[38;2;119;166;255m alt+enter newline · paste \u001b[39m\u001b[38;2;135;145;165m───────────────────────────────────────────────\u001b[39m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[1m\u001b[38;2;119;166;255m→ ask Analyze a frozen run with citations\u001b[39m\u001b[22m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K analyze\u001b[38;2;135;145;165m Run a named trace-analysis recipe\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K approve\u001b[38;2;135;145;165m unavailable — Interaction response is not exposed by the current runtime adapter\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K automate\u001b[38;2;135;145;165m unavailable — Interaction automation requires the shared response contract\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K activity\u001b[38;2;135;145;165m Open run, tool, worker, and usage activity\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m (1/10)\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[50.50391,"o","\u001b[9A\u001b[5G\u001b[?25l"] +[50.511282,"i","k"] +[50.513858,"o","\u001b[?2026h\u001b[19A\r\u001b[2K │ Trim dashes │ \u001b[38;2;255;205;117m--Hello, World!--\u001b[39m → \u001b[38;2;255;205;117mhello-world\u001b[39m │ both ends │ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K ├─────────────────────────────┼──────────────────────────────────────┼───────────────────────────────────────────────┤ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K │ Lowercase + digits survive │ \u001b[38;2;255;205;117mTop 10 Songs (2024)\u001b[39m → │ digits are not punctuation │ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K │ │ \u001b[38;2;255;205;117mtop-10-songs-2024\u001b[39m │ │ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K ├─────────────────────────────┼──────────────────────────────────────┼───────────────────────────────────────────────┤ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K │ Degenerate input │ \u001b[38;2;255;205;117m!!! ___ ---\u001b[39m → \u001b[38;2;255;205;117m''\u001b[39m, \u001b[38;2;255;205;117m''\u001b[39m → \u001b[38;2;255;205;117m''\u001b[39m │ trim can empty the string without throwing │ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K ├─────────────────────────────┼──────────────────────────────────────┼───────────────────────────────────────────────┤ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K │ Idempotence │ \u001b[38;2;255;205;117mslugify(slugify(x)) === slugify(x)\u001b[39m │ \u001b[38;2;255;205;117m Ünïcödé__Slug!! \u001b[39m → \u001b[38;2;255;205;117municode-slug\u001b[39m, stable on │ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K │ │ │ re-run │ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K └─────────────────────────────┴──────────────────────────────────────┴───────────────────────────────────────────────┘ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K One behavior worth flagging: characters with no ASCII decomposition (CJK, Cyrillic, emoji) are treated as punctuation \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K and become dashes — \u001b[38;2;255;205;117mslugify('日本語')\u001b[39m is \u001b[38;2;255;205;117m''\u001b[39m. That's the standard ASCII-slug tradeoff and matches the \"collapse \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K punctuation\" rule as written, but if you need transliteration instead, that's a different (larger) change. \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Bash\u001b[39m 1/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 2/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 3/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 4/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 5/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m "] +[50.513899,"o"," \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Write\u001b[39m 6/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Edit\u001b[39m 7/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Write\u001b[39m 8/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Write\u001b[39m 9/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Bash\u001b[39m 10/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m/ask\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;2;135;145;165m──────────────────────────────────────────────\u001b[39m\u001b[38;2;119;166;255m alt+enter newline · paste \u001b[39m\u001b[38;2;135;145;165m───────────────────────────────────────────────\u001b[39m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[1m\u001b[38;2;119;166;255m→ ask Analyze a frozen run with citations\u001b[39m\u001b[22m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[4A\u001b[6G\u001b[?25l"] +[50.520199,"i"," "] +[50.522609,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m/ask \u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[50.522733,"o","\u001b[7G\u001b[?25l"] +[50.529082,"i","W"] +[50.531674,"o","\u001b[?2026h\u001b[24A\r\u001b[2K │ Collapse runs │ \u001b[38;2;255;205;117ma -- _ b\u001b[39m → \u001b[38;2;255;205;117ma-b\u001b[39m │ mixed space/dash/underscore run → one dash │ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K ├─────────────────────────────┼──────────────────────────────────────┼───────────────────────────────────────────────┤ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K │ Trim dashes │ \u001b[38;2;255;205;117m--Hello, World!--\u001b[39m → \u001b[38;2;255;205;117mhello-world\u001b[39m │ both ends │ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K ├─────────────────────────────┼──────────────────────────────────────┼───────────────────────────────────────────────┤ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K │ Lowercase + digits survive │ \u001b[38;2;255;205;117mTop 10 Songs (2024)\u001b[39m → │ digits are not punctuation │ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K │ │ \u001b[38;2;255;205;117mtop-10-songs-2024\u001b[39m │ │ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K ├─────────────────────────────┼──────────────────────────────────────┼───────────────────────────────────────────────┤ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K │ Degenerate input │ \u001b[38;2;255;205;117m!!! ___ ---\u001b[39m → \u001b[38;2;255;205;117m''\u001b[39m, \u001b[38;2;255;205;117m''\u001b[39m → \u001b[38;2;255;205;117m''\u001b[39m │ trim can empty the string without throwing │ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K ├─────────────────────────────┼──────────────────────────────────────┼───────────────────────────────────────────────┤ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K │ Idempotence │ \u001b[38;2;255;205;117mslugify(slugify(x)) === slugify(x)\u001b[39m │ \u001b[38;2;255;205;117m Ünïcödé__Slug!! \u001b[39m → \u001b[38;2;255;205;117municode-slug\u001b[39m, stable on │ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K │ │ │ re-run │ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K └─────────────────────────────┴──────────────────────────────────────┴───────────────────────────────────────────────┘ \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K One behavior worth flagging: characters with no ASCII decomposition (CJK, Cyrillic, emoji) are treated as punctuation \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K and become dashes — \u001b[38;2;255;205;117mslugify('日本語')\u001b[39m is \u001b[38;2;255;205;117m''\u001b[39m. That's the standard ASCII-slug tradeoff and matches the \"collapse \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K punctuation\" rule as written, but if you need transliteration instead, that's a different (larger) change. \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Bash\u001b[39m 1/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 2/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 3/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 4/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n"] +[50.531716,"o","\u001b[2K \u001b[38;2;135;145;165m· Read\u001b[39m 5/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Write\u001b[39m 6/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Edit\u001b[39m 7/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Write\u001b[39m 8/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Write\u001b[39m 9/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;2;135;145;165m· Bash\u001b[39m 10/10\u001b[38;2;135;145;165m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m/ask W\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[2A\u001b[8G\u001b[?25l"] +[50.537999,"i","h"] +[50.54013,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m/ask Wh\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[50.540151,"o","\u001b[9G\u001b[?25l"] +[50.547359,"i","a"] +[50.549617,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m/ask Wha\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[50.549728,"o","\u001b[10G\u001b[?25l"] +[50.556043,"i","t"] +[50.558077,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m/ask What\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[50.558095,"o","\u001b[11G\u001b[?25l"] +[50.565305,"i"," "] +[50.568905,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m/ask What \u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[12G"] +[50.568929,"o","\u001b[?25l"] +[50.574134,"i","c"] +[50.576043,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m/ask What c\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[50.576066,"o","\u001b[13G\u001b[?25l"] +[50.583416,"i","h"] +[50.586019,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m/ask What ch\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[14G"] +[50.586045,"o","\u001b[?25l"] +[50.592274,"i","a"] +[50.595457,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m/ask What cha\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[15G\u001b[?25l"] +[50.601677,"i","n"] +[50.603963,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m/ask What chan\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[50.604027,"o","\u001b[16G\u001b[?25l"] +[50.611307,"i","g"] +[50.613568,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m/ask What chang\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[17G\u001b[?25l"] +[50.619982,"i","e"] +[50.621982,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m/ask What change\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[18G\u001b[?25l"] +[50.62926,"i","d"] +[50.631253,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m/ask What changed\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[50.631286,"o","\u001b[19G\u001b[?25l"] +[50.638501,"i",","] +[50.640515,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m/ask What changed,\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[20G\u001b[?25l"] +[50.646721,"i"," "] +[50.648683,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m/ask What changed, \u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[21G"] +[50.64871,"o","\u001b[?25l"] +[50.656078,"i","w"] +[50.658691,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m/ask What changed, w\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[22G"] +[50.658715,"o","\u001b[?25l"] +[50.664922,"i","h"] +[50.668217,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m/ask What changed, wh\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[50.668246,"o","\u001b[23G\u001b[?25l"] +[50.674519,"i","a"] +[50.676804,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m/ask What changed, wha\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[24G\u001b[?25l"] +[50.68305,"i","t"] +[50.685995,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m/ask What changed, what\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[50.686022,"o","\u001b[25G\u001b[?25l"] +[50.692408,"i"," "] +[50.694316,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m/ask What changed, what \u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[26G\u001b[?25l"] +[50.701602,"i","w"] +[50.704515,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m/ask What changed, what w\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[27G\u001b[?25l"] +[50.710814,"i","a"] +[50.713642,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m/ask What changed, what wa\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[28G"] +[50.713687,"o","\u001b[?25l"] +[50.720034,"i","s"] +[50.722557,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m/ask What changed, what was\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[29G\u001b[?25l"] +[50.728862,"i"," "] +[50.731677,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m/ask What changed, what was \u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[30G\u001b[?25l"] +[50.738271,"i","v"] +[50.741302,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m/ask What changed, what was v\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[31G\u001b[?25l"] +[50.747643,"i","e"] +[50.750742,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m/ask What changed, what was ve\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[32G\u001b[?25l"] +[50.75704,"i","r"] +[50.760057,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m/ask What changed, what was ver\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[50.760099,"o","\u001b[33G\u001b[?25l"] +[50.766386,"i","i"] +[50.769166,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m/ask What changed, what was veri\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[34G"] +[50.769201,"o","\u001b[?25l"] +[50.77565,"i","f"] +[50.778443,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m/ask What changed, what was verif\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[35G\u001b[?25l"] +[50.784713,"i","i"] +[50.786825,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m/ask What changed, what was verifi\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[36G"] +[50.786856,"o","\u001b[?25l"] +[50.79414,"i","e"] +[50.796284,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m/ask What changed, what was verifie\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[37G"] +[50.796318,"o","\u001b[?25l"] +[50.803618,"i","d"] +[50.805749,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m/ask What changed, what was verified\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[38G"] +[50.805785,"o","\u001b[?25l"] +[50.813533,"i",","] +[50.816925,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m/ask What changed, what was verified,\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[39G\u001b[?25l"] +[50.822214,"i"," "] +[50.824456,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m/ask What changed, what was verified, \u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[40G\u001b[?25l"] +[50.830771,"i","a"] +[50.832918,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m/ask What changed, what was verified, a\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[41G\u001b[?25l"] +[50.840203,"i","n"] +[50.843737,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m/ask What changed, what was verified, an\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[42G"] +[50.843778,"o","\u001b[?25l"] +[50.849072,"i","d"] +[50.852041,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m/ask What changed, what was verified, and\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[43G\u001b[?25l"] +[50.858317,"i"," "] +[50.860767,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m/ask What changed, what was verified, and \u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[44G"] +[50.860805,"o","\u001b[?25l"] +[50.867089,"i","w"] +[50.8697,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m/ask What changed, what was verified, and w\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[50.869739,"o","\u001b[45G\u001b[?25l"] +[50.876063,"i","h"] +[50.878674,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m/ask What changed, what was verified, and wh\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[46G\u001b[?25l"] +[50.884937,"i","a"] +[50.888193,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m/ask What changed, what was verified, and wha\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[47G\u001b[?25l"] +[50.89373,"i","t"] +[50.895964,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m/ask What changed, what was verified, and what\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[48G\u001b[?25l"] +[50.903261,"i"," "] +[50.905529,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m/ask What changed, what was verified, and what \u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[50.905571,"o","\u001b[49G\u001b[?25l"] +[50.911826,"i","s"] +[50.91381,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m/ask What changed, what was verified, and what s\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[50G\u001b[?25l"] +[50.921115,"i","h"] +[50.924236,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m/ask What changed, what was verified, and what sh\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[51G\u001b[?25l"] +[50.930509,"i","o"] +[50.932988,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m/ask What changed, what was verified, and what sho\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[50.933035,"o","\u001b[52G\u001b[?25l"] +[50.939317,"i","u"] +[50.941742,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m/ask What changed, what was verified, and what shou\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[53G"] +[50.941773,"o","\u001b[?25l"] +[50.94803,"i","l"] +[50.949982,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m/ask What changed, what was verified, and what shoul\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[54G\u001b[?25l"] +[50.957261,"i","d"] +[50.960153,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m/ask What changed, what was verified, and what should\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[55G\u001b[?25l"] +[50.966421,"i"," "] +[50.968869,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m/ask What changed, what was verified, and what should \u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[56G"] +[50.968903,"o","\u001b[?25l"] +[50.975425,"i","I"] +[50.977772,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m/ask What changed, what was verified, and what should I\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[57G\u001b[?25l"] +[50.984039,"i"," "] +[50.986739,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m/ask What changed, what was verified, and what should I \u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[50.986771,"o","\u001b[58G\u001b[?25l"] +[50.993013,"i","r"] +[50.995783,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m/ask What changed, what was verified, and what should I r\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[50.995813,"o","\u001b[59G\u001b[?25l"] +[51.002134,"i","e"] +[51.004863,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m/ask What changed, what was verified, and what should I re\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[60G"] +[51.004909,"o","\u001b[?25l"] +[51.011173,"i","v"] +[51.01381,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m/ask What changed, what was verified, and what should I rev\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[51.01384,"o","\u001b[61G\u001b[?25l"] +[51.020176,"i","i"] +[51.022724,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m/ask What changed, what was verified, and what should I revi\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[62G"] +[51.022765,"o","\u001b[?25l"] +[51.029053,"i","e"] +[51.032046,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m/ask What changed, what was verified, and what should I revie\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[63G"] +[51.032074,"o","\u001b[?25l"] +[51.038532,"i","w"] +[51.041018,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m/ask What changed, what was verified, and what should I review\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[64G\u001b[?25l"] +[51.047384,"i","?"] +[51.050051,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m/ask What changed, what was verified, and what should I review?\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[65G\u001b[?25l"] +[51.056412,"i","\r"] +[51.058714,"o","\u001b[?2026h\r\u001b[2K\u001b[38;2;119;166;255m›\u001b[39m\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[2G\u001b[?25l"] +[51.060034,"o","\u001b[?25l"] +[51.130356,"o","\u001b[?2026h\u001b[26A\r\u001b[2K\u001b[0m\u001b]8;;\u0007 \u001b[1m\u001b[38;2;151;190;255mactivity · analyses\u001b[39m\u001b[22m \u001b[38;2;135;145;165m›\u001b[39m \u001b[38;2;119;166;255m/ask · frozen question\u001b[39m \u001b[38;2;135;145;165m· running · 1/1\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007\u001b[38;2;135;145;165m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────\u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 \u001b[38;2;135;145;165msource run run-b413c4d4…751b83 · Product engineer · claude-code · claude-code/opus · Local CLI Bridge\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 source: 9605fa8a577610b…2e34cc2f · frozen \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 analyst: configured analyst · running \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 route: runner claude-code · configured model sonnet \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 \u001b[38;2;119;166;255m── question\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 What changed, what was verified, and what should I review? \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 \u001b[38;2;119;166;255m── findings\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 No findings were returned. \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 \u001b[38;2;119;166;255m── model use\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 0 model calls reported. \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 \u001b[38;2;119;166;255m── run receipt\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 source complete: yes \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 findings: 0 \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 runner: claude-code \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 agent-eval: 0.145.1 \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 check source-frozen: passed · digest 9605fa8a577610b0dd46c7e937738dbb717a6951c2bf032dc17df4ab2e34cc2f \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 check source-completeness: passed \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 \u001b[38;2;119;166;255m── activity record\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 time: 2026-08-12T12:40:51.353Z \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 run: run-b413c4d4-820d-453f-93d2-ff52f9751b83 \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 "] +[51.130429,"o"," \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007\u001b[38;2;135;145;165m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────\u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 \u001b[38;2;135;145;165mPgUp/PgDn detail · ←/esc close · tab filter: analyses\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[?25l"] +[61.44912,"o","\u001b[?25l"] +[121.580207,"o","\u001b[?25l"] +[121.580288,"o","\u001b[?25l"] +[121.605905,"o","\u001b[?2026h\u001b[29A\r\u001b[2K\u001b[0m\u001b]8;;\u0007 \u001b[1m\u001b[38;2;151;190;255mactivity · analyses\u001b[39m\u001b[22m \u001b[38;2;135;145;165m›\u001b[39m \u001b[38;2;119;166;255m/ask · frozen question\u001b[39m \u001b[38;2;135;145;165m· complete · 1/1\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007\u001b[38;2;135;145;165m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────\u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 \u001b[38;2;135;145;165msource run run-b413c4d4…751b83 · Product engineer · claude-code · claude-code/opus · Local CLI Bridge\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 source: 9605fa8a577610b…2e34cc2f · frozen \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 analyst: configured analyst · completed \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 route: runner claude-code · configured model sonnet \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 \u001b[38;2;119;166;255m── question\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 What changed, what was verified, and what should I review? \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 \u001b[38;2;119;166;255m── findings\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 1. [1] The run implemented a new src/slugify.js module performing NFKD normalization, diacritic stripping, lowercasing,\u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 punctuation-to-dash collapsing, and dash trimming. · info · confidence 0.95 \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 \u001b[38;2;135;145;165m↳ [1] `src/slugify.js` — five ordered steps, one per requirement: · event event-101e0099f…319073f3\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 2. [2] Verification consisted of running the test suite, which reported 10 passed and 0 failed in 47.55 ms. · info · \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 confidence 0.95 \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 \u001b[38;2;135;145;165m↳ [2] `npm test` → **10 passed, 0 failed** (duration 47.55 ms). · event event-101e0099f…319073f3\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 3. [3] The operator should review the behavior for non-ASCII scripts (e.g. CJK, Cyrillic, emoji), which have no ASCII \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 decomposition and are treated as punctuation, collapsing to an empty string rather than being transliterated. · medium \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 · confidence 0.9 \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 \u001b[38;2;135;145;165m↳ [3] characters with no ASCII decomposition (CJK, Cyrillic, emoji) are treated as punctuation and become dashes · \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 \u001b[38;2;135;145;165mevent event-101e0099f…319073f3\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 \u001b[38;2;119;166;255m── next\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 next: ask a narrower question, or use /compare against another frozen run \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 \u001b[38;2;"] +[121.606015,"o","119;166;255m── model use\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 5 calls · tokens 10 in / 3122 out · cost ~$0.3246 · model 49982ms \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 model call #1 claude-code/sonnet · tokens 2 in / 246 out · cost ~$0.075312 · latency 5721ms \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 model call #2 claude-code/sonnet · tokens 2 in / 271 out · cost ~$0.055605 · latency 8056ms \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 model call #3 claude-code/sonnet · tokens 2 in / 348 out · cost ~$0.05676 · latency 8051ms \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 model call #4 claude-code/sonnet · tokens 2 in / 609 out · cost ~$0.060675 · latency 10484ms \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007\u001b[38;2;135;145;165m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────\u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 \u001b[38;2;135;145;165mPgUp/PgDn detail · ←/esc close · page 1/2 · tab filter: analyses\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[121.606066,"o","\u001b[?25l"] +[121.616066,"o","\u001b[0m"] diff --git a/artifacts/demo/braid-live.gif b/artifacts/demo/braid-live.gif new file mode 100644 index 0000000..f6e840c Binary files /dev/null and b/artifacts/demo/braid-live.gif differ diff --git a/artifacts/demo/braid-live.json b/artifacts/demo/braid-live.json new file mode 100644 index 0000000..121b3a6 --- /dev/null +++ b/artifacts/demo/braid-live.json @@ -0,0 +1,147 @@ +{ + "schemaVersion": 2, + "status": "passed", + "capturedAt": "2026-08-12T12:42:08.886Z", + "source": { + "commit": "8cce4c6e8aade3b1fc51f558d65a823ac12a5e1a", + "packageVersion": "0.1.1", + "tarball": "tangle-network-braid-0.1.1.tgz", + "tarballSha256": "9bbd7b5b9f78b3d202dbb09d4375970edfd4965ffc5ad94b04bd7fa6ce01c4c7", + "packageProofSha256": "d6b3c565d628046ff29a46508ee7f0209931e08fe08711d03e58ea3229880989" + }, + "route": { + "connection": "Local CLI Bridge", + "endpoint": "http://127.0.0.1:3345", + "runner": "claude-code", + "runnerVersion": "2.1.228 (Claude Code)", + "provider": null, + "model": "opus" + }, + "profile": { + "name": "Product engineer", + "reasoningEffort": "high", + "maxOutputTokens": 32768 + }, + "analysisProfile": { + "name": "Trace analyst", + "runner": "claude-code", + "model": "sonnet", + "reasoningEffort": "high", + "maxOutputTokens": 16384 + }, + "task": { + "prompt": "Finish the slugify function. Normalize Unicode accents, treat underscores as punctuation, collapse punctuation and spaces to one dash, trim dashes, and lowercase. Add edge-case tests, run them, and summarize the proof.", + "runId": "run-b413c4d4-820d-453f-93d2-ff52f9751b83", + "providerSessionId": "session-braid-run-b413c4d4-820d-453f-93d2-ff52f9751b83", + "inputTokens": 15, + "outputTokens": 2944, + "llmCalls": null, + "llmLatencyMs": null, + "normalizedToolCalls": 10, + "normalizedToolResults": 0, + "toolProjection": { + "status": "blocked-upstream", + "issue": "https://github.com/tangle-network/agent-runtime/issues/762" + }, + "workspaceProof": { + "testOutput": "> test\n> node --test --test-reporter=spec\n\n(node:3864502) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.\n(Use `node --trace-warnings ...` to show where the warning was created)\n\u001b[32m✔ joins words with a dash \u001b[90m(1.009307ms)\u001b[39m\u001b[39m\n\u001b[32m✔ strips Unicode accents \u001b[90m(0.196197ms)\u001b[39m\u001b[39m\n\u001b[32m✔ normalizes precomposed and decomposed forms alike \u001b[90m(0.075729ms)\u001b[39m\u001b[39m\n\u001b[32m✔ treats underscores as punctuation \u001b[90m(0.654959ms)\u001b[39m\u001b[39m\n\u001b[32m✔ collapses runs of punctuation and spaces to one dash \u001b[90m(0.061551ms)\u001b[39m\u001b[39m\n\u001b[32m✔ trims leading and trailing dashes \u001b[90m(0.080679ms)\u001b[39m\u001b[39m\n\u001b[32m✔ keeps digits \u001b[90m(0.046902ms)\u001b[39m\u001b[39m\n\u001b[32m✔ returns an empty string for punctuation-only input \u001b[90m(0.037904ms)\u001b[39m\u001b[39m\n\u001b[32m✔ returns an empty string for empty input \u001b[90m(0.053705ms)\u001b[39m\u001b[39m\n\u001b[32m✔ is idempotent \u001b[90m(0.120778ms)\u001b[39m\u001b[39m\n\u001b[34mℹ tests 10\u001b[39m\n\u001b[34mℹ suites 0\u001b[39m\n\u001b[34mℹ pass 10\u001b[39m\n\u001b[34mℹ fail 0\u001b[39m\n\u001b[34mℹ cancelled 0\u001b[39m\n\u001b[34mℹ skipped 0\u001b[39m\n\u001b[34mℹ todo 0\u001b[39m\n\u001b[34mℹ duration_ms 40.398807\u001b[39m\n(node:3864496) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.\n(Use `node --trace-warnings ...` to show where the warning was created)", + "samples": [ + "cafe-deja-vu", + "ship-it" + ], + "sourceSha256": "04c489c2a24d8fe578abc57d22998978ca01670f062b7c5c9afb952a7fd4d6e9", + "testSha256": "42068ff1ccab7c5ddfb03027ff2f55ddcc931ea16364b620ea87867ec6545c37" + } + }, + "analysis": { + "question": "What changed, what was verified, and what should I review?", + "runtimeManager": "bundled uv", + "pythonVersion": "3.12", + "agentEvalRpcVersion": "0.145.1", + "id": "analysis-017ec9050a1b5f534bea4fd2d31e3bd7624fd6e9", + "status": "completed", + "findings": 3, + "supportedFindings": 3, + "citationSupport": "passed", + "configuredModel": "sonnet", + "observedModels": [ + "claude-code/sonnet" + ], + "modelCalls": 5, + "inputTokens": 10, + "outputTokens": 3122, + "costUsd": null, + "estimatedCostUsd": 0.324612, + "costStatus": "estimated", + "modelCallEvidence": [ + { + "sequence": 1, + "model": "claude-code/sonnet", + "inputTokens": 2, + "outputTokens": 246, + "tokensKnown": true, + "costUsd": 0.075312, + "costStatus": "estimated", + "latencyMs": 5721, + "outcome": "succeeded" + }, + { + "sequence": 2, + "model": "claude-code/sonnet", + "inputTokens": 2, + "outputTokens": 271, + "tokensKnown": true, + "costUsd": 0.055604999999999995, + "costStatus": "estimated", + "latencyMs": 8056, + "outcome": "succeeded" + }, + { + "sequence": 3, + "model": "claude-code/sonnet", + "inputTokens": 2, + "outputTokens": 348, + "tokensKnown": true, + "costUsd": 0.05676, + "costStatus": "estimated", + "latencyMs": 8051, + "outcome": "succeeded" + }, + { + "sequence": 4, + "model": "claude-code/sonnet", + "inputTokens": 2, + "outputTokens": 609, + "tokensKnown": true, + "costUsd": 0.06067499999999999, + "costStatus": "estimated", + "latencyMs": 10484, + "outcome": "succeeded" + }, + { + "sequence": 5, + "model": "claude-code/sonnet", + "inputTokens": 2, + "outputTokens": 1648, + "tokensKnown": true, + "costUsd": 0.07626, + "costStatus": "estimated", + "latencyMs": 17670, + "outcome": "succeeded" + } + ], + "modelLatencyMs": 49982, + "wallTimeMs": 59982 + }, + "terminal": { + "columns": 120, + "rows": 30, + "finalRevision": 36 + }, + "artifacts": { + "braid-live.cast": "40eedba7245c0fb5728d7500a6a8080a083def613a89cbbf8934bdbabba135b3", + "braid-live.gif": "038ca8e599e09856d56706a6063cb5ea3319bb9d1d64edcb8bfb67bb1003eed8", + "braid-live.png": "f92520e708be76f3e9aa6a9f6199e16881ef89ef973fcea2cb7b015fcd1ada89", + "braid-live.txt": "602d468c667c6d12616842761caba2105ed023ee2359f8f46c98500152950d3b" + } +} diff --git a/artifacts/demo/braid-live.png b/artifacts/demo/braid-live.png new file mode 100644 index 0000000..a775b7e Binary files /dev/null and b/artifacts/demo/braid-live.png differ diff --git a/artifacts/demo/braid-live.txt b/artifacts/demo/braid-live.txt new file mode 100644 index 0000000..1e2735f --- /dev/null +++ b/artifacts/demo/braid-live.txt @@ -0,0 +1,30 @@ + activity · analyses › /ask · frozen question · complete · 1/1 +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── + source run run-b413c4d4…751b83 · Product engineer · claude-code · claude-code/opus · Local CLI Bridge + source: 9605fa8a577610b…2e34cc2f · frozen + analyst: configured analyst · completed + route: runner claude-code · configured model sonnet + ── question + What changed, what was verified, and what should I review? + ── findings + 1. [1] The run implemented a new src/slugify.js module performing NFKD normalization, diacritic stripping, lowercasing, + punctuation-to-dash collapsing, and dash trimming. · info · confidence 0.95 + ↳ [1] `src/slugify.js` — five ordered steps, one per requirement: · event event-101e0099f…319073f3 + 2. [2] Verification consisted of running the test suite, which reported 10 passed and 0 failed in 47.55 ms. · info · + confidence 0.95 + ↳ [2] `npm test` → **10 passed, 0 failed** (duration 47.55 ms). · event event-101e0099f…319073f3 + 3. [3] The operator should review the behavior for non-ASCII scripts (e.g. CJK, Cyrillic, emoji), which have no ASCII + decomposition and are treated as punctuation, collapsing to an empty string rather than being transliterated. · medium + · confidence 0.9 + ↳ [3] characters with no ASCII decomposition (CJK, Cyrillic, emoji) are treated as punctuation and become dashes · + event event-101e0099f…319073f3 + ── next + next: ask a narrower question, or use /compare against another frozen run + ── model use + 5 calls · tokens 10 in / 3122 out · cost ~$0.3246 · model 49982ms + model call #1 claude-code/sonnet · tokens 2 in / 246 out · cost ~$0.075312 · latency 5721ms + model call #2 claude-code/sonnet · tokens 2 in / 271 out · cost ~$0.055605 · latency 8056ms + model call #3 claude-code/sonnet · tokens 2 in / 348 out · cost ~$0.05676 · latency 8051ms + model call #4 claude-code/sonnet · tokens 2 in / 609 out · cost ~$0.060675 · latency 10484ms +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── + PgUp/PgDn detail · ←/esc close · page 1/2 · tab filter: analyses diff --git a/artifacts/verification/live/tangle-sandbox-braid-execution-stress-production-20260812.json b/artifacts/verification/live/tangle-sandbox-braid-execution-stress-production-20260812.json new file mode 100644 index 0000000..a0eb6a7 --- /dev/null +++ b/artifacts/verification/live/tangle-sandbox-braid-execution-stress-production-20260812.json @@ -0,0 +1,7166 @@ +{ + "schemaVersion": "braid.tangle-sandbox-execution-soak.v1", + "status": "passed", + "proofId": "1786535411687-5262edae", + "startedAt": "2026-08-12T11:50:11.687Z", + "completedAt": "2026-08-12T11:53:30.241Z", + "requestedRuns": 20, + "attemptedRuns": 20, + "concurrency": 4, + "failures": [], + "config": { + "endpoint": { + "scheme": "https", + "host": "sandbox.tangle.tools" + }, + "connectionId": "connection-live-tangle-sandbox", + "connectionKind": "tangle-sandbox", + "credentialConfigured": true, + "model": "tangle-router/glm-5.2", + "runner": "opencode", + "provider": "tangle-router" + }, + "account": { + "stable": true, + "identityDigest": "46505ff5225e7d5c46dcafdfa3142412c4746e7b317b166f6bafa7f443923c46" + }, + "usage": { + "before": { + "computeMinutes": 762, + "gpuSeconds": 0, + "gpuCostUsd": 0, + "gpuProviderCostUsd": 0, + "activeSandboxes": 4, + "totalSandboxes": 9212, + "periodStart": "2026-08-12T00:00:00.000Z", + "periodEnd": "2026-08-13T00:00:00.000Z" + }, + "after": { + "computeMinutes": 770, + "gpuSeconds": 0, + "gpuCostUsd": 0, + "gpuProviderCostUsd": 0, + "activeSandboxes": 4, + "totalSandboxes": 9232, + "periodStart": "2026-08-12T00:00:00.000Z", + "periodEnd": "2026-08-13T00:00:00.000Z" + }, + "delta": { + "activeSandboxes": 0, + "totalSandboxes": 20, + "computeMinutes": 8, + "gpuSeconds": 0, + "gpuCostUsd": 0, + "unknownFields": [] + } + }, + "latencyMs": { + "n": 20, + "min": 24671.073481, + "median": 27981.293942999997, + "p90": 34737.55657, + "max": 44578.092463999994 + }, + "attempts": [ + { + "index": 0, + "status": "passed", + "elapsedMs": 30823.970297999997, + "runId": "run-d4c057c8-e073-4fdf-b147-8c1623978403", + "operationId": "op-live-required-send-4c87fa66-c9f9-4581-bcf0-a5f893d22fbb", + "model": "tangle-router/glm-5.2", + "runner": "opencode", + "message": "BRAID_CLOUD_EXECUTION_1786535411687-5262EDAE_0_OK", + "environmentId": "sandbox-067b7ade5526", + "automaticCleanup": true, + "usage": { + "tokens": { + "status": "observed", + "input": 478, + "output": 23 + }, + "cost": { + "status": "reported", + "usd": 0 + } + }, + "observation": { + "localEnvironmentId": "environment-22223edc972f7df867348599b109ea4a", + "providerEnvironmentId": "sandbox-067b7ade5526", + "environmentRecord": { + "id": "environment-22223edc972f7df867348599b109ea4a", + "connectionId": "connection-live-tangle-sandbox", + "kind": "sandbox", + "provider": "tangle-sandbox", + "providerEnvironmentId": "sandbox-067b7ade5526", + "lifecycle": "destroyed", + "lifecycleMode": "ephemeral", + "cleanup": "delete-after-turn", + "continuity": "unavailable", + "location": "remote", + "runtimeEndpointHost": "sandbox-067b7ade5526.tangle.sh", + "storagePersistence": "persistent-home", + "accountUsage": { + "completeness": "provider-reported-possibly-defaulted", + "computeMinutes": 762, + "gpuSeconds": 0, + "gpuCostUsd": 0, + "activeSandboxes": 5, + "totalSandboxes": 9212, + "maximumConcurrentSandboxes": 25, + "maximumCpuCores": 8, + "maximumRamGB": 16, + "maximumStorageGB": 256, + "usagePeriodStart": "2026-08-12T00:00:00.000Z", + "usagePeriodEnd": "2026-08-13T00:00:00.000Z", + "sampledAt": "2026-08-12T11:50:18.753Z" + }, + "unavailableTelemetry": [ + "disk-usage:not-exposed-by-provider", + "effective-resources:not-exposed-by-provider", + "machine-id:not-exposed-by-provider", + "machine-ip:not-exposed-by-provider", + "per-sandbox-cpu-ram-storage-cost:not-exposed-by-provider", + "runtime-cpu-memory-usage:request-failed-or-timed-out", + "verified-placement:not-exposed-by-client", + "verified-region:not-exposed-by-provider" + ], + "createdAt": "2026-08-12T11:50:18.198Z", + "updatedAt": "2026-08-12T11:50:45.160Z" + }, + "run": { + "inputTokens": { + "status": "observed", + "value": 478 + }, + "outputTokens": { + "status": "observed", + "value": 23 + }, + "reasoningTokens": { + "status": "missing" + }, + "cacheReadTokens": { + "status": "missing" + }, + "cacheWriteTokens": { + "status": "missing" + }, + "costUsd": { + "status": "observed", + "value": 0 + }, + "costStatus": { + "status": "observed", + "value": "reported" + }, + "usage": { + "status": "missing" + }, + "cost": { + "status": "missing" + }, + "tokensKnown": { + "status": "missing" + }, + "usdKnown": { + "status": "missing" + }, + "usageCompleteness": { + "status": "missing" + }, + "latencyMs": { + "status": "missing" + }, + "durationMs": { + "status": "missing" + }, + "model": { + "status": "observed", + "value": "tangle-router/glm-5.2" + }, + "provider": { + "status": "observed", + "value": "tangle-sandbox" + }, + "runner": { + "status": "observed", + "value": "opencode" + }, + "operationId": { + "status": "observed", + "value": "op-live-required-send-4c87fa66-c9f9-4581-bcf0-a5f893d22fbb" + }, + "profileSnapshotId": { + "status": "missing" + }, + "connectionId": { + "status": "observed", + "value": "connection-live-tangle-sandbox" + }, + "providerSessionId": { + "status": "missing" + }, + "environmentId": { + "status": "observed", + "value": "environment-22223edc972f7df867348599b109ea4a" + }, + "replayCursor": { + "status": "missing" + }, + "lastCursor": { + "status": "missing" + }, + "lastProviderSequence": { + "status": "missing" + }, + "eventCount": { + "status": "missing" + }, + "contentBytes": { + "status": "observed", + "value": 456 + }, + "contentTruncated": { + "status": "missing" + }, + "missingSequence": { + "status": "missing" + }, + "terminalReason": { + "status": "missing" + }, + "materializationDigest": { + "status": "observed", + "value": "77a667b692bc0a2deec98773920cce4f19f677304305648813bb7dbc8cbef934" + }, + "complete": { + "status": "missing" + }, + "status": { + "status": "observed", + "value": "completed" + }, + "error": { + "status": "missing" + }, + "startedAt": { + "status": "missing" + }, + "updatedAt": { + "status": "missing" + }, + "terminalAt": { + "status": "missing" + }, + "placement": { + "status": "missing" + }, + "resourceSample": { + "status": "missing" + }, + "requestedResources": { + "status": "missing" + }, + "machineId": { + "status": "missing" + }, + "runtimeEndpointHost": { + "status": "missing" + }, + "requestedRegion": { + "status": "missing" + }, + "verifiedRegion": { + "status": "missing" + }, + "storagePersistence": { + "status": "missing" + }, + "gpu": { + "status": "missing" + } + }, + "environment": { + "kind": { + "status": "observed", + "value": "sandbox" + }, + "providerEnvironmentId": { + "status": "observed", + "value": "sandbox-067b7ade5526" + }, + "provider": { + "status": "observed", + "value": "tangle-sandbox" + }, + "name": { + "status": "missing" + }, + "lifecycle": { + "status": "observed", + "value": "destroyed" + }, + "lifecycleMode": { + "status": "observed", + "value": "ephemeral" + }, + "cleanup": { + "status": "observed", + "value": "delete-after-turn" + }, + "continuity": { + "status": "observed", + "value": "unavailable" + }, + "location": { + "status": "observed", + "value": "remote" + }, + "region": { + "status": "missing" + }, + "runtimeEndpointHost": { + "status": "observed", + "value": "sandbox-067b7ade5526.tangle.sh" + }, + "machineId": { + "status": "missing" + }, + "requestedRegion": { + "status": "missing" + }, + "verifiedRegion": { + "status": "missing" + }, + "storagePersistence": { + "status": "observed", + "value": "persistent-home" + }, + "requestedResources": { + "status": "missing" + }, + "resourceSample": { + "status": "missing" + }, + "gpu": { + "status": "missing" + }, + "accountUsage": { + "status": "observed", + "value": { + "completeness": "provider-reported-possibly-defaulted", + "computeMinutes": 762, + "gpuSeconds": 0, + "gpuCostUsd": 0, + "activeSandboxes": 5, + "totalSandboxes": 9212, + "maximumConcurrentSandboxes": 25, + "maximumCpuCores": 8, + "maximumRamGB": 16, + "maximumStorageGB": 256, + "usagePeriodStart": "2026-08-12T00:00:00.000Z", + "usagePeriodEnd": "2026-08-13T00:00:00.000Z", + "sampledAt": "2026-08-12T11:50:18.753Z" + } + }, + "unavailableTelemetry": { + "status": "observed", + "value": [ + "disk-usage:not-exposed-by-provider", + "effective-resources:not-exposed-by-provider", + "machine-id:not-exposed-by-provider", + "machine-ip:not-exposed-by-provider", + "per-sandbox-cpu-ram-storage-cost:not-exposed-by-provider", + "runtime-cpu-memory-usage:request-failed-or-timed-out", + "verified-placement:not-exposed-by-client", + "verified-region:not-exposed-by-provider" + ] + }, + "placement": { + "status": "missing" + }, + "repository": { + "status": "missing" + }, + "gitRef": { + "status": "missing" + }, + "workingDirectory": { + "status": "missing" + }, + "image": { + "status": "missing" + }, + "createdAt": { + "status": "observed", + "value": "2026-08-12T11:50:18.198Z" + }, + "startedAt": { + "status": "missing" + }, + "lastActivityAt": { + "status": "missing" + }, + "expiresAt": { + "status": "missing" + }, + "updatedAt": { + "status": "observed", + "value": "2026-08-12T11:50:45.160Z" + } + } + }, + "cleanup": { + "status": "passed", + "matchedCount": 0, + "remainingCount": 0 + } + }, + { + "index": 1, + "status": "passed", + "elapsedMs": 44578.092463999994, + "runId": "run-eb6ab231-6f54-4a46-9b38-bb7e4b5ab845", + "operationId": "op-live-required-send-9ade344b-dddf-42f6-bc1e-05af11358f56", + "model": "tangle-router/glm-5.2", + "runner": "opencode", + "message": "BRAID_CLOUD_EXECUTION_1786535411687-5262EDAE_1_OK", + "environmentId": "sandbox-83432013d8cb", + "automaticCleanup": true, + "usage": { + "tokens": { + "status": "observed", + "input": 478, + "output": 23 + }, + "cost": { + "status": "reported", + "usd": 0 + } + }, + "observation": { + "localEnvironmentId": "environment-c3f419d48bb4393dfa5ac4fbb96ffa9f", + "providerEnvironmentId": "sandbox-83432013d8cb", + "environmentRecord": { + "id": "environment-c3f419d48bb4393dfa5ac4fbb96ffa9f", + "connectionId": "connection-live-tangle-sandbox", + "kind": "sandbox", + "provider": "tangle-sandbox", + "providerEnvironmentId": "sandbox-83432013d8cb", + "lifecycle": "destroyed", + "lifecycleMode": "ephemeral", + "cleanup": "delete-after-turn", + "continuity": "unavailable", + "location": "remote", + "runtimeEndpointHost": "sandbox-83432013d8cb.tangle.sh", + "storagePersistence": "persistent-home", + "accountUsage": { + "completeness": "provider-reported-possibly-defaulted", + "computeMinutes": 763, + "gpuSeconds": 0, + "gpuCostUsd": 0, + "activeSandboxes": 4, + "totalSandboxes": 9213, + "maximumConcurrentSandboxes": 25, + "maximumCpuCores": 8, + "maximumRamGB": 16, + "maximumStorageGB": 256, + "usagePeriodStart": "2026-08-12T00:00:00.000Z", + "usagePeriodEnd": "2026-08-13T00:00:00.000Z", + "sampledAt": "2026-08-12T11:50:50.457Z" + }, + "unavailableTelemetry": [ + "disk-usage:not-exposed-by-provider", + "effective-resources:not-exposed-by-provider", + "machine-id:not-exposed-by-provider", + "machine-ip:not-exposed-by-provider", + "per-sandbox-cpu-ram-storage-cost:not-exposed-by-provider", + "runtime-cpu-memory-usage:request-failed-or-timed-out", + "verified-placement:not-exposed-by-client", + "verified-region:not-exposed-by-provider" + ], + "createdAt": "2026-08-12T11:50:52.901Z", + "updatedAt": "2026-08-12T11:51:30.092Z" + }, + "run": { + "inputTokens": { + "status": "observed", + "value": 478 + }, + "outputTokens": { + "status": "observed", + "value": 23 + }, + "reasoningTokens": { + "status": "missing" + }, + "cacheReadTokens": { + "status": "missing" + }, + "cacheWriteTokens": { + "status": "missing" + }, + "costUsd": { + "status": "observed", + "value": 0 + }, + "costStatus": { + "status": "observed", + "value": "reported" + }, + "usage": { + "status": "missing" + }, + "cost": { + "status": "missing" + }, + "tokensKnown": { + "status": "missing" + }, + "usdKnown": { + "status": "missing" + }, + "usageCompleteness": { + "status": "missing" + }, + "latencyMs": { + "status": "missing" + }, + "durationMs": { + "status": "missing" + }, + "model": { + "status": "observed", + "value": "tangle-router/glm-5.2" + }, + "provider": { + "status": "observed", + "value": "tangle-sandbox" + }, + "runner": { + "status": "observed", + "value": "opencode" + }, + "operationId": { + "status": "observed", + "value": "op-live-required-send-9ade344b-dddf-42f6-bc1e-05af11358f56" + }, + "profileSnapshotId": { + "status": "missing" + }, + "connectionId": { + "status": "observed", + "value": "connection-live-tangle-sandbox" + }, + "providerSessionId": { + "status": "missing" + }, + "environmentId": { + "status": "observed", + "value": "environment-c3f419d48bb4393dfa5ac4fbb96ffa9f" + }, + "replayCursor": { + "status": "missing" + }, + "lastCursor": { + "status": "missing" + }, + "lastProviderSequence": { + "status": "missing" + }, + "eventCount": { + "status": "missing" + }, + "contentBytes": { + "status": "observed", + "value": 456 + }, + "contentTruncated": { + "status": "missing" + }, + "missingSequence": { + "status": "missing" + }, + "terminalReason": { + "status": "missing" + }, + "materializationDigest": { + "status": "observed", + "value": "df1396568264769d22bea43f85c0385f938fa1c2de78dee135d40449161d74a2" + }, + "complete": { + "status": "missing" + }, + "status": { + "status": "observed", + "value": "completed" + }, + "error": { + "status": "missing" + }, + "startedAt": { + "status": "missing" + }, + "updatedAt": { + "status": "missing" + }, + "terminalAt": { + "status": "missing" + }, + "placement": { + "status": "missing" + }, + "resourceSample": { + "status": "missing" + }, + "requestedResources": { + "status": "missing" + }, + "machineId": { + "status": "missing" + }, + "runtimeEndpointHost": { + "status": "missing" + }, + "requestedRegion": { + "status": "missing" + }, + "verifiedRegion": { + "status": "missing" + }, + "storagePersistence": { + "status": "missing" + }, + "gpu": { + "status": "missing" + } + }, + "environment": { + "kind": { + "status": "observed", + "value": "sandbox" + }, + "providerEnvironmentId": { + "status": "observed", + "value": "sandbox-83432013d8cb" + }, + "provider": { + "status": "observed", + "value": "tangle-sandbox" + }, + "name": { + "status": "missing" + }, + "lifecycle": { + "status": "observed", + "value": "destroyed" + }, + "lifecycleMode": { + "status": "observed", + "value": "ephemeral" + }, + "cleanup": { + "status": "observed", + "value": "delete-after-turn" + }, + "continuity": { + "status": "observed", + "value": "unavailable" + }, + "location": { + "status": "observed", + "value": "remote" + }, + "region": { + "status": "missing" + }, + "runtimeEndpointHost": { + "status": "observed", + "value": "sandbox-83432013d8cb.tangle.sh" + }, + "machineId": { + "status": "missing" + }, + "requestedRegion": { + "status": "missing" + }, + "verifiedRegion": { + "status": "missing" + }, + "storagePersistence": { + "status": "observed", + "value": "persistent-home" + }, + "requestedResources": { + "status": "missing" + }, + "resourceSample": { + "status": "missing" + }, + "gpu": { + "status": "missing" + }, + "accountUsage": { + "status": "observed", + "value": { + "completeness": "provider-reported-possibly-defaulted", + "computeMinutes": 763, + "gpuSeconds": 0, + "gpuCostUsd": 0, + "activeSandboxes": 4, + "totalSandboxes": 9213, + "maximumConcurrentSandboxes": 25, + "maximumCpuCores": 8, + "maximumRamGB": 16, + "maximumStorageGB": 256, + "usagePeriodStart": "2026-08-12T00:00:00.000Z", + "usagePeriodEnd": "2026-08-13T00:00:00.000Z", + "sampledAt": "2026-08-12T11:50:50.457Z" + } + }, + "unavailableTelemetry": { + "status": "observed", + "value": [ + "disk-usage:not-exposed-by-provider", + "effective-resources:not-exposed-by-provider", + "machine-id:not-exposed-by-provider", + "machine-ip:not-exposed-by-provider", + "per-sandbox-cpu-ram-storage-cost:not-exposed-by-provider", + "runtime-cpu-memory-usage:request-failed-or-timed-out", + "verified-placement:not-exposed-by-client", + "verified-region:not-exposed-by-provider" + ] + }, + "placement": { + "status": "missing" + }, + "repository": { + "status": "missing" + }, + "gitRef": { + "status": "missing" + }, + "workingDirectory": { + "status": "missing" + }, + "image": { + "status": "missing" + }, + "createdAt": { + "status": "observed", + "value": "2026-08-12T11:50:52.901Z" + }, + "startedAt": { + "status": "missing" + }, + "lastActivityAt": { + "status": "missing" + }, + "expiresAt": { + "status": "missing" + }, + "updatedAt": { + "status": "observed", + "value": "2026-08-12T11:51:30.092Z" + } + } + }, + "cleanup": { + "status": "passed", + "matchedCount": 0, + "remainingCount": 0 + } + }, + { + "index": 2, + "status": "passed", + "elapsedMs": 34737.55657, + "runId": "run-1873e541-93fc-42d3-99c8-df3e2b93f445", + "operationId": "op-live-required-send-18b35250-b909-456c-a61f-179707d98632", + "model": "tangle-router/glm-5.2", + "runner": "opencode", + "message": "BRAID_CLOUD_EXECUTION_1786535411687-5262EDAE_2_OK", + "environmentId": "sandbox-093db5a608ea", + "automaticCleanup": true, + "usage": { + "tokens": { + "status": "observed", + "input": 478, + "output": 23 + }, + "cost": { + "status": "reported", + "usd": 0 + } + }, + "observation": { + "localEnvironmentId": "environment-b91375996402a16a2c846b7a07a3046e", + "providerEnvironmentId": "sandbox-093db5a608ea", + "environmentRecord": { + "id": "environment-b91375996402a16a2c846b7a07a3046e", + "connectionId": "connection-live-tangle-sandbox", + "kind": "sandbox", + "provider": "tangle-sandbox", + "providerEnvironmentId": "sandbox-093db5a608ea", + "lifecycle": "destroyed", + "lifecycleMode": "ephemeral", + "cleanup": "delete-after-turn", + "continuity": "unavailable", + "location": "remote", + "runtimeEndpointHost": "sandbox-093db5a608ea.tangle.sh", + "storagePersistence": "persistent-home", + "accountUsage": { + "completeness": "provider-reported-possibly-defaulted", + "computeMinutes": 763, + "gpuSeconds": 0, + "gpuCostUsd": 0, + "activeSandboxes": 4, + "totalSandboxes": 9213, + "maximumConcurrentSandboxes": 25, + "maximumCpuCores": 8, + "maximumRamGB": 16, + "maximumStorageGB": 256, + "usagePeriodStart": "2026-08-12T00:00:00.000Z", + "usagePeriodEnd": "2026-08-13T00:00:00.000Z", + "sampledAt": "2026-08-12T11:50:51.234Z" + }, + "unavailableTelemetry": [ + "disk-usage:not-exposed-by-provider", + "effective-resources:not-exposed-by-provider", + "machine-id:not-exposed-by-provider", + "machine-ip:not-exposed-by-provider", + "per-sandbox-cpu-ram-storage-cost:not-exposed-by-provider", + "runtime-cpu-memory-usage:request-failed-or-timed-out", + "verified-placement:not-exposed-by-client", + "verified-region:not-exposed-by-provider" + ], + "createdAt": "2026-08-12T11:50:52.740Z", + "updatedAt": "2026-08-12T11:51:20.167Z" + }, + "run": { + "inputTokens": { + "status": "observed", + "value": 478 + }, + "outputTokens": { + "status": "observed", + "value": 23 + }, + "reasoningTokens": { + "status": "missing" + }, + "cacheReadTokens": { + "status": "missing" + }, + "cacheWriteTokens": { + "status": "missing" + }, + "costUsd": { + "status": "observed", + "value": 0 + }, + "costStatus": { + "status": "observed", + "value": "reported" + }, + "usage": { + "status": "missing" + }, + "cost": { + "status": "missing" + }, + "tokensKnown": { + "status": "missing" + }, + "usdKnown": { + "status": "missing" + }, + "usageCompleteness": { + "status": "missing" + }, + "latencyMs": { + "status": "missing" + }, + "durationMs": { + "status": "missing" + }, + "model": { + "status": "observed", + "value": "tangle-router/glm-5.2" + }, + "provider": { + "status": "observed", + "value": "tangle-sandbox" + }, + "runner": { + "status": "observed", + "value": "opencode" + }, + "operationId": { + "status": "observed", + "value": "op-live-required-send-18b35250-b909-456c-a61f-179707d98632" + }, + "profileSnapshotId": { + "status": "missing" + }, + "connectionId": { + "status": "observed", + "value": "connection-live-tangle-sandbox" + }, + "providerSessionId": { + "status": "missing" + }, + "environmentId": { + "status": "observed", + "value": "environment-b91375996402a16a2c846b7a07a3046e" + }, + "replayCursor": { + "status": "missing" + }, + "lastCursor": { + "status": "missing" + }, + "lastProviderSequence": { + "status": "missing" + }, + "eventCount": { + "status": "missing" + }, + "contentBytes": { + "status": "observed", + "value": 456 + }, + "contentTruncated": { + "status": "missing" + }, + "missingSequence": { + "status": "missing" + }, + "terminalReason": { + "status": "missing" + }, + "materializationDigest": { + "status": "observed", + "value": "fea3f623f25c7f5c30244cc335a75491dd9b7ad56e58b199de5d4a76bed8ec82" + }, + "complete": { + "status": "missing" + }, + "status": { + "status": "observed", + "value": "completed" + }, + "error": { + "status": "missing" + }, + "startedAt": { + "status": "missing" + }, + "updatedAt": { + "status": "missing" + }, + "terminalAt": { + "status": "missing" + }, + "placement": { + "status": "missing" + }, + "resourceSample": { + "status": "missing" + }, + "requestedResources": { + "status": "missing" + }, + "machineId": { + "status": "missing" + }, + "runtimeEndpointHost": { + "status": "missing" + }, + "requestedRegion": { + "status": "missing" + }, + "verifiedRegion": { + "status": "missing" + }, + "storagePersistence": { + "status": "missing" + }, + "gpu": { + "status": "missing" + } + }, + "environment": { + "kind": { + "status": "observed", + "value": "sandbox" + }, + "providerEnvironmentId": { + "status": "observed", + "value": "sandbox-093db5a608ea" + }, + "provider": { + "status": "observed", + "value": "tangle-sandbox" + }, + "name": { + "status": "missing" + }, + "lifecycle": { + "status": "observed", + "value": "destroyed" + }, + "lifecycleMode": { + "status": "observed", + "value": "ephemeral" + }, + "cleanup": { + "status": "observed", + "value": "delete-after-turn" + }, + "continuity": { + "status": "observed", + "value": "unavailable" + }, + "location": { + "status": "observed", + "value": "remote" + }, + "region": { + "status": "missing" + }, + "runtimeEndpointHost": { + "status": "observed", + "value": "sandbox-093db5a608ea.tangle.sh" + }, + "machineId": { + "status": "missing" + }, + "requestedRegion": { + "status": "missing" + }, + "verifiedRegion": { + "status": "missing" + }, + "storagePersistence": { + "status": "observed", + "value": "persistent-home" + }, + "requestedResources": { + "status": "missing" + }, + "resourceSample": { + "status": "missing" + }, + "gpu": { + "status": "missing" + }, + "accountUsage": { + "status": "observed", + "value": { + "completeness": "provider-reported-possibly-defaulted", + "computeMinutes": 763, + "gpuSeconds": 0, + "gpuCostUsd": 0, + "activeSandboxes": 4, + "totalSandboxes": 9213, + "maximumConcurrentSandboxes": 25, + "maximumCpuCores": 8, + "maximumRamGB": 16, + "maximumStorageGB": 256, + "usagePeriodStart": "2026-08-12T00:00:00.000Z", + "usagePeriodEnd": "2026-08-13T00:00:00.000Z", + "sampledAt": "2026-08-12T11:50:51.234Z" + } + }, + "unavailableTelemetry": { + "status": "observed", + "value": [ + "disk-usage:not-exposed-by-provider", + "effective-resources:not-exposed-by-provider", + "machine-id:not-exposed-by-provider", + "machine-ip:not-exposed-by-provider", + "per-sandbox-cpu-ram-storage-cost:not-exposed-by-provider", + "runtime-cpu-memory-usage:request-failed-or-timed-out", + "verified-placement:not-exposed-by-client", + "verified-region:not-exposed-by-provider" + ] + }, + "placement": { + "status": "missing" + }, + "repository": { + "status": "missing" + }, + "gitRef": { + "status": "missing" + }, + "workingDirectory": { + "status": "missing" + }, + "image": { + "status": "missing" + }, + "createdAt": { + "status": "observed", + "value": "2026-08-12T11:50:52.740Z" + }, + "startedAt": { + "status": "missing" + }, + "lastActivityAt": { + "status": "missing" + }, + "expiresAt": { + "status": "missing" + }, + "updatedAt": { + "status": "observed", + "value": "2026-08-12T11:51:20.167Z" + } + } + }, + "cleanup": { + "status": "passed", + "matchedCount": 0, + "remainingCount": 0 + } + }, + { + "index": 3, + "status": "passed", + "elapsedMs": 33442.34804699999, + "runId": "run-def240c5-1fdd-4ff8-ad9a-d02d3fb742cf", + "operationId": "op-live-required-send-ae21ff1c-1cb2-4d4f-9dbb-1f9b2ec1a21d", + "model": "tangle-router/glm-5.2", + "runner": "opencode", + "message": "BRAID_CLOUD_EXECUTION_1786535411687-5262EDAE_3_OK", + "environmentId": "sandbox-bb6ca2b585fb", + "automaticCleanup": true, + "usage": { + "tokens": { + "status": "observed", + "input": 478, + "output": 23 + }, + "cost": { + "status": "reported", + "usd": 0 + } + }, + "observation": { + "localEnvironmentId": "environment-e14740cbc19d6e6ead4f065a7039ca0d", + "providerEnvironmentId": "sandbox-bb6ca2b585fb", + "environmentRecord": { + "id": "environment-e14740cbc19d6e6ead4f065a7039ca0d", + "connectionId": "connection-live-tangle-sandbox", + "kind": "sandbox", + "provider": "tangle-sandbox", + "providerEnvironmentId": "sandbox-bb6ca2b585fb", + "lifecycle": "destroyed", + "lifecycleMode": "ephemeral", + "cleanup": "delete-after-turn", + "continuity": "unavailable", + "location": "remote", + "runtimeEndpointHost": "sandbox-bb6ca2b585fb.tangle.sh", + "storagePersistence": "persistent-home", + "accountUsage": { + "completeness": "provider-reported-possibly-defaulted", + "computeMinutes": 763, + "gpuSeconds": 0, + "gpuCostUsd": 0, + "activeSandboxes": 4, + "totalSandboxes": 9213, + "maximumConcurrentSandboxes": 25, + "maximumCpuCores": 8, + "maximumRamGB": 16, + "maximumStorageGB": 256, + "usagePeriodStart": "2026-08-12T00:00:00.000Z", + "usagePeriodEnd": "2026-08-13T00:00:00.000Z", + "sampledAt": "2026-08-12T11:50:51.218Z" + }, + "unavailableTelemetry": [ + "disk-usage:not-exposed-by-provider", + "effective-resources:not-exposed-by-provider", + "machine-id:not-exposed-by-provider", + "machine-ip:not-exposed-by-provider", + "per-sandbox-cpu-ram-storage-cost:not-exposed-by-provider", + "runtime-cpu-memory-usage:request-failed-or-timed-out", + "verified-placement:not-exposed-by-client", + "verified-region:not-exposed-by-provider" + ], + "createdAt": "2026-08-12T11:50:51.839Z", + "updatedAt": "2026-08-12T11:51:20.449Z" + }, + "run": { + "inputTokens": { + "status": "observed", + "value": 478 + }, + "outputTokens": { + "status": "observed", + "value": 23 + }, + "reasoningTokens": { + "status": "missing" + }, + "cacheReadTokens": { + "status": "missing" + }, + "cacheWriteTokens": { + "status": "missing" + }, + "costUsd": { + "status": "observed", + "value": 0 + }, + "costStatus": { + "status": "observed", + "value": "reported" + }, + "usage": { + "status": "missing" + }, + "cost": { + "status": "missing" + }, + "tokensKnown": { + "status": "missing" + }, + "usdKnown": { + "status": "missing" + }, + "usageCompleteness": { + "status": "missing" + }, + "latencyMs": { + "status": "missing" + }, + "durationMs": { + "status": "missing" + }, + "model": { + "status": "observed", + "value": "tangle-router/glm-5.2" + }, + "provider": { + "status": "observed", + "value": "tangle-sandbox" + }, + "runner": { + "status": "observed", + "value": "opencode" + }, + "operationId": { + "status": "observed", + "value": "op-live-required-send-ae21ff1c-1cb2-4d4f-9dbb-1f9b2ec1a21d" + }, + "profileSnapshotId": { + "status": "missing" + }, + "connectionId": { + "status": "observed", + "value": "connection-live-tangle-sandbox" + }, + "providerSessionId": { + "status": "missing" + }, + "environmentId": { + "status": "observed", + "value": "environment-e14740cbc19d6e6ead4f065a7039ca0d" + }, + "replayCursor": { + "status": "missing" + }, + "lastCursor": { + "status": "missing" + }, + "lastProviderSequence": { + "status": "missing" + }, + "eventCount": { + "status": "missing" + }, + "contentBytes": { + "status": "observed", + "value": 456 + }, + "contentTruncated": { + "status": "missing" + }, + "missingSequence": { + "status": "missing" + }, + "terminalReason": { + "status": "missing" + }, + "materializationDigest": { + "status": "observed", + "value": "7f99b5f1cb9c8c9a6dff1a440cb877cabf3da7e8b348aeec0b15de0401561502" + }, + "complete": { + "status": "missing" + }, + "status": { + "status": "observed", + "value": "completed" + }, + "error": { + "status": "missing" + }, + "startedAt": { + "status": "missing" + }, + "updatedAt": { + "status": "missing" + }, + "terminalAt": { + "status": "missing" + }, + "placement": { + "status": "missing" + }, + "resourceSample": { + "status": "missing" + }, + "requestedResources": { + "status": "missing" + }, + "machineId": { + "status": "missing" + }, + "runtimeEndpointHost": { + "status": "missing" + }, + "requestedRegion": { + "status": "missing" + }, + "verifiedRegion": { + "status": "missing" + }, + "storagePersistence": { + "status": "missing" + }, + "gpu": { + "status": "missing" + } + }, + "environment": { + "kind": { + "status": "observed", + "value": "sandbox" + }, + "providerEnvironmentId": { + "status": "observed", + "value": "sandbox-bb6ca2b585fb" + }, + "provider": { + "status": "observed", + "value": "tangle-sandbox" + }, + "name": { + "status": "missing" + }, + "lifecycle": { + "status": "observed", + "value": "destroyed" + }, + "lifecycleMode": { + "status": "observed", + "value": "ephemeral" + }, + "cleanup": { + "status": "observed", + "value": "delete-after-turn" + }, + "continuity": { + "status": "observed", + "value": "unavailable" + }, + "location": { + "status": "observed", + "value": "remote" + }, + "region": { + "status": "missing" + }, + "runtimeEndpointHost": { + "status": "observed", + "value": "sandbox-bb6ca2b585fb.tangle.sh" + }, + "machineId": { + "status": "missing" + }, + "requestedRegion": { + "status": "missing" + }, + "verifiedRegion": { + "status": "missing" + }, + "storagePersistence": { + "status": "observed", + "value": "persistent-home" + }, + "requestedResources": { + "status": "missing" + }, + "resourceSample": { + "status": "missing" + }, + "gpu": { + "status": "missing" + }, + "accountUsage": { + "status": "observed", + "value": { + "completeness": "provider-reported-possibly-defaulted", + "computeMinutes": 763, + "gpuSeconds": 0, + "gpuCostUsd": 0, + "activeSandboxes": 4, + "totalSandboxes": 9213, + "maximumConcurrentSandboxes": 25, + "maximumCpuCores": 8, + "maximumRamGB": 16, + "maximumStorageGB": 256, + "usagePeriodStart": "2026-08-12T00:00:00.000Z", + "usagePeriodEnd": "2026-08-13T00:00:00.000Z", + "sampledAt": "2026-08-12T11:50:51.218Z" + } + }, + "unavailableTelemetry": { + "status": "observed", + "value": [ + "disk-usage:not-exposed-by-provider", + "effective-resources:not-exposed-by-provider", + "machine-id:not-exposed-by-provider", + "machine-ip:not-exposed-by-provider", + "per-sandbox-cpu-ram-storage-cost:not-exposed-by-provider", + "runtime-cpu-memory-usage:request-failed-or-timed-out", + "verified-placement:not-exposed-by-client", + "verified-region:not-exposed-by-provider" + ] + }, + "placement": { + "status": "missing" + }, + "repository": { + "status": "missing" + }, + "gitRef": { + "status": "missing" + }, + "workingDirectory": { + "status": "missing" + }, + "image": { + "status": "missing" + }, + "createdAt": { + "status": "observed", + "value": "2026-08-12T11:50:51.839Z" + }, + "startedAt": { + "status": "missing" + }, + "lastActivityAt": { + "status": "missing" + }, + "expiresAt": { + "status": "missing" + }, + "updatedAt": { + "status": "observed", + "value": "2026-08-12T11:51:20.449Z" + } + } + }, + "cleanup": { + "status": "passed", + "matchedCount": 0, + "remainingCount": 0 + } + }, + { + "index": 4, + "status": "passed", + "elapsedMs": 43989.565059, + "runId": "run-45d60cfc-4a5f-493f-b593-2036e9f91ac8", + "operationId": "op-live-required-send-abd55e86-4097-4ff5-b384-9399c4947ede", + "model": "tangle-router/glm-5.2", + "runner": "opencode", + "message": "BRAID_CLOUD_EXECUTION_1786535411687-5262EDAE_4_OK", + "environmentId": "sandbox-61311d509bdc", + "automaticCleanup": true, + "usage": { + "tokens": { + "status": "observed", + "input": 553, + "output": 23 + }, + "cost": { + "status": "reported", + "usd": 0 + } + }, + "observation": { + "localEnvironmentId": "environment-9a7267c2f927c643077be3f0d66c2f94", + "providerEnvironmentId": "sandbox-61311d509bdc", + "environmentRecord": { + "id": "environment-9a7267c2f927c643077be3f0d66c2f94", + "connectionId": "connection-live-tangle-sandbox", + "kind": "sandbox", + "provider": "tangle-sandbox", + "providerEnvironmentId": "sandbox-61311d509bdc", + "lifecycle": "destroyed", + "lifecycleMode": "ephemeral", + "cleanup": "delete-after-turn", + "continuity": "unavailable", + "location": "remote", + "runtimeEndpointHost": "sandbox-61311d509bdc.tangle.sh", + "storagePersistence": "persistent-home", + "accountUsage": { + "completeness": "provider-reported-possibly-defaulted", + "computeMinutes": 763, + "gpuSeconds": 0, + "gpuCostUsd": 0, + "activeSandboxes": 4, + "totalSandboxes": 9213, + "maximumConcurrentSandboxes": 25, + "maximumCpuCores": 8, + "maximumRamGB": 16, + "maximumStorageGB": 256, + "usagePeriodStart": "2026-08-12T00:00:00.000Z", + "usagePeriodEnd": "2026-08-13T00:00:00.000Z", + "sampledAt": "2026-08-12T11:50:51.385Z" + }, + "unavailableTelemetry": [ + "disk-usage:not-exposed-by-provider", + "effective-resources:not-exposed-by-provider", + "machine-id:not-exposed-by-provider", + "machine-ip:not-exposed-by-provider", + "per-sandbox-cpu-ram-storage-cost:not-exposed-by-provider", + "runtime-cpu-memory-usage:request-failed-or-timed-out", + "verified-placement:not-exposed-by-client", + "verified-region:not-exposed-by-provider" + ], + "createdAt": "2026-08-12T11:50:51.629Z", + "updatedAt": "2026-08-12T11:51:30.998Z" + }, + "run": { + "inputTokens": { + "status": "observed", + "value": 553 + }, + "outputTokens": { + "status": "observed", + "value": 23 + }, + "reasoningTokens": { + "status": "missing" + }, + "cacheReadTokens": { + "status": "missing" + }, + "cacheWriteTokens": { + "status": "missing" + }, + "costUsd": { + "status": "observed", + "value": 0 + }, + "costStatus": { + "status": "observed", + "value": "reported" + }, + "usage": { + "status": "missing" + }, + "cost": { + "status": "missing" + }, + "tokensKnown": { + "status": "missing" + }, + "usdKnown": { + "status": "missing" + }, + "usageCompleteness": { + "status": "missing" + }, + "latencyMs": { + "status": "missing" + }, + "durationMs": { + "status": "missing" + }, + "model": { + "status": "observed", + "value": "tangle-router/glm-5.2" + }, + "provider": { + "status": "observed", + "value": "tangle-sandbox" + }, + "runner": { + "status": "observed", + "value": "opencode" + }, + "operationId": { + "status": "observed", + "value": "op-live-required-send-abd55e86-4097-4ff5-b384-9399c4947ede" + }, + "profileSnapshotId": { + "status": "missing" + }, + "connectionId": { + "status": "observed", + "value": "connection-live-tangle-sandbox" + }, + "providerSessionId": { + "status": "missing" + }, + "environmentId": { + "status": "observed", + "value": "environment-9a7267c2f927c643077be3f0d66c2f94" + }, + "replayCursor": { + "status": "missing" + }, + "lastCursor": { + "status": "missing" + }, + "lastProviderSequence": { + "status": "missing" + }, + "eventCount": { + "status": "missing" + }, + "contentBytes": { + "status": "observed", + "value": 541 + }, + "contentTruncated": { + "status": "missing" + }, + "missingSequence": { + "status": "missing" + }, + "terminalReason": { + "status": "missing" + }, + "materializationDigest": { + "status": "observed", + "value": "622643113a2ff6793298af6edb953b27193fb73e22de489a46e127a8350860d6" + }, + "complete": { + "status": "missing" + }, + "status": { + "status": "observed", + "value": "completed" + }, + "error": { + "status": "missing" + }, + "startedAt": { + "status": "missing" + }, + "updatedAt": { + "status": "missing" + }, + "terminalAt": { + "status": "missing" + }, + "placement": { + "status": "missing" + }, + "resourceSample": { + "status": "missing" + }, + "requestedResources": { + "status": "missing" + }, + "machineId": { + "status": "missing" + }, + "runtimeEndpointHost": { + "status": "missing" + }, + "requestedRegion": { + "status": "missing" + }, + "verifiedRegion": { + "status": "missing" + }, + "storagePersistence": { + "status": "missing" + }, + "gpu": { + "status": "missing" + } + }, + "environment": { + "kind": { + "status": "observed", + "value": "sandbox" + }, + "providerEnvironmentId": { + "status": "observed", + "value": "sandbox-61311d509bdc" + }, + "provider": { + "status": "observed", + "value": "tangle-sandbox" + }, + "name": { + "status": "missing" + }, + "lifecycle": { + "status": "observed", + "value": "destroyed" + }, + "lifecycleMode": { + "status": "observed", + "value": "ephemeral" + }, + "cleanup": { + "status": "observed", + "value": "delete-after-turn" + }, + "continuity": { + "status": "observed", + "value": "unavailable" + }, + "location": { + "status": "observed", + "value": "remote" + }, + "region": { + "status": "missing" + }, + "runtimeEndpointHost": { + "status": "observed", + "value": "sandbox-61311d509bdc.tangle.sh" + }, + "machineId": { + "status": "missing" + }, + "requestedRegion": { + "status": "missing" + }, + "verifiedRegion": { + "status": "missing" + }, + "storagePersistence": { + "status": "observed", + "value": "persistent-home" + }, + "requestedResources": { + "status": "missing" + }, + "resourceSample": { + "status": "missing" + }, + "gpu": { + "status": "missing" + }, + "accountUsage": { + "status": "observed", + "value": { + "completeness": "provider-reported-possibly-defaulted", + "computeMinutes": 763, + "gpuSeconds": 0, + "gpuCostUsd": 0, + "activeSandboxes": 4, + "totalSandboxes": 9213, + "maximumConcurrentSandboxes": 25, + "maximumCpuCores": 8, + "maximumRamGB": 16, + "maximumStorageGB": 256, + "usagePeriodStart": "2026-08-12T00:00:00.000Z", + "usagePeriodEnd": "2026-08-13T00:00:00.000Z", + "sampledAt": "2026-08-12T11:50:51.385Z" + } + }, + "unavailableTelemetry": { + "status": "observed", + "value": [ + "disk-usage:not-exposed-by-provider", + "effective-resources:not-exposed-by-provider", + "machine-id:not-exposed-by-provider", + "machine-ip:not-exposed-by-provider", + "per-sandbox-cpu-ram-storage-cost:not-exposed-by-provider", + "runtime-cpu-memory-usage:request-failed-or-timed-out", + "verified-placement:not-exposed-by-client", + "verified-region:not-exposed-by-provider" + ] + }, + "placement": { + "status": "missing" + }, + "repository": { + "status": "missing" + }, + "gitRef": { + "status": "missing" + }, + "workingDirectory": { + "status": "missing" + }, + "image": { + "status": "missing" + }, + "createdAt": { + "status": "observed", + "value": "2026-08-12T11:50:51.629Z" + }, + "startedAt": { + "status": "missing" + }, + "lastActivityAt": { + "status": "missing" + }, + "expiresAt": { + "status": "missing" + }, + "updatedAt": { + "status": "observed", + "value": "2026-08-12T11:51:30.998Z" + } + } + }, + "cleanup": { + "status": "passed", + "matchedCount": 0, + "remainingCount": 0 + } + }, + { + "index": 5, + "status": "passed", + "elapsedMs": 27340.502363000007, + "runId": "run-beca0982-ffc0-41ef-8274-21e0ff9a7dc2", + "operationId": "op-live-required-send-e8b42ea1-1063-410d-89fc-f820c02f9f78", + "model": "tangle-router/glm-5.2", + "runner": "opencode", + "message": "BRAID_CLOUD_EXECUTION_1786535411687-5262EDAE_5_OK", + "environmentId": "sandbox-9d6349d1e596", + "automaticCleanup": true, + "usage": { + "tokens": { + "status": "observed", + "input": 478, + "output": 23 + }, + "cost": { + "status": "reported", + "usd": 0 + } + }, + "observation": { + "localEnvironmentId": "environment-59f13c0bd975de78457547e98c180b17", + "providerEnvironmentId": "sandbox-9d6349d1e596", + "environmentRecord": { + "id": "environment-59f13c0bd975de78457547e98c180b17", + "connectionId": "connection-live-tangle-sandbox", + "kind": "sandbox", + "provider": "tangle-sandbox", + "providerEnvironmentId": "sandbox-9d6349d1e596", + "lifecycle": "destroyed", + "lifecycleMode": "ephemeral", + "cleanup": "delete-after-turn", + "continuity": "unavailable", + "location": "remote", + "runtimeEndpointHost": "sandbox-9d6349d1e596.tangle.sh", + "storagePersistence": "persistent-home", + "accountUsage": { + "completeness": "provider-reported-possibly-defaulted", + "computeMinutes": 764, + "gpuSeconds": 0, + "gpuCostUsd": 0, + "activeSandboxes": 7, + "totalSandboxes": 9217, + "maximumConcurrentSandboxes": 25, + "maximumCpuCores": 8, + "maximumRamGB": 16, + "maximumStorageGB": 256, + "usagePeriodStart": "2026-08-12T00:00:00.000Z", + "usagePeriodEnd": "2026-08-13T00:00:00.000Z", + "sampledAt": "2026-08-12T11:51:25.006Z" + }, + "unavailableTelemetry": [ + "disk-usage:not-exposed-by-provider", + "effective-resources:not-exposed-by-provider", + "machine-id:not-exposed-by-provider", + "machine-ip:not-exposed-by-provider", + "per-sandbox-cpu-ram-storage-cost:not-exposed-by-provider", + "runtime-cpu-memory-usage:request-failed-or-timed-out", + "verified-placement:not-exposed-by-client", + "verified-region:not-exposed-by-provider" + ], + "createdAt": "2026-08-12T11:51:24.987Z", + "updatedAt": "2026-08-12T11:51:49.151Z" + }, + "run": { + "inputTokens": { + "status": "observed", + "value": 478 + }, + "outputTokens": { + "status": "observed", + "value": 23 + }, + "reasoningTokens": { + "status": "missing" + }, + "cacheReadTokens": { + "status": "missing" + }, + "cacheWriteTokens": { + "status": "missing" + }, + "costUsd": { + "status": "observed", + "value": 0 + }, + "costStatus": { + "status": "observed", + "value": "reported" + }, + "usage": { + "status": "missing" + }, + "cost": { + "status": "missing" + }, + "tokensKnown": { + "status": "missing" + }, + "usdKnown": { + "status": "missing" + }, + "usageCompleteness": { + "status": "missing" + }, + "latencyMs": { + "status": "missing" + }, + "durationMs": { + "status": "missing" + }, + "model": { + "status": "observed", + "value": "tangle-router/glm-5.2" + }, + "provider": { + "status": "observed", + "value": "tangle-sandbox" + }, + "runner": { + "status": "observed", + "value": "opencode" + }, + "operationId": { + "status": "observed", + "value": "op-live-required-send-e8b42ea1-1063-410d-89fc-f820c02f9f78" + }, + "profileSnapshotId": { + "status": "missing" + }, + "connectionId": { + "status": "observed", + "value": "connection-live-tangle-sandbox" + }, + "providerSessionId": { + "status": "missing" + }, + "environmentId": { + "status": "observed", + "value": "environment-59f13c0bd975de78457547e98c180b17" + }, + "replayCursor": { + "status": "missing" + }, + "lastCursor": { + "status": "missing" + }, + "lastProviderSequence": { + "status": "missing" + }, + "eventCount": { + "status": "missing" + }, + "contentBytes": { + "status": "observed", + "value": 456 + }, + "contentTruncated": { + "status": "missing" + }, + "missingSequence": { + "status": "missing" + }, + "terminalReason": { + "status": "missing" + }, + "materializationDigest": { + "status": "observed", + "value": "d9f3ac974cdc32c699650835174a042bba5fabf56500fa3c8bc70b120a823384" + }, + "complete": { + "status": "missing" + }, + "status": { + "status": "observed", + "value": "completed" + }, + "error": { + "status": "missing" + }, + "startedAt": { + "status": "missing" + }, + "updatedAt": { + "status": "missing" + }, + "terminalAt": { + "status": "missing" + }, + "placement": { + "status": "missing" + }, + "resourceSample": { + "status": "missing" + }, + "requestedResources": { + "status": "missing" + }, + "machineId": { + "status": "missing" + }, + "runtimeEndpointHost": { + "status": "missing" + }, + "requestedRegion": { + "status": "missing" + }, + "verifiedRegion": { + "status": "missing" + }, + "storagePersistence": { + "status": "missing" + }, + "gpu": { + "status": "missing" + } + }, + "environment": { + "kind": { + "status": "observed", + "value": "sandbox" + }, + "providerEnvironmentId": { + "status": "observed", + "value": "sandbox-9d6349d1e596" + }, + "provider": { + "status": "observed", + "value": "tangle-sandbox" + }, + "name": { + "status": "missing" + }, + "lifecycle": { + "status": "observed", + "value": "destroyed" + }, + "lifecycleMode": { + "status": "observed", + "value": "ephemeral" + }, + "cleanup": { + "status": "observed", + "value": "delete-after-turn" + }, + "continuity": { + "status": "observed", + "value": "unavailable" + }, + "location": { + "status": "observed", + "value": "remote" + }, + "region": { + "status": "missing" + }, + "runtimeEndpointHost": { + "status": "observed", + "value": "sandbox-9d6349d1e596.tangle.sh" + }, + "machineId": { + "status": "missing" + }, + "requestedRegion": { + "status": "missing" + }, + "verifiedRegion": { + "status": "missing" + }, + "storagePersistence": { + "status": "observed", + "value": "persistent-home" + }, + "requestedResources": { + "status": "missing" + }, + "resourceSample": { + "status": "missing" + }, + "gpu": { + "status": "missing" + }, + "accountUsage": { + "status": "observed", + "value": { + "completeness": "provider-reported-possibly-defaulted", + "computeMinutes": 764, + "gpuSeconds": 0, + "gpuCostUsd": 0, + "activeSandboxes": 7, + "totalSandboxes": 9217, + "maximumConcurrentSandboxes": 25, + "maximumCpuCores": 8, + "maximumRamGB": 16, + "maximumStorageGB": 256, + "usagePeriodStart": "2026-08-12T00:00:00.000Z", + "usagePeriodEnd": "2026-08-13T00:00:00.000Z", + "sampledAt": "2026-08-12T11:51:25.006Z" + } + }, + "unavailableTelemetry": { + "status": "observed", + "value": [ + "disk-usage:not-exposed-by-provider", + "effective-resources:not-exposed-by-provider", + "machine-id:not-exposed-by-provider", + "machine-ip:not-exposed-by-provider", + "per-sandbox-cpu-ram-storage-cost:not-exposed-by-provider", + "runtime-cpu-memory-usage:request-failed-or-timed-out", + "verified-placement:not-exposed-by-client", + "verified-region:not-exposed-by-provider" + ] + }, + "placement": { + "status": "missing" + }, + "repository": { + "status": "missing" + }, + "gitRef": { + "status": "missing" + }, + "workingDirectory": { + "status": "missing" + }, + "image": { + "status": "missing" + }, + "createdAt": { + "status": "observed", + "value": "2026-08-12T11:51:24.987Z" + }, + "startedAt": { + "status": "missing" + }, + "lastActivityAt": { + "status": "missing" + }, + "expiresAt": { + "status": "missing" + }, + "updatedAt": { + "status": "observed", + "value": "2026-08-12T11:51:49.151Z" + } + } + }, + "cleanup": { + "status": "passed", + "matchedCount": 0, + "remainingCount": 0 + } + }, + { + "index": 6, + "status": "passed", + "elapsedMs": 27361.453007999997, + "runId": "run-786feba7-7b13-43e7-b228-1fc2cb0b4fa5", + "operationId": "op-live-required-send-e72d5595-507f-4dd3-b181-b327e341b5aa", + "model": "tangle-router/glm-5.2", + "runner": "opencode", + "message": "BRAID_CLOUD_EXECUTION_1786535411687-5262EDAE_6_OK", + "environmentId": "sandbox-33a1564d0a12", + "automaticCleanup": true, + "usage": { + "tokens": { + "status": "observed", + "input": 478, + "output": 23 + }, + "cost": { + "status": "reported", + "usd": 0 + } + }, + "observation": { + "localEnvironmentId": "environment-4d4f18bc4d6458d89065d25b162347de", + "providerEnvironmentId": "sandbox-33a1564d0a12", + "environmentRecord": { + "id": "environment-4d4f18bc4d6458d89065d25b162347de", + "connectionId": "connection-live-tangle-sandbox", + "kind": "sandbox", + "provider": "tangle-sandbox", + "providerEnvironmentId": "sandbox-33a1564d0a12", + "lifecycle": "destroyed", + "lifecycleMode": "ephemeral", + "cleanup": "delete-after-turn", + "continuity": "unavailable", + "location": "remote", + "runtimeEndpointHost": "sandbox-33a1564d0a12.tangle.sh", + "storagePersistence": "persistent-home", + "accountUsage": { + "completeness": "provider-reported-possibly-defaulted", + "computeMinutes": 764, + "gpuSeconds": 0, + "gpuCostUsd": 0, + "activeSandboxes": 7, + "totalSandboxes": 9217, + "maximumConcurrentSandboxes": 25, + "maximumCpuCores": 8, + "maximumRamGB": 16, + "maximumStorageGB": 256, + "usagePeriodStart": "2026-08-12T00:00:00.000Z", + "usagePeriodEnd": "2026-08-13T00:00:00.000Z", + "sampledAt": "2026-08-12T11:51:25.507Z" + }, + "unavailableTelemetry": [ + "disk-usage:not-exposed-by-provider", + "effective-resources:not-exposed-by-provider", + "machine-id:not-exposed-by-provider", + "machine-ip:not-exposed-by-provider", + "per-sandbox-cpu-ram-storage-cost:not-exposed-by-provider", + "runtime-cpu-memory-usage:request-failed-or-timed-out", + "verified-placement:not-exposed-by-client", + "verified-region:not-exposed-by-provider" + ], + "createdAt": "2026-08-12T11:51:26.951Z", + "updatedAt": "2026-08-12T11:51:50.269Z" + }, + "run": { + "inputTokens": { + "status": "observed", + "value": 478 + }, + "outputTokens": { + "status": "observed", + "value": 23 + }, + "reasoningTokens": { + "status": "missing" + }, + "cacheReadTokens": { + "status": "missing" + }, + "cacheWriteTokens": { + "status": "missing" + }, + "costUsd": { + "status": "observed", + "value": 0 + }, + "costStatus": { + "status": "observed", + "value": "reported" + }, + "usage": { + "status": "missing" + }, + "cost": { + "status": "missing" + }, + "tokensKnown": { + "status": "missing" + }, + "usdKnown": { + "status": "missing" + }, + "usageCompleteness": { + "status": "missing" + }, + "latencyMs": { + "status": "missing" + }, + "durationMs": { + "status": "missing" + }, + "model": { + "status": "observed", + "value": "tangle-router/glm-5.2" + }, + "provider": { + "status": "observed", + "value": "tangle-sandbox" + }, + "runner": { + "status": "observed", + "value": "opencode" + }, + "operationId": { + "status": "observed", + "value": "op-live-required-send-e72d5595-507f-4dd3-b181-b327e341b5aa" + }, + "profileSnapshotId": { + "status": "missing" + }, + "connectionId": { + "status": "observed", + "value": "connection-live-tangle-sandbox" + }, + "providerSessionId": { + "status": "missing" + }, + "environmentId": { + "status": "observed", + "value": "environment-4d4f18bc4d6458d89065d25b162347de" + }, + "replayCursor": { + "status": "missing" + }, + "lastCursor": { + "status": "missing" + }, + "lastProviderSequence": { + "status": "missing" + }, + "eventCount": { + "status": "missing" + }, + "contentBytes": { + "status": "observed", + "value": 456 + }, + "contentTruncated": { + "status": "missing" + }, + "missingSequence": { + "status": "missing" + }, + "terminalReason": { + "status": "missing" + }, + "materializationDigest": { + "status": "observed", + "value": "195b85497f154bc629ce78a0c554c2691e7938beed346d0ab784599d9f497ac6" + }, + "complete": { + "status": "missing" + }, + "status": { + "status": "observed", + "value": "completed" + }, + "error": { + "status": "missing" + }, + "startedAt": { + "status": "missing" + }, + "updatedAt": { + "status": "missing" + }, + "terminalAt": { + "status": "missing" + }, + "placement": { + "status": "missing" + }, + "resourceSample": { + "status": "missing" + }, + "requestedResources": { + "status": "missing" + }, + "machineId": { + "status": "missing" + }, + "runtimeEndpointHost": { + "status": "missing" + }, + "requestedRegion": { + "status": "missing" + }, + "verifiedRegion": { + "status": "missing" + }, + "storagePersistence": { + "status": "missing" + }, + "gpu": { + "status": "missing" + } + }, + "environment": { + "kind": { + "status": "observed", + "value": "sandbox" + }, + "providerEnvironmentId": { + "status": "observed", + "value": "sandbox-33a1564d0a12" + }, + "provider": { + "status": "observed", + "value": "tangle-sandbox" + }, + "name": { + "status": "missing" + }, + "lifecycle": { + "status": "observed", + "value": "destroyed" + }, + "lifecycleMode": { + "status": "observed", + "value": "ephemeral" + }, + "cleanup": { + "status": "observed", + "value": "delete-after-turn" + }, + "continuity": { + "status": "observed", + "value": "unavailable" + }, + "location": { + "status": "observed", + "value": "remote" + }, + "region": { + "status": "missing" + }, + "runtimeEndpointHost": { + "status": "observed", + "value": "sandbox-33a1564d0a12.tangle.sh" + }, + "machineId": { + "status": "missing" + }, + "requestedRegion": { + "status": "missing" + }, + "verifiedRegion": { + "status": "missing" + }, + "storagePersistence": { + "status": "observed", + "value": "persistent-home" + }, + "requestedResources": { + "status": "missing" + }, + "resourceSample": { + "status": "missing" + }, + "gpu": { + "status": "missing" + }, + "accountUsage": { + "status": "observed", + "value": { + "completeness": "provider-reported-possibly-defaulted", + "computeMinutes": 764, + "gpuSeconds": 0, + "gpuCostUsd": 0, + "activeSandboxes": 7, + "totalSandboxes": 9217, + "maximumConcurrentSandboxes": 25, + "maximumCpuCores": 8, + "maximumRamGB": 16, + "maximumStorageGB": 256, + "usagePeriodStart": "2026-08-12T00:00:00.000Z", + "usagePeriodEnd": "2026-08-13T00:00:00.000Z", + "sampledAt": "2026-08-12T11:51:25.507Z" + } + }, + "unavailableTelemetry": { + "status": "observed", + "value": [ + "disk-usage:not-exposed-by-provider", + "effective-resources:not-exposed-by-provider", + "machine-id:not-exposed-by-provider", + "machine-ip:not-exposed-by-provider", + "per-sandbox-cpu-ram-storage-cost:not-exposed-by-provider", + "runtime-cpu-memory-usage:request-failed-or-timed-out", + "verified-placement:not-exposed-by-client", + "verified-region:not-exposed-by-provider" + ] + }, + "placement": { + "status": "missing" + }, + "repository": { + "status": "missing" + }, + "gitRef": { + "status": "missing" + }, + "workingDirectory": { + "status": "missing" + }, + "image": { + "status": "missing" + }, + "createdAt": { + "status": "observed", + "value": "2026-08-12T11:51:26.951Z" + }, + "startedAt": { + "status": "missing" + }, + "lastActivityAt": { + "status": "missing" + }, + "expiresAt": { + "status": "missing" + }, + "updatedAt": { + "status": "observed", + "value": "2026-08-12T11:51:50.269Z" + } + } + }, + "cleanup": { + "status": "passed", + "matchedCount": 0, + "remainingCount": 0 + } + }, + { + "index": 7, + "status": "passed", + "elapsedMs": 26791.463044000004, + "runId": "run-bc5a53dc-f914-47f4-985c-85fed6e49b42", + "operationId": "op-live-required-send-e9c85e01-5da0-4c5a-a751-884aa48ee4a2", + "model": "tangle-router/glm-5.2", + "runner": "opencode", + "message": "BRAID_CLOUD_EXECUTION_1786535411687-5262EDAE_7_OK", + "environmentId": "sandbox-a34f82c014b1", + "automaticCleanup": true, + "usage": { + "tokens": { + "status": "observed", + "input": 478, + "output": 23 + }, + "cost": { + "status": "reported", + "usd": 0 + } + }, + "observation": { + "localEnvironmentId": "environment-91272b7380b090462631a51f1643f19f", + "providerEnvironmentId": "sandbox-a34f82c014b1", + "environmentRecord": { + "id": "environment-91272b7380b090462631a51f1643f19f", + "connectionId": "connection-live-tangle-sandbox", + "kind": "sandbox", + "provider": "tangle-sandbox", + "providerEnvironmentId": "sandbox-a34f82c014b1", + "lifecycle": "destroyed", + "lifecycleMode": "ephemeral", + "cleanup": "delete-after-turn", + "continuity": "unavailable", + "location": "remote", + "runtimeEndpointHost": "sandbox-a34f82c014b1.tangle.sh", + "storagePersistence": "persistent-home", + "accountUsage": { + "completeness": "provider-reported-possibly-defaulted", + "computeMinutes": 765, + "gpuSeconds": 0, + "gpuCostUsd": 0, + "activeSandboxes": 6, + "totalSandboxes": 9219, + "maximumConcurrentSandboxes": 25, + "maximumCpuCores": 8, + "maximumRamGB": 16, + "maximumStorageGB": 256, + "usagePeriodStart": "2026-08-12T00:00:00.000Z", + "usagePeriodEnd": "2026-08-13T00:00:00.000Z", + "sampledAt": "2026-08-12T11:51:35.355Z" + }, + "unavailableTelemetry": [ + "disk-usage:not-exposed-by-provider", + "effective-resources:not-exposed-by-provider", + "machine-id:not-exposed-by-provider", + "machine-ip:not-exposed-by-provider", + "per-sandbox-cpu-ram-storage-cost:not-exposed-by-provider", + "runtime-cpu-memory-usage:request-failed-or-timed-out", + "verified-placement:not-exposed-by-client", + "verified-region:not-exposed-by-provider" + ], + "createdAt": "2026-08-12T11:51:35.550Z", + "updatedAt": "2026-08-12T11:51:58.559Z" + }, + "run": { + "inputTokens": { + "status": "observed", + "value": 478 + }, + "outputTokens": { + "status": "observed", + "value": 23 + }, + "reasoningTokens": { + "status": "missing" + }, + "cacheReadTokens": { + "status": "missing" + }, + "cacheWriteTokens": { + "status": "missing" + }, + "costUsd": { + "status": "observed", + "value": 0 + }, + "costStatus": { + "status": "observed", + "value": "reported" + }, + "usage": { + "status": "missing" + }, + "cost": { + "status": "missing" + }, + "tokensKnown": { + "status": "missing" + }, + "usdKnown": { + "status": "missing" + }, + "usageCompleteness": { + "status": "missing" + }, + "latencyMs": { + "status": "missing" + }, + "durationMs": { + "status": "missing" + }, + "model": { + "status": "observed", + "value": "tangle-router/glm-5.2" + }, + "provider": { + "status": "observed", + "value": "tangle-sandbox" + }, + "runner": { + "status": "observed", + "value": "opencode" + }, + "operationId": { + "status": "observed", + "value": "op-live-required-send-e9c85e01-5da0-4c5a-a751-884aa48ee4a2" + }, + "profileSnapshotId": { + "status": "missing" + }, + "connectionId": { + "status": "observed", + "value": "connection-live-tangle-sandbox" + }, + "providerSessionId": { + "status": "missing" + }, + "environmentId": { + "status": "observed", + "value": "environment-91272b7380b090462631a51f1643f19f" + }, + "replayCursor": { + "status": "missing" + }, + "lastCursor": { + "status": "missing" + }, + "lastProviderSequence": { + "status": "missing" + }, + "eventCount": { + "status": "missing" + }, + "contentBytes": { + "status": "observed", + "value": 456 + }, + "contentTruncated": { + "status": "missing" + }, + "missingSequence": { + "status": "missing" + }, + "terminalReason": { + "status": "missing" + }, + "materializationDigest": { + "status": "observed", + "value": "0697e416b8cb4f14e8680e6b40102128236b236d9ce46b84e684f2b1ef1a8d0d" + }, + "complete": { + "status": "missing" + }, + "status": { + "status": "observed", + "value": "completed" + }, + "error": { + "status": "missing" + }, + "startedAt": { + "status": "missing" + }, + "updatedAt": { + "status": "missing" + }, + "terminalAt": { + "status": "missing" + }, + "placement": { + "status": "missing" + }, + "resourceSample": { + "status": "missing" + }, + "requestedResources": { + "status": "missing" + }, + "machineId": { + "status": "missing" + }, + "runtimeEndpointHost": { + "status": "missing" + }, + "requestedRegion": { + "status": "missing" + }, + "verifiedRegion": { + "status": "missing" + }, + "storagePersistence": { + "status": "missing" + }, + "gpu": { + "status": "missing" + } + }, + "environment": { + "kind": { + "status": "observed", + "value": "sandbox" + }, + "providerEnvironmentId": { + "status": "observed", + "value": "sandbox-a34f82c014b1" + }, + "provider": { + "status": "observed", + "value": "tangle-sandbox" + }, + "name": { + "status": "missing" + }, + "lifecycle": { + "status": "observed", + "value": "destroyed" + }, + "lifecycleMode": { + "status": "observed", + "value": "ephemeral" + }, + "cleanup": { + "status": "observed", + "value": "delete-after-turn" + }, + "continuity": { + "status": "observed", + "value": "unavailable" + }, + "location": { + "status": "observed", + "value": "remote" + }, + "region": { + "status": "missing" + }, + "runtimeEndpointHost": { + "status": "observed", + "value": "sandbox-a34f82c014b1.tangle.sh" + }, + "machineId": { + "status": "missing" + }, + "requestedRegion": { + "status": "missing" + }, + "verifiedRegion": { + "status": "missing" + }, + "storagePersistence": { + "status": "observed", + "value": "persistent-home" + }, + "requestedResources": { + "status": "missing" + }, + "resourceSample": { + "status": "missing" + }, + "gpu": { + "status": "missing" + }, + "accountUsage": { + "status": "observed", + "value": { + "completeness": "provider-reported-possibly-defaulted", + "computeMinutes": 765, + "gpuSeconds": 0, + "gpuCostUsd": 0, + "activeSandboxes": 6, + "totalSandboxes": 9219, + "maximumConcurrentSandboxes": 25, + "maximumCpuCores": 8, + "maximumRamGB": 16, + "maximumStorageGB": 256, + "usagePeriodStart": "2026-08-12T00:00:00.000Z", + "usagePeriodEnd": "2026-08-13T00:00:00.000Z", + "sampledAt": "2026-08-12T11:51:35.355Z" + } + }, + "unavailableTelemetry": { + "status": "observed", + "value": [ + "disk-usage:not-exposed-by-provider", + "effective-resources:not-exposed-by-provider", + "machine-id:not-exposed-by-provider", + "machine-ip:not-exposed-by-provider", + "per-sandbox-cpu-ram-storage-cost:not-exposed-by-provider", + "runtime-cpu-memory-usage:request-failed-or-timed-out", + "verified-placement:not-exposed-by-client", + "verified-region:not-exposed-by-provider" + ] + }, + "placement": { + "status": "missing" + }, + "repository": { + "status": "missing" + }, + "gitRef": { + "status": "missing" + }, + "workingDirectory": { + "status": "missing" + }, + "image": { + "status": "missing" + }, + "createdAt": { + "status": "observed", + "value": "2026-08-12T11:51:35.550Z" + }, + "startedAt": { + "status": "missing" + }, + "lastActivityAt": { + "status": "missing" + }, + "expiresAt": { + "status": "missing" + }, + "updatedAt": { + "status": "observed", + "value": "2026-08-12T11:51:58.559Z" + } + } + }, + "cleanup": { + "status": "passed", + "matchedCount": 0, + "remainingCount": 0 + } + }, + { + "index": 8, + "status": "passed", + "elapsedMs": 27981.293942999997, + "runId": "run-3f9aa659-9c22-4aaa-8a67-c315ba6be4bb", + "operationId": "op-live-required-send-4d10c9d3-b5de-48a2-8aeb-61f5c09e8dd5", + "model": "tangle-router/glm-5.2", + "runner": "opencode", + "message": "BRAID_CLOUD_EXECUTION_1786535411687-5262EDAE_8_OK", + "environmentId": "sandbox-10c1bb3a068b", + "automaticCleanup": true, + "usage": { + "tokens": { + "status": "observed", + "input": 502, + "output": 23 + }, + "cost": { + "status": "reported", + "usd": 0 + } + }, + "observation": { + "localEnvironmentId": "environment-4bce21e06ff6ab2af9b55cd0c0ed091f", + "providerEnvironmentId": "sandbox-10c1bb3a068b", + "environmentRecord": { + "id": "environment-4bce21e06ff6ab2af9b55cd0c0ed091f", + "connectionId": "connection-live-tangle-sandbox", + "kind": "sandbox", + "provider": "tangle-sandbox", + "providerEnvironmentId": "sandbox-10c1bb3a068b", + "lifecycle": "destroyed", + "lifecycleMode": "ephemeral", + "cleanup": "delete-after-turn", + "continuity": "unavailable", + "location": "remote", + "runtimeEndpointHost": "sandbox-10c1bb3a068b.tangle.sh", + "storagePersistence": "persistent-home", + "accountUsage": { + "completeness": "provider-reported-possibly-defaulted", + "computeMinutes": 765, + "gpuSeconds": 0, + "gpuCostUsd": 0, + "activeSandboxes": 7, + "totalSandboxes": 9219, + "maximumConcurrentSandboxes": 25, + "maximumCpuCores": 8, + "maximumRamGB": 16, + "maximumStorageGB": 256, + "usagePeriodStart": "2026-08-12T00:00:00.000Z", + "usagePeriodEnd": "2026-08-13T00:00:00.000Z", + "sampledAt": "2026-08-12T11:51:35.513Z" + }, + "unavailableTelemetry": [ + "disk-usage:not-exposed-by-provider", + "effective-resources:not-exposed-by-provider", + "machine-id:not-exposed-by-provider", + "machine-ip:not-exposed-by-provider", + "per-sandbox-cpu-ram-storage-cost:not-exposed-by-provider", + "runtime-cpu-memory-usage:request-failed-or-timed-out", + "verified-placement:not-exposed-by-client", + "verified-region:not-exposed-by-provider" + ], + "createdAt": "2026-08-12T11:51:36.930Z", + "updatedAt": "2026-08-12T11:52:00.490Z" + }, + "run": { + "inputTokens": { + "status": "observed", + "value": 502 + }, + "outputTokens": { + "status": "observed", + "value": 23 + }, + "reasoningTokens": { + "status": "missing" + }, + "cacheReadTokens": { + "status": "missing" + }, + "cacheWriteTokens": { + "status": "missing" + }, + "costUsd": { + "status": "observed", + "value": 0 + }, + "costStatus": { + "status": "observed", + "value": "reported" + }, + "usage": { + "status": "missing" + }, + "cost": { + "status": "missing" + }, + "tokensKnown": { + "status": "missing" + }, + "usdKnown": { + "status": "missing" + }, + "usageCompleteness": { + "status": "missing" + }, + "latencyMs": { + "status": "missing" + }, + "durationMs": { + "status": "missing" + }, + "model": { + "status": "observed", + "value": "tangle-router/glm-5.2" + }, + "provider": { + "status": "observed", + "value": "tangle-sandbox" + }, + "runner": { + "status": "observed", + "value": "opencode" + }, + "operationId": { + "status": "observed", + "value": "op-live-required-send-4d10c9d3-b5de-48a2-8aeb-61f5c09e8dd5" + }, + "profileSnapshotId": { + "status": "missing" + }, + "connectionId": { + "status": "observed", + "value": "connection-live-tangle-sandbox" + }, + "providerSessionId": { + "status": "missing" + }, + "environmentId": { + "status": "observed", + "value": "environment-4bce21e06ff6ab2af9b55cd0c0ed091f" + }, + "replayCursor": { + "status": "missing" + }, + "lastCursor": { + "status": "missing" + }, + "lastProviderSequence": { + "status": "missing" + }, + "eventCount": { + "status": "missing" + }, + "contentBytes": { + "status": "observed", + "value": 535 + }, + "contentTruncated": { + "status": "missing" + }, + "missingSequence": { + "status": "missing" + }, + "terminalReason": { + "status": "missing" + }, + "materializationDigest": { + "status": "observed", + "value": "7d0e3c6f605905b9e54c4d985f4d55533923cabaeae28e20c8ce9f9fdf3a9d83" + }, + "complete": { + "status": "missing" + }, + "status": { + "status": "observed", + "value": "completed" + }, + "error": { + "status": "missing" + }, + "startedAt": { + "status": "missing" + }, + "updatedAt": { + "status": "missing" + }, + "terminalAt": { + "status": "missing" + }, + "placement": { + "status": "missing" + }, + "resourceSample": { + "status": "missing" + }, + "requestedResources": { + "status": "missing" + }, + "machineId": { + "status": "missing" + }, + "runtimeEndpointHost": { + "status": "missing" + }, + "requestedRegion": { + "status": "missing" + }, + "verifiedRegion": { + "status": "missing" + }, + "storagePersistence": { + "status": "missing" + }, + "gpu": { + "status": "missing" + } + }, + "environment": { + "kind": { + "status": "observed", + "value": "sandbox" + }, + "providerEnvironmentId": { + "status": "observed", + "value": "sandbox-10c1bb3a068b" + }, + "provider": { + "status": "observed", + "value": "tangle-sandbox" + }, + "name": { + "status": "missing" + }, + "lifecycle": { + "status": "observed", + "value": "destroyed" + }, + "lifecycleMode": { + "status": "observed", + "value": "ephemeral" + }, + "cleanup": { + "status": "observed", + "value": "delete-after-turn" + }, + "continuity": { + "status": "observed", + "value": "unavailable" + }, + "location": { + "status": "observed", + "value": "remote" + }, + "region": { + "status": "missing" + }, + "runtimeEndpointHost": { + "status": "observed", + "value": "sandbox-10c1bb3a068b.tangle.sh" + }, + "machineId": { + "status": "missing" + }, + "requestedRegion": { + "status": "missing" + }, + "verifiedRegion": { + "status": "missing" + }, + "storagePersistence": { + "status": "observed", + "value": "persistent-home" + }, + "requestedResources": { + "status": "missing" + }, + "resourceSample": { + "status": "missing" + }, + "gpu": { + "status": "missing" + }, + "accountUsage": { + "status": "observed", + "value": { + "completeness": "provider-reported-possibly-defaulted", + "computeMinutes": 765, + "gpuSeconds": 0, + "gpuCostUsd": 0, + "activeSandboxes": 7, + "totalSandboxes": 9219, + "maximumConcurrentSandboxes": 25, + "maximumCpuCores": 8, + "maximumRamGB": 16, + "maximumStorageGB": 256, + "usagePeriodStart": "2026-08-12T00:00:00.000Z", + "usagePeriodEnd": "2026-08-13T00:00:00.000Z", + "sampledAt": "2026-08-12T11:51:35.513Z" + } + }, + "unavailableTelemetry": { + "status": "observed", + "value": [ + "disk-usage:not-exposed-by-provider", + "effective-resources:not-exposed-by-provider", + "machine-id:not-exposed-by-provider", + "machine-ip:not-exposed-by-provider", + "per-sandbox-cpu-ram-storage-cost:not-exposed-by-provider", + "runtime-cpu-memory-usage:request-failed-or-timed-out", + "verified-placement:not-exposed-by-client", + "verified-region:not-exposed-by-provider" + ] + }, + "placement": { + "status": "missing" + }, + "repository": { + "status": "missing" + }, + "gitRef": { + "status": "missing" + }, + "workingDirectory": { + "status": "missing" + }, + "image": { + "status": "missing" + }, + "createdAt": { + "status": "observed", + "value": "2026-08-12T11:51:36.930Z" + }, + "startedAt": { + "status": "missing" + }, + "lastActivityAt": { + "status": "missing" + }, + "expiresAt": { + "status": "missing" + }, + "updatedAt": { + "status": "observed", + "value": "2026-08-12T11:52:00.490Z" + } + } + }, + "cleanup": { + "status": "passed", + "matchedCount": 0, + "remainingCount": 0 + } + }, + { + "index": 9, + "status": "passed", + "elapsedMs": 27430.584648999997, + "runId": "run-1961834e-31de-4d63-ba6e-ac392e749bb7", + "operationId": "op-live-required-send-eda9f8f3-9f9c-415c-8cc5-98589eb084a9", + "model": "tangle-router/glm-5.2", + "runner": "opencode", + "message": "BRAID_CLOUD_EXECUTION_1786535411687-5262EDAE_9_OK", + "environmentId": "sandbox-a816603048ea", + "automaticCleanup": true, + "usage": { + "tokens": { + "status": "observed", + "input": 478, + "output": 23 + }, + "cost": { + "status": "reported", + "usd": 0 + } + }, + "observation": { + "localEnvironmentId": "environment-60228459a30691680b0697e98e193002", + "providerEnvironmentId": "sandbox-a816603048ea", + "environmentRecord": { + "id": "environment-60228459a30691680b0697e98e193002", + "connectionId": "connection-live-tangle-sandbox", + "kind": "sandbox", + "provider": "tangle-sandbox", + "providerEnvironmentId": "sandbox-a816603048ea", + "lifecycle": "destroyed", + "lifecycleMode": "ephemeral", + "cleanup": "delete-after-turn", + "continuity": "unavailable", + "location": "remote", + "runtimeEndpointHost": "sandbox-a816603048ea.tangle.sh", + "storagePersistence": "persistent-home", + "accountUsage": { + "completeness": "provider-reported-possibly-defaulted", + "computeMinutes": 765, + "gpuSeconds": 0, + "gpuCostUsd": 0, + "activeSandboxes": 6, + "totalSandboxes": 9221, + "maximumConcurrentSandboxes": 25, + "maximumCpuCores": 8, + "maximumRamGB": 16, + "maximumStorageGB": 256, + "usagePeriodStart": "2026-08-12T00:00:00.000Z", + "usagePeriodEnd": "2026-08-13T00:00:00.000Z", + "sampledAt": "2026-08-12T11:51:53.376Z" + }, + "unavailableTelemetry": [ + "disk-usage:not-exposed-by-provider", + "effective-resources:not-exposed-by-provider", + "machine-id:not-exposed-by-provider", + "machine-ip:not-exposed-by-provider", + "per-sandbox-cpu-ram-storage-cost:not-exposed-by-provider", + "runtime-cpu-memory-usage:request-failed-or-timed-out", + "verified-placement:not-exposed-by-client", + "verified-region:not-exposed-by-provider" + ], + "createdAt": "2026-08-12T11:51:54.243Z", + "updatedAt": "2026-08-12T11:52:17.432Z" + }, + "run": { + "inputTokens": { + "status": "observed", + "value": 478 + }, + "outputTokens": { + "status": "observed", + "value": 23 + }, + "reasoningTokens": { + "status": "missing" + }, + "cacheReadTokens": { + "status": "missing" + }, + "cacheWriteTokens": { + "status": "missing" + }, + "costUsd": { + "status": "observed", + "value": 0 + }, + "costStatus": { + "status": "observed", + "value": "reported" + }, + "usage": { + "status": "missing" + }, + "cost": { + "status": "missing" + }, + "tokensKnown": { + "status": "missing" + }, + "usdKnown": { + "status": "missing" + }, + "usageCompleteness": { + "status": "missing" + }, + "latencyMs": { + "status": "missing" + }, + "durationMs": { + "status": "missing" + }, + "model": { + "status": "observed", + "value": "tangle-router/glm-5.2" + }, + "provider": { + "status": "observed", + "value": "tangle-sandbox" + }, + "runner": { + "status": "observed", + "value": "opencode" + }, + "operationId": { + "status": "observed", + "value": "op-live-required-send-eda9f8f3-9f9c-415c-8cc5-98589eb084a9" + }, + "profileSnapshotId": { + "status": "missing" + }, + "connectionId": { + "status": "observed", + "value": "connection-live-tangle-sandbox" + }, + "providerSessionId": { + "status": "missing" + }, + "environmentId": { + "status": "observed", + "value": "environment-60228459a30691680b0697e98e193002" + }, + "replayCursor": { + "status": "missing" + }, + "lastCursor": { + "status": "missing" + }, + "lastProviderSequence": { + "status": "missing" + }, + "eventCount": { + "status": "missing" + }, + "contentBytes": { + "status": "observed", + "value": 456 + }, + "contentTruncated": { + "status": "missing" + }, + "missingSequence": { + "status": "missing" + }, + "terminalReason": { + "status": "missing" + }, + "materializationDigest": { + "status": "observed", + "value": "a93195b00ef55f8e7ed608ae5931f21e08bcfbaf1efc3a287faf27ceec65634b" + }, + "complete": { + "status": "missing" + }, + "status": { + "status": "observed", + "value": "completed" + }, + "error": { + "status": "missing" + }, + "startedAt": { + "status": "missing" + }, + "updatedAt": { + "status": "missing" + }, + "terminalAt": { + "status": "missing" + }, + "placement": { + "status": "missing" + }, + "resourceSample": { + "status": "missing" + }, + "requestedResources": { + "status": "missing" + }, + "machineId": { + "status": "missing" + }, + "runtimeEndpointHost": { + "status": "missing" + }, + "requestedRegion": { + "status": "missing" + }, + "verifiedRegion": { + "status": "missing" + }, + "storagePersistence": { + "status": "missing" + }, + "gpu": { + "status": "missing" + } + }, + "environment": { + "kind": { + "status": "observed", + "value": "sandbox" + }, + "providerEnvironmentId": { + "status": "observed", + "value": "sandbox-a816603048ea" + }, + "provider": { + "status": "observed", + "value": "tangle-sandbox" + }, + "name": { + "status": "missing" + }, + "lifecycle": { + "status": "observed", + "value": "destroyed" + }, + "lifecycleMode": { + "status": "observed", + "value": "ephemeral" + }, + "cleanup": { + "status": "observed", + "value": "delete-after-turn" + }, + "continuity": { + "status": "observed", + "value": "unavailable" + }, + "location": { + "status": "observed", + "value": "remote" + }, + "region": { + "status": "missing" + }, + "runtimeEndpointHost": { + "status": "observed", + "value": "sandbox-a816603048ea.tangle.sh" + }, + "machineId": { + "status": "missing" + }, + "requestedRegion": { + "status": "missing" + }, + "verifiedRegion": { + "status": "missing" + }, + "storagePersistence": { + "status": "observed", + "value": "persistent-home" + }, + "requestedResources": { + "status": "missing" + }, + "resourceSample": { + "status": "missing" + }, + "gpu": { + "status": "missing" + }, + "accountUsage": { + "status": "observed", + "value": { + "completeness": "provider-reported-possibly-defaulted", + "computeMinutes": 765, + "gpuSeconds": 0, + "gpuCostUsd": 0, + "activeSandboxes": 6, + "totalSandboxes": 9221, + "maximumConcurrentSandboxes": 25, + "maximumCpuCores": 8, + "maximumRamGB": 16, + "maximumStorageGB": 256, + "usagePeriodStart": "2026-08-12T00:00:00.000Z", + "usagePeriodEnd": "2026-08-13T00:00:00.000Z", + "sampledAt": "2026-08-12T11:51:53.376Z" + } + }, + "unavailableTelemetry": { + "status": "observed", + "value": [ + "disk-usage:not-exposed-by-provider", + "effective-resources:not-exposed-by-provider", + "machine-id:not-exposed-by-provider", + "machine-ip:not-exposed-by-provider", + "per-sandbox-cpu-ram-storage-cost:not-exposed-by-provider", + "runtime-cpu-memory-usage:request-failed-or-timed-out", + "verified-placement:not-exposed-by-client", + "verified-region:not-exposed-by-provider" + ] + }, + "placement": { + "status": "missing" + }, + "repository": { + "status": "missing" + }, + "gitRef": { + "status": "missing" + }, + "workingDirectory": { + "status": "missing" + }, + "image": { + "status": "missing" + }, + "createdAt": { + "status": "observed", + "value": "2026-08-12T11:51:54.243Z" + }, + "startedAt": { + "status": "missing" + }, + "lastActivityAt": { + "status": "missing" + }, + "expiresAt": { + "status": "missing" + }, + "updatedAt": { + "status": "observed", + "value": "2026-08-12T11:52:17.432Z" + } + } + }, + "cleanup": { + "status": "passed", + "matchedCount": 0, + "remainingCount": 0 + } + }, + { + "index": 10, + "status": "passed", + "elapsedMs": 27224.80052399999, + "runId": "run-d491b8a7-b183-4f7f-97bf-22be68d89054", + "operationId": "op-live-required-send-2383acb6-1a2b-4cfa-967a-5c13935c5dbf", + "model": "tangle-router/glm-5.2", + "runner": "opencode", + "message": "BRAID_CLOUD_EXECUTION_1786535411687-5262EDAE_10_OK", + "environmentId": "sandbox-0f144cd753a7", + "automaticCleanup": true, + "usage": { + "tokens": { + "status": "observed", + "input": 478, + "output": 23 + }, + "cost": { + "status": "reported", + "usd": 0 + } + }, + "observation": { + "localEnvironmentId": "environment-5f04d798b3ded44856e5b77e6fa9acf2", + "providerEnvironmentId": "sandbox-0f144cd753a7", + "environmentRecord": { + "id": "environment-5f04d798b3ded44856e5b77e6fa9acf2", + "connectionId": "connection-live-tangle-sandbox", + "kind": "sandbox", + "provider": "tangle-sandbox", + "providerEnvironmentId": "sandbox-0f144cd753a7", + "lifecycle": "destroyed", + "lifecycleMode": "ephemeral", + "cleanup": "delete-after-turn", + "continuity": "unavailable", + "location": "remote", + "runtimeEndpointHost": "sandbox-0f144cd753a7.tangle.sh", + "storagePersistence": "persistent-home", + "accountUsage": { + "completeness": "provider-reported-possibly-defaulted", + "computeMinutes": 765, + "gpuSeconds": 0, + "gpuCostUsd": 0, + "activeSandboxes": 6, + "totalSandboxes": 9221, + "maximumConcurrentSandboxes": 25, + "maximumCpuCores": 8, + "maximumRamGB": 16, + "maximumStorageGB": 256, + "usagePeriodStart": "2026-08-12T00:00:00.000Z", + "usagePeriodEnd": "2026-08-13T00:00:00.000Z", + "sampledAt": "2026-08-12T11:51:54.708Z" + }, + "unavailableTelemetry": [ + "disk-usage:not-exposed-by-provider", + "effective-resources:not-exposed-by-provider", + "machine-id:not-exposed-by-provider", + "machine-ip:not-exposed-by-provider", + "per-sandbox-cpu-ram-storage-cost:not-exposed-by-provider", + "runtime-cpu-memory-usage:request-failed-or-timed-out", + "verified-placement:not-exposed-by-client", + "verified-region:not-exposed-by-provider" + ], + "createdAt": "2026-08-12T11:51:55.300Z", + "updatedAt": "2026-08-12T11:52:18.559Z" + }, + "run": { + "inputTokens": { + "status": "observed", + "value": 478 + }, + "outputTokens": { + "status": "observed", + "value": 23 + }, + "reasoningTokens": { + "status": "missing" + }, + "cacheReadTokens": { + "status": "missing" + }, + "cacheWriteTokens": { + "status": "missing" + }, + "costUsd": { + "status": "observed", + "value": 0 + }, + "costStatus": { + "status": "observed", + "value": "reported" + }, + "usage": { + "status": "missing" + }, + "cost": { + "status": "missing" + }, + "tokensKnown": { + "status": "missing" + }, + "usdKnown": { + "status": "missing" + }, + "usageCompleteness": { + "status": "missing" + }, + "latencyMs": { + "status": "missing" + }, + "durationMs": { + "status": "missing" + }, + "model": { + "status": "observed", + "value": "tangle-router/glm-5.2" + }, + "provider": { + "status": "observed", + "value": "tangle-sandbox" + }, + "runner": { + "status": "observed", + "value": "opencode" + }, + "operationId": { + "status": "observed", + "value": "op-live-required-send-2383acb6-1a2b-4cfa-967a-5c13935c5dbf" + }, + "profileSnapshotId": { + "status": "missing" + }, + "connectionId": { + "status": "observed", + "value": "connection-live-tangle-sandbox" + }, + "providerSessionId": { + "status": "missing" + }, + "environmentId": { + "status": "observed", + "value": "environment-5f04d798b3ded44856e5b77e6fa9acf2" + }, + "replayCursor": { + "status": "missing" + }, + "lastCursor": { + "status": "missing" + }, + "lastProviderSequence": { + "status": "missing" + }, + "eventCount": { + "status": "missing" + }, + "contentBytes": { + "status": "observed", + "value": 460 + }, + "contentTruncated": { + "status": "missing" + }, + "missingSequence": { + "status": "missing" + }, + "terminalReason": { + "status": "missing" + }, + "materializationDigest": { + "status": "observed", + "value": "e60485183f2e210e89f574fc95c1242e302b6a81262237ab323bb1e84a0319ab" + }, + "complete": { + "status": "missing" + }, + "status": { + "status": "observed", + "value": "completed" + }, + "error": { + "status": "missing" + }, + "startedAt": { + "status": "missing" + }, + "updatedAt": { + "status": "missing" + }, + "terminalAt": { + "status": "missing" + }, + "placement": { + "status": "missing" + }, + "resourceSample": { + "status": "missing" + }, + "requestedResources": { + "status": "missing" + }, + "machineId": { + "status": "missing" + }, + "runtimeEndpointHost": { + "status": "missing" + }, + "requestedRegion": { + "status": "missing" + }, + "verifiedRegion": { + "status": "missing" + }, + "storagePersistence": { + "status": "missing" + }, + "gpu": { + "status": "missing" + } + }, + "environment": { + "kind": { + "status": "observed", + "value": "sandbox" + }, + "providerEnvironmentId": { + "status": "observed", + "value": "sandbox-0f144cd753a7" + }, + "provider": { + "status": "observed", + "value": "tangle-sandbox" + }, + "name": { + "status": "missing" + }, + "lifecycle": { + "status": "observed", + "value": "destroyed" + }, + "lifecycleMode": { + "status": "observed", + "value": "ephemeral" + }, + "cleanup": { + "status": "observed", + "value": "delete-after-turn" + }, + "continuity": { + "status": "observed", + "value": "unavailable" + }, + "location": { + "status": "observed", + "value": "remote" + }, + "region": { + "status": "missing" + }, + "runtimeEndpointHost": { + "status": "observed", + "value": "sandbox-0f144cd753a7.tangle.sh" + }, + "machineId": { + "status": "missing" + }, + "requestedRegion": { + "status": "missing" + }, + "verifiedRegion": { + "status": "missing" + }, + "storagePersistence": { + "status": "observed", + "value": "persistent-home" + }, + "requestedResources": { + "status": "missing" + }, + "resourceSample": { + "status": "missing" + }, + "gpu": { + "status": "missing" + }, + "accountUsage": { + "status": "observed", + "value": { + "completeness": "provider-reported-possibly-defaulted", + "computeMinutes": 765, + "gpuSeconds": 0, + "gpuCostUsd": 0, + "activeSandboxes": 6, + "totalSandboxes": 9221, + "maximumConcurrentSandboxes": 25, + "maximumCpuCores": 8, + "maximumRamGB": 16, + "maximumStorageGB": 256, + "usagePeriodStart": "2026-08-12T00:00:00.000Z", + "usagePeriodEnd": "2026-08-13T00:00:00.000Z", + "sampledAt": "2026-08-12T11:51:54.708Z" + } + }, + "unavailableTelemetry": { + "status": "observed", + "value": [ + "disk-usage:not-exposed-by-provider", + "effective-resources:not-exposed-by-provider", + "machine-id:not-exposed-by-provider", + "machine-ip:not-exposed-by-provider", + "per-sandbox-cpu-ram-storage-cost:not-exposed-by-provider", + "runtime-cpu-memory-usage:request-failed-or-timed-out", + "verified-placement:not-exposed-by-client", + "verified-region:not-exposed-by-provider" + ] + }, + "placement": { + "status": "missing" + }, + "repository": { + "status": "missing" + }, + "gitRef": { + "status": "missing" + }, + "workingDirectory": { + "status": "missing" + }, + "image": { + "status": "missing" + }, + "createdAt": { + "status": "observed", + "value": "2026-08-12T11:51:55.300Z" + }, + "startedAt": { + "status": "missing" + }, + "lastActivityAt": { + "status": "missing" + }, + "expiresAt": { + "status": "missing" + }, + "updatedAt": { + "status": "observed", + "value": "2026-08-12T11:52:18.559Z" + } + } + }, + "cleanup": { + "status": "passed", + "matchedCount": 0, + "remainingCount": 0 + } + }, + { + "index": 11, + "status": "passed", + "elapsedMs": 27253.263657000003, + "runId": "run-68783918-364a-4fe9-a8d5-360737f4b707", + "operationId": "op-live-required-send-641dbd3c-5ac5-4103-8beb-c41be9bdcb9c", + "model": "tangle-router/glm-5.2", + "runner": "opencode", + "message": "BRAID_CLOUD_EXECUTION_1786535411687-5262EDAE_11_OK", + "environmentId": "sandbox-a0f596b268e2", + "automaticCleanup": true, + "usage": { + "tokens": { + "status": "observed", + "input": 558, + "output": 23 + }, + "cost": { + "status": "reported", + "usd": 0 + } + }, + "observation": { + "localEnvironmentId": "environment-1252eb1011589e192d7842c3854fb55a", + "providerEnvironmentId": "sandbox-a0f596b268e2", + "environmentRecord": { + "id": "environment-1252eb1011589e192d7842c3854fb55a", + "connectionId": "connection-live-tangle-sandbox", + "kind": "sandbox", + "provider": "tangle-sandbox", + "providerEnvironmentId": "sandbox-a0f596b268e2", + "lifecycle": "destroyed", + "lifecycleMode": "ephemeral", + "cleanup": "delete-after-turn", + "continuity": "unavailable", + "location": "remote", + "runtimeEndpointHost": "sandbox-a0f596b268e2.tangle.sh", + "storagePersistence": "persistent-home", + "accountUsage": { + "completeness": "provider-reported-possibly-defaulted", + "computeMinutes": 766, + "gpuSeconds": 0, + "gpuCostUsd": 0, + "activeSandboxes": 6, + "totalSandboxes": 9222, + "maximumConcurrentSandboxes": 25, + "maximumCpuCores": 8, + "maximumRamGB": 16, + "maximumStorageGB": 256, + "usagePeriodStart": "2026-08-12T00:00:00.000Z", + "usagePeriodEnd": "2026-08-13T00:00:00.000Z", + "sampledAt": "2026-08-12T11:52:02.233Z" + }, + "unavailableTelemetry": [ + "disk-usage:not-exposed-by-provider", + "effective-resources:not-exposed-by-provider", + "machine-id:not-exposed-by-provider", + "machine-ip:not-exposed-by-provider", + "per-sandbox-cpu-ram-storage-cost:not-exposed-by-provider", + "runtime-cpu-memory-usage:request-failed-or-timed-out", + "verified-placement:not-exposed-by-client", + "verified-region:not-exposed-by-provider" + ], + "createdAt": "2026-08-12T11:52:03.926Z", + "updatedAt": "2026-08-12T11:52:27.253Z" + }, + "run": { + "inputTokens": { + "status": "observed", + "value": 558 + }, + "outputTokens": { + "status": "observed", + "value": 23 + }, + "reasoningTokens": { + "status": "missing" + }, + "cacheReadTokens": { + "status": "missing" + }, + "cacheWriteTokens": { + "status": "missing" + }, + "costUsd": { + "status": "observed", + "value": 0 + }, + "costStatus": { + "status": "observed", + "value": "reported" + }, + "usage": { + "status": "missing" + }, + "cost": { + "status": "missing" + }, + "tokensKnown": { + "status": "missing" + }, + "usdKnown": { + "status": "missing" + }, + "usageCompleteness": { + "status": "missing" + }, + "latencyMs": { + "status": "missing" + }, + "durationMs": { + "status": "missing" + }, + "model": { + "status": "observed", + "value": "tangle-router/glm-5.2" + }, + "provider": { + "status": "observed", + "value": "tangle-sandbox" + }, + "runner": { + "status": "observed", + "value": "opencode" + }, + "operationId": { + "status": "observed", + "value": "op-live-required-send-641dbd3c-5ac5-4103-8beb-c41be9bdcb9c" + }, + "profileSnapshotId": { + "status": "missing" + }, + "connectionId": { + "status": "observed", + "value": "connection-live-tangle-sandbox" + }, + "providerSessionId": { + "status": "missing" + }, + "environmentId": { + "status": "observed", + "value": "environment-1252eb1011589e192d7842c3854fb55a" + }, + "replayCursor": { + "status": "missing" + }, + "lastCursor": { + "status": "missing" + }, + "lastProviderSequence": { + "status": "missing" + }, + "eventCount": { + "status": "missing" + }, + "contentBytes": { + "status": "observed", + "value": 565 + }, + "contentTruncated": { + "status": "missing" + }, + "missingSequence": { + "status": "missing" + }, + "terminalReason": { + "status": "missing" + }, + "materializationDigest": { + "status": "observed", + "value": "b5383d70c688e365eb016fba4a306356dcedefca1b74659d658b22665d247a45" + }, + "complete": { + "status": "missing" + }, + "status": { + "status": "observed", + "value": "completed" + }, + "error": { + "status": "missing" + }, + "startedAt": { + "status": "missing" + }, + "updatedAt": { + "status": "missing" + }, + "terminalAt": { + "status": "missing" + }, + "placement": { + "status": "missing" + }, + "resourceSample": { + "status": "missing" + }, + "requestedResources": { + "status": "missing" + }, + "machineId": { + "status": "missing" + }, + "runtimeEndpointHost": { + "status": "missing" + }, + "requestedRegion": { + "status": "missing" + }, + "verifiedRegion": { + "status": "missing" + }, + "storagePersistence": { + "status": "missing" + }, + "gpu": { + "status": "missing" + } + }, + "environment": { + "kind": { + "status": "observed", + "value": "sandbox" + }, + "providerEnvironmentId": { + "status": "observed", + "value": "sandbox-a0f596b268e2" + }, + "provider": { + "status": "observed", + "value": "tangle-sandbox" + }, + "name": { + "status": "missing" + }, + "lifecycle": { + "status": "observed", + "value": "destroyed" + }, + "lifecycleMode": { + "status": "observed", + "value": "ephemeral" + }, + "cleanup": { + "status": "observed", + "value": "delete-after-turn" + }, + "continuity": { + "status": "observed", + "value": "unavailable" + }, + "location": { + "status": "observed", + "value": "remote" + }, + "region": { + "status": "missing" + }, + "runtimeEndpointHost": { + "status": "observed", + "value": "sandbox-a0f596b268e2.tangle.sh" + }, + "machineId": { + "status": "missing" + }, + "requestedRegion": { + "status": "missing" + }, + "verifiedRegion": { + "status": "missing" + }, + "storagePersistence": { + "status": "observed", + "value": "persistent-home" + }, + "requestedResources": { + "status": "missing" + }, + "resourceSample": { + "status": "missing" + }, + "gpu": { + "status": "missing" + }, + "accountUsage": { + "status": "observed", + "value": { + "completeness": "provider-reported-possibly-defaulted", + "computeMinutes": 766, + "gpuSeconds": 0, + "gpuCostUsd": 0, + "activeSandboxes": 6, + "totalSandboxes": 9222, + "maximumConcurrentSandboxes": 25, + "maximumCpuCores": 8, + "maximumRamGB": 16, + "maximumStorageGB": 256, + "usagePeriodStart": "2026-08-12T00:00:00.000Z", + "usagePeriodEnd": "2026-08-13T00:00:00.000Z", + "sampledAt": "2026-08-12T11:52:02.233Z" + } + }, + "unavailableTelemetry": { + "status": "observed", + "value": [ + "disk-usage:not-exposed-by-provider", + "effective-resources:not-exposed-by-provider", + "machine-id:not-exposed-by-provider", + "machine-ip:not-exposed-by-provider", + "per-sandbox-cpu-ram-storage-cost:not-exposed-by-provider", + "runtime-cpu-memory-usage:request-failed-or-timed-out", + "verified-placement:not-exposed-by-client", + "verified-region:not-exposed-by-provider" + ] + }, + "placement": { + "status": "missing" + }, + "repository": { + "status": "missing" + }, + "gitRef": { + "status": "missing" + }, + "workingDirectory": { + "status": "missing" + }, + "image": { + "status": "missing" + }, + "createdAt": { + "status": "observed", + "value": "2026-08-12T11:52:03.926Z" + }, + "startedAt": { + "status": "missing" + }, + "lastActivityAt": { + "status": "missing" + }, + "expiresAt": { + "status": "missing" + }, + "updatedAt": { + "status": "observed", + "value": "2026-08-12T11:52:27.253Z" + } + } + }, + "cleanup": { + "status": "passed", + "matchedCount": 0, + "remainingCount": 0 + } + }, + { + "index": 12, + "status": "passed", + "elapsedMs": 26031.052383999995, + "runId": "run-f4348589-63e6-457f-b868-841518f5ae00", + "operationId": "op-live-required-send-9cb4efab-a305-4dab-b7c5-bf2f714c3227", + "model": "tangle-router/glm-5.2", + "runner": "opencode", + "message": "BRAID_CLOUD_EXECUTION_1786535411687-5262EDAE_12_OK", + "environmentId": "sandbox-307ccb60d7c2", + "automaticCleanup": true, + "usage": { + "tokens": { + "status": "observed", + "input": 502, + "output": 23 + }, + "cost": { + "status": "reported", + "usd": 0 + } + }, + "observation": { + "localEnvironmentId": "environment-7f51dbf003e3a8c382341ce022a9ed08", + "providerEnvironmentId": "sandbox-307ccb60d7c2", + "environmentRecord": { + "id": "environment-7f51dbf003e3a8c382341ce022a9ed08", + "connectionId": "connection-live-tangle-sandbox", + "kind": "sandbox", + "provider": "tangle-sandbox", + "providerEnvironmentId": "sandbox-307ccb60d7c2", + "lifecycle": "destroyed", + "lifecycleMode": "ephemeral", + "cleanup": "delete-after-turn", + "continuity": "unavailable", + "location": "remote", + "runtimeEndpointHost": "sandbox-307ccb60d7c2.tangle.sh", + "storagePersistence": "persistent-home", + "accountUsage": { + "completeness": "provider-reported-possibly-defaulted", + "computeMinutes": 766, + "gpuSeconds": 0, + "gpuCostUsd": 0, + "activeSandboxes": 6, + "totalSandboxes": 9223, + "maximumConcurrentSandboxes": 25, + "maximumCpuCores": 8, + "maximumRamGB": 16, + "maximumStorageGB": 256, + "usagePeriodStart": "2026-08-12T00:00:00.000Z", + "usagePeriodEnd": "2026-08-13T00:00:00.000Z", + "sampledAt": "2026-08-12T11:52:03.892Z" + }, + "unavailableTelemetry": [ + "disk-usage:not-exposed-by-provider", + "effective-resources:not-exposed-by-provider", + "machine-id:not-exposed-by-provider", + "machine-ip:not-exposed-by-provider", + "per-sandbox-cpu-ram-storage-cost:not-exposed-by-provider", + "runtime-cpu-memory-usage:request-failed-or-timed-out", + "verified-placement:not-exposed-by-client", + "verified-region:not-exposed-by-provider" + ], + "createdAt": "2026-08-12T11:52:04.890Z", + "updatedAt": "2026-08-12T11:52:27.731Z" + }, + "run": { + "inputTokens": { + "status": "observed", + "value": 502 + }, + "outputTokens": { + "status": "observed", + "value": 23 + }, + "reasoningTokens": { + "status": "missing" + }, + "cacheReadTokens": { + "status": "missing" + }, + "cacheWriteTokens": { + "status": "missing" + }, + "costUsd": { + "status": "observed", + "value": 0 + }, + "costStatus": { + "status": "observed", + "value": "reported" + }, + "usage": { + "status": "missing" + }, + "cost": { + "status": "missing" + }, + "tokensKnown": { + "status": "missing" + }, + "usdKnown": { + "status": "missing" + }, + "usageCompleteness": { + "status": "missing" + }, + "latencyMs": { + "status": "missing" + }, + "durationMs": { + "status": "missing" + }, + "model": { + "status": "observed", + "value": "tangle-router/glm-5.2" + }, + "provider": { + "status": "observed", + "value": "tangle-sandbox" + }, + "runner": { + "status": "observed", + "value": "opencode" + }, + "operationId": { + "status": "observed", + "value": "op-live-required-send-9cb4efab-a305-4dab-b7c5-bf2f714c3227" + }, + "profileSnapshotId": { + "status": "missing" + }, + "connectionId": { + "status": "observed", + "value": "connection-live-tangle-sandbox" + }, + "providerSessionId": { + "status": "missing" + }, + "environmentId": { + "status": "observed", + "value": "environment-7f51dbf003e3a8c382341ce022a9ed08" + }, + "replayCursor": { + "status": "missing" + }, + "lastCursor": { + "status": "missing" + }, + "lastProviderSequence": { + "status": "missing" + }, + "eventCount": { + "status": "missing" + }, + "contentBytes": { + "status": "observed", + "value": 539 + }, + "contentTruncated": { + "status": "missing" + }, + "missingSequence": { + "status": "missing" + }, + "terminalReason": { + "status": "missing" + }, + "materializationDigest": { + "status": "observed", + "value": "df331b9c4e1a7074376b3216cf77b54462e87e848b14a10c74ed8e475f21fd07" + }, + "complete": { + "status": "missing" + }, + "status": { + "status": "observed", + "value": "completed" + }, + "error": { + "status": "missing" + }, + "startedAt": { + "status": "missing" + }, + "updatedAt": { + "status": "missing" + }, + "terminalAt": { + "status": "missing" + }, + "placement": { + "status": "missing" + }, + "resourceSample": { + "status": "missing" + }, + "requestedResources": { + "status": "missing" + }, + "machineId": { + "status": "missing" + }, + "runtimeEndpointHost": { + "status": "missing" + }, + "requestedRegion": { + "status": "missing" + }, + "verifiedRegion": { + "status": "missing" + }, + "storagePersistence": { + "status": "missing" + }, + "gpu": { + "status": "missing" + } + }, + "environment": { + "kind": { + "status": "observed", + "value": "sandbox" + }, + "providerEnvironmentId": { + "status": "observed", + "value": "sandbox-307ccb60d7c2" + }, + "provider": { + "status": "observed", + "value": "tangle-sandbox" + }, + "name": { + "status": "missing" + }, + "lifecycle": { + "status": "observed", + "value": "destroyed" + }, + "lifecycleMode": { + "status": "observed", + "value": "ephemeral" + }, + "cleanup": { + "status": "observed", + "value": "delete-after-turn" + }, + "continuity": { + "status": "observed", + "value": "unavailable" + }, + "location": { + "status": "observed", + "value": "remote" + }, + "region": { + "status": "missing" + }, + "runtimeEndpointHost": { + "status": "observed", + "value": "sandbox-307ccb60d7c2.tangle.sh" + }, + "machineId": { + "status": "missing" + }, + "requestedRegion": { + "status": "missing" + }, + "verifiedRegion": { + "status": "missing" + }, + "storagePersistence": { + "status": "observed", + "value": "persistent-home" + }, + "requestedResources": { + "status": "missing" + }, + "resourceSample": { + "status": "missing" + }, + "gpu": { + "status": "missing" + }, + "accountUsage": { + "status": "observed", + "value": { + "completeness": "provider-reported-possibly-defaulted", + "computeMinutes": 766, + "gpuSeconds": 0, + "gpuCostUsd": 0, + "activeSandboxes": 6, + "totalSandboxes": 9223, + "maximumConcurrentSandboxes": 25, + "maximumCpuCores": 8, + "maximumRamGB": 16, + "maximumStorageGB": 256, + "usagePeriodStart": "2026-08-12T00:00:00.000Z", + "usagePeriodEnd": "2026-08-13T00:00:00.000Z", + "sampledAt": "2026-08-12T11:52:03.892Z" + } + }, + "unavailableTelemetry": { + "status": "observed", + "value": [ + "disk-usage:not-exposed-by-provider", + "effective-resources:not-exposed-by-provider", + "machine-id:not-exposed-by-provider", + "machine-ip:not-exposed-by-provider", + "per-sandbox-cpu-ram-storage-cost:not-exposed-by-provider", + "runtime-cpu-memory-usage:request-failed-or-timed-out", + "verified-placement:not-exposed-by-client", + "verified-region:not-exposed-by-provider" + ] + }, + "placement": { + "status": "missing" + }, + "repository": { + "status": "missing" + }, + "gitRef": { + "status": "missing" + }, + "workingDirectory": { + "status": "missing" + }, + "image": { + "status": "missing" + }, + "createdAt": { + "status": "observed", + "value": "2026-08-12T11:52:04.890Z" + }, + "startedAt": { + "status": "missing" + }, + "lastActivityAt": { + "status": "missing" + }, + "expiresAt": { + "status": "missing" + }, + "updatedAt": { + "status": "observed", + "value": "2026-08-12T11:52:27.731Z" + } + } + }, + "cleanup": { + "status": "passed", + "matchedCount": 0, + "remainingCount": 0 + } + }, + { + "index": 13, + "status": "passed", + "elapsedMs": 28946.765106000006, + "runId": "run-00613f47-46ac-47c9-9a4d-f92a2514c729", + "operationId": "op-live-required-send-6092e02b-5339-4e9a-97a4-f5f057bb6a7f", + "model": "tangle-router/glm-5.2", + "runner": "opencode", + "message": "BRAID_CLOUD_EXECUTION_1786535411687-5262EDAE_13_OK", + "environmentId": "sandbox-51bea4ee66aa", + "automaticCleanup": true, + "usage": { + "tokens": { + "status": "observed", + "input": 499, + "output": 23 + }, + "cost": { + "status": "reported", + "usd": 0 + } + }, + "observation": { + "localEnvironmentId": "environment-4f59f3b7690150e80ed409060dacd25c", + "providerEnvironmentId": "sandbox-51bea4ee66aa", + "environmentRecord": { + "id": "environment-4f59f3b7690150e80ed409060dacd25c", + "connectionId": "connection-live-tangle-sandbox", + "kind": "sandbox", + "provider": "tangle-sandbox", + "providerEnvironmentId": "sandbox-51bea4ee66aa", + "lifecycle": "destroyed", + "lifecycleMode": "ephemeral", + "cleanup": "delete-after-turn", + "continuity": "unavailable", + "location": "remote", + "runtimeEndpointHost": "sandbox-51bea4ee66aa.tangle.sh", + "storagePersistence": "persistent-home", + "accountUsage": { + "completeness": "provider-reported-possibly-defaulted", + "computeMinutes": 767, + "gpuSeconds": 0, + "gpuCostUsd": 0, + "activeSandboxes": 6, + "totalSandboxes": 9225, + "maximumConcurrentSandboxes": 25, + "maximumCpuCores": 8, + "maximumRamGB": 16, + "maximumStorageGB": 256, + "usagePeriodStart": "2026-08-12T00:00:00.000Z", + "usagePeriodEnd": "2026-08-13T00:00:00.000Z", + "sampledAt": "2026-08-12T11:52:22.006Z" + }, + "unavailableTelemetry": [ + "disk-usage:not-exposed-by-provider", + "effective-resources:not-exposed-by-provider", + "machine-id:not-exposed-by-provider", + "machine-ip:not-exposed-by-provider", + "per-sandbox-cpu-ram-storage-cost:not-exposed-by-provider", + "runtime-cpu-memory-usage:request-failed-or-timed-out", + "verified-placement:not-exposed-by-client", + "verified-region:not-exposed-by-provider" + ], + "createdAt": "2026-08-12T11:52:22.995Z", + "updatedAt": "2026-08-12T11:52:47.733Z" + }, + "run": { + "inputTokens": { + "status": "observed", + "value": 499 + }, + "outputTokens": { + "status": "observed", + "value": 23 + }, + "reasoningTokens": { + "status": "missing" + }, + "cacheReadTokens": { + "status": "missing" + }, + "cacheWriteTokens": { + "status": "missing" + }, + "costUsd": { + "status": "observed", + "value": 0 + }, + "costStatus": { + "status": "observed", + "value": "reported" + }, + "usage": { + "status": "missing" + }, + "cost": { + "status": "missing" + }, + "tokensKnown": { + "status": "missing" + }, + "usdKnown": { + "status": "missing" + }, + "usageCompleteness": { + "status": "missing" + }, + "latencyMs": { + "status": "missing" + }, + "durationMs": { + "status": "missing" + }, + "model": { + "status": "observed", + "value": "tangle-router/glm-5.2" + }, + "provider": { + "status": "observed", + "value": "tangle-sandbox" + }, + "runner": { + "status": "observed", + "value": "opencode" + }, + "operationId": { + "status": "observed", + "value": "op-live-required-send-6092e02b-5339-4e9a-97a4-f5f057bb6a7f" + }, + "profileSnapshotId": { + "status": "missing" + }, + "connectionId": { + "status": "observed", + "value": "connection-live-tangle-sandbox" + }, + "providerSessionId": { + "status": "missing" + }, + "environmentId": { + "status": "observed", + "value": "environment-4f59f3b7690150e80ed409060dacd25c" + }, + "replayCursor": { + "status": "missing" + }, + "lastCursor": { + "status": "missing" + }, + "lastProviderSequence": { + "status": "missing" + }, + "eventCount": { + "status": "missing" + }, + "contentBytes": { + "status": "observed", + "value": 519 + }, + "contentTruncated": { + "status": "missing" + }, + "missingSequence": { + "status": "missing" + }, + "terminalReason": { + "status": "missing" + }, + "materializationDigest": { + "status": "observed", + "value": "72ca3420eb07053ab55415b499608720874b7d73406903f56c5d093bf10bf917" + }, + "complete": { + "status": "missing" + }, + "status": { + "status": "observed", + "value": "completed" + }, + "error": { + "status": "missing" + }, + "startedAt": { + "status": "missing" + }, + "updatedAt": { + "status": "missing" + }, + "terminalAt": { + "status": "missing" + }, + "placement": { + "status": "missing" + }, + "resourceSample": { + "status": "missing" + }, + "requestedResources": { + "status": "missing" + }, + "machineId": { + "status": "missing" + }, + "runtimeEndpointHost": { + "status": "missing" + }, + "requestedRegion": { + "status": "missing" + }, + "verifiedRegion": { + "status": "missing" + }, + "storagePersistence": { + "status": "missing" + }, + "gpu": { + "status": "missing" + } + }, + "environment": { + "kind": { + "status": "observed", + "value": "sandbox" + }, + "providerEnvironmentId": { + "status": "observed", + "value": "sandbox-51bea4ee66aa" + }, + "provider": { + "status": "observed", + "value": "tangle-sandbox" + }, + "name": { + "status": "missing" + }, + "lifecycle": { + "status": "observed", + "value": "destroyed" + }, + "lifecycleMode": { + "status": "observed", + "value": "ephemeral" + }, + "cleanup": { + "status": "observed", + "value": "delete-after-turn" + }, + "continuity": { + "status": "observed", + "value": "unavailable" + }, + "location": { + "status": "observed", + "value": "remote" + }, + "region": { + "status": "missing" + }, + "runtimeEndpointHost": { + "status": "observed", + "value": "sandbox-51bea4ee66aa.tangle.sh" + }, + "machineId": { + "status": "missing" + }, + "requestedRegion": { + "status": "missing" + }, + "verifiedRegion": { + "status": "missing" + }, + "storagePersistence": { + "status": "observed", + "value": "persistent-home" + }, + "requestedResources": { + "status": "missing" + }, + "resourceSample": { + "status": "missing" + }, + "gpu": { + "status": "missing" + }, + "accountUsage": { + "status": "observed", + "value": { + "completeness": "provider-reported-possibly-defaulted", + "computeMinutes": 767, + "gpuSeconds": 0, + "gpuCostUsd": 0, + "activeSandboxes": 6, + "totalSandboxes": 9225, + "maximumConcurrentSandboxes": 25, + "maximumCpuCores": 8, + "maximumRamGB": 16, + "maximumStorageGB": 256, + "usagePeriodStart": "2026-08-12T00:00:00.000Z", + "usagePeriodEnd": "2026-08-13T00:00:00.000Z", + "sampledAt": "2026-08-12T11:52:22.006Z" + } + }, + "unavailableTelemetry": { + "status": "observed", + "value": [ + "disk-usage:not-exposed-by-provider", + "effective-resources:not-exposed-by-provider", + "machine-id:not-exposed-by-provider", + "machine-ip:not-exposed-by-provider", + "per-sandbox-cpu-ram-storage-cost:not-exposed-by-provider", + "runtime-cpu-memory-usage:request-failed-or-timed-out", + "verified-placement:not-exposed-by-client", + "verified-region:not-exposed-by-provider" + ] + }, + "placement": { + "status": "missing" + }, + "repository": { + "status": "missing" + }, + "gitRef": { + "status": "missing" + }, + "workingDirectory": { + "status": "missing" + }, + "image": { + "status": "missing" + }, + "createdAt": { + "status": "observed", + "value": "2026-08-12T11:52:22.995Z" + }, + "startedAt": { + "status": "missing" + }, + "lastActivityAt": { + "status": "missing" + }, + "expiresAt": { + "status": "missing" + }, + "updatedAt": { + "status": "observed", + "value": "2026-08-12T11:52:47.733Z" + } + } + }, + "cleanup": { + "status": "passed", + "matchedCount": 0, + "remainingCount": 0 + } + }, + { + "index": 14, + "status": "passed", + "elapsedMs": 29423.976594000007, + "runId": "run-a543fcb8-9569-48d8-81d1-fea3f8cc283b", + "operationId": "op-live-required-send-6db73dfc-eb8d-45ef-aad5-a550a099f28b", + "model": "tangle-router/glm-5.2", + "runner": "opencode", + "message": "BRAID_CLOUD_EXECUTION_1786535411687-5262EDAE_14_OK", + "environmentId": "sandbox-3547adf627fb", + "automaticCleanup": true, + "usage": { + "tokens": { + "status": "observed", + "input": 557, + "output": 23 + }, + "cost": { + "status": "reported", + "usd": 0 + } + }, + "observation": { + "localEnvironmentId": "environment-09cd8bcc14ee98283d610a94541c6947", + "providerEnvironmentId": "sandbox-3547adf627fb", + "environmentRecord": { + "id": "environment-09cd8bcc14ee98283d610a94541c6947", + "connectionId": "connection-live-tangle-sandbox", + "kind": "sandbox", + "provider": "tangle-sandbox", + "providerEnvironmentId": "sandbox-3547adf627fb", + "lifecycle": "destroyed", + "lifecycleMode": "ephemeral", + "cleanup": "delete-after-turn", + "continuity": "unavailable", + "location": "remote", + "runtimeEndpointHost": "sandbox-3547adf627fb.tangle.sh", + "storagePersistence": "persistent-home", + "accountUsage": { + "completeness": "provider-reported-possibly-defaulted", + "computeMinutes": 767, + "gpuSeconds": 0, + "gpuCostUsd": 0, + "activeSandboxes": 6, + "totalSandboxes": 9225, + "maximumConcurrentSandboxes": 25, + "maximumCpuCores": 8, + "maximumRamGB": 16, + "maximumStorageGB": 256, + "usagePeriodStart": "2026-08-12T00:00:00.000Z", + "usagePeriodEnd": "2026-08-13T00:00:00.000Z", + "sampledAt": "2026-08-12T11:52:22.627Z" + }, + "unavailableTelemetry": [ + "disk-usage:not-exposed-by-provider", + "effective-resources:not-exposed-by-provider", + "machine-id:not-exposed-by-provider", + "machine-ip:not-exposed-by-provider", + "per-sandbox-cpu-ram-storage-cost:not-exposed-by-provider", + "runtime-cpu-memory-usage:request-failed-or-timed-out", + "verified-placement:not-exposed-by-client", + "verified-region:not-exposed-by-provider" + ], + "createdAt": "2026-08-12T11:52:23.576Z", + "updatedAt": "2026-08-12T11:52:49.843Z" + }, + "run": { + "inputTokens": { + "status": "observed", + "value": 557 + }, + "outputTokens": { + "status": "observed", + "value": 23 + }, + "reasoningTokens": { + "status": "missing" + }, + "cacheReadTokens": { + "status": "missing" + }, + "cacheWriteTokens": { + "status": "missing" + }, + "costUsd": { + "status": "observed", + "value": 0 + }, + "costStatus": { + "status": "observed", + "value": "reported" + }, + "usage": { + "status": "missing" + }, + "cost": { + "status": "missing" + }, + "tokensKnown": { + "status": "missing" + }, + "usdKnown": { + "status": "missing" + }, + "usageCompleteness": { + "status": "missing" + }, + "latencyMs": { + "status": "missing" + }, + "durationMs": { + "status": "missing" + }, + "model": { + "status": "observed", + "value": "tangle-router/glm-5.2" + }, + "provider": { + "status": "observed", + "value": "tangle-sandbox" + }, + "runner": { + "status": "observed", + "value": "opencode" + }, + "operationId": { + "status": "observed", + "value": "op-live-required-send-6db73dfc-eb8d-45ef-aad5-a550a099f28b" + }, + "profileSnapshotId": { + "status": "missing" + }, + "connectionId": { + "status": "observed", + "value": "connection-live-tangle-sandbox" + }, + "providerSessionId": { + "status": "missing" + }, + "environmentId": { + "status": "observed", + "value": "environment-09cd8bcc14ee98283d610a94541c6947" + }, + "replayCursor": { + "status": "missing" + }, + "lastCursor": { + "status": "missing" + }, + "lastProviderSequence": { + "status": "missing" + }, + "eventCount": { + "status": "missing" + }, + "contentBytes": { + "status": "observed", + "value": 538 + }, + "contentTruncated": { + "status": "missing" + }, + "missingSequence": { + "status": "missing" + }, + "terminalReason": { + "status": "missing" + }, + "materializationDigest": { + "status": "observed", + "value": "dccd38b363fe45658ade97ca73afd213c5d09c740f354fdebe6ea07fa9921ac2" + }, + "complete": { + "status": "missing" + }, + "status": { + "status": "observed", + "value": "completed" + }, + "error": { + "status": "missing" + }, + "startedAt": { + "status": "missing" + }, + "updatedAt": { + "status": "missing" + }, + "terminalAt": { + "status": "missing" + }, + "placement": { + "status": "missing" + }, + "resourceSample": { + "status": "missing" + }, + "requestedResources": { + "status": "missing" + }, + "machineId": { + "status": "missing" + }, + "runtimeEndpointHost": { + "status": "missing" + }, + "requestedRegion": { + "status": "missing" + }, + "verifiedRegion": { + "status": "missing" + }, + "storagePersistence": { + "status": "missing" + }, + "gpu": { + "status": "missing" + } + }, + "environment": { + "kind": { + "status": "observed", + "value": "sandbox" + }, + "providerEnvironmentId": { + "status": "observed", + "value": "sandbox-3547adf627fb" + }, + "provider": { + "status": "observed", + "value": "tangle-sandbox" + }, + "name": { + "status": "missing" + }, + "lifecycle": { + "status": "observed", + "value": "destroyed" + }, + "lifecycleMode": { + "status": "observed", + "value": "ephemeral" + }, + "cleanup": { + "status": "observed", + "value": "delete-after-turn" + }, + "continuity": { + "status": "observed", + "value": "unavailable" + }, + "location": { + "status": "observed", + "value": "remote" + }, + "region": { + "status": "missing" + }, + "runtimeEndpointHost": { + "status": "observed", + "value": "sandbox-3547adf627fb.tangle.sh" + }, + "machineId": { + "status": "missing" + }, + "requestedRegion": { + "status": "missing" + }, + "verifiedRegion": { + "status": "missing" + }, + "storagePersistence": { + "status": "observed", + "value": "persistent-home" + }, + "requestedResources": { + "status": "missing" + }, + "resourceSample": { + "status": "missing" + }, + "gpu": { + "status": "missing" + }, + "accountUsage": { + "status": "observed", + "value": { + "completeness": "provider-reported-possibly-defaulted", + "computeMinutes": 767, + "gpuSeconds": 0, + "gpuCostUsd": 0, + "activeSandboxes": 6, + "totalSandboxes": 9225, + "maximumConcurrentSandboxes": 25, + "maximumCpuCores": 8, + "maximumRamGB": 16, + "maximumStorageGB": 256, + "usagePeriodStart": "2026-08-12T00:00:00.000Z", + "usagePeriodEnd": "2026-08-13T00:00:00.000Z", + "sampledAt": "2026-08-12T11:52:22.627Z" + } + }, + "unavailableTelemetry": { + "status": "observed", + "value": [ + "disk-usage:not-exposed-by-provider", + "effective-resources:not-exposed-by-provider", + "machine-id:not-exposed-by-provider", + "machine-ip:not-exposed-by-provider", + "per-sandbox-cpu-ram-storage-cost:not-exposed-by-provider", + "runtime-cpu-memory-usage:request-failed-or-timed-out", + "verified-placement:not-exposed-by-client", + "verified-region:not-exposed-by-provider" + ] + }, + "placement": { + "status": "missing" + }, + "repository": { + "status": "missing" + }, + "gitRef": { + "status": "missing" + }, + "workingDirectory": { + "status": "missing" + }, + "image": { + "status": "missing" + }, + "createdAt": { + "status": "observed", + "value": "2026-08-12T11:52:23.576Z" + }, + "startedAt": { + "status": "missing" + }, + "lastActivityAt": { + "status": "missing" + }, + "expiresAt": { + "status": "missing" + }, + "updatedAt": { + "status": "observed", + "value": "2026-08-12T11:52:49.843Z" + } + } + }, + "cleanup": { + "status": "passed", + "matchedCount": 0, + "remainingCount": 0 + } + }, + { + "index": 15, + "status": "passed", + "elapsedMs": 28840.258147000015, + "runId": "run-227117f8-98fc-4914-96b7-639a64ece6f1", + "operationId": "op-live-required-send-d7e2acb6-1b10-45cf-a3d0-d76ae58ecf3e", + "model": "tangle-router/glm-5.2", + "runner": "opencode", + "message": "BRAID_CLOUD_EXECUTION_1786535411687-5262EDAE_15_OK", + "environmentId": "sandbox-80279ba5e7f8", + "automaticCleanup": true, + "usage": { + "tokens": { + "status": "observed", + "input": 574, + "output": 23 + }, + "cost": { + "status": "reported", + "usd": 0 + } + }, + "observation": { + "localEnvironmentId": "environment-45e9e85fcdd821f283105e0cc17957c5", + "providerEnvironmentId": "sandbox-80279ba5e7f8", + "environmentRecord": { + "id": "environment-45e9e85fcdd821f283105e0cc17957c5", + "connectionId": "connection-live-tangle-sandbox", + "kind": "sandbox", + "provider": "tangle-sandbox", + "providerEnvironmentId": "sandbox-80279ba5e7f8", + "lifecycle": "destroyed", + "lifecycleMode": "ephemeral", + "cleanup": "delete-after-turn", + "continuity": "unavailable", + "location": "remote", + "runtimeEndpointHost": "sandbox-80279ba5e7f8.tangle.sh", + "storagePersistence": "persistent-home", + "accountUsage": { + "completeness": "provider-reported-possibly-defaulted", + "computeMinutes": 767, + "gpuSeconds": 0, + "gpuCostUsd": 0, + "activeSandboxes": 6, + "totalSandboxes": 9226, + "maximumConcurrentSandboxes": 25, + "maximumCpuCores": 8, + "maximumRamGB": 16, + "maximumStorageGB": 256, + "usagePeriodStart": "2026-08-12T00:00:00.000Z", + "usagePeriodEnd": "2026-08-13T00:00:00.000Z", + "sampledAt": "2026-08-12T11:52:31.431Z" + }, + "unavailableTelemetry": [ + "disk-usage:not-exposed-by-provider", + "effective-resources:not-exposed-by-provider", + "machine-id:not-exposed-by-provider", + "machine-ip:not-exposed-by-provider", + "per-sandbox-cpu-ram-storage-cost:not-exposed-by-provider", + "runtime-cpu-memory-usage:request-failed-or-timed-out", + "verified-placement:not-exposed-by-client", + "verified-region:not-exposed-by-provider" + ], + "createdAt": "2026-08-12T11:52:32.470Z", + "updatedAt": "2026-08-12T11:52:57.829Z" + }, + "run": { + "inputTokens": { + "status": "observed", + "value": 574 + }, + "outputTokens": { + "status": "observed", + "value": 23 + }, + "reasoningTokens": { + "status": "missing" + }, + "cacheReadTokens": { + "status": "missing" + }, + "cacheWriteTokens": { + "status": "missing" + }, + "costUsd": { + "status": "observed", + "value": 0 + }, + "costStatus": { + "status": "observed", + "value": "reported" + }, + "usage": { + "status": "missing" + }, + "cost": { + "status": "missing" + }, + "tokensKnown": { + "status": "missing" + }, + "usdKnown": { + "status": "missing" + }, + "usageCompleteness": { + "status": "missing" + }, + "latencyMs": { + "status": "missing" + }, + "durationMs": { + "status": "missing" + }, + "model": { + "status": "observed", + "value": "tangle-router/glm-5.2" + }, + "provider": { + "status": "observed", + "value": "tangle-sandbox" + }, + "runner": { + "status": "observed", + "value": "opencode" + }, + "operationId": { + "status": "observed", + "value": "op-live-required-send-d7e2acb6-1b10-45cf-a3d0-d76ae58ecf3e" + }, + "profileSnapshotId": { + "status": "missing" + }, + "connectionId": { + "status": "observed", + "value": "connection-live-tangle-sandbox" + }, + "providerSessionId": { + "status": "missing" + }, + "environmentId": { + "status": "observed", + "value": "environment-45e9e85fcdd821f283105e0cc17957c5" + }, + "replayCursor": { + "status": "missing" + }, + "lastCursor": { + "status": "missing" + }, + "lastProviderSequence": { + "status": "missing" + }, + "eventCount": { + "status": "missing" + }, + "contentBytes": { + "status": "observed", + "value": 620 + }, + "contentTruncated": { + "status": "missing" + }, + "missingSequence": { + "status": "missing" + }, + "terminalReason": { + "status": "missing" + }, + "materializationDigest": { + "status": "observed", + "value": "f0cca21d4cb66355d794465286b4f790bd29df7c6bc121521b4732d08ad76f5e" + }, + "complete": { + "status": "missing" + }, + "status": { + "status": "observed", + "value": "completed" + }, + "error": { + "status": "missing" + }, + "startedAt": { + "status": "missing" + }, + "updatedAt": { + "status": "missing" + }, + "terminalAt": { + "status": "missing" + }, + "placement": { + "status": "missing" + }, + "resourceSample": { + "status": "missing" + }, + "requestedResources": { + "status": "missing" + }, + "machineId": { + "status": "missing" + }, + "runtimeEndpointHost": { + "status": "missing" + }, + "requestedRegion": { + "status": "missing" + }, + "verifiedRegion": { + "status": "missing" + }, + "storagePersistence": { + "status": "missing" + }, + "gpu": { + "status": "missing" + } + }, + "environment": { + "kind": { + "status": "observed", + "value": "sandbox" + }, + "providerEnvironmentId": { + "status": "observed", + "value": "sandbox-80279ba5e7f8" + }, + "provider": { + "status": "observed", + "value": "tangle-sandbox" + }, + "name": { + "status": "missing" + }, + "lifecycle": { + "status": "observed", + "value": "destroyed" + }, + "lifecycleMode": { + "status": "observed", + "value": "ephemeral" + }, + "cleanup": { + "status": "observed", + "value": "delete-after-turn" + }, + "continuity": { + "status": "observed", + "value": "unavailable" + }, + "location": { + "status": "observed", + "value": "remote" + }, + "region": { + "status": "missing" + }, + "runtimeEndpointHost": { + "status": "observed", + "value": "sandbox-80279ba5e7f8.tangle.sh" + }, + "machineId": { + "status": "missing" + }, + "requestedRegion": { + "status": "missing" + }, + "verifiedRegion": { + "status": "missing" + }, + "storagePersistence": { + "status": "observed", + "value": "persistent-home" + }, + "requestedResources": { + "status": "missing" + }, + "resourceSample": { + "status": "missing" + }, + "gpu": { + "status": "missing" + }, + "accountUsage": { + "status": "observed", + "value": { + "completeness": "provider-reported-possibly-defaulted", + "computeMinutes": 767, + "gpuSeconds": 0, + "gpuCostUsd": 0, + "activeSandboxes": 6, + "totalSandboxes": 9226, + "maximumConcurrentSandboxes": 25, + "maximumCpuCores": 8, + "maximumRamGB": 16, + "maximumStorageGB": 256, + "usagePeriodStart": "2026-08-12T00:00:00.000Z", + "usagePeriodEnd": "2026-08-13T00:00:00.000Z", + "sampledAt": "2026-08-12T11:52:31.431Z" + } + }, + "unavailableTelemetry": { + "status": "observed", + "value": [ + "disk-usage:not-exposed-by-provider", + "effective-resources:not-exposed-by-provider", + "machine-id:not-exposed-by-provider", + "machine-ip:not-exposed-by-provider", + "per-sandbox-cpu-ram-storage-cost:not-exposed-by-provider", + "runtime-cpu-memory-usage:request-failed-or-timed-out", + "verified-placement:not-exposed-by-client", + "verified-region:not-exposed-by-provider" + ] + }, + "placement": { + "status": "missing" + }, + "repository": { + "status": "missing" + }, + "gitRef": { + "status": "missing" + }, + "workingDirectory": { + "status": "missing" + }, + "image": { + "status": "missing" + }, + "createdAt": { + "status": "observed", + "value": "2026-08-12T11:52:32.470Z" + }, + "startedAt": { + "status": "missing" + }, + "lastActivityAt": { + "status": "missing" + }, + "expiresAt": { + "status": "missing" + }, + "updatedAt": { + "status": "observed", + "value": "2026-08-12T11:52:57.829Z" + } + } + }, + "cleanup": { + "status": "passed", + "matchedCount": 0, + "remainingCount": 0 + } + }, + { + "index": 16, + "status": "passed", + "elapsedMs": 28907.320368999994, + "runId": "run-cfb4dbdb-6e03-4a2d-87c0-8e2d7456d00f", + "operationId": "op-live-required-send-326f937c-bd1a-44cc-9700-3bb63638ae19", + "model": "tangle-router/glm-5.2", + "runner": "opencode", + "message": "BRAID_CLOUD_EXECUTION_1786535411687-5262EDAE_16_OK", + "environmentId": "sandbox-b7abb7c08151", + "automaticCleanup": true, + "usage": { + "tokens": { + "status": "observed", + "input": 573, + "output": 23 + }, + "cost": { + "status": "reported", + "usd": 0 + } + }, + "observation": { + "localEnvironmentId": "environment-b7a0a88ab194b93911ceaa1311941c40", + "providerEnvironmentId": "sandbox-b7abb7c08151", + "environmentRecord": { + "id": "environment-b7a0a88ab194b93911ceaa1311941c40", + "connectionId": "connection-live-tangle-sandbox", + "kind": "sandbox", + "provider": "tangle-sandbox", + "providerEnvironmentId": "sandbox-b7abb7c08151", + "lifecycle": "destroyed", + "lifecycleMode": "ephemeral", + "cleanup": "delete-after-turn", + "continuity": "unavailable", + "location": "remote", + "runtimeEndpointHost": "sandbox-b7abb7c08151.tangle.sh", + "storagePersistence": "persistent-home", + "accountUsage": { + "completeness": "provider-reported-possibly-defaulted", + "computeMinutes": 767, + "gpuSeconds": 0, + "gpuCostUsd": 0, + "activeSandboxes": 7, + "totalSandboxes": 9227, + "maximumConcurrentSandboxes": 25, + "maximumCpuCores": 8, + "maximumRamGB": 16, + "maximumStorageGB": 256, + "usagePeriodStart": "2026-08-12T00:00:00.000Z", + "usagePeriodEnd": "2026-08-13T00:00:00.000Z", + "sampledAt": "2026-08-12T11:52:32.476Z" + }, + "unavailableTelemetry": [ + "disk-usage:not-exposed-by-provider", + "effective-resources:not-exposed-by-provider", + "machine-id:not-exposed-by-provider", + "machine-ip:not-exposed-by-provider", + "per-sandbox-cpu-ram-storage-cost:not-exposed-by-provider", + "runtime-cpu-memory-usage:request-failed-or-timed-out", + "verified-placement:not-exposed-by-client", + "verified-region:not-exposed-by-provider" + ], + "createdAt": "2026-08-12T11:52:33.670Z", + "updatedAt": "2026-08-12T11:52:58.108Z" + }, + "run": { + "inputTokens": { + "status": "observed", + "value": 573 + }, + "outputTokens": { + "status": "observed", + "value": 23 + }, + "reasoningTokens": { + "status": "missing" + }, + "cacheReadTokens": { + "status": "missing" + }, + "cacheWriteTokens": { + "status": "missing" + }, + "costUsd": { + "status": "observed", + "value": 0 + }, + "costStatus": { + "status": "observed", + "value": "reported" + }, + "usage": { + "status": "missing" + }, + "cost": { + "status": "missing" + }, + "tokensKnown": { + "status": "missing" + }, + "usdKnown": { + "status": "missing" + }, + "usageCompleteness": { + "status": "missing" + }, + "latencyMs": { + "status": "missing" + }, + "durationMs": { + "status": "missing" + }, + "model": { + "status": "observed", + "value": "tangle-router/glm-5.2" + }, + "provider": { + "status": "observed", + "value": "tangle-sandbox" + }, + "runner": { + "status": "observed", + "value": "opencode" + }, + "operationId": { + "status": "observed", + "value": "op-live-required-send-326f937c-bd1a-44cc-9700-3bb63638ae19" + }, + "profileSnapshotId": { + "status": "missing" + }, + "connectionId": { + "status": "observed", + "value": "connection-live-tangle-sandbox" + }, + "providerSessionId": { + "status": "missing" + }, + "environmentId": { + "status": "observed", + "value": "environment-b7a0a88ab194b93911ceaa1311941c40" + }, + "replayCursor": { + "status": "missing" + }, + "lastCursor": { + "status": "missing" + }, + "lastProviderSequence": { + "status": "missing" + }, + "eventCount": { + "status": "missing" + }, + "contentBytes": { + "status": "observed", + "value": 558 + }, + "contentTruncated": { + "status": "missing" + }, + "missingSequence": { + "status": "missing" + }, + "terminalReason": { + "status": "missing" + }, + "materializationDigest": { + "status": "observed", + "value": "b8d7b8bf763a13d8a1d23fdfa684c9de6676f21920a715e4cab92ac9089639b9" + }, + "complete": { + "status": "missing" + }, + "status": { + "status": "observed", + "value": "completed" + }, + "error": { + "status": "missing" + }, + "startedAt": { + "status": "missing" + }, + "updatedAt": { + "status": "missing" + }, + "terminalAt": { + "status": "missing" + }, + "placement": { + "status": "missing" + }, + "resourceSample": { + "status": "missing" + }, + "requestedResources": { + "status": "missing" + }, + "machineId": { + "status": "missing" + }, + "runtimeEndpointHost": { + "status": "missing" + }, + "requestedRegion": { + "status": "missing" + }, + "verifiedRegion": { + "status": "missing" + }, + "storagePersistence": { + "status": "missing" + }, + "gpu": { + "status": "missing" + } + }, + "environment": { + "kind": { + "status": "observed", + "value": "sandbox" + }, + "providerEnvironmentId": { + "status": "observed", + "value": "sandbox-b7abb7c08151" + }, + "provider": { + "status": "observed", + "value": "tangle-sandbox" + }, + "name": { + "status": "missing" + }, + "lifecycle": { + "status": "observed", + "value": "destroyed" + }, + "lifecycleMode": { + "status": "observed", + "value": "ephemeral" + }, + "cleanup": { + "status": "observed", + "value": "delete-after-turn" + }, + "continuity": { + "status": "observed", + "value": "unavailable" + }, + "location": { + "status": "observed", + "value": "remote" + }, + "region": { + "status": "missing" + }, + "runtimeEndpointHost": { + "status": "observed", + "value": "sandbox-b7abb7c08151.tangle.sh" + }, + "machineId": { + "status": "missing" + }, + "requestedRegion": { + "status": "missing" + }, + "verifiedRegion": { + "status": "missing" + }, + "storagePersistence": { + "status": "observed", + "value": "persistent-home" + }, + "requestedResources": { + "status": "missing" + }, + "resourceSample": { + "status": "missing" + }, + "gpu": { + "status": "missing" + }, + "accountUsage": { + "status": "observed", + "value": { + "completeness": "provider-reported-possibly-defaulted", + "computeMinutes": 767, + "gpuSeconds": 0, + "gpuCostUsd": 0, + "activeSandboxes": 7, + "totalSandboxes": 9227, + "maximumConcurrentSandboxes": 25, + "maximumCpuCores": 8, + "maximumRamGB": 16, + "maximumStorageGB": 256, + "usagePeriodStart": "2026-08-12T00:00:00.000Z", + "usagePeriodEnd": "2026-08-13T00:00:00.000Z", + "sampledAt": "2026-08-12T11:52:32.476Z" + } + }, + "unavailableTelemetry": { + "status": "observed", + "value": [ + "disk-usage:not-exposed-by-provider", + "effective-resources:not-exposed-by-provider", + "machine-id:not-exposed-by-provider", + "machine-ip:not-exposed-by-provider", + "per-sandbox-cpu-ram-storage-cost:not-exposed-by-provider", + "runtime-cpu-memory-usage:request-failed-or-timed-out", + "verified-placement:not-exposed-by-client", + "verified-region:not-exposed-by-provider" + ] + }, + "placement": { + "status": "missing" + }, + "repository": { + "status": "missing" + }, + "gitRef": { + "status": "missing" + }, + "workingDirectory": { + "status": "missing" + }, + "image": { + "status": "missing" + }, + "createdAt": { + "status": "observed", + "value": "2026-08-12T11:52:33.670Z" + }, + "startedAt": { + "status": "missing" + }, + "lastActivityAt": { + "status": "missing" + }, + "expiresAt": { + "status": "missing" + }, + "updatedAt": { + "status": "observed", + "value": "2026-08-12T11:52:58.108Z" + } + } + }, + "cleanup": { + "status": "passed", + "matchedCount": 0, + "remainingCount": 0 + } + }, + { + "index": 17, + "status": "passed", + "elapsedMs": 24671.073481, + "runId": "run-120ad926-4737-4ddc-8dab-e315dde0de48", + "operationId": "op-live-required-send-5692f5f5-7793-44f5-8e84-ee91f4dceed6", + "model": "tangle-router/glm-5.2", + "runner": "opencode", + "message": "BRAID_CLOUD_EXECUTION_1786535411687-5262EDAE_17_OK", + "environmentId": "sandbox-0dd159fdf7ff", + "automaticCleanup": true, + "usage": { + "tokens": { + "status": "observed", + "input": 507, + "output": 23 + }, + "cost": { + "status": "reported", + "usd": 0 + } + }, + "observation": { + "localEnvironmentId": "environment-1567b0e8890ba3062c040f3b31ce2683", + "providerEnvironmentId": "sandbox-0dd159fdf7ff", + "environmentRecord": { + "id": "environment-1567b0e8890ba3062c040f3b31ce2683", + "connectionId": "connection-live-tangle-sandbox", + "kind": "sandbox", + "provider": "tangle-sandbox", + "providerEnvironmentId": "sandbox-0dd159fdf7ff", + "lifecycle": "destroyed", + "lifecycleMode": "ephemeral", + "cleanup": "delete-after-turn", + "continuity": "unavailable", + "location": "remote", + "runtimeEndpointHost": "sandbox-0dd159fdf7ff.tangle.sh", + "storagePersistence": "persistent-home", + "accountUsage": { + "completeness": "provider-reported-possibly-defaulted", + "computeMinutes": 768, + "gpuSeconds": 0, + "gpuCostUsd": 0, + "activeSandboxes": 6, + "totalSandboxes": 9229, + "maximumConcurrentSandboxes": 25, + "maximumCpuCores": 8, + "maximumRamGB": 16, + "maximumStorageGB": 256, + "usagePeriodStart": "2026-08-12T00:00:00.000Z", + "usagePeriodEnd": "2026-08-13T00:00:00.000Z", + "sampledAt": "2026-08-12T11:52:52.318Z" + }, + "unavailableTelemetry": [ + "disk-usage:not-exposed-by-provider", + "effective-resources:not-exposed-by-provider", + "machine-id:not-exposed-by-provider", + "machine-ip:not-exposed-by-provider", + "per-sandbox-cpu-ram-storage-cost:not-exposed-by-provider", + "runtime-cpu-memory-usage:request-failed-or-timed-out", + "verified-placement:not-exposed-by-client", + "verified-region:not-exposed-by-provider" + ], + "createdAt": "2026-08-12T11:52:52.982Z", + "updatedAt": "2026-08-12T11:53:14.122Z" + }, + "run": { + "inputTokens": { + "status": "observed", + "value": 507 + }, + "outputTokens": { + "status": "observed", + "value": 23 + }, + "reasoningTokens": { + "status": "missing" + }, + "cacheReadTokens": { + "status": "missing" + }, + "cacheWriteTokens": { + "status": "missing" + }, + "costUsd": { + "status": "observed", + "value": 0 + }, + "costStatus": { + "status": "observed", + "value": "reported" + }, + "usage": { + "status": "missing" + }, + "cost": { + "status": "missing" + }, + "tokensKnown": { + "status": "missing" + }, + "usdKnown": { + "status": "missing" + }, + "usageCompleteness": { + "status": "missing" + }, + "latencyMs": { + "status": "missing" + }, + "durationMs": { + "status": "missing" + }, + "model": { + "status": "observed", + "value": "tangle-router/glm-5.2" + }, + "provider": { + "status": "observed", + "value": "tangle-sandbox" + }, + "runner": { + "status": "observed", + "value": "opencode" + }, + "operationId": { + "status": "observed", + "value": "op-live-required-send-5692f5f5-7793-44f5-8e84-ee91f4dceed6" + }, + "profileSnapshotId": { + "status": "missing" + }, + "connectionId": { + "status": "observed", + "value": "connection-live-tangle-sandbox" + }, + "providerSessionId": { + "status": "missing" + }, + "environmentId": { + "status": "observed", + "value": "environment-1567b0e8890ba3062c040f3b31ce2683" + }, + "replayCursor": { + "status": "missing" + }, + "lastCursor": { + "status": "missing" + }, + "lastProviderSequence": { + "status": "missing" + }, + "eventCount": { + "status": "missing" + }, + "contentBytes": { + "status": "observed", + "value": 549 + }, + "contentTruncated": { + "status": "missing" + }, + "missingSequence": { + "status": "missing" + }, + "terminalReason": { + "status": "missing" + }, + "materializationDigest": { + "status": "observed", + "value": "072565bf84d370c52d77aa34689ce912d196f23872bff3885fbd82cd434c4dac" + }, + "complete": { + "status": "missing" + }, + "status": { + "status": "observed", + "value": "completed" + }, + "error": { + "status": "missing" + }, + "startedAt": { + "status": "missing" + }, + "updatedAt": { + "status": "missing" + }, + "terminalAt": { + "status": "missing" + }, + "placement": { + "status": "missing" + }, + "resourceSample": { + "status": "missing" + }, + "requestedResources": { + "status": "missing" + }, + "machineId": { + "status": "missing" + }, + "runtimeEndpointHost": { + "status": "missing" + }, + "requestedRegion": { + "status": "missing" + }, + "verifiedRegion": { + "status": "missing" + }, + "storagePersistence": { + "status": "missing" + }, + "gpu": { + "status": "missing" + } + }, + "environment": { + "kind": { + "status": "observed", + "value": "sandbox" + }, + "providerEnvironmentId": { + "status": "observed", + "value": "sandbox-0dd159fdf7ff" + }, + "provider": { + "status": "observed", + "value": "tangle-sandbox" + }, + "name": { + "status": "missing" + }, + "lifecycle": { + "status": "observed", + "value": "destroyed" + }, + "lifecycleMode": { + "status": "observed", + "value": "ephemeral" + }, + "cleanup": { + "status": "observed", + "value": "delete-after-turn" + }, + "continuity": { + "status": "observed", + "value": "unavailable" + }, + "location": { + "status": "observed", + "value": "remote" + }, + "region": { + "status": "missing" + }, + "runtimeEndpointHost": { + "status": "observed", + "value": "sandbox-0dd159fdf7ff.tangle.sh" + }, + "machineId": { + "status": "missing" + }, + "requestedRegion": { + "status": "missing" + }, + "verifiedRegion": { + "status": "missing" + }, + "storagePersistence": { + "status": "observed", + "value": "persistent-home" + }, + "requestedResources": { + "status": "missing" + }, + "resourceSample": { + "status": "missing" + }, + "gpu": { + "status": "missing" + }, + "accountUsage": { + "status": "observed", + "value": { + "completeness": "provider-reported-possibly-defaulted", + "computeMinutes": 768, + "gpuSeconds": 0, + "gpuCostUsd": 0, + "activeSandboxes": 6, + "totalSandboxes": 9229, + "maximumConcurrentSandboxes": 25, + "maximumCpuCores": 8, + "maximumRamGB": 16, + "maximumStorageGB": 256, + "usagePeriodStart": "2026-08-12T00:00:00.000Z", + "usagePeriodEnd": "2026-08-13T00:00:00.000Z", + "sampledAt": "2026-08-12T11:52:52.318Z" + } + }, + "unavailableTelemetry": { + "status": "observed", + "value": [ + "disk-usage:not-exposed-by-provider", + "effective-resources:not-exposed-by-provider", + "machine-id:not-exposed-by-provider", + "machine-ip:not-exposed-by-provider", + "per-sandbox-cpu-ram-storage-cost:not-exposed-by-provider", + "runtime-cpu-memory-usage:request-failed-or-timed-out", + "verified-placement:not-exposed-by-client", + "verified-region:not-exposed-by-provider" + ] + }, + "placement": { + "status": "missing" + }, + "repository": { + "status": "missing" + }, + "gitRef": { + "status": "missing" + }, + "workingDirectory": { + "status": "missing" + }, + "image": { + "status": "missing" + }, + "createdAt": { + "status": "observed", + "value": "2026-08-12T11:52:52.982Z" + }, + "startedAt": { + "status": "missing" + }, + "lastActivityAt": { + "status": "missing" + }, + "expiresAt": { + "status": "missing" + }, + "updatedAt": { + "status": "observed", + "value": "2026-08-12T11:53:14.122Z" + } + } + }, + "cleanup": { + "status": "passed", + "matchedCount": 0, + "remainingCount": 0 + } + }, + { + "index": 18, + "status": "passed", + "elapsedMs": 25512.787458000006, + "runId": "run-c5289351-1adb-4c19-9835-fdff970a4249", + "operationId": "op-live-required-send-008d4885-8461-41a8-a3b0-957d48eacc41", + "model": "tangle-router/glm-5.2", + "runner": "opencode", + "message": "BRAID_CLOUD_EXECUTION_1786535411687-5262EDAE_18_OK", + "environmentId": "sandbox-e3fb1a9f927c", + "automaticCleanup": true, + "usage": { + "tokens": { + "status": "observed", + "input": 478, + "output": 23 + }, + "cost": { + "status": "reported", + "usd": 0 + } + }, + "observation": { + "localEnvironmentId": "environment-b75aa39c903626a6f3e8774bfad58493", + "providerEnvironmentId": "sandbox-e3fb1a9f927c", + "environmentRecord": { + "id": "environment-b75aa39c903626a6f3e8774bfad58493", + "connectionId": "connection-live-tangle-sandbox", + "kind": "sandbox", + "provider": "tangle-sandbox", + "providerEnvironmentId": "sandbox-e3fb1a9f927c", + "lifecycle": "destroyed", + "lifecycleMode": "ephemeral", + "cleanup": "delete-after-turn", + "continuity": "unavailable", + "location": "remote", + "runtimeEndpointHost": "sandbox-e3fb1a9f927c.tangle.sh", + "storagePersistence": "persistent-home", + "accountUsage": { + "completeness": "provider-reported-possibly-defaulted", + "computeMinutes": 768, + "gpuSeconds": 0, + "gpuCostUsd": 0, + "activeSandboxes": 7, + "totalSandboxes": 9229, + "maximumConcurrentSandboxes": 25, + "maximumCpuCores": 8, + "maximumRamGB": 16, + "maximumStorageGB": 256, + "usagePeriodStart": "2026-08-12T00:00:00.000Z", + "usagePeriodEnd": "2026-08-13T00:00:00.000Z", + "sampledAt": "2026-08-12T11:52:53.498Z" + }, + "unavailableTelemetry": [ + "disk-usage:not-exposed-by-provider", + "effective-resources:not-exposed-by-provider", + "machine-id:not-exposed-by-provider", + "machine-ip:not-exposed-by-provider", + "per-sandbox-cpu-ram-storage-cost:not-exposed-by-provider", + "runtime-cpu-memory-usage:request-failed-or-timed-out", + "verified-placement:not-exposed-by-client", + "verified-region:not-exposed-by-provider" + ], + "createdAt": "2026-08-12T11:52:54.149Z", + "updatedAt": "2026-08-12T11:53:16.513Z" + }, + "run": { + "inputTokens": { + "status": "observed", + "value": 478 + }, + "outputTokens": { + "status": "observed", + "value": 23 + }, + "reasoningTokens": { + "status": "missing" + }, + "cacheReadTokens": { + "status": "missing" + }, + "cacheWriteTokens": { + "status": "missing" + }, + "costUsd": { + "status": "observed", + "value": 0 + }, + "costStatus": { + "status": "observed", + "value": "reported" + }, + "usage": { + "status": "missing" + }, + "cost": { + "status": "missing" + }, + "tokensKnown": { + "status": "missing" + }, + "usdKnown": { + "status": "missing" + }, + "usageCompleteness": { + "status": "missing" + }, + "latencyMs": { + "status": "missing" + }, + "durationMs": { + "status": "missing" + }, + "model": { + "status": "observed", + "value": "tangle-router/glm-5.2" + }, + "provider": { + "status": "observed", + "value": "tangle-sandbox" + }, + "runner": { + "status": "observed", + "value": "opencode" + }, + "operationId": { + "status": "observed", + "value": "op-live-required-send-008d4885-8461-41a8-a3b0-957d48eacc41" + }, + "profileSnapshotId": { + "status": "missing" + }, + "connectionId": { + "status": "observed", + "value": "connection-live-tangle-sandbox" + }, + "providerSessionId": { + "status": "missing" + }, + "environmentId": { + "status": "observed", + "value": "environment-b75aa39c903626a6f3e8774bfad58493" + }, + "replayCursor": { + "status": "missing" + }, + "lastCursor": { + "status": "missing" + }, + "lastProviderSequence": { + "status": "missing" + }, + "eventCount": { + "status": "missing" + }, + "contentBytes": { + "status": "observed", + "value": 460 + }, + "contentTruncated": { + "status": "missing" + }, + "missingSequence": { + "status": "missing" + }, + "terminalReason": { + "status": "missing" + }, + "materializationDigest": { + "status": "observed", + "value": "458052175cd76d5b6f238d4467fd2cb3469f5f8894e0027cfb4d55b936befba5" + }, + "complete": { + "status": "missing" + }, + "status": { + "status": "observed", + "value": "completed" + }, + "error": { + "status": "missing" + }, + "startedAt": { + "status": "missing" + }, + "updatedAt": { + "status": "missing" + }, + "terminalAt": { + "status": "missing" + }, + "placement": { + "status": "missing" + }, + "resourceSample": { + "status": "missing" + }, + "requestedResources": { + "status": "missing" + }, + "machineId": { + "status": "missing" + }, + "runtimeEndpointHost": { + "status": "missing" + }, + "requestedRegion": { + "status": "missing" + }, + "verifiedRegion": { + "status": "missing" + }, + "storagePersistence": { + "status": "missing" + }, + "gpu": { + "status": "missing" + } + }, + "environment": { + "kind": { + "status": "observed", + "value": "sandbox" + }, + "providerEnvironmentId": { + "status": "observed", + "value": "sandbox-e3fb1a9f927c" + }, + "provider": { + "status": "observed", + "value": "tangle-sandbox" + }, + "name": { + "status": "missing" + }, + "lifecycle": { + "status": "observed", + "value": "destroyed" + }, + "lifecycleMode": { + "status": "observed", + "value": "ephemeral" + }, + "cleanup": { + "status": "observed", + "value": "delete-after-turn" + }, + "continuity": { + "status": "observed", + "value": "unavailable" + }, + "location": { + "status": "observed", + "value": "remote" + }, + "region": { + "status": "missing" + }, + "runtimeEndpointHost": { + "status": "observed", + "value": "sandbox-e3fb1a9f927c.tangle.sh" + }, + "machineId": { + "status": "missing" + }, + "requestedRegion": { + "status": "missing" + }, + "verifiedRegion": { + "status": "missing" + }, + "storagePersistence": { + "status": "observed", + "value": "persistent-home" + }, + "requestedResources": { + "status": "missing" + }, + "resourceSample": { + "status": "missing" + }, + "gpu": { + "status": "missing" + }, + "accountUsage": { + "status": "observed", + "value": { + "completeness": "provider-reported-possibly-defaulted", + "computeMinutes": 768, + "gpuSeconds": 0, + "gpuCostUsd": 0, + "activeSandboxes": 7, + "totalSandboxes": 9229, + "maximumConcurrentSandboxes": 25, + "maximumCpuCores": 8, + "maximumRamGB": 16, + "maximumStorageGB": 256, + "usagePeriodStart": "2026-08-12T00:00:00.000Z", + "usagePeriodEnd": "2026-08-13T00:00:00.000Z", + "sampledAt": "2026-08-12T11:52:53.498Z" + } + }, + "unavailableTelemetry": { + "status": "observed", + "value": [ + "disk-usage:not-exposed-by-provider", + "effective-resources:not-exposed-by-provider", + "machine-id:not-exposed-by-provider", + "machine-ip:not-exposed-by-provider", + "per-sandbox-cpu-ram-storage-cost:not-exposed-by-provider", + "runtime-cpu-memory-usage:request-failed-or-timed-out", + "verified-placement:not-exposed-by-client", + "verified-region:not-exposed-by-provider" + ] + }, + "placement": { + "status": "missing" + }, + "repository": { + "status": "missing" + }, + "gitRef": { + "status": "missing" + }, + "workingDirectory": { + "status": "missing" + }, + "image": { + "status": "missing" + }, + "createdAt": { + "status": "observed", + "value": "2026-08-12T11:52:54.149Z" + }, + "startedAt": { + "status": "missing" + }, + "lastActivityAt": { + "status": "missing" + }, + "expiresAt": { + "status": "missing" + }, + "updatedAt": { + "status": "observed", + "value": "2026-08-12T11:53:16.513Z" + } + } + }, + "cleanup": { + "status": "passed", + "matchedCount": 0, + "remainingCount": 0 + } + }, + { + "index": 19, + "status": "passed", + "elapsedMs": 28808.335885000008, + "runId": "run-84198034-bad9-4e6e-830e-40c2741a225d", + "operationId": "op-live-required-send-6db856fb-bb63-4f1e-868a-a9f4d7d9ee90", + "model": "tangle-router/glm-5.2", + "runner": "opencode", + "message": "BRAID_CLOUD_EXECUTION_1786535411687-5262EDAE_19_OK", + "environmentId": "sandbox-6faba110f08b", + "automaticCleanup": true, + "usage": { + "tokens": { + "status": "observed", + "input": 575, + "output": 23 + }, + "cost": { + "status": "reported", + "usd": 0 + } + }, + "observation": { + "localEnvironmentId": "environment-36981531507c22b545b3866cef5810b2", + "providerEnvironmentId": "sandbox-6faba110f08b", + "environmentRecord": { + "id": "environment-36981531507c22b545b3866cef5810b2", + "connectionId": "connection-live-tangle-sandbox", + "kind": "sandbox", + "provider": "tangle-sandbox", + "providerEnvironmentId": "sandbox-6faba110f08b", + "lifecycle": "destroyed", + "lifecycleMode": "ephemeral", + "cleanup": "delete-after-turn", + "continuity": "unavailable", + "location": "remote", + "runtimeEndpointHost": "sandbox-6faba110f08b.tangle.sh", + "storagePersistence": "persistent-home", + "accountUsage": { + "completeness": "provider-reported-possibly-defaulted", + "computeMinutes": 769, + "gpuSeconds": 0, + "gpuCostUsd": 0, + "activeSandboxes": 6, + "totalSandboxes": 9230, + "maximumConcurrentSandboxes": 25, + "maximumCpuCores": 8, + "maximumRamGB": 16, + "maximumStorageGB": 256, + "usagePeriodStart": "2026-08-12T00:00:00.000Z", + "usagePeriodEnd": "2026-08-13T00:00:00.000Z", + "sampledAt": "2026-08-12T11:53:00.941Z" + }, + "unavailableTelemetry": [ + "disk-usage:not-exposed-by-provider", + "effective-resources:not-exposed-by-provider", + "machine-id:not-exposed-by-provider", + "machine-ip:not-exposed-by-provider", + "per-sandbox-cpu-ram-storage-cost:not-exposed-by-provider", + "runtime-cpu-memory-usage:request-failed-or-timed-out", + "verified-placement:not-exposed-by-client", + "verified-region:not-exposed-by-provider" + ], + "createdAt": "2026-08-12T11:53:01.884Z", + "updatedAt": "2026-08-12T11:53:27.487Z" + }, + "run": { + "inputTokens": { + "status": "observed", + "value": 575 + }, + "outputTokens": { + "status": "observed", + "value": 23 + }, + "reasoningTokens": { + "status": "missing" + }, + "cacheReadTokens": { + "status": "missing" + }, + "cacheWriteTokens": { + "status": "missing" + }, + "costUsd": { + "status": "observed", + "value": 0 + }, + "costStatus": { + "status": "observed", + "value": "reported" + }, + "usage": { + "status": "missing" + }, + "cost": { + "status": "missing" + }, + "tokensKnown": { + "status": "missing" + }, + "usdKnown": { + "status": "missing" + }, + "usageCompleteness": { + "status": "missing" + }, + "latencyMs": { + "status": "missing" + }, + "durationMs": { + "status": "missing" + }, + "model": { + "status": "observed", + "value": "tangle-router/glm-5.2" + }, + "provider": { + "status": "observed", + "value": "tangle-sandbox" + }, + "runner": { + "status": "observed", + "value": "opencode" + }, + "operationId": { + "status": "observed", + "value": "op-live-required-send-6db856fb-bb63-4f1e-868a-a9f4d7d9ee90" + }, + "profileSnapshotId": { + "status": "missing" + }, + "connectionId": { + "status": "observed", + "value": "connection-live-tangle-sandbox" + }, + "providerSessionId": { + "status": "missing" + }, + "environmentId": { + "status": "observed", + "value": "environment-36981531507c22b545b3866cef5810b2" + }, + "replayCursor": { + "status": "missing" + }, + "lastCursor": { + "status": "missing" + }, + "lastProviderSequence": { + "status": "missing" + }, + "eventCount": { + "status": "missing" + }, + "contentBytes": { + "status": "observed", + "value": 622 + }, + "contentTruncated": { + "status": "missing" + }, + "missingSequence": { + "status": "missing" + }, + "terminalReason": { + "status": "missing" + }, + "materializationDigest": { + "status": "observed", + "value": "a6b0da265a89717a6fa6b4156dda7c51118c7ca08ab2a795f4533bf95ac5941b" + }, + "complete": { + "status": "missing" + }, + "status": { + "status": "observed", + "value": "completed" + }, + "error": { + "status": "missing" + }, + "startedAt": { + "status": "missing" + }, + "updatedAt": { + "status": "missing" + }, + "terminalAt": { + "status": "missing" + }, + "placement": { + "status": "missing" + }, + "resourceSample": { + "status": "missing" + }, + "requestedResources": { + "status": "missing" + }, + "machineId": { + "status": "missing" + }, + "runtimeEndpointHost": { + "status": "missing" + }, + "requestedRegion": { + "status": "missing" + }, + "verifiedRegion": { + "status": "missing" + }, + "storagePersistence": { + "status": "missing" + }, + "gpu": { + "status": "missing" + } + }, + "environment": { + "kind": { + "status": "observed", + "value": "sandbox" + }, + "providerEnvironmentId": { + "status": "observed", + "value": "sandbox-6faba110f08b" + }, + "provider": { + "status": "observed", + "value": "tangle-sandbox" + }, + "name": { + "status": "missing" + }, + "lifecycle": { + "status": "observed", + "value": "destroyed" + }, + "lifecycleMode": { + "status": "observed", + "value": "ephemeral" + }, + "cleanup": { + "status": "observed", + "value": "delete-after-turn" + }, + "continuity": { + "status": "observed", + "value": "unavailable" + }, + "location": { + "status": "observed", + "value": "remote" + }, + "region": { + "status": "missing" + }, + "runtimeEndpointHost": { + "status": "observed", + "value": "sandbox-6faba110f08b.tangle.sh" + }, + "machineId": { + "status": "missing" + }, + "requestedRegion": { + "status": "missing" + }, + "verifiedRegion": { + "status": "missing" + }, + "storagePersistence": { + "status": "observed", + "value": "persistent-home" + }, + "requestedResources": { + "status": "missing" + }, + "resourceSample": { + "status": "missing" + }, + "gpu": { + "status": "missing" + }, + "accountUsage": { + "status": "observed", + "value": { + "completeness": "provider-reported-possibly-defaulted", + "computeMinutes": 769, + "gpuSeconds": 0, + "gpuCostUsd": 0, + "activeSandboxes": 6, + "totalSandboxes": 9230, + "maximumConcurrentSandboxes": 25, + "maximumCpuCores": 8, + "maximumRamGB": 16, + "maximumStorageGB": 256, + "usagePeriodStart": "2026-08-12T00:00:00.000Z", + "usagePeriodEnd": "2026-08-13T00:00:00.000Z", + "sampledAt": "2026-08-12T11:53:00.941Z" + } + }, + "unavailableTelemetry": { + "status": "observed", + "value": [ + "disk-usage:not-exposed-by-provider", + "effective-resources:not-exposed-by-provider", + "machine-id:not-exposed-by-provider", + "machine-ip:not-exposed-by-provider", + "per-sandbox-cpu-ram-storage-cost:not-exposed-by-provider", + "runtime-cpu-memory-usage:request-failed-or-timed-out", + "verified-placement:not-exposed-by-client", + "verified-region:not-exposed-by-provider" + ] + }, + "placement": { + "status": "missing" + }, + "repository": { + "status": "missing" + }, + "gitRef": { + "status": "missing" + }, + "workingDirectory": { + "status": "missing" + }, + "image": { + "status": "missing" + }, + "createdAt": { + "status": "observed", + "value": "2026-08-12T11:53:01.884Z" + }, + "startedAt": { + "status": "missing" + }, + "lastActivityAt": { + "status": "missing" + }, + "expiresAt": { + "status": "missing" + }, + "updatedAt": { + "status": "observed", + "value": "2026-08-12T11:53:27.487Z" + } + } + }, + "cleanup": { + "status": "passed", + "matchedCount": 0, + "remainingCount": 0 + } + } + ] +} diff --git a/artifacts/verification/w6/120x40-plain.txt b/artifacts/verification/w6/120x40-plain.txt index 95c8862..a1c8793 100644 --- a/artifacts/verification/w6/120x40-plain.txt +++ b/artifacts/verification/w6/120x40-plain.txt @@ -1,4 +1,4 @@ -Braid ready — /home/drew/code/braid-release-ux-final-wt +Braid ready — /home/drew/code/braid-transcript-first-wt status: ready for a message conversation: New conversation; branch: branch-1 profile: Braid starter; runner: pi; connection: deterministic fixture @@ -6,7 +6,7 @@ appearance: none; high contrast off; reduced motion off event 2: draft.changed; status: ready for a message event 3: run.requested; status: admitted: W6 plain proof event 4: effect.upserted; status: streaming -event 5: run.provider.event; status: streaming: {"type":"backend_start","task":{"id":"turn-8e6634ed-a799-4f51-b154-32cbd6b8e17d","intent":"W6 plain proof","metadata":{"identity":{"profileDigest":"sha256:8344a03c82d5cb9207910fe0cb05ef0757b58b8cea9a7cc82f36055694d58983","taskDigest":"sha256:f75eb08b6c6a3120e5297421baed1e5e2553899395ec5981e0fda4c9ce725300"}}},"session":{"id":"70e9bca8-cce1-41f4-ac4b-bf9f05d5b264","backend":"executor","status":"active","createdAt":"2026-08-11T05:08:04.929Z","updatedAt":"2026-08-11T05:08:04.929Z","metadata":"[unavailable]"}," +event 5: run.provider.event; status: streaming: {"type":"backend_start","task":{"id":"turn-c7a112f8-ec11-44cb-beed-76fd6693a70b","intent":"W6 plain proof","metadata":{"identity":{"profileDigest":"sha256:8344a03c82d5cb9207910fe0cb05ef0757b58b8cea9a7cc82f36055694d58983","taskDigest":"sha256:f75eb08b6c6a3120e5297421baed1e5e2553899395ec5981e0fda4c9ce725300"}}},"session":{"id":"5396aad7-ceec-411e-a001-ce0a2edf2a12","backend":"executor","status":"active","createdAt":"2026-08-12T12:37:26.903Z","updatedAt":"2026-08-12T12:37:26.903Z","metadata":"[unavailable]"}," event 6: application.shutdown.requested; status: streaming event 7: run.control.requested; status: cancelling: requested event 8: run.cancel.requested; status: cancelling: requested diff --git a/artifacts/verification/w6/120x40.png b/artifacts/verification/w6/120x40.png index 93c51b0..2a62892 100644 Binary files a/artifacts/verification/w6/120x40.png and b/artifacts/verification/w6/120x40.png differ diff --git a/artifacts/verification/w6/120x40.txt b/artifacts/verification/w6/120x40.txt index 0cf8586..023edcd 100644 --- a/artifacts/verification/w6/120x40.txt +++ b/artifacts/verification/w6/120x40.txt @@ -1,6 +1,4 @@ -braid cwd braid-release-ux-final-wt · session New conversation · branch branch-1 -AgentProfile Braid starter · runner pi · model fixture/deterministic deterministic fixture · thinking none - W6 visual proof + › W6 visual proof · run result · Ctrl+E details Fixture response through pi: W6 visual proof @@ -32,9 +30,11 @@ AgentProfile Braid starter · runner pi · model fixture/deterministic -──────────────────────────────────────────────────────── › send ──────────────────────────────────────────────────────── + + + + › -────────────────────────────────────────────── alt+enter newline · paste ─────────────────────────────────────────────── -completed Ctrl+E detail · turns in 8 · out 12 · cost unknown · calls 1 · model 50ms +profile Braid starter · pi / fixture/deterministic · deterministic fixture in 8 · out 12 · calls 1 · model 50ms diff --git a/artifacts/verification/w6/200x60-plain.txt b/artifacts/verification/w6/200x60-plain.txt index 95c8862..a1c8793 100644 --- a/artifacts/verification/w6/200x60-plain.txt +++ b/artifacts/verification/w6/200x60-plain.txt @@ -1,4 +1,4 @@ -Braid ready — /home/drew/code/braid-release-ux-final-wt +Braid ready — /home/drew/code/braid-transcript-first-wt status: ready for a message conversation: New conversation; branch: branch-1 profile: Braid starter; runner: pi; connection: deterministic fixture @@ -6,7 +6,7 @@ appearance: none; high contrast off; reduced motion off event 2: draft.changed; status: ready for a message event 3: run.requested; status: admitted: W6 plain proof event 4: effect.upserted; status: streaming -event 5: run.provider.event; status: streaming: {"type":"backend_start","task":{"id":"turn-8e6634ed-a799-4f51-b154-32cbd6b8e17d","intent":"W6 plain proof","metadata":{"identity":{"profileDigest":"sha256:8344a03c82d5cb9207910fe0cb05ef0757b58b8cea9a7cc82f36055694d58983","taskDigest":"sha256:f75eb08b6c6a3120e5297421baed1e5e2553899395ec5981e0fda4c9ce725300"}}},"session":{"id":"70e9bca8-cce1-41f4-ac4b-bf9f05d5b264","backend":"executor","status":"active","createdAt":"2026-08-11T05:08:04.929Z","updatedAt":"2026-08-11T05:08:04.929Z","metadata":"[unavailable]"}," +event 5: run.provider.event; status: streaming: {"type":"backend_start","task":{"id":"turn-c7a112f8-ec11-44cb-beed-76fd6693a70b","intent":"W6 plain proof","metadata":{"identity":{"profileDigest":"sha256:8344a03c82d5cb9207910fe0cb05ef0757b58b8cea9a7cc82f36055694d58983","taskDigest":"sha256:f75eb08b6c6a3120e5297421baed1e5e2553899395ec5981e0fda4c9ce725300"}}},"session":{"id":"5396aad7-ceec-411e-a001-ce0a2edf2a12","backend":"executor","status":"active","createdAt":"2026-08-12T12:37:26.903Z","updatedAt":"2026-08-12T12:37:26.903Z","metadata":"[unavailable]"}," event 6: application.shutdown.requested; status: streaming event 7: run.control.requested; status: cancelling: requested event 8: run.cancel.requested; status: cancelling: requested diff --git a/artifacts/verification/w6/200x60.png b/artifacts/verification/w6/200x60.png index c3b52cf..074d7b0 100644 Binary files a/artifacts/verification/w6/200x60.png and b/artifacts/verification/w6/200x60.png differ diff --git a/artifacts/verification/w6/200x60.txt b/artifacts/verification/w6/200x60.txt index cecd55e..8c1e5aa 100644 --- a/artifacts/verification/w6/200x60.txt +++ b/artifacts/verification/w6/200x60.txt @@ -1,6 +1,4 @@ -braid cwd braid-release-ux-final-wt · session New conversation · branch branch-1 -AgentProfile Braid starter · runner pi · model fixture/deterministic deterministic fixture · thinking none - W6 visual proof + › W6 visual proof · run result · Ctrl+E details Fixture response through pi: W6 visual proof @@ -52,9 +50,11 @@ AgentProfile Braid starter · runner pi · model fixture/deterministic -──────────────────────────────────────────────────────────────────────────────────────────────── › send ──────────────────────────────────────────────────────────────────────────────────────────────── + + + + › -────────────────────────────────────────────────────────────────────────────────────── alt+enter newline · paste ─────────────────────────────────────────────────────────────────────────────────────── -completed Ctrl+E detail · turns in 8 · out 12 · cost unknown · calls 1 · model 50ms +profile Braid starter · pi / fixture/deterministic · deterministic fixture in 8 · out 12 · calls 1 · model 49ms diff --git a/artifacts/verification/w6/40x12-plain.txt b/artifacts/verification/w6/40x12-plain.txt index 95c8862..a1c8793 100644 --- a/artifacts/verification/w6/40x12-plain.txt +++ b/artifacts/verification/w6/40x12-plain.txt @@ -1,4 +1,4 @@ -Braid ready — /home/drew/code/braid-release-ux-final-wt +Braid ready — /home/drew/code/braid-transcript-first-wt status: ready for a message conversation: New conversation; branch: branch-1 profile: Braid starter; runner: pi; connection: deterministic fixture @@ -6,7 +6,7 @@ appearance: none; high contrast off; reduced motion off event 2: draft.changed; status: ready for a message event 3: run.requested; status: admitted: W6 plain proof event 4: effect.upserted; status: streaming -event 5: run.provider.event; status: streaming: {"type":"backend_start","task":{"id":"turn-8e6634ed-a799-4f51-b154-32cbd6b8e17d","intent":"W6 plain proof","metadata":{"identity":{"profileDigest":"sha256:8344a03c82d5cb9207910fe0cb05ef0757b58b8cea9a7cc82f36055694d58983","taskDigest":"sha256:f75eb08b6c6a3120e5297421baed1e5e2553899395ec5981e0fda4c9ce725300"}}},"session":{"id":"70e9bca8-cce1-41f4-ac4b-bf9f05d5b264","backend":"executor","status":"active","createdAt":"2026-08-11T05:08:04.929Z","updatedAt":"2026-08-11T05:08:04.929Z","metadata":"[unavailable]"}," +event 5: run.provider.event; status: streaming: {"type":"backend_start","task":{"id":"turn-c7a112f8-ec11-44cb-beed-76fd6693a70b","intent":"W6 plain proof","metadata":{"identity":{"profileDigest":"sha256:8344a03c82d5cb9207910fe0cb05ef0757b58b8cea9a7cc82f36055694d58983","taskDigest":"sha256:f75eb08b6c6a3120e5297421baed1e5e2553899395ec5981e0fda4c9ce725300"}}},"session":{"id":"5396aad7-ceec-411e-a001-ce0a2edf2a12","backend":"executor","status":"active","createdAt":"2026-08-12T12:37:26.903Z","updatedAt":"2026-08-12T12:37:26.903Z","metadata":"[unavailable]"}," event 6: application.shutdown.requested; status: streaming event 7: run.control.requested; status: cancelling: requested event 8: run.cancel.requested; status: cancelling: requested diff --git a/artifacts/verification/w6/40x12.png b/artifacts/verification/w6/40x12.png index a7c3d41..dfe4f04 100644 Binary files a/artifacts/verification/w6/40x12.png and b/artifacts/verification/w6/40x12.png differ diff --git a/artifacts/verification/w6/40x12.txt b/artifacts/verification/w6/40x12.txt index 5e7383f..d85051b 100644 --- a/artifacts/verification/w6/40x12.txt +++ b/artifacts/verification/w6/40x12.txt @@ -1,12 +1,12 @@ -braid AgentProfile Braid starter -pi / deterministic · fixture + › W6 visual proof · run result · Ctrl+E details Fixture response through pi: W6 visual proof -──────────────── › send ──────────────── + + + › -────── alt+enter newline · paste ─────── -completed Ctrl+E detail +Braid starter Ctrl+E detail diff --git a/artifacts/verification/w6/80x24-automation.gif b/artifacts/verification/w6/80x24-automation.gif index 4d488a0..c872da9 100644 Binary files a/artifacts/verification/w6/80x24-automation.gif and b/artifacts/verification/w6/80x24-automation.gif differ diff --git a/artifacts/verification/w6/80x24-plain.txt b/artifacts/verification/w6/80x24-plain.txt index 95c8862..a1c8793 100644 --- a/artifacts/verification/w6/80x24-plain.txt +++ b/artifacts/verification/w6/80x24-plain.txt @@ -1,4 +1,4 @@ -Braid ready — /home/drew/code/braid-release-ux-final-wt +Braid ready — /home/drew/code/braid-transcript-first-wt status: ready for a message conversation: New conversation; branch: branch-1 profile: Braid starter; runner: pi; connection: deterministic fixture @@ -6,7 +6,7 @@ appearance: none; high contrast off; reduced motion off event 2: draft.changed; status: ready for a message event 3: run.requested; status: admitted: W6 plain proof event 4: effect.upserted; status: streaming -event 5: run.provider.event; status: streaming: {"type":"backend_start","task":{"id":"turn-8e6634ed-a799-4f51-b154-32cbd6b8e17d","intent":"W6 plain proof","metadata":{"identity":{"profileDigest":"sha256:8344a03c82d5cb9207910fe0cb05ef0757b58b8cea9a7cc82f36055694d58983","taskDigest":"sha256:f75eb08b6c6a3120e5297421baed1e5e2553899395ec5981e0fda4c9ce725300"}}},"session":{"id":"70e9bca8-cce1-41f4-ac4b-bf9f05d5b264","backend":"executor","status":"active","createdAt":"2026-08-11T05:08:04.929Z","updatedAt":"2026-08-11T05:08:04.929Z","metadata":"[unavailable]"}," +event 5: run.provider.event; status: streaming: {"type":"backend_start","task":{"id":"turn-c7a112f8-ec11-44cb-beed-76fd6693a70b","intent":"W6 plain proof","metadata":{"identity":{"profileDigest":"sha256:8344a03c82d5cb9207910fe0cb05ef0757b58b8cea9a7cc82f36055694d58983","taskDigest":"sha256:f75eb08b6c6a3120e5297421baed1e5e2553899395ec5981e0fda4c9ce725300"}}},"session":{"id":"5396aad7-ceec-411e-a001-ce0a2edf2a12","backend":"executor","status":"active","createdAt":"2026-08-12T12:37:26.903Z","updatedAt":"2026-08-12T12:37:26.903Z","metadata":"[unavailable]"}," event 6: application.shutdown.requested; status: streaming event 7: run.control.requested; status: cancelling: requested event 8: run.cancel.requested; status: cancelling: requested diff --git a/artifacts/verification/w6/80x24-transcript-keyboard.gif b/artifacts/verification/w6/80x24-transcript-keyboard.gif index 154c588..f0b369e 100644 Binary files a/artifacts/verification/w6/80x24-transcript-keyboard.gif and b/artifacts/verification/w6/80x24-transcript-keyboard.gif differ diff --git a/artifacts/verification/w6/80x24.png b/artifacts/verification/w6/80x24.png index c4f4eaa..40b3862 100644 Binary files a/artifacts/verification/w6/80x24.png and b/artifacts/verification/w6/80x24.png differ diff --git a/artifacts/verification/w6/80x24.txt b/artifacts/verification/w6/80x24.txt index 359a231..4adc54a 100644 --- a/artifacts/verification/w6/80x24.txt +++ b/artifacts/verification/w6/80x24.txt @@ -1,6 +1,4 @@ -braid cwd braid-release-ux-final-wt · session New conversation -AgentProfile Braid starter · pi / deterministic deterministic fixture - W6 visual proof + › W6 visual proof · run result · Ctrl+E details Fixture response through pi: W6 visual proof @@ -16,9 +14,11 @@ AgentProfile Braid starter · pi / deterministic deterministic fixtur -──────────────────────────────────── › send ──────────────────────────────────── + + + + › -────────────────────────── alt+enter newline · paste ─────────────────────────── -completed Ctrl+E detail +profile Braid starter · pi / deterministic Ctrl+E detail diff --git a/artifacts/verification/w6/braid.gif b/artifacts/verification/w6/braid.gif index 5e8df20..f2f9ccd 100644 Binary files a/artifacts/verification/w6/braid.gif and b/artifacts/verification/w6/braid.gif differ diff --git a/artifacts/verification/w6/braid.png b/artifacts/verification/w6/braid.png index afd64bb..542e470 100644 Binary files a/artifacts/verification/w6/braid.png and b/artifacts/verification/w6/braid.png differ diff --git a/artifacts/verification/w6/braid.txt b/artifacts/verification/w6/braid.txt index 8ca98ba..53ca971 100644 --- a/artifacts/verification/w6/braid.txt +++ b/artifacts/verification/w6/braid.txt @@ -1,18 +1,18 @@ -braid cwd agent-sdk · session New conversation · branch branch-1 -AgentProfile Release engineer · runner pi · model openai-codex/gpt-5.6-luna Local CLI Bridge · thinking high - Trace this request from AgentProfile to the selected runner. + › Which coding agent will handle my next task, and where will it run? · run result · Ctrl+E details - Route confirmed. Your AgentProfile stays intact while agent-runtime sends this turn through Pi over Local CLI Bridge. - Braid keeps the conversation, forks, approvals, and trace analysis in one place. + Pi will run the task through Local CLI Bridge with gpt-5.6-luna. The Release engineer AgentProfile supplies its + instructions and permissions. Braid keeps the transcript, approvals, branches, and trace analysis. + + + + -──────────────────────────────────────────────────────── › send ──────────────────────────────────────────────────────── › -────────────────────────────────────────────── alt+enter newline · paste ─────────────────────────────────────────────── -completed Ctrl+E detail · turns in 1.3k · out 96 · ~$0.0026 · calls 1 · model 1193ms +profile Release engineer · pi / openai-codex/gpt-5.6-luna · Local CLI Bridge · high in 1.3k · out 96 · ~$0.0026 diff --git a/artifacts/verification/w6/capture-manifest.json b/artifacts/verification/w6/capture-manifest.json index 9b0e713..9cb4e40 100644 --- a/artifacts/verification/w6/capture-manifest.json +++ b/artifacts/verification/w6/capture-manifest.json @@ -1,11 +1,11 @@ { "schemaVersion": 3, - "generatedAt": "2026-08-11T05:08:31.688Z", + "generatedAt": "2026-08-12T12:38:14.171Z", "command": "pnpm capture:visual", "binary": "clean npm install from generated tarball", "binarySha256": "141288e0fe917635d723b4b70d464dc49baff14cc356f54de1d3f8faa5d8254f", - "tarball": "tangle-network-braid-0.1.0.tgz", - "tarballSha256": "32f19ba707aa659f776a604fe241111fe2bfb68928ac6f005014d81bd924182c", + "tarball": "tangle-network-braid-0.1.1.tgz", + "tarballSha256": "9bbd7b5b9f78b3d202dbb09d4375970edfd4965ffc5ad94b04bd7fa6ce01c4c7", "fixture": "deterministic", "terminal": "node-pty/xterm-256color", "node": "v24.13.0", @@ -42,7 +42,7 @@ "steps": [ "Inspect AgentProfile", "Run through Local CLI Bridge", - "Inspect tool activity", + "Inspect live work and the retained run", "Analyze the frozen trace with /ask", "Review the analysis list and details together" ], @@ -190,91 +190,91 @@ "artifacts": [ { "path": "40x12.txt", - "sha256": "4654634e3a86a933a8029a8e4d92c7da8a9f1074b9107d3c967db15ea1fb8d3c", + "sha256": "06bd2cb4454d314c0fba112343013f35ecb532d7eae36eaf0f81bc5919c7bfa7", "kind": "terminal-frame", "columns": 40, "rows": 12 }, { "path": "40x12-plain.txt", - "sha256": "2bd9bb20ddd3c3561af5c37beb86939aeffc927668262ad2f84fa7d4490ef7f8", + "sha256": "079768ad779ecd50135cdbbc133aeee9b6281ddfffbb22af4e0178bfe41dc615", "kind": "plain-frame", "columns": 40, "rows": 12 }, { "path": "40x12.png", - "sha256": "e1b17099668943faf28446583e66ee904301d889855abd51927802412be2604d", + "sha256": "448563badac78d2c989ede14d92ebad7393f95b5874a766d915ec76c62492780", "kind": "png", "columns": 40, "rows": 12 }, { "path": "80x24.txt", - "sha256": "2b336d6f6bb964fcbbf7b5965cb7deca5e4087f3f4c369f727160ba0b9c24f6c", + "sha256": "9b17cc3d0785706ed07325649a553637adc8ffafc5642ebab2a22197126a99c5", "kind": "terminal-frame", "columns": 80, "rows": 24 }, { "path": "80x24-plain.txt", - "sha256": "2bd9bb20ddd3c3561af5c37beb86939aeffc927668262ad2f84fa7d4490ef7f8", + "sha256": "079768ad779ecd50135cdbbc133aeee9b6281ddfffbb22af4e0178bfe41dc615", "kind": "plain-frame", "columns": 80, "rows": 24 }, { "path": "80x24.png", - "sha256": "3388013dd77d0438f97f876cb5aa581c6f59ad6af9b1ef7f62187148b97e151f", + "sha256": "36ec2775eff6d256e107c4f194ea65aeb59e43187ce17c37c27e623bf7ab7c92", "kind": "png", "columns": 80, "rows": 24 }, { "path": "120x40.txt", - "sha256": "82265b2f851881fc596ef0872022d1310245cd1b2de42db91732b8f8cb824b62", + "sha256": "1a52a383a1fd94baae24f061f064c12dc2db5c1f557ba16deba89eb25832815a", "kind": "terminal-frame", "columns": 120, "rows": 40 }, { "path": "120x40-plain.txt", - "sha256": "2bd9bb20ddd3c3561af5c37beb86939aeffc927668262ad2f84fa7d4490ef7f8", + "sha256": "079768ad779ecd50135cdbbc133aeee9b6281ddfffbb22af4e0178bfe41dc615", "kind": "plain-frame", "columns": 120, "rows": 40 }, { "path": "120x40.png", - "sha256": "0c958303be8c8f41adbca168c2d673306a847e6b239b47baccee8d31fcd44b1d", + "sha256": "ae1db9182dae754026070d8a924ccab59bd0cac44a58f4ba68046206f1460beb", "kind": "png", "columns": 120, "rows": 40 }, { "path": "200x60.txt", - "sha256": "a33142be803c91acbad545d2ec7d4842e99220c001db295b6252f14e421fbbc6", + "sha256": "6b967e1a525d2f1f5f4d1507744bc2c4969b8d16decda9e31e821658618be57e", "kind": "terminal-frame", "columns": 200, "rows": 60 }, { "path": "200x60-plain.txt", - "sha256": "2bd9bb20ddd3c3561af5c37beb86939aeffc927668262ad2f84fa7d4490ef7f8", + "sha256": "079768ad779ecd50135cdbbc133aeee9b6281ddfffbb22af4e0178bfe41dc615", "kind": "plain-frame", "columns": 200, "rows": 60 }, { "path": "200x60.png", - "sha256": "aeb0f7844dbc0aebdb3204e24b4f6a092ba03360f10426534473173299ed4fc9", + "sha256": "9e5fa392f597a70fcc527da54cae93a945fbcb998867fbdc5708652e940021bc", "kind": "png", "columns": 200, "rows": 60 }, { "path": "states/empty.json", - "sha256": "24090eeaeb3ea3b6a47ddc6d23f115535710e5063bc21e33ee9b54c41a8a68b8", + "sha256": "46ee54a2d8a5c933eff1768c7b57aa7f7b93efdcfbd55aeaf435927132f2644e", "kind": "semantic-state", "state": "empty", "columns": 80, @@ -282,7 +282,7 @@ }, { "path": "states/empty.txt", - "sha256": "263c0ed31108c1aa7e0922f6f7087481c085567c61006f454a40b692b4e41338", + "sha256": "42a217605b309c5c5ac02531d3b86119a203af7f292ab27f320a967c615a865a", "kind": "plain-frame", "state": "empty", "columns": 80, @@ -290,7 +290,7 @@ }, { "path": "raw/empty.cast", - "sha256": "9acccd4a35ec4903e82e79218db2dc6ed64e01e09f821252bd865fe6b1238f89", + "sha256": "330fd536c7f9edd639b9b6828913f3d49c33509d871141b5652d8c80eac444a8", "kind": "asciicast", "state": "empty", "columns": 80, @@ -298,7 +298,7 @@ }, { "path": "states/empty.ansi", - "sha256": "62bfbb66dcadd48b4d218d7a1e544cbc0e8755b99a0a124bbdfc9be74d95f246", + "sha256": "2de36ed50acbd770bf7217f43386f32f573d3967c6df77d5bc5db931a030a1b9", "kind": "ansi", "state": "empty", "columns": 80, @@ -306,7 +306,7 @@ }, { "path": "states/empty.png", - "sha256": "be3ae078693747206c617a2d43a91d53da0175a636f6f937e4b373cdea031bf4", + "sha256": "e276855bcab549b345a2bd412464a49bfafe3a53ca6fb23d934b5286ec6b0316", "kind": "png", "state": "empty", "columns": 80, @@ -314,7 +314,7 @@ }, { "path": "states/active-streaming.json", - "sha256": "9cda475f377003d40fa52b706733cdd0f8d129ec0fe37616e66ff58026ddb8a7", + "sha256": "0e178e31768a1de7f89794767413d1bc161351ffd5207d6476bbec8ffe7c9393", "kind": "semantic-state", "state": "active-streaming", "columns": 80, @@ -322,7 +322,7 @@ }, { "path": "states/active-streaming.txt", - "sha256": "4fcf598d1d35c90fa2da4eb6641083673e443262b272eb9940139fc88a7a18d7", + "sha256": "fca88cebc11ef71b95b55deeabc3ffa29551f4bb23a9756eb4739ee3b3618de0", "kind": "plain-frame", "state": "active-streaming", "columns": 80, @@ -330,7 +330,7 @@ }, { "path": "raw/active-streaming.cast", - "sha256": "2684745898bf17da453618f00fce03f19646c7e36c63566d50ca44430d695711", + "sha256": "95032f9ff36f0349ab3beaeda0aa17dd403587162c4cb8388e07feb07c9f0910", "kind": "asciicast", "state": "active-streaming", "columns": 80, @@ -338,7 +338,7 @@ }, { "path": "states/active-streaming.ansi", - "sha256": "3617a14855da1a4a4943287dbf210d75cdcd7987e0b60d9bbca24798acf533e2", + "sha256": "c6530f7b36e87ee3737df4ff3df59d6551dc6711c5eda7ec54eb3efd62d35a0a", "kind": "ansi", "state": "active-streaming", "columns": 80, @@ -346,7 +346,7 @@ }, { "path": "states/active-streaming.png", - "sha256": "6603aab77a23b962692b3dbd13bf9fd35ee8f13104d57077fbf61cbc79f89f39", + "sha256": "4639ab1c23b7b6bc526cb51bf5b92de0eb621ea1f9f802e0fdf63cd39c9e0ce3", "kind": "png", "state": "active-streaming", "columns": 80, @@ -354,7 +354,7 @@ }, { "path": "states/interaction.json", - "sha256": "cbcb35d7ca1a6c713fe2d589160eb4414b209b6e025aed5f01bf53060aca9245", + "sha256": "e20dfbbbe4595799e7daf11268e2027b7f69d4683317077ba3b606eff5df5739", "kind": "semantic-state", "state": "interaction", "columns": 80, @@ -370,7 +370,7 @@ }, { "path": "raw/interaction.cast", - "sha256": "8776941478fd7de58d62d0ee46c02ebcdb6415ef2785d5c6d2951b2a84a202aa", + "sha256": "b56bc79da2c5e504648a47963f4e41cabb718e5a5817510771610ba1efb5fdcf", "kind": "asciicast", "state": "interaction", "columns": 80, @@ -378,7 +378,7 @@ }, { "path": "states/interaction.ansi", - "sha256": "a0da96fe443ffb09d3393c10ff8e55215d052a395163f69dff1010389088bacd", + "sha256": "03c62fde015d3d5e12886d9cace2750d244f1a9f625b4d7170c8a865f4d393e5", "kind": "ansi", "state": "interaction", "columns": 80, @@ -386,7 +386,7 @@ }, { "path": "states/interaction.png", - "sha256": "69bd3ca006a52fd87f3a2aa975d56b0503506c4e55c7ece3f55e30361c397015", + "sha256": "be5ff9e21e002530cb78fdbba96830056b703b5d739e14d7f98889d3f88fd556", "kind": "png", "state": "interaction", "columns": 80, @@ -394,7 +394,7 @@ }, { "path": "states/automation.json", - "sha256": "bd67a840d1e6c75c76def3301dd12bf6bf96be357b94d4fd6c1db83faeac38e9", + "sha256": "2ae692ab250879c46d1308f854d12d682d8b16b13a69b15e42a3490dc4fbbde7", "kind": "semantic-state", "state": "automation", "columns": 80, @@ -402,7 +402,7 @@ }, { "path": "states/automation.txt", - "sha256": "475e46090bb7d7c2563bde7038bba81ab2a3f09bd804045cc9fba3333f0891e7", + "sha256": "82d0afcc23d1988502a1b580d44db01a8dd0c9c5f9d16dcca113d31148470f10", "kind": "plain-frame", "state": "automation", "columns": 80, @@ -410,7 +410,7 @@ }, { "path": "raw/automation.cast", - "sha256": "9e67b5c3ebe4efe7756d8dea87f20971c2b321669846199953f578ea060a1432", + "sha256": "65f4519ecdb0ba8809eae6860da72427b595fcfcf5cd7301ea051b60d39a745d", "kind": "asciicast", "state": "automation", "columns": 80, @@ -418,7 +418,7 @@ }, { "path": "states/automation.ansi", - "sha256": "6371733b990ca50ed9c84e1e83295baef5f2d8c55f29ef8cb7e06442955be103", + "sha256": "fc25c7b836b7a6fcd4e6e33c8e609c8bd5e0228e41861beaae4df5628925f949", "kind": "ansi", "state": "automation", "columns": 80, @@ -426,7 +426,7 @@ }, { "path": "states/automation.png", - "sha256": "0476cdfc3757d11e1b43140a2f7e714eb9b79f9e5f996a252aa90dd66d81bf41", + "sha256": "f97a1184b127ddca1620be4c4da9a8823f5f9ddd362fc6d99ec5c97fb7403c88", "kind": "png", "state": "automation", "columns": 80, @@ -434,7 +434,7 @@ }, { "path": "states/fork-preview.json", - "sha256": "ff7950c0b9382f88520ed973be6dd01ef4dbee988ef79a937270ab958471dc58", + "sha256": "b78c3d8548d6626434774860499edd7c7876f8b108057ab13dceb6921b3bb3d7", "kind": "semantic-state", "state": "fork-preview", "columns": 80, @@ -442,7 +442,7 @@ }, { "path": "states/fork-preview.txt", - "sha256": "bf3a769b7843aa23421e042c0653f44a2b9810bc72e4c14250a3f84e8b06f5a8", + "sha256": "50255443ac4b8706749cdbcc7e37a082aefaaad32c8ece52be7abe6aad1edcb6", "kind": "plain-frame", "state": "fork-preview", "columns": 80, @@ -450,7 +450,7 @@ }, { "path": "raw/fork-preview.cast", - "sha256": "bb033d51f66e6df93c509c4d09ebc9f8220e03573329c7bca0ea42ddff540dd3", + "sha256": "c7645af81c3839e219613200ed1434737feb08611f78efeb0b599d65a5e50b94", "kind": "asciicast", "state": "fork-preview", "columns": 80, @@ -458,7 +458,7 @@ }, { "path": "states/fork-preview.ansi", - "sha256": "7a1197c41fad0b16813986de6b7b7580d0d74e1e3f08afa323cd0c3285a34ca2", + "sha256": "6e69ec241a014064a1dec7248e772142ec72deb0f3b1c7f7cc57b9211c62bf9f", "kind": "ansi", "state": "fork-preview", "columns": 80, @@ -466,7 +466,7 @@ }, { "path": "states/fork-preview.png", - "sha256": "da7a075037ef99b8774a9061268d1f0b1a4659119fe70b1d7b8d97368a03c822", + "sha256": "1e3563f55e99f06097e964d1ff8c353ce52efd3d816c9551e7c125cf0e678ccb", "kind": "png", "state": "fork-preview", "columns": 80, @@ -474,7 +474,7 @@ }, { "path": "states/graph-or-analysis.json", - "sha256": "b1a59b926099e807c94c63051eef6744f603d2c86719ec36f9eea881a5a63070", + "sha256": "280dc31c5894850302479ed25930948d5ed52fb8fbd8a8f42aa49144661a15fb", "kind": "semantic-state", "state": "graph-or-analysis", "columns": 80, @@ -482,7 +482,7 @@ }, { "path": "states/graph-or-analysis.txt", - "sha256": "ee73fb242ff9e7ae0ecae20374c04d896eaa1c72715edeb37305b531be66e247", + "sha256": "de00305382582cdc1ed849578990abc3fdeae1fc388bc7322c7d656370cf3543", "kind": "plain-frame", "state": "graph-or-analysis", "columns": 80, @@ -490,7 +490,7 @@ }, { "path": "raw/graph-or-analysis.cast", - "sha256": "e2ee657535df3deec823a3ed6a9885e780345b62e663b98cf28cb0c94d946377", + "sha256": "79aaf455381f031749dee0e59fc71809c33c6a29fadef8ae767fd2d3bd5eed0f", "kind": "asciicast", "state": "graph-or-analysis", "columns": 80, @@ -498,7 +498,7 @@ }, { "path": "states/graph-or-analysis.ansi", - "sha256": "038e02c01ccb3bf495279885ae93756ee78fa706fe9c32f1a100cac23f0f4ebe", + "sha256": "6881a55457e95a7dbacd7526600b54883f116d1d74f021b66b61babf3510d04a", "kind": "ansi", "state": "graph-or-analysis", "columns": 80, @@ -506,7 +506,7 @@ }, { "path": "states/graph-or-analysis.png", - "sha256": "6bab0871454c55caf825fc0a1bf568009efcfbba42862fbee1e820f2d23bbb59", + "sha256": "ed849a9a6f02b4a931dbd6b20b735fd3f341e8b4f851bf362b574146720a049a", "kind": "png", "state": "graph-or-analysis", "columns": 80, @@ -514,7 +514,7 @@ }, { "path": "states/analysis.json", - "sha256": "e0cef073cd9306223dd246279123b4fe95fb0065801981462f443bfe5b0dd871", + "sha256": "994b96517d6981f69d8ca61c90378fed9efaaead6ea2c15f7e889e0012aaed0e", "kind": "semantic-state", "state": "analysis", "columns": 80, @@ -530,7 +530,7 @@ }, { "path": "raw/analysis.cast", - "sha256": "5054dd238430450a97fbedb492d11c77ed1a67f7dea9cf027fb44957ccaefbcc", + "sha256": "f5181342eeb963b7bab41884918361ca0df2ce005f2ba4a0c288c3217969e0ed", "kind": "asciicast", "state": "analysis", "columns": 80, @@ -538,7 +538,7 @@ }, { "path": "states/analysis.ansi", - "sha256": "3306ed0aba8bc0ecc38b6c8df21f04e75041a69dd4ebd91d8e94ed7525e2881f", + "sha256": "5e3ced49f5b91590b308bdb45696c7b5c33f3ff82483182d7e3666448cdbd89b", "kind": "ansi", "state": "analysis", "columns": 80, @@ -546,7 +546,7 @@ }, { "path": "states/analysis.png", - "sha256": "8cdec3f31eafe76556439ef20065020641ee679286bdbcb6a28564a868d1701d", + "sha256": "ca4f2c9e206f35006b98057527d15bd3edc0b98a65c8a2ffa7360f51f69e8cd4", "kind": "png", "state": "analysis", "columns": 80, @@ -554,7 +554,7 @@ }, { "path": "states/comparison.json", - "sha256": "1c30333a6b6908fbd9c790f5546a4c847c4ddcd4d2c0d71e2e02a510261603f1", + "sha256": "14d4223261bbf219177ec65cde2a3e10aacc397fbe4a1332e47581934c664460", "kind": "semantic-state", "state": "comparison", "columns": 80, @@ -570,7 +570,7 @@ }, { "path": "raw/comparison.cast", - "sha256": "da1894a9ba42aede325c9f1c75accce60c51693f86d726fed31965af0a7bb577", + "sha256": "338a18f2fc67cddefb1c01151b32ca10a28091ac710258fc73e767591a17043e", "kind": "asciicast", "state": "comparison", "columns": 80, @@ -578,7 +578,7 @@ }, { "path": "states/comparison.ansi", - "sha256": "0ea4162022756e7503ee66cd7ee4896caa1d0ad77d0ae40079287f7961b1e5ac", + "sha256": "30b05a8bdf89cd5bf2f4acae27363dea64dc575667ec749f70d71c78421a8b58", "kind": "ansi", "state": "comparison", "columns": 80, @@ -586,7 +586,7 @@ }, { "path": "states/comparison.png", - "sha256": "616c3ec8ea10eddbdcecc72c2c808df4fa7cee74f554d49f5c7be8f30b84ea76", + "sha256": "19ec095185e2936f728727096aa00c17d5f8c7239e79e0493f7855c71ea8114a", "kind": "png", "state": "comparison", "columns": 80, @@ -594,7 +594,7 @@ }, { "path": "states/profile.json", - "sha256": "02921286efe04ad0a0090707457328815b5a22d82d049667643088dfcb236676", + "sha256": "f47dbe4ccd7178a4c76295ad5247b0f1fcfddb4b9c8a388e771843e88979d06d", "kind": "semantic-state", "state": "profile", "columns": 80, @@ -602,7 +602,7 @@ }, { "path": "states/profile.txt", - "sha256": "ee55f62300bb246d0c17c60909c3f5bf5c3aaae9fe16f64e1bcb5a763207cf15", + "sha256": "6a80d3fb1340f87de3eadd69b63ea7728699602860fa6c3d4848372053b9ec29", "kind": "plain-frame", "state": "profile", "columns": 80, @@ -610,7 +610,7 @@ }, { "path": "raw/profile.cast", - "sha256": "a360efde65e1d4568fa70e7fc645805558557622b30af1482a8eecff3cae2123", + "sha256": "2cbce115bbd07064ec1f60de4fefada320f8a4fd7fa9139731b8cfcf60c4158f", "kind": "asciicast", "state": "profile", "columns": 80, @@ -618,7 +618,7 @@ }, { "path": "states/profile.ansi", - "sha256": "56097fb92a6538a58609965b3c8d06599345b9db049835bcc0fe0d8fb69ea5c3", + "sha256": "f0d3f1dd19e1cb79542d321953b2655a9790a436ddee393439363acc047d5ad7", "kind": "ansi", "state": "profile", "columns": 80, @@ -626,7 +626,7 @@ }, { "path": "states/profile.png", - "sha256": "6b6c192be74774103a117c4cfb5878529f0073a76c9149b08b752bd94ed93670", + "sha256": "baa74d63563fc18d26678e4baccb306e465a1f45738a908ebffab09b059aa1bd", "kind": "png", "state": "profile", "columns": 80, @@ -634,7 +634,7 @@ }, { "path": "states/narrow.json", - "sha256": "bd7a1f4547c4abc4f8554cfd1159cc35968ddee955b5d9c2d52f746482c98fd1", + "sha256": "5c1d195eeb9f278ff7cada9bf8288f7dca9d1a7ff44a4a2580ddd7a20db8e617", "kind": "semantic-state", "state": "narrow", "columns": 40, @@ -642,7 +642,7 @@ }, { "path": "states/narrow.txt", - "sha256": "7574212e3ab8fe163a1645284113374424a760bbf3d81f59a3a4f59247457a48", + "sha256": "e01838efb7f91c10849b39be7e9b5283549d7fe5b058bf7a251effdfabfa2117", "kind": "plain-frame", "state": "narrow", "columns": 40, @@ -650,7 +650,7 @@ }, { "path": "raw/narrow.cast", - "sha256": "c6c53561b0bed98bbf283de7a420619040768963f230f1e5b1cbd0d910ef1ddc", + "sha256": "5e085dd96c7218704656478dd72e83c40fd6e231f5a3f9d9029ee3cfd0c449b4", "kind": "asciicast", "state": "narrow", "columns": 40, @@ -658,7 +658,7 @@ }, { "path": "states/narrow.ansi", - "sha256": "a7121ea6e651dc12d21299b8388d99f35383cfa8f451bd750d05eea62b7e7711", + "sha256": "5a2f5d8635f98b399247f58de032840146d7dab7c2ec70f265d0e94b6ffe99ed", "kind": "ansi", "state": "narrow", "columns": 40, @@ -666,7 +666,7 @@ }, { "path": "states/narrow.png", - "sha256": "2ed1d4600278f5dfb2dac1abea845ef95cf5a0c55d7cdb6539cf263ecfda1ae1", + "sha256": "2f121a3d785bb6dbca7fa79bc7c071cbd28f5151cc569e3450b0bc8f91179d55", "kind": "png", "state": "narrow", "columns": 40, @@ -674,7 +674,7 @@ }, { "path": "states/failure-or-reconnect.json", - "sha256": "d534f613e81949cf6512988a51167500eb091d40a1803b9a3096da8af795a61b", + "sha256": "5e36663029014857ea1c9895f743d484347e2561bb4cccb2dd8c59424a35e843", "kind": "semantic-state", "state": "failure-or-reconnect", "columns": 80, @@ -682,7 +682,7 @@ }, { "path": "states/failure-or-reconnect.txt", - "sha256": "e3d6e658522834b423f1fdcc1e5204c5023aa88720b47b6df6cea51e5ce6d812", + "sha256": "649a5db60a906d5ed28b556dbc6231c36d11a3ae8e89fa473cdce4c392c4e447", "kind": "plain-frame", "state": "failure-or-reconnect", "columns": 80, @@ -690,7 +690,7 @@ }, { "path": "raw/failure-or-reconnect.cast", - "sha256": "84115111b71bb5683a5b8ae445a5f4f1d5f06c683b44213cbdabf942888b0ceb", + "sha256": "bed225eb46cd87158e6987db8e50c2554cdd6f66dd423b609521f7c35541a6f3", "kind": "asciicast", "state": "failure-or-reconnect", "columns": 80, @@ -698,7 +698,7 @@ }, { "path": "states/failure-or-reconnect.ansi", - "sha256": "fc0b5aefb0d0254b9115afc0d4d9fe30d4a86c4b5864b662499d76786d70229e", + "sha256": "0174698483fc5254eef1ff90bbc187ab259e448dc24ddef72e26cf4a6c0fa86c", "kind": "ansi", "state": "failure-or-reconnect", "columns": 80, @@ -706,7 +706,7 @@ }, { "path": "states/failure-or-reconnect.png", - "sha256": "2499ef7734e8871d7d853ebb27abae6fc770d5dbe3fecf95be46f6feeb32a154", + "sha256": "0c6976145f9bc93b884b9f05d13d8048852f235b6cc533986d99129b8da53f5a", "kind": "png", "state": "failure-or-reconnect", "columns": 80, @@ -714,14 +714,14 @@ }, { "path": "80x24-flow.gif", - "sha256": "1ec0e6f03b5db1c721728d9544f3d8808e81dc651f16d9dfdc1c9574c1f0677c", + "sha256": "c3719c1f9c30651e7a5ddae10b39601f1953a6eee75f589ecbd83ebdbbee1532", "kind": "flow", "columns": 80, "rows": 24 }, { "path": "80x24-automation.gif", - "sha256": "038baaa1516a9ad9a7e644e6dc30627086cb43cabd0e6b282362c0939b4cff0e", + "sha256": "1353469379fa0e744353f51fbe1424dec50ef78bd9092ebefec55e67fab04383", "kind": "automation-flow", "state": "automation", "columns": 80, @@ -729,42 +729,42 @@ }, { "path": "raw/braid-demo.cast", - "sha256": "1010fb2252d84cde2bfa16fd180ded97eaaf1f05188180e91bab0c7a4cad3614", + "sha256": "1151b0fe2e5148d6531567d89920efcb01958207a67b162e80f4b48d2d8b7b53", "kind": "product-asciicast", "columns": 120, "rows": 18 }, { "path": "braid.txt", - "sha256": "5cf70c7f535d1748ac417cae3bc2a563297a1e4f36774e01fe924e1775e8a7e5", + "sha256": "9da6cead048673c48402c4eb40dc9696d39c7bdcb1a2103b755098209f00f53e", "kind": "product-frame", "columns": 120, "rows": 18 }, { "path": "braid.png", - "sha256": "149717716c6e256534dad1f15c4ab6551e9e3d0dcd39d18b71b24c3dbc7afcf4", + "sha256": "433912d21bbcc26edf7ebf5d84d66aab161e208f0ac0881cc9150d2f710a3552", "kind": "product-png", "columns": 120, "rows": 18 }, { "path": "braid.gif", - "sha256": "87baa7cc81264af179211256cfdfb825c656f30284df654e84b67e5cc9c6e0f6", + "sha256": "c29b2c06c59eea7c9d9cbef7ccc01bb1e0a7a7bdc6e183a0a83b42a86d4f2cdf", "kind": "product-flow", "columns": 120, "rows": 18 }, { "path": "raw/transcript-keyboard.cast", - "sha256": "27142899d3d16d5424c5b2c74da87734bda5fe625459e1a855b85788ccda064a", + "sha256": "81afea3985d5c1589ea675a95eb83a6f6fd4b8f9221206611d3568750a81c186", "kind": "keyboard-asciicast", "columns": 80, "rows": 24 }, { "path": "80x24-transcript-keyboard.gif", - "sha256": "2596a16ed0c92c24bbddd90c306ee746d5ab88a6cc47db85f67c08a1395cca82", + "sha256": "160203e6e25b15251ffb1ffd55eb3e6e0209a584c31017bcd3a746add1e595a9", "kind": "keyboard-flow", "columns": 80, "rows": 24 diff --git a/artifacts/verification/w6/package-proof.json b/artifacts/verification/w6/package-proof.json index 96be697..aa550da 100644 --- a/artifacts/verification/w6/package-proof.json +++ b/artifacts/verification/w6/package-proof.json @@ -1,10 +1,10 @@ { - "tarball": "tangle-network-braid-0.1.0.tgz", - "sha256": "32f19ba707aa659f776a604fe241111fe2bfb68928ac6f005014d81bd924182c", - "version": "0.1.0", - "gitCommit": "4121be21db2d42bb19065019dfe76bc2294a648a", - "treeSha256": "c7a7a3a756da84b2d42f2a6c06753a13f16d92da", - "sourceDigest": "c470ee08b93fe3544a9475bfe424d004ab00943ec16ede1b7090737ba6315636", + "tarball": "tangle-network-braid-0.1.1.tgz", + "sha256": "9bbd7b5b9f78b3d202dbb09d4375970edfd4965ffc5ad94b04bd7fa6ce01c4c7", + "version": "0.1.1", + "gitCommit": "8cce4c6e8aade3b1fc51f558d65a823ac12a5e1a", + "treeSha256": "43230a9eb82fc93f603a6f3d3996829b3610c265", + "sourceDigest": "94b73eeb273cbfc5fba6b88b56e6090b812a10c92f459adfef8da18385802dfd", "isolatedBuild": true, "sourceCheckout": "isolated-copy-of-worktree", "packageFileManifest": { @@ -17,18 +17,18 @@ }, { "path": "package/README.md", - "size": 12960, - "sha256": "9d19be58c39d3fcd748e363ba5c0c08ac108953f56024f9708fe10c3a227603b" + "size": 13851, + "sha256": "283c26ab5eea55db87f1e6a753a7541e67c0e41519af5fa5c3b83b15b6abd3b7" }, { "path": "package/THIRD_PARTY_LICENSES.json", - "size": 4100, - "sha256": "029786d2ba6c98dc496668c54946553a6202bfc6e841fcd9843f55fd9b80d380" + "size": 4377, + "sha256": "326c8d65a2b272a992a7d4d7b500f4617b5c3d54d59dc389f779813bc20f7e19" }, { "path": "package/THIRD_PARTY_NOTICES.md", - "size": 4315, - "sha256": "d3f5835eb6cc0fa3bc0a1286a38a553d7f5ffcc0d381832eeeb27d30f62b59a2" + "size": 4530, + "sha256": "eb843989e3fbecd81a4faa5228e828440891cf35eb1ac75d724a3b75a7f8aa58" }, { "path": "package/dist/adapters/agent-interface/harness-runtime.d.ts", @@ -112,23 +112,23 @@ }, { "path": "package/dist/adapters/analysis/agent-eval-version.d.ts", - "size": 102, - "sha256": "348db1d5907b396cedc9bb0e48a12b5bcfbe3873f92ffd3025ff34345ca8e4f7" + "size": 97, + "sha256": "a4bf3e2ea906572e1635785b439a6f575b48f399d459cfc4722909bdcac389d8" }, { "path": "package/dist/adapters/analysis/agent-eval-version.d.ts.map", - "size": 185, - "sha256": "2eeaf417608dba2a86d10b32ac370cc5d9e1bbdbddc45312adc4ffd12ba4b999" + "size": 186, + "sha256": "a611b70667f029645f1625135510837d203cad4ee97af84539f07f704b76296b" }, { "path": "package/dist/adapters/analysis/agent-eval-version.js", - "size": 92, - "sha256": "2f271b0e3ae7f3cb1a4bf880d44d1df23d6462b346515d9c50295835c7fe9a25" + "size": 213, + "sha256": "98faed1e4906b484d3291a9a195bcb10a0d64ffb50fac6efb059b788891675d2" }, { "path": "package/dist/adapters/analysis/agent-eval-version.js.map", - "size": 193, - "sha256": "cc6ecd66602e0f1d4e8343ecb7dd981bb0e0c10b0146f1aa11d1d894ac6925b2" + "size": 257, + "sha256": "4b6e404407ecd6ce48d27c1770a01e74d5414842e487e4fa88bf84ddde77a231" }, { "path": "package/dist/adapters/analysis/citations.d.ts", @@ -138,17 +138,17 @@ { "path": "package/dist/adapters/analysis/citations.d.ts.map", "size": 782, - "sha256": "a493854b64d77168e218604b7409e27b2b7ec0c7044c79cbd71460497e6d373b" + "sha256": "bb37071a3b38eccb66bb0113be836075f2bb8ca22221d89aae9c3798efc88bb7" }, { "path": "package/dist/adapters/analysis/citations.js", - "size": 8783, - "sha256": "0ca4e45b4a49097211f6fe855859c923577c511338841ab6fa475149e10fe072" + "size": 8996, + "sha256": "5860bdeb304649f901c5820327b9eda5f62ac3c7f167d12665c3c79841a6c220" }, { "path": "package/dist/adapters/analysis/citations.js.map", - "size": 9063, - "sha256": "1af1b84f7328227736b5a7c17a626557359577a61ca7ae72753ae390462d310a" + "size": 9370, + "sha256": "98165f545cb50f11e245188c0d17b72ce3c5771d57ae85cdd63e1f8e45ccdc94" }, { "path": "package/dist/adapters/analysis/eval-analyst.d.ts", @@ -170,6 +170,26 @@ "size": 4992, "sha256": "4a463468214cfea58b1eade62d9e27b3d9b419ff83a23852f20298b6f23cf2b4" }, + { + "path": "package/dist/adapters/analysis/managed-analysis-runtime.d.ts", + "size": 897, + "sha256": "c1439b0218494692e09003bacaf7e753b47951183f99a465d14f258f1f3aa870" + }, + { + "path": "package/dist/adapters/analysis/managed-analysis-runtime.d.ts.map", + "size": 776, + "sha256": "b9cbaf6ef311abf0b8d80b482cdf352db3a4758fe1c787e9ac0641047e94ab24" + }, + { + "path": "package/dist/adapters/analysis/managed-analysis-runtime.js", + "size": 2352, + "sha256": "ca083a3f19a4027437fa7e91d9cb27c81ba4e5175840ad9ae6ac3d6441561914" + }, + { + "path": "package/dist/adapters/analysis/managed-analysis-runtime.js.map", + "size": 1803, + "sha256": "cf8695d4ad8419515c600cbe0ed536f26928ba0e2ed188f1fe0ebb060ee8b925" + }, { "path": "package/dist/adapters/analysis/model-execution-scope.d.ts", "size": 546, @@ -192,43 +212,43 @@ }, { "path": "package/dist/adapters/analysis/production-analysis-analyst.d.ts", - "size": 1266, - "sha256": "6d75f6f45f098ef6aeddf9db3ffbad07a1091408eb89d13ad9210f69373e0e9b" + "size": 1330, + "sha256": "e0cbf84f1d3c5542e8868fe1b20b702f60f6b4275919fa7538c724500a3fbe1e" }, { "path": "package/dist/adapters/analysis/production-analysis-analyst.d.ts.map", - "size": 921, - "sha256": "46e57c8fb2025299f5ea849f38f25d1f9aeae40e88726b3bbbb882e6a6291a95" + "size": 984, + "sha256": "e1a3e58a46edbbddb495422c186df83734847e6339007e983cef99577ef816ce" }, { "path": "package/dist/adapters/analysis/production-analysis-analyst.js", - "size": 2869, - "sha256": "6709e328baa541e02ebd225e5050b4e64148b19ebd77d902638cca9e23221786" + "size": 3462, + "sha256": "903060184d2448de17d6bd42f063102f93093175a7d0fdc8b9369ef5328a4d6f" }, { "path": "package/dist/adapters/analysis/production-analysis-analyst.js.map", - "size": 2465, - "sha256": "54264bb8e05195e648efb1772c70738cf65c18363897896c53762c5bf0f71779" + "size": 2984, + "sha256": "245f8822595763435256dbc7d6b2d43b8984c0a4ca91d431c1e7bf0f5e20475b" }, { "path": "package/dist/adapters/analysis/python-runner.d.ts", - "size": 1672, - "sha256": "4d8f499de60a52b40cf52a74c85558c9953fda0481b544d13bb7b86530f2f057" + "size": 1783, + "sha256": "86d5ff23172299d3fe4ba51fb58ed614a417ae1797b4568548722b1fb0091b1f" }, { "path": "package/dist/adapters/analysis/python-runner.d.ts.map", - "size": 1448, - "sha256": "867150ce12071de93a141ecb8d68244edf8f09b33923686867760faa21fecc64" + "size": 1552, + "sha256": "f19f89b4e05088e78b1ee940a7a91b20ba36b9379440f3dcc6bc558930b1539f" }, { "path": "package/dist/adapters/analysis/python-runner.js", - "size": 5135, - "sha256": "5b80e8f57ef82d3bde177e56c677b2fe2e4f2c066852940a388f959d6d4a1eb2" + "size": 9108, + "sha256": "a0666c019074941f9acf48808c6500574e23e35021c17efa6ff793da60948513" }, { "path": "package/dist/adapters/analysis/python-runner.js.map", - "size": 4450, - "sha256": "d10047dcf6158bd9b7b769d409d9b232367461a53d5cea9d285f6ac1631a8d5a" + "size": 7915, + "sha256": "7b7bf6d816a45f8bc90b128e6d6386a8e8e7aa82d1f847a2e5770f9a4f953235" }, { "path": "package/dist/adapters/analysis/question-analyst.d.ts", @@ -242,13 +262,13 @@ }, { "path": "package/dist/adapters/analysis/question-analyst.js", - "size": 4024, - "sha256": "5c2686c49c9b63fdd1438cd6feba92fc4ff967603fb540b898fd7a9c1a425fa4" + "size": 4083, + "sha256": "e38ca07b1b152d64789b6741f762b79a5afd07b9cea1b42502579aced5f8a617" }, { "path": "package/dist/adapters/analysis/question-analyst.js.map", "size": 2039, - "sha256": "c45a06a8082d91faf97451374d355381254d3e1bfda7780a1ccba0311600ec57" + "sha256": "c2b15889aa1580681ec683c51cc1148f392f8b1fd434f26027b199b0dfa7ca09" }, { "path": "package/dist/adapters/analysis/runtime-model-owner.d.ts", @@ -257,38 +277,58 @@ }, { "path": "package/dist/adapters/analysis/runtime-model-owner.d.ts.map", - "size": 1561, - "sha256": "6c161e38ac9fa4325340ca2a9d0514a6c9ace58861c8f9119ccf374c149bd9ad" + "size": 1562, + "sha256": "7f9332fc49421e118c6256548efa47b3fcc817a1b7d36e5cc500306c0ced8366" }, { "path": "package/dist/adapters/analysis/runtime-model-owner.js", - "size": 22712, - "sha256": "944ab66ed05f051a77ca29f9a7c91eb6e0d3ff170a37e5a3ff162b7c4883ff0a" + "size": 27038, + "sha256": "6e13f505c024a1462e95347f7acaf65b6ad9c29349dfcd7520f7298a8a55fd77" }, { "path": "package/dist/adapters/analysis/runtime-model-owner.js.map", - "size": 21224, - "sha256": "20637c7893cdddce2eac5b6e574e2f859433573a597122c41f7b8a4fc1929a49" + "size": 24898, + "sha256": "950e22bdc5838ff9c411796c3a4132e3abe5bd92531a4fa7ecb9b2fa4677d765" + }, + { + "path": "package/dist/adapters/analysis/runtime-retained-model-call.d.ts", + "size": 1122, + "sha256": "c7a750523df00f22eda9de75aedafc85322fb92aedf5d6a809f6596262a79955" + }, + { + "path": "package/dist/adapters/analysis/runtime-retained-model-call.d.ts.map", + "size": 971, + "sha256": "cacf46c8de5f1506b7d7951ef547f9fdacd9b5ce51eae584f67eca1586571014" + }, + { + "path": "package/dist/adapters/analysis/runtime-retained-model-call.js", + "size": 3574, + "sha256": "6e8a395ea4f7024d00bc6588d9bf1458fd776171918534be5ad622be3306352c" + }, + { + "path": "package/dist/adapters/analysis/runtime-retained-model-call.js.map", + "size": 3343, + "sha256": "98d85ea86e7bcb26da03568eaea8f8ea32d7006f60c41bcd1cf52af5a0b0369d" }, { "path": "package/dist/adapters/analysis/trace-analysis-adapter.d.ts", - "size": 3842, - "sha256": "72f13c4b949b61be02d0be1a919f03fd1013a8d34967d1e431a9a4f2344945ea" + "size": 3941, + "sha256": "77190da966f2a6e28a511d19c8b6c9c79467ff5da9b2a28549e8b09a6cce1fa9" }, { "path": "package/dist/adapters/analysis/trace-analysis-adapter.d.ts.map", - "size": 2694, - "sha256": "236814edec88eea9fb623908fb76bc326fcd295927c8b5b7e97e91578aa5d4db" + "size": 2788, + "sha256": "3bcb2d02337a235048c7c29062156e12431a375e6617a82210585730af0bc287" }, { "path": "package/dist/adapters/analysis/trace-analysis-adapter.js", - "size": 10844, - "sha256": "2662824e18b3b8b73b8fa30a4ee40b52dfc6a568c8938174bc37ce30eee58cfb" + "size": 12054, + "sha256": "6b8361494a47f105213ba2a3a02599abc01011c84bc1becfd58977f58a1b3251" }, { "path": "package/dist/adapters/analysis/trace-analysis-adapter.js.map", - "size": 8320, - "sha256": "9e91924ed375a470249937b04acae1d9fa8c3803e203f242e050418c961a0ba2" + "size": 9073, + "sha256": "5b0928ea0b10e724c18af6f03d3c3d345ca577942fdab792955993dd7213facf" }, { "path": "package/dist/adapters/analysis/trace-event-projection.d.ts", @@ -302,33 +342,33 @@ }, { "path": "package/dist/adapters/analysis/trace-event-projection.js", - "size": 8981, - "sha256": "f114fbd433a2437c38e684302113a23af84396b64033a59b34bd8277f0e41ca5" + "size": 8998, + "sha256": "869cf48146c81faced4c73cd0aa52d3eef2aea69c2918c10677abf4cf16f4e0f" }, { "path": "package/dist/adapters/analysis/trace-event-projection.js.map", "size": 8107, - "sha256": "ef7e0d5a6a8373588ae5b01adb958e9f3c796a484523f83f072e66c5c2ae8cb1" + "sha256": "0859c6e488ab4c6272b78ed765b80d44a0ff17d05be8f54afdbe12dd270a082c" }, { "path": "package/dist/adapters/analysis/trace-store.d.ts", - "size": 676, - "sha256": "b56cff4f4cd9a81f30a308e1559fe53389d8e8302676c097859afc687a862dcd" + "size": 683, + "sha256": "f6f7051a6dc5ed8237c98be486cade49f1f75f568c42444466a22688a0f8c9ac" }, { "path": "package/dist/adapters/analysis/trace-store.d.ts.map", "size": 680, - "sha256": "0e33a48fbcb3d4cd3d9d38fc7c390effef6b57cc2a3882aa6a4c158d7315a0c4" + "sha256": "e4e6fc735d98fc891e0b12abb2cc2e543bf191d5e487548bd8eb686fbbaeee78" }, { "path": "package/dist/adapters/analysis/trace-store.js", - "size": 8144, - "sha256": "f094cdb8b207fea5ffaee1d462573accad669a33230c9a848b3e4c178ce10a1a" + "size": 8151, + "sha256": "bcc347808cca2284688c7c1018ac34322878ed00c12aa52e806d655ef46e16be" }, { "path": "package/dist/adapters/analysis/trace-store.js.map", "size": 8303, - "sha256": "c819e272ff00b045fbe0f93e44381c3699112f71168974ba1017c14a697b3436" + "sha256": "1e6dd6245dccb1c95c4842e628d078dbd4504675501b1aa6fda7d606af127d40" }, { "path": "package/dist/adapters/connections/cli-bridge-health.d.ts", @@ -352,23 +392,23 @@ }, { "path": "package/dist/adapters/connections/cli-bridge-model-route.d.ts", - "size": 1607, - "sha256": "094b00d3f280aaf1f122c825a876dc8323d450c4dc2aaae1ed940b0d67e0bf89" + "size": 1608, + "sha256": "d9276194dd6970601c24ed641674410bc65ed783174b5d1dbf0c6eb793693034" }, { "path": "package/dist/adapters/connections/cli-bridge-model-route.d.ts.map", - "size": 1093, - "sha256": "094241a5c351c39c8b4e7ad33540b745b56576847c830bac54613f93a531ec1a" + "size": 1098, + "sha256": "43dfe704afc5ad50be4c25d94843594eb249a18c5cc7f31fc750ebfed2fbf86c" }, { "path": "package/dist/adapters/connections/cli-bridge-model-route.js", - "size": 2463, - "sha256": "05f091f0940fa4abbf1fa76bc5b8ad0f2d20a1df16932c45aca001530cdc4e05" + "size": 2484, + "sha256": "d2fab728f110d1936914dd1d1dfd5e1fb1343e2b25d843928bdf50ac355318c5" }, { "path": "package/dist/adapters/connections/cli-bridge-model-route.js.map", - "size": 2188, - "sha256": "5134c84f74487e35f3d90641f60fc1d9af78e498971b92038032bd9ef912449f" + "size": 2192, + "sha256": "2496fd78acc05fd7008eac3762355e60c83a627293a113fb6c18d3b5f43e264c" }, { "path": "package/dist/adapters/connections/cli-bridge-model-validation.d.ts", @@ -522,13 +562,13 @@ }, { "path": "package/dist/adapters/credentials/headless-key.js", - "size": 7981, - "sha256": "5a9278f13405d3f04edf62682ab1227aaf1c4da9ae6a79d027bb81b6113df516" + "size": 8023, + "sha256": "af41339d9dfc48b310389656e2d7d5c4b4d8aea7fbe906f0e7033e34e804260e" }, { "path": "package/dist/adapters/credentials/headless-key.js.map", - "size": 7513, - "sha256": "5790752706b845595860aab90e575aedd26dfe0aa36ed61a837846f7136987cf" + "size": 7538, + "sha256": "ba2d6c7d7a0ee94d5af749aed7eea673965df7a26d2276580b41532b0f145759" }, { "path": "package/dist/adapters/credentials/headless-store.d.ts", @@ -590,25 +630,45 @@ "size": 5832, "sha256": "3816f10325085676f9313270108eeca8567cde61a99204c8ffbbdcd5448df110" }, + { + "path": "package/dist/adapters/persistence/posix-at.d.ts", + "size": 692, + "sha256": "9d12f15c20341153740c0f7be39cc6697b873836e076663de27d0f019fa77c61" + }, + { + "path": "package/dist/adapters/persistence/posix-at.d.ts.map", + "size": 751, + "sha256": "040554ad9b731858dd599b5366f4dbf1d8d74a90eb102beb8f94ac3de1a864b8" + }, + { + "path": "package/dist/adapters/persistence/posix-at.js", + "size": 3953, + "sha256": "f6207f94be87e928824bd387e7d632abdf5d087d7cf9485d204071ff9c65cd3d" + }, + { + "path": "package/dist/adapters/persistence/posix-at.js.map", + "size": 3961, + "sha256": "d8946fc18f6d5797d3a48316da7f917b16cfdb03b82b0bbe1ae6542f705dfddd" + }, { "path": "package/dist/adapters/persistence/safe-file-descriptor.d.ts", - "size": 2320, - "sha256": "1ab0855fb3807b49dd14956c092c6dc4169a891f4467aff15fedeb3ae357acb7" + "size": 2364, + "sha256": "db1933a258a3ef5a3e3b274326b1f9170fbea32051337a5d7a717849a193de5b" }, { "path": "package/dist/adapters/persistence/safe-file-descriptor.d.ts.map", - "size": 1932, - "sha256": "74c9da32ba980ebdd26c63ee42cbd0836c15595eea6a94abe14c769eb9bfeca2" + "size": 1999, + "sha256": "4bf6e1a133d06af267b05841c17dd61a2f7817f24049aa54128e7c7344091999" }, { "path": "package/dist/adapters/persistence/safe-file-descriptor.js", - "size": 8924, - "sha256": "13de5fe6eed47ea799d4c03bdf54f89afeafe4fce34f5484b3c7146456526ace" + "size": 9251, + "sha256": "f8dfe74abbbe534aa18741113a8e3ea91c88755e8bdf958d3726315f615dee55" }, { "path": "package/dist/adapters/persistence/safe-file-descriptor.js.map", - "size": 8360, - "sha256": "8587c6208f0526f13b331d378fc0a450f4fb56c1de04d664f8f3d635071a64b0" + "size": 8608, + "sha256": "405905ff391d920e268463553ef0071a1b8bf01c0a394a45dc050f0c0e0e0b36" }, { "path": "package/dist/adapters/persistence/safe-file-io.d.ts", @@ -618,37 +678,37 @@ { "path": "package/dist/adapters/persistence/safe-file-io.d.ts.map", "size": 1136, - "sha256": "8ae3be311034dc6800d657dfa03d368f06d1b7b89230b69c36b6c66b98409854" + "sha256": "7cc955fc70cb2c7c85e9f1191b8bafeb19a070a010a1b35e7e2e9017285de3d5" }, { "path": "package/dist/adapters/persistence/safe-file-io.js", - "size": 8652, - "sha256": "b5ff78511b4e4f46b43e9d339d99bbb9003d3aa3ed25b65bbd4a705c51c6eabe" + "size": 8654, + "sha256": "eb3ae4292a1340dd4131ddac58cfc7ea0bbb2a80faf2cddac92ff4cb976101cc" }, { "path": "package/dist/adapters/persistence/safe-file-io.js.map", "size": 8280, - "sha256": "36e45125d6e079cccc57b2caecb6c001e3f3ab897f6a98b0371129e4ceb2bac6" + "sha256": "1f75169c4a04bc29fbf627d96cd8c51769c75807abbe55fcf99d2eecfa999f66" }, { "path": "package/dist/adapters/persistence/safe-file.d.ts", "size": 643, - "sha256": "eba2d76cbe14786da27eb563557e99e2bbea3a452971f1d78e1e3bcd8ff71ea3" + "sha256": "c9388bb68aa2e35b5370f959cdee41f8d6f54371e7e237849339b0ff2e6afc32" }, { "path": "package/dist/adapters/persistence/safe-file.d.ts.map", "size": 522, - "sha256": "83708535cf86856d95ef7f499414ee3820099f3e2d92d16205d23bb91fbf69cb" + "sha256": "27e90f9af0eb43af24d629a7c2c347605f3156333cb890617a30215897eeaeac" }, { "path": "package/dist/adapters/persistence/safe-file.js", - "size": 3777, - "sha256": "5f1bbfd4769853f96c6cfb178723ba015aeecf8b525900a7d9cc5003912a52d0" + "size": 3739, + "sha256": "19697fcd1ff16d66d666573c259083e3933fa8ea9960e599a432860018900813" }, { "path": "package/dist/adapters/persistence/safe-file.js.map", - "size": 3501, - "sha256": "d0e59cead3ac7ea67306fbb98831693606a0323fa7b0a8f7dbcd48d1481d4e8d" + "size": 3446, + "sha256": "fc34c2dfa2a9f32d558608d52b9c7ddca2fcc808b96b28cf87a3683b6b6ce8ff" }, { "path": "package/dist/adapters/runtime/agent-runtime-execution.d.ts", @@ -658,37 +718,77 @@ { "path": "package/dist/adapters/runtime/agent-runtime-execution.d.ts.map", "size": 1198, - "sha256": "36fd0989ae66130968f078fe97504f4e577cc6d26b5620377fcaf9bda02110e3" + "sha256": "6aaf179e085df8c39e2de8dcf4a7fc3a444a1badf718943f182e33dee85c6e45" }, { "path": "package/dist/adapters/runtime/agent-runtime-execution.js", - "size": 11140, - "sha256": "89aa14788171abe517bc6f5c7c24a4cc6f3fdcd5cbb4632a352924bfb077faa1" + "size": 12178, + "sha256": "5a264648ea3bb1297678242fd312b1661b226701d3aa7a560fb3483eceb1750c" }, { "path": "package/dist/adapters/runtime/agent-runtime-execution.js.map", - "size": 9477, - "sha256": "e7ae18b12c5f6c78d3ca9f7d8dd6ce317cb7977b9ec7609e2eb9ffb6ee87e4d4" + "size": 10394, + "sha256": "10312964675206f2a940a038d65b2dac5b4abe1b1722ea4c20163b5bfa0abf25" }, { "path": "package/dist/adapters/runtime/agent-runtime-version.d.ts", - "size": 107, - "sha256": "a5e22cdcdb461851583960d9bd48034eaf872d9f2c08b94f3dd84ffa4e087e5b" + "size": 103, + "sha256": "72d581fcca9c54e80730cd76d6d47b4c2dfe2cb9c398352de97e6f735a255438" }, { "path": "package/dist/adapters/runtime/agent-runtime-version.d.ts.map", - "size": 190, - "sha256": "3258ebeab72360ef1df4b872f8b60d6a755a7d18fe3e6b1350eee46d672d9f8b" + "size": 191, + "sha256": "ebb839fcb33d4945e3f05165a304efa3624e3d95ced6fd3e6ff9f380bb7081e7" }, { "path": "package/dist/adapters/runtime/agent-runtime-version.js", - "size": 97, - "sha256": "f04d27c737010a8bc4e9eda0806f7023d82af81b25e98ef2fad51d3c36a62886" + "size": 222, + "sha256": "bbda970824ed45c0e0117e30be45758c4277415ba83a89047859c859f455d9ef" }, { "path": "package/dist/adapters/runtime/agent-runtime-version.js.map", - "size": 198, - "sha256": "62c6ac978e36af1518282671fc1e4b770d0884d33e96350df8ac84f3d14ee7bd" + "size": 262, + "sha256": "2ac975e46e4c95050633d4b59bf66f8168389488a1a66fa4d5f1ec1594939954" + }, + { + "path": "package/dist/adapters/runtime/cli-bridge-retained-execution.d.ts", + "size": 955, + "sha256": "40b3737254af5d0e212279083079942ac0d84b1f848402d34728735608971b67" + }, + { + "path": "package/dist/adapters/runtime/cli-bridge-retained-execution.d.ts.map", + "size": 760, + "sha256": "880e01de26c18837c306d0f284bf550b448b773e19fe5fe670ee6a5beb97c509" + }, + { + "path": "package/dist/adapters/runtime/cli-bridge-retained-execution.js", + "size": 878, + "sha256": "744fc62ae467a73cad60cb342ca5cd8cd98082d999a5037510c1f3318fb100b6" + }, + { + "path": "package/dist/adapters/runtime/cli-bridge-retained-execution.js.map", + "size": 832, + "sha256": "664bb4c366b1a9330771718f94deef0ff371d99fc016f76b30813abbd3cf450d" + }, + { + "path": "package/dist/adapters/runtime/cli-bridge-retained-run.d.ts", + "size": 1578, + "sha256": "8fdbc3e344352e37b4c3106e4660372be28c877736a180b2a3007697cda59d73" + }, + { + "path": "package/dist/adapters/runtime/cli-bridge-retained-run.d.ts.map", + "size": 1189, + "sha256": "9dfae3e8697d0c07efec8bc535e33a394bfc9bf93f3dfe72e00d10de11005a2a" + }, + { + "path": "package/dist/adapters/runtime/cli-bridge-retained-run.js", + "size": 6065, + "sha256": "39fac099974d3502d9c0eccd708d1451ccaba6c21fe89f2e8548c27a43328458" + }, + { + "path": "package/dist/adapters/runtime/cli-bridge-retained-run.js.map", + "size": 5455, + "sha256": "3961a21eba78f18fa19fbd6f2d256cf40579cbec49b253606e33dab0a41d678f" }, { "path": "package/dist/adapters/runtime/execution-observation-source.d.ts", @@ -732,63 +832,63 @@ }, { "path": "package/dist/adapters/runtime/production-backend-common.d.ts", - "size": 1824, - "sha256": "d1f92abcf44eab911e2d06b7be12676b0924024057ce98688dc25e5f06ee4deb" + "size": 1879, + "sha256": "837776aa995c62c3cc86dc0578dd64767879496cd069172b02a49ba6580e61d7" }, { "path": "package/dist/adapters/runtime/production-backend-common.d.ts.map", - "size": 1496, - "sha256": "dbdaec806029470ef71ae0a4f09564defe8a5bee60249438d0fcdf179f498e9a" + "size": 1557, + "sha256": "0024857ced2b7c4e51c7656b303a53d7ba3dc1eab79f39cb5602debeb301228a" }, { "path": "package/dist/adapters/runtime/production-backend-common.js", - "size": 3079, - "sha256": "b8791a7d129643b0fb67600e1b75d4935e0d0a3dc1f02130d88b25c8d60c1482" + "size": 3130, + "sha256": "50604a012228a5cd3fbb408b29e780522697c825a018f61be3ead7f562043d6f" }, { "path": "package/dist/adapters/runtime/production-backend-common.js.map", - "size": 2619, - "sha256": "38e7d11ef1b019caef4d49316d5e1d7c5fa4ef6aa45559721993559f7294efde" + "size": 2671, + "sha256": "b7b3170c7e6bb3df365b05d7386efade7d4bd5d747805b409855bafb7a11c15a" }, { "path": "package/dist/adapters/runtime/production-backend-resolver.d.ts", - "size": 893, - "sha256": "c19d1d50e0631a1b603f5973c74aa4d708398f044c668b8c864d9ab8624be478" + "size": 1225, + "sha256": "a4e64ebe477e98884b1cd7c24cc54e95eb985e85efe439652a7e0452543c7b52" }, { "path": "package/dist/adapters/runtime/production-backend-resolver.d.ts.map", - "size": 645, - "sha256": "7e0bbab2ef25be8a95e855278b25a1c1feaea067642fd2d7f764829e1f5ee968" + "size": 809, + "sha256": "6a83f4a3fa25b70b71929882c7015f75946ecc7fcf93c25df07cf2111e7357e2" }, { "path": "package/dist/adapters/runtime/production-backend-resolver.js", - "size": 1482, - "sha256": "e4ec5693a7737861f51a39129438ea09fea97c4ba39396a8a572b25f22f94f01" + "size": 2194, + "sha256": "4f76944413eaa1857670e7b2af89a0c3098129c9e5ae77e5bc756bad1414d5ae" }, { "path": "package/dist/adapters/runtime/production-backend-resolver.js.map", - "size": 1305, - "sha256": "cb7847c699e1b8368e38a238248af1afa56f4cdd4ccd1a9525a08be4ef39703c" + "size": 1918, + "sha256": "787b7f76db27cb3480fb11a0c3d4a3163819ed9e0a03a813a6a8f96ea35653ec" }, { "path": "package/dist/adapters/runtime/production-cli-bridge-backend.d.ts", - "size": 603, - "sha256": "caf626f6079597b600ac1128d720d4cc4e268bcc8a5996001d578e5c6ed732de" + "size": 1520, + "sha256": "d66413d46c603cf9db9acf2de37c1a3d7f62eb30e1c39c08aab7c22e2e286de7" }, { "path": "package/dist/adapters/runtime/production-cli-bridge-backend.d.ts.map", - "size": 555, - "sha256": "fd2a4060f93de8024981656218020b57005cce3c835bb98ff948329a0159cb17" + "size": 1353, + "sha256": "ed3b66747ca31dc0c0a7523e7adafe781d2b204d4e027169d4366d8343fd3c82" }, { "path": "package/dist/adapters/runtime/production-cli-bridge-backend.js", - "size": 4209, - "sha256": "d108b3bc8360b0a087dec8918753f9b79a2cacdea5aa230e68c24baf0be425cf" + "size": 4965, + "sha256": "2432dce768135950165f21404f555ed1bd9875e1d1f30b3931f366460c4970a1" }, { "path": "package/dist/adapters/runtime/production-cli-bridge-backend.js.map", - "size": 2936, - "sha256": "9e4a57b884f8b6d53ae589fd7c78682a06a4706427e26756b2e591f52042a79c" + "size": 3581, + "sha256": "812770051d69d250c412352f16a9f8df39291cd416491831fe84616d6c7eee1b" }, { "path": "package/dist/adapters/runtime/production-tangle-inference-backend.d.ts", @@ -818,17 +918,117 @@ { "path": "package/dist/adapters/runtime/production-tangle-sandbox-backend.d.ts.map", "size": 544, - "sha256": "c8cdff5049e9ac7fe468385cd4fa32e1e92a74486d607a7296f78e4d31c5ec38" + "sha256": "02ff474d9a1b343a6da1abe6ea7f3290ba7cbac4832f932c14e7e5601355d00a" }, { "path": "package/dist/adapters/runtime/production-tangle-sandbox-backend.js", - "size": 5280, - "sha256": "95ce894e38065d28d27698d27f3df28a9788b0028b88710f4136a9d41a181e66" + "size": 5492, + "sha256": "5b4e4363be70dd5b1bb9187333b077fad4e851cf49c4857c97cd58ed10c19efe" }, { "path": "package/dist/adapters/runtime/production-tangle-sandbox-backend.js.map", - "size": 4148, - "sha256": "49bd4bc2e4f1bb6a4315f2a1b81acfc97f3e585817e73ded59e6ace8c912e64a" + "size": 4361, + "sha256": "9974209934eb6630997f30a962490347436b2cdf922fa0ab61f4c17835168b67" + }, + { + "path": "package/dist/adapters/runtime/retained-execution-contract.d.ts", + "size": 2460, + "sha256": "b51c721ec24f6c86bba450ff5b9131881e56ea1eaf107c8b1694b41b65947939" + }, + { + "path": "package/dist/adapters/runtime/retained-execution-contract.d.ts.map", + "size": 2173, + "sha256": "9eede1676787269a6e3de00cf8282f610ea5ae33785dd70a33d42fc4cec53660" + }, + { + "path": "package/dist/adapters/runtime/retained-execution-contract.js", + "size": 66, + "sha256": "b39bdb5cbe9c31f8c2c02d4c05917a967bedd69cd8d61c33cb7dcf0bf2135a2f" + }, + { + "path": "package/dist/adapters/runtime/retained-execution-contract.js.map", + "size": 169, + "sha256": "27e9b3474affd48e88c2e3e60aea3e18c8f82d5c8bda34166a2cb5ea944ef5d0" + }, + { + "path": "package/dist/adapters/runtime/retained-execution-projection.d.ts", + "size": 1162, + "sha256": "09ed7b813c38a2756955cbb3f0254eaddf702f5b1fcc5f42795ae740284653e5" + }, + { + "path": "package/dist/adapters/runtime/retained-execution-projection.d.ts.map", + "size": 1002, + "sha256": "c0b9345c243e4f46d47c043eaf83961912b9d3676c71c409b0e5fdad8ec7850d" + }, + { + "path": "package/dist/adapters/runtime/retained-execution-projection.js", + "size": 3639, + "sha256": "5df00c80762e5bf9f2d6c5bd897f2e843962ed5c86ce3b7d9af84472efb7e62f" + }, + { + "path": "package/dist/adapters/runtime/retained-execution-projection.js.map", + "size": 3911, + "sha256": "14f8a867ce06e87fee84a12f8b71f2b3fd3ac85168fd85ebee3695ba5f9f5432" + }, + { + "path": "package/dist/adapters/runtime/retained-execution-state.d.ts", + "size": 2401, + "sha256": "043622e9805bd870961898982f4d487699d1e1a9aa03cccd6a3427738b7a42ef" + }, + { + "path": "package/dist/adapters/runtime/retained-execution-state.d.ts.map", + "size": 2172, + "sha256": "b2f99ed9d40ef3f1b39931a48e33beaf7aa365c2a259142f68fa23c7c01fb87c" + }, + { + "path": "package/dist/adapters/runtime/retained-execution-state.js", + "size": 7295, + "sha256": "3462d0b76d3aeeeff55648c5dbae8e8f76d5a9f8ab938e1c2def9c01c112d5a0" + }, + { + "path": "package/dist/adapters/runtime/retained-execution-state.js.map", + "size": 7243, + "sha256": "dced53ef9ca59d3dc3784fcfdf37f3773fc180b8c2118d2d78899f7f8596ba2d" + }, + { + "path": "package/dist/adapters/runtime/retained-execution-stream.d.ts", + "size": 760, + "sha256": "f3d0a9fc7fe016d11732ca2677bfa4eb95c5fdc1d6f98ea95d2607f12930d711" + }, + { + "path": "package/dist/adapters/runtime/retained-execution-stream.d.ts.map", + "size": 741, + "sha256": "f134ca88b2c7c8d6ad5503fe36c05c8118f87e95fdef50044d3c9770be8572b0" + }, + { + "path": "package/dist/adapters/runtime/retained-execution-stream.js", + "size": 1994, + "sha256": "9cb42c96b9090606894ac83b69eeb479ec8c1058499526db8b1155346edf59be" + }, + { + "path": "package/dist/adapters/runtime/retained-execution-stream.js.map", + "size": 2027, + "sha256": "6754486918f9a3580280a2324ccf70c86c46fb9bdf14479eafed51e42eb20b55" + }, + { + "path": "package/dist/adapters/runtime/retained-execution.d.ts", + "size": 1947, + "sha256": "93fa949ae8883fa332b86f4b41e2c6a8b2a38825d46d92caa2b98468d1d9c105" + }, + { + "path": "package/dist/adapters/runtime/retained-execution.d.ts.map", + "size": 1696, + "sha256": "816f6f9bd33619c3048fc6164a0281f39241d2569e78161e0ba10c643ddfd799" + }, + { + "path": "package/dist/adapters/runtime/retained-execution.js", + "size": 13100, + "sha256": "e0aa0856c3fa88c838d7388e08bd1baf5988a037b78d57175bcccc2ec3eb0344" + }, + { + "path": "package/dist/adapters/runtime/retained-execution.js.map", + "size": 12597, + "sha256": "b96a06ce39c55146c1de2bb73eb6aa5bf6c19002df107d0939ed4511656ebe53" }, { "path": "package/dist/adapters/runtime/sandbox-account-observation.d.ts", @@ -898,17 +1098,37 @@ { "path": "package/dist/adapters/runtime/sandbox-observation.d.ts.map", "size": 497, - "sha256": "505456e0949c0a5207b9e8de26afe078a3b40ecf3d859fbc5af3b354cd279afc" + "sha256": "99188b347b6a5d439f3a2601cfa1dfea6d0b6d1aa3593ca70d746712447e357a" }, { "path": "package/dist/adapters/runtime/sandbox-observation.js", - "size": 5807, - "sha256": "f6a39714531661c782bab9dd71adb1adb13a4bb4cc087338fc21c0d996b6fc20" + "size": 5916, + "sha256": "fb33be6f00cf81f99417d55f726e5e42b5b8f23b774ac4a771b5aa3acd258c03" }, { "path": "package/dist/adapters/runtime/sandbox-observation.js.map", - "size": 5553, - "sha256": "9c034fa286632af258a67dd94c2b1e42fbefbc0bbdff3dd1960710e5269fe9be" + "size": 5753, + "sha256": "5dd84952ad0fe15e11371255be3fe386934844a2d5c9d3c38ae69d8f39843267" + }, + { + "path": "package/dist/adapters/runtime/sandbox-result-projection.d.ts", + "size": 647, + "sha256": "f824e355f41d57657e08940f23c011a4ca0c1e00a471343f78811a0e42ac415b" + }, + { + "path": "package/dist/adapters/runtime/sandbox-result-projection.d.ts.map", + "size": 521, + "sha256": "b5c89484b8dc4ddf291a1e5de58dc3bd35ee9046110bf4e382fba82d78b6de02" + }, + { + "path": "package/dist/adapters/runtime/sandbox-result-projection.js", + "size": 8735, + "sha256": "2a605837432b0331608c76b49c30708b8ab8cea2c66260a1fa77df7168d3a106" + }, + { + "path": "package/dist/adapters/runtime/sandbox-result-projection.js.map", + "size": 8907, + "sha256": "dfa0ae766dd6f7b5b316ea312bbd429350f69626d18a1f87a80de145f8520c64" }, { "path": "package/dist/adapters/runtime/supervisor-control.d.ts", @@ -1132,23 +1352,23 @@ }, { "path": "package/dist/adapters/storage/sqlite-bound-open.d.ts", - "size": 882, - "sha256": "266b8f0ff393ced6be60b06ab4b0f1d856e8a7c99cd2c8129030f48726d5d6ba" + "size": 911, + "sha256": "03ce613c64bd5c5c82cf7508c38e9bb6f410e11299e137fd49b9cc16653c03a6" }, { "path": "package/dist/adapters/storage/sqlite-bound-open.d.ts.map", "size": 599, - "sha256": "20b18aba1d1988d899148d3423058e499cd466b7e79bd10a6b729589a536f4e8" + "sha256": "6089b7636b10338760d4a70d5e08dc1e4242954c90b2eba243325221727e4518" }, { "path": "package/dist/adapters/storage/sqlite-bound-open.js", - "size": 6584, - "sha256": "2410d34d7e77e2954da93f28da660236de4fa160a0ab5ab7d32fdcfe182c1edb" + "size": 7476, + "sha256": "a8a2e90e8e4cb501ce0414cde66a1a0a6dc1e66c1847ebfe361af3e38f22fde2" }, { "path": "package/dist/adapters/storage/sqlite-bound-open.js.map", - "size": 5687, - "sha256": "75b800a869188bdb4a6355c8b6285cc25222f4fd5e602b59718f909686b88b77" + "size": 6365, + "sha256": "8046acc41b628ce9d54e95031d8a0436e3a1de916272078679b1f0793bde1448" }, { "path": "package/dist/adapters/storage/sqlite-content.d.ts", @@ -1818,17 +2038,17 @@ { "path": "package/dist/adapters/tui/ui-capabilities.d.ts.map", "size": 445, - "sha256": "e4eb7526e21b52deba132a3e6cecf041b57288b77668947eef5b5b583e112baa" + "sha256": "392739a9af3e39d3fb32d09520c348c1fd581a603a3ddb2dfa3bfde0653c5aca" }, { "path": "package/dist/adapters/tui/ui-capabilities.js", - "size": 8344, - "sha256": "db50285d1ab2e7afd700b135bfbc6538dd786c1d43782fafd94e3c4aeaf40b0d" + "size": 8909, + "sha256": "b5f11054e7888f62301bdaed54510113d198bdd37a819485ab1b88b559e20bd9" }, { "path": "package/dist/adapters/tui/ui-capabilities.js.map", - "size": 6340, - "sha256": "9025eb74514f08c10cd0a14184416bb8019ca0d0e0b51785d096b9d2e1321b2d" + "size": 6726, + "sha256": "a356d8d9dc1246bdf8d77fc6284dae8370cd3118cb866e34ccdc8d605362f91d" }, { "path": "package/dist/adapters/tui/ui-command-dispatch.d.ts", @@ -1838,17 +2058,17 @@ { "path": "package/dist/adapters/tui/ui-command-dispatch.d.ts.map", "size": 453, - "sha256": "5502c4dfe15cda89b557b22fedc6a67ea4c58a1440087c9ac70638117c343286" + "sha256": "2fb22d94030da6e516ee32fdcedec48a36f71c5422f2f06cb1be4428c962463c" }, { "path": "package/dist/adapters/tui/ui-command-dispatch.js", - "size": 4382, - "sha256": "60d7d9d1904e2881250173750b783545d49f402c083e5f07049f2886e1f9a5ea" + "size": 5553, + "sha256": "f84ab30c6b0438e80ee57d879a62ada1a4c81e17045bc859ce7410a9773d6642" }, { "path": "package/dist/adapters/tui/ui-command-dispatch.js.map", - "size": 4213, - "sha256": "cf58ff59736ff5fbcd9108028c0a8b5fc7a6b2776535208b55316a928af61fe9" + "size": 5211, + "sha256": "97ddbc0161cad21417fd6c5c27d5fd40005bb2e817e0700ce5a645ab229103c9" }, { "path": "package/dist/adapters/tui/ui-conversation-dispatch.d.ts", @@ -1962,13 +2182,13 @@ }, { "path": "package/dist/adapters/tui/ui-entity-details.js", - "size": 7102, - "sha256": "d43b26fc2054d13cf42bb80fa94f77540fef35b9d93962ff587f8cdbd6d7f7fa" + "size": 7251, + "sha256": "69151cbc0b7f989d5436284f2a753b4025aa4e09e754c13e976f7d113651bd8e" }, { "path": "package/dist/adapters/tui/ui-entity-details.js.map", - "size": 7306, - "sha256": "0364cef643d31387123608e2a8439e9ad224337145d2442af26eaeb54dd102b4" + "size": 7394, + "sha256": "c3c79e51248a499961add1b925b82768f643127699b50c9c1bd9586e13668394" }, { "path": "package/dist/adapters/tui/ui-fixtures.d.ts", @@ -1978,17 +2198,17 @@ { "path": "package/dist/adapters/tui/ui-fixtures.d.ts.map", "size": 594, - "sha256": "39b85a2b86c7b020236fcb0ca501be2b0632674537db3c3b843ef098b0a5c8fd" + "sha256": "7fff3586cdc106bef66c2ddc1fc4d308b371e77885ee9ea78f82d0134c1bad5c" }, { "path": "package/dist/adapters/tui/ui-fixtures.js", - "size": 10643, - "sha256": "8ef20950eb166cab5d72a288a77157cffe0bef98d0297f46aeb8c59a65377a1d" + "size": 10662, + "sha256": "cce2e53d28f76b474638a1aeabd6b74db8436db6fe34118d4a586c15838ec275" }, { "path": "package/dist/adapters/tui/ui-fixtures.js.map", - "size": 7694, - "sha256": "eb60d6e8fccf454e76afc5b5839e151ac6aa2bd7383d75a25f59985bc4f1dfe7" + "size": 7714, + "sha256": "c9f0c75d2dae6ff6baa67b315360fd17b4cd1900a59abf1a81737d0a119939ca" }, { "path": "package/dist/adapters/tui/ui-headless-dispatch.d.ts", @@ -2018,17 +2238,17 @@ { "path": "package/dist/adapters/tui/ui-intelligence-dispatch.d.ts.map", "size": 636, - "sha256": "5d366ae1343b29824781cc2adf8b73149c7f92482c7f14288abe91c639147194" + "sha256": "cfcc60ba650daa6f7e1481932f17693d68e33cca23921dda8ea232457759750d" }, { "path": "package/dist/adapters/tui/ui-intelligence-dispatch.js", - "size": 15946, - "sha256": "8c3a22125d892c6caf15d3b63425cb7a3edb4ed0f93c81e5b423d16189f9cf83" + "size": 16091, + "sha256": "1abf86c2baa70055557711269585007cdf28e42a301abb4e1b0a1ab5489bca26" }, { "path": "package/dist/adapters/tui/ui-intelligence-dispatch.js.map", - "size": 14773, - "sha256": "caacf96b1bc0b42d300b6eaed8de7b439e8eddf13f2c29703b3046c769b6f0be" + "size": 14939, + "sha256": "3775af77c52e93a9f56a14384a6b75e9d465808aeccf170ebf277023086844a0" }, { "path": "package/dist/adapters/tui/ui-intelligence-fixtures.d.ts", @@ -2058,17 +2278,17 @@ { "path": "package/dist/adapters/tui/ui-intelligence-result-view.d.ts.map", "size": 493, - "sha256": "aac293ea7d48b62e471b1498f52bf706728bd34a045b58a28998edb4da9fef2d" + "sha256": "3bf9fa7f0384234150be01d957cc61c5a033caa3ed2e1e4965d070fde0d2e590" }, { "path": "package/dist/adapters/tui/ui-intelligence-result-view.js", - "size": 6286, - "sha256": "d65ca0d19139695028448d179c355b325c9eb4b7b3d041927f5d6d575269dd7c" + "size": 6451, + "sha256": "ab5644ed7143e921f39406168ee764da87d27830aa9b435ff23fcacee1714c72" }, { "path": "package/dist/adapters/tui/ui-intelligence-result-view.js.map", - "size": 6574, - "sha256": "085859789424ff8c9190204913b4874263d9a69da592f5849e9825aac16b7e6f" + "size": 6662, + "sha256": "8a3f8aa79d8c932cfb4ece570da289f463a795319dd8269c8140adc2982d2393" }, { "path": "package/dist/adapters/tui/ui-interaction-receipt.d.ts", @@ -2098,17 +2318,17 @@ { "path": "package/dist/adapters/tui/ui-projection.d.ts.map", "size": 1420, - "sha256": "6bd8601f6c309fc2f113d4dc0a69f1850764419474c5efe0fc0e455302c3f47f" + "sha256": "3dcf8a90f96aee0b2d8781cefac8997864c8c8d8b723c6bce4695d173afa4fd6" }, { "path": "package/dist/adapters/tui/ui-projection.js", - "size": 22936, - "sha256": "6bced52bc9bef8a392cbd8e5985ffc18053f904ae09cc07613bad89d205c0645" + "size": 24137, + "sha256": "13221a70667c4f51bf96adf829050390b5a3028849a0cf0de1e2f54c44368116" }, { "path": "package/dist/adapters/tui/ui-projection.js.map", - "size": 24302, - "sha256": "7e048296aa6ae52a5d94124212052c22cf44d4821b5352f0376d3aa06d0be227" + "size": 25155, + "sha256": "06951a7cca72682a76585a08676ca8d120e32be64bcfe2476cf6cc91a77e6d46" }, { "path": "package/dist/adapters/tui/ui-semantic-state.d.ts", @@ -2332,23 +2552,23 @@ }, { "path": "package/dist/app/analysis-comparison-request.d.ts", - "size": 522, - "sha256": "9509085b40b643e2615f3c7ce19a6d223a1f8f641d63db0f49442a31b73b3ec8" + "size": 531, + "sha256": "31eb2406963d6f61f96fe17cd072557d42dfa6b61eee0dca85c118a86233128a" }, { "path": "package/dist/app/analysis-comparison-request.d.ts.map", - "size": 458, - "sha256": "3d187ecf88b58e271b3afde86f77e090a32fc63130fde567ad0707f2222b638f" + "size": 473, + "sha256": "2c0033cf3bb31563983715c5aca38bbf55a697aa00ef839151dcf539c3eb82cf" }, { "path": "package/dist/app/analysis-comparison-request.js", - "size": 1677, - "sha256": "33b78fa386d88e76babdc765475c51dff5dac3afa3d0e5fc552206417f79c8fa" + "size": 1754, + "sha256": "cf60b6cd55f5965a3c2827b09fb6b83d289e52d48096393686491d7c486fea20" }, { "path": "package/dist/app/analysis-comparison-request.js.map", - "size": 1898, - "sha256": "f378bd95852158ec24d8ca7cfbd2bcf66eae21d02628c258ec3386d590beb149" + "size": 1919, + "sha256": "bbdb4cda2776f796884c1249c38f12164f1dab6a2f26b0ef81b11f88bb10ad76" }, { "path": "package/dist/app/analysis-comparison.d.ts", @@ -2362,13 +2582,13 @@ }, { "path": "package/dist/app/analysis-comparison.js", - "size": 4394, - "sha256": "156e53fc3afc3caec6dac89a8e15a9fcd84cbc32da42c7ca94cbf711eee75c75" + "size": 4406, + "sha256": "f86710ffe81da7df05d469c150a3637e729858d3c90e0838aff5f09bcf58b923" }, { "path": "package/dist/app/analysis-comparison.js.map", - "size": 3970, - "sha256": "9298715ae9403fc3cc6d21ad8a4bc3a2af86c8d49fe8d8ac3dd28465d6d5e9d9" + "size": 3980, + "sha256": "b089a701dd20b3b801d13047fd5c15f206ce9a64a73eacdc35f38af62b65f35d" }, { "path": "package/dist/app/analysis-execution-session.d.ts", @@ -2518,37 +2738,37 @@ { "path": "package/dist/app/analysis-promotion.d.ts.map", "size": 740, - "sha256": "665b7c576b3f1914d8d43409dc2fc7667a4ae9c59f3643eae95bba2e7ccf6819" + "sha256": "a75af56ba9d7790764016191475e74daa151475c187a4c370f713103d0c7f851" }, { "path": "package/dist/app/analysis-promotion.js", - "size": 8983, - "sha256": "d49f3c8dacec9389050429aab54ca64c0646f6fdbeb5cb14240d337fff7b42c7" + "size": 8968, + "sha256": "c1e59aa1e97f7e031b514ddca35bed908ffc8f0106dc974600a7c29e3594252a" }, { "path": "package/dist/app/analysis-promotion.js.map", - "size": 7912, - "sha256": "cca8d62cca9a9614e3d2b233d009b306d52d457eea4c0e67c1676c7a6781a306" + "size": 7886, + "sha256": "d95ac2ac4f027fb17fa1702c4bd77d973508828243c537c3f569bc59293fa763" }, { "path": "package/dist/app/analysis-request.d.ts", - "size": 563, - "sha256": "5cb2eed9b5522b0d381549490d84487d3bd24e3ce6c6e4601963e71f7b25851e" + "size": 572, + "sha256": "91fe1c4fb4ea4b669d242638781af916a8fc191e774e4bfaecd4df6d47c826ff" }, { "path": "package/dist/app/analysis-request.d.ts.map", - "size": 496, - "sha256": "8c910ba9d488a855f2927081827df281c4a067a1e74b32b74957c97827bd386c" + "size": 510, + "sha256": "548d7a568b21859b706458083674609d392c5dc9cc53055ddef6df32d451a765" }, { "path": "package/dist/app/analysis-request.js", - "size": 1686, - "sha256": "80cd0d13aa70c2f40511d80c03e562a535e3e44a995ebbb3b656fa30cba6be93" + "size": 1647, + "sha256": "e9bdc599ae67c522c14c12c3121cf7dceea225effa9d77ac0c2ce65562274f63" }, { "path": "package/dist/app/analysis-request.js.map", - "size": 1415, - "sha256": "80c7cc4a542ea24ad215dd873283e8a03aaa8ecde35d673b2d021374b4242b76" + "size": 1389, + "sha256": "4498d518e97cc7c7280ae116c7a35b1d8fdf14f81628e0a9928ab12817623379" }, { "path": "package/dist/app/analysis-result-mapper.d.ts", @@ -2582,43 +2802,43 @@ }, { "path": "package/dist/app/analysis-service.js", - "size": 8660, - "sha256": "0d41732d07a40e0b13353d8c526a1c0addb1aa84e50a154f9b057029e5dc8590" + "size": 8666, + "sha256": "359ac6f5d8bc539e2e94e789a046ca032b5be0f2419dad474bbb2a67638e6681" }, { "path": "package/dist/app/analysis-service.js.map", - "size": 7553, - "sha256": "3c3fe0d161040d2bdddf00f486524fe243a693afa763e7d104e794ac99c80b4e" + "size": 7558, + "sha256": "2671605691a955f0ee5c40ed7db6d3d1e1c499335e20598fcacc03fd369ae5a3" }, { "path": "package/dist/app/analysis-source.d.ts", - "size": 1870, - "sha256": "088666a779b7795818dbc7583bbcc84e465cbbb8a3f38a0651e116369b4166e2" + "size": 2335, + "sha256": "6737bb29c701f7fa0a3868a7cc96027fac869190f58487d4f2b8003ecf536855" }, { "path": "package/dist/app/analysis-source.d.ts.map", - "size": 1394, - "sha256": "89d9626bddf2cbad1d6c4d5919581593e25d5d437e01adfea6e2d1c3b55e2339" + "size": 1685, + "sha256": "bd8c057d0ab7d6d49bcea80a15c475ac93eb1accccc675bc270e1829e77b09f9" }, { "path": "package/dist/app/analysis-source.js", - "size": 11275, - "sha256": "982b098e6e41ad6925dfc2aa065a159241477c2f2e3978e94c134917886aefdb" + "size": 12253, + "sha256": "7f7444ac233c85bfa3e766bcdf07ab8cf556d48c04b067db84ab5dd79065d3da" }, { "path": "package/dist/app/analysis-source.js.map", - "size": 11310, - "sha256": "46c671e6dc11693743681351c58ea0492b00385314452b3fdf06236c0ab1be01" + "size": 12333, + "sha256": "e3ad38f99030b87cfb44d564b915c58f42aa6ad0c66ce3714e14ad303860e494" }, { "path": "package/dist/app/analysis-types.d.ts", - "size": 4781, - "sha256": "192f6c1b40a9bc81bd2aacde121357df322eba0efe0055a1835b9700e6bab139" + "size": 4888, + "sha256": "33bc4f0777ee09bb86c97a5262e182e8a3638e21d609b48025a1a2b9c1d0664c" }, { "path": "package/dist/app/analysis-types.d.ts.map", - "size": 4332, - "sha256": "75b57a24db58fc1e20faffe076533aa8595724393a4b3aebdfedc9ee9976ee16" + "size": 4431, + "sha256": "4e82a615dad1fbbc71b78fc029b88c126a2c86dd83bb16e010e47ca73cc4a222" }, { "path": "package/dist/app/analysis-types.js", @@ -2628,7 +2848,7 @@ { "path": "package/dist/app/analysis-types.js.map", "size": 945, - "sha256": "41bd198806f0aaff9d049ded7c11c265e89e3fbe7f8a8a61c08f565f7bd33561" + "sha256": "cbfb0fc550cb48654de46f64ffaf65c66a6473fef74033ce59a0920675886eba" }, { "path": "package/dist/app/application-guards.d.ts", @@ -2752,13 +2972,13 @@ }, { "path": "package/dist/app/application-ports.d.ts", - "size": 5122, - "sha256": "61c82255b87e565cc4ff5aed42e437452cfdf897c70b1f758e8a18ef01cd69cc" + "size": 5198, + "sha256": "07473bf969fc45196d35601b8c454059008da13be87fb3c14d61704455262a01" }, { "path": "package/dist/app/application-ports.d.ts.map", - "size": 4463, - "sha256": "fc2c01d80678daeffde481bf3c21ecb168f8ec54c96bd4212c18abbfe58fffc3" + "size": 4518, + "sha256": "29929c71a97717503fc164ef281942937b9044ae5c23093f5f71f21c9c33d67d" }, { "path": "package/dist/app/application-ports.js", @@ -2792,23 +3012,23 @@ }, { "path": "package/dist/app/application-support.d.ts", - "size": 3105, - "sha256": "5bac60dc793b619215c0738e354642c8e69a59c2c16d25105d24c9f24c98bae5" + "size": 3270, + "sha256": "1a9a4bf2b40924affdcd242cc9e739587738500e5573a46a2bcf1b986193eb79" }, { "path": "package/dist/app/application-support.d.ts.map", - "size": 2848, - "sha256": "ad708d8185b408efb85934ef549780b2e069a2e9a575fbf9f1cea8d44faa5c98" + "size": 3031, + "sha256": "4b7c97001b39e369a3a7c423f73d27b254699d8bd980938ec91299189098bb99" }, { "path": "package/dist/app/application-support.js", - "size": 15894, - "sha256": "163c4c1172492b747e69cdc61d8a1f2427eca1d32208c868001496059cfa7b6a" + "size": 16150, + "sha256": "47cfe05bf12b3ea36310bdd92b6ac803b080102e1a3071a05a2147d905b7110e" }, { "path": "package/dist/app/application-support.js.map", - "size": 14819, - "sha256": "4b399b36b864237df28d576e2a8772949c0fb08184f6ac95aeab4ecfbf29f781" + "size": 15113, + "sha256": "9aa0fa411c09a8ad5aa84a2e4da78d9fd6b45683f2744e488ad63ee514d534ca" }, { "path": "package/dist/app/application-transition.d.ts", @@ -2858,17 +3078,17 @@ { "path": "package/dist/app/application.d.ts.map", "size": 3614, - "sha256": "273e3851bfed61d5c73cf9a9b28a3d6a2c2513d53ffe477f09a366a099c7ef51" + "sha256": "4a7120afbe538f28092f2591f4e263e8504f9b9810534e51b4b17ee520750967" }, { "path": "package/dist/app/application.js", - "size": 21508, - "sha256": "8b2e2510bbc49f609091024113802c80cee5ad1e6f997fd1426925bd36fe5a28" + "size": 22387, + "sha256": "fc43ae827a6b8336ef1a2b905c742019bebcb24b6178a5fad39d3274ae7c5866" }, { "path": "package/dist/app/application.js.map", - "size": 19733, - "sha256": "0215fffd763b4901d32e6077abbf3f8bf6d2194bd1a8148245089d39b7190a23" + "size": 20500, + "sha256": "45844db9eb7dc6fe59b56b64b6952c9a6611867bd13e4e6fed2b6c8f45eaa908" }, { "path": "package/dist/app/automation-actions.d.ts", @@ -3262,13 +3482,13 @@ }, { "path": "package/dist/app/control-effects.js", - "size": 6906, - "sha256": "5b2c135d7ea859e47bfb66a410ef4dbe0b5199dee137a372ab76cf13bdcd1144" + "size": 7660, + "sha256": "2686d57c6465392979e21132f5314419b14607655420e4c54066b54ad36db335" }, { "path": "package/dist/app/control-effects.js.map", - "size": 6486, - "sha256": "60052fb7fdb9e11f85e341d1f831f9dacc9cc4534bb6bead1f567b5bea799639" + "size": 7212, + "sha256": "1e6f5f95447da168b582ed9919d4d2a464b1767b70d6b3f40be9b347bb94c3a2" }, { "path": "package/dist/app/conversation-branches.d.ts", @@ -3932,23 +4152,23 @@ }, { "path": "package/dist/app/journal.d.ts", - "size": 1260, - "sha256": "f414b60250bc2e148de32e9080261f187ad65e0d320336a1a86a17a97b30175a" + "size": 1411, + "sha256": "c896f751d78e8b1606a43018c9e3e6d477aff2e4f36fb72b02aceaa0cedf4115" }, { "path": "package/dist/app/journal.d.ts.map", - "size": 1207, - "sha256": "def9260e7b6a2aaf0ce807c4bb4e80ccfd4735430abdf4720d4ffcbd719f5ee1" + "size": 1368, + "sha256": "e1e7eaaaa913ed7f39f567ec1b40a66e98258cd3b867f3df25cb451190ed7786" }, { "path": "package/dist/app/journal.js", - "size": 3050, - "sha256": "0404e0ba7b9907ad2cfa99c354c284540300200463024dc5ca5e3cf0cb7bf0a4" + "size": 3324, + "sha256": "fc7a979d7dbb14b65ee3375bfa9215d7d0f91f1c4d9d0c0425690c5131e7f605" }, { "path": "package/dist/app/journal.js.map", - "size": 3128, - "sha256": "fd582cae1f2c3f2d729194ad350505bffd6346cec9f03b72a2d10dee2e58a17e" + "size": 3436, + "sha256": "004251bc751ae546f952f1ff477afe579d04f015921aff71f49f33c8f3178524" }, { "path": "package/dist/app/legacy-cancel.d.ts", @@ -4018,17 +4238,17 @@ { "path": "package/dist/app/production-composition.d.ts.map", "size": 1392, - "sha256": "a55d6f2ba718ba5c0e1942a9e490a8d756b60d11005fb3f2d3805b8dac1c6f4e" + "sha256": "efd65f3408f3f767ad24edc193cd048a1c842cd18e32800c8e945e219de98e1e" }, { "path": "package/dist/app/production-composition.js", - "size": 4850, - "sha256": "9526b3e76880ef727fda2a4507e4a0f54fff20946aa60f766939fbdf410ead34" + "size": 5582, + "sha256": "67a436ad1fe04c9145b7488cdeed6d3603748a5a24cd4b24bf0e34de98659f34" }, { "path": "package/dist/app/production-composition.js.map", - "size": 3873, - "sha256": "661bf37a7321c782039b95b455ebca877fe3ec28538a214e37c7cee9fdeb91ac" + "size": 4397, + "sha256": "e0b52554bbe95022409c7c6cf3c3ccebec5456e87295f367c6d837c4d742bcae" }, { "path": "package/dist/app/profile-action-support.d.ts", @@ -4278,17 +4498,17 @@ { "path": "package/dist/app/provider-snapshot.d.ts.map", "size": 532, - "sha256": "a616c337e1f1dc022522e31d50dc28158c3459df62dd28484a7cfaa6eccb879b" + "sha256": "a648c60cc3cff44e676a91c57ce55d9550af23c8138c68bda9c9af294808f552" }, { "path": "package/dist/app/provider-snapshot.js", - "size": 1841, - "sha256": "c4b07661168832b11d211d38bf58dcb99a1446c7e561db4729c68e4d0a014fe2" + "size": 2042, + "sha256": "279730b808e233223a81b8702ab5b2103e60f16a04b567bbd29ba1f3efe506f0" }, { "path": "package/dist/app/provider-snapshot.js.map", - "size": 2109, - "sha256": "33a5cd93342e7d750b0abb5e13201cb1342eb1a4f24bcf83fb760c165914573b" + "size": 2376, + "sha256": "29d33f569ceabef1a93d782a4d12ecb55fc2b66e673f494eb30e001e1fc09b79" }, { "path": "package/dist/app/provider-values.d.ts", @@ -4457,18 +4677,18 @@ }, { "path": "package/dist/app/run-controls.d.ts.map", - "size": 1096, - "sha256": "c3da5faee371a04fd7b457d23870da52fdf0e5ad9a2a452039fd29507fba9e9d" + "size": 1097, + "sha256": "655516d8e7136b219224bb428583655658577ceac834af3c2529ad6ea1c00a65" }, { "path": "package/dist/app/run-controls.js", - "size": 19600, - "sha256": "50dd50f87ce928aa63cfd6bf548e8f3e7aa2734862c8b04cd03860896db886e7" + "size": 19843, + "sha256": "df36451f7b9191850dfdfa0648e4165c8f90d8da573e4f40b1a7e6a8d97039d3" }, { "path": "package/dist/app/run-controls.js.map", - "size": 17784, - "sha256": "9b5a77bfc4f0585e38bb2a60fdfd9dc8096f0a3f138d4ee4b96d3b401407d446" + "size": 18132, + "sha256": "6a25cdc743535dd284d67a327849037336931219a04f4e7daa2a6ede73c4b3e2" }, { "path": "package/dist/app/run-event-mapper.d.ts", @@ -4478,17 +4698,17 @@ { "path": "package/dist/app/run-event-mapper.d.ts.map", "size": 818, - "sha256": "8804280bae15fa9aa1dd9fc06ed69d20dbc3a1cc287112f98ab465934f846f5f" + "sha256": "05c9229d4e41ca2f2a54b97b228dce7fd00530437d1d4a996b308ee4e18eb68d" }, { "path": "package/dist/app/run-event-mapper.js", - "size": 18046, - "sha256": "c9e694736c3abaa1892c3917d0580bf9fc84d3d646bdb14775051afbd0dd103f" + "size": 18669, + "sha256": "94a81a7e31334affd8b9670d3f2da7823cacf152710037321154be5d4200ee00" }, { "path": "package/dist/app/run-event-mapper.js.map", - "size": 16443, - "sha256": "ea09c96ee6e24664551547eec57480c98897a7a442dcc90b8b7395cf1ae19f9b" + "size": 16997, + "sha256": "bf4b775720d4e083907ff0f4857eb32acdbf75ef0884e7c71a8a89c759cc6206" }, { "path": "package/dist/app/run-execution-snapshot.d.ts", @@ -4518,17 +4738,17 @@ { "path": "package/dist/app/run-execution.d.ts.map", "size": 474, - "sha256": "db3236f4a15c056f1dda9dcf377717ddf7e4525b70826b0b1f9265b497caa0e3" + "sha256": "a90c94e8c51a8f20457e4c9367818a0ef0a37890a3bd5cb24bd00ef739c13e32" }, { "path": "package/dist/app/run-execution.js", - "size": 9327, - "sha256": "4507c1917281a10714eee9c5282f06776a4eb9751138ece0e70a07210d89d6f3" + "size": 9428, + "sha256": "ce0729d1bbaccce2173f8d6984c7edcdf3b5f72da9f09dc03c189480c590b551" }, { "path": "package/dist/app/run-execution.js.map", - "size": 8642, - "sha256": "44495a2c22a2dae14980c5152007ba17f0d1e0a7c0ac13b576883ac9c68bebef" + "size": 8750, + "sha256": "31fa5ebcd665dd33753c75bfbc69f0f5e784d2d5e9f7dad3fbd9b7f3bbe447c6" }, { "path": "package/dist/app/run-ingestion.d.ts", @@ -4578,17 +4798,17 @@ { "path": "package/dist/app/run-replay.d.ts.map", "size": 410, - "sha256": "30dff3e67d18de285ff2c712ecb574d45804021abf431cdbcc093a7bb0279fbc" + "sha256": "3936b70acc65fac94dd03618db7c284cd033892d4214dce1e2800702439ce1b3" }, { "path": "package/dist/app/run-replay.js", - "size": 7505, - "sha256": "874c2a4f99df561753977eaa174bd964e26ed8b704b43e40920ebd70317c1f22" + "size": 8085, + "sha256": "1ffe8e1e005859405ae4e48940ec73eacfb2b4f2c830e97a3483dda82e9c8fbb" }, { "path": "package/dist/app/run-replay.js.map", - "size": 7020, - "sha256": "c3886eb57d19f16c919ae7ca63fc6b531adfc49d9e7889e4412ae2edd32547e9" + "size": 7661, + "sha256": "2e9ff073399f7a3a1b03ed17509119f66a3d84aa8be030aac06145d8433f8c29" }, { "path": "package/dist/app/run-status.d.ts", @@ -4672,23 +4892,23 @@ }, { "path": "package/dist/app/storage-journal.d.ts", - "size": 1982, - "sha256": "6e0666e0986fe00f43fee30de7471a2bc4db9573b90f485d773a1db2182e5b42" + "size": 2098, + "sha256": "9c2e91ad5dec7badba4282d49037d61e2792829982e28e886857b88724dddc4e" }, { "path": "package/dist/app/storage-journal.d.ts.map", - "size": 1692, - "sha256": "ed74cee78f55f72a5481ff6584b79d94079ef27ddef03d32a3dda625567e93ec" + "size": 1820, + "sha256": "351be267cec71275f76212b48db50fde7bc9f93939621ba6452fad308f2b9480" }, { "path": "package/dist/app/storage-journal.js", - "size": 8805, - "sha256": "16469663d6bb49b3e4ac987de2631b9a279bd68a6ab787051aebe1236017a343" + "size": 9010, + "sha256": "5aab6563de87937b47c469797c65f043706b3ee90dabc116e7f879b89e429641" }, { "path": "package/dist/app/storage-journal.js.map", - "size": 8255, - "sha256": "6b884423c6f1a611571f632f0fed43defba7aa6154e740de95d5ca6bfa7d33c6" + "size": 8507, + "sha256": "426dc3f53f8e2493b17c910f186b4560221470ca3cd1b17a90ef886598afca42" }, { "path": "package/dist/app/supervisor-projection.d.ts", @@ -4798,17 +5018,17 @@ { "path": "package/dist/bin/braid-runtime.d.ts.map", "size": 241, - "sha256": "68b4c40501489a89e38edd67a9bc173ac1497fa00dac7ea61167d161d9675bad" + "sha256": "fd4c3c10a937098b65bd2685c27830f339b4d6eb3e4f9e18884342c3f80bd41c" }, { "path": "package/dist/bin/braid-runtime.js", - "size": 7579, - "sha256": "7b0b8285261b43f2505cfd5c2f90985dea27de6ac85208983e3287a45ed2ab56" + "size": 7520, + "sha256": "6716d7065c1cf0f7ec06b9c4dcfc616feba83b8148087f57747f74c229567f5e" }, { "path": "package/dist/bin/braid-runtime.js.map", - "size": 6110, - "sha256": "ecdbfe454cf2af9e58e913950ff2ce3181769b4d234cba7b3029529202566ea3" + "size": 6042, + "sha256": "57cf28545ba0de003b11ecaf967fe6efe754d8f2c4ac76cd6447033537b743ee" }, { "path": "package/dist/bin/braid.d.ts", @@ -4902,13 +5122,13 @@ }, { "path": "package/dist/bin/plain.js", - "size": 8038, - "sha256": "4aeceaba5dc75d7bc08fc271d7cd29da31c796793f8eea62467ca228dd6824e7" + "size": 8005, + "sha256": "971b27cbe04ab807557553e1adb61a717e601e22ffed821c0fe804e6851389c2" }, { "path": "package/dist/bin/plain.js.map", - "size": 7280, - "sha256": "5aaf4e6f7a218a001fd20af70e354f08465a5eb60a941cf176d20e7a8dbc8e81" + "size": 7230, + "sha256": "06444ff4a5e93e016dc8a21fa32cfd57bab45b6870b4cc6706118b77220b0a29" }, { "path": "package/dist/bin/production-active-profile.d.ts", @@ -5218,17 +5438,17 @@ { "path": "package/dist/bin/production-profile-projection.d.ts.map", "size": 731, - "sha256": "bb829df248bcdd59e8aa0fa75d397b42bc391c9c6d318f86da498afbdd4b72b2" + "sha256": "46a9efb068471d18cec848df3539da35f759685548f2eef7568f2ad349bc48ad" }, { "path": "package/dist/bin/production-profile-projection.js", - "size": 8378, - "sha256": "53d42dccb77395d4a69440f55ae0ecc212508a4d91e334a6f117f35ee76879bd" + "size": 8372, + "sha256": "c6a497eaee02bd80b5f3baff88cbcbbb7c951dfb20ad7bdc793f5c895e3ee01b" }, { "path": "package/dist/bin/production-profile-projection.js.map", - "size": 7563, - "sha256": "a19057d172d2ca49d53a31f4d66db4563e36e9178817114b8282ddd2ad9f456d" + "size": 7634, + "sha256": "5abec5b3c6db0c86ddd032bae8f151a0ca93735dbd348bfe5f41814006c131db" }, { "path": "package/dist/bin/production-setup-credentials.d.ts", @@ -5252,23 +5472,23 @@ }, { "path": "package/dist/bin/production-setup-discovery.d.ts", - "size": 489, - "sha256": "24f99dfa2b9d1b50e9bce2f7f74b5eff42d001129f3712e2d63024b5eec0987d" + "size": 913, + "sha256": "cc2fb19ad4b89bca4fe5b4545aa9377a16e4e8788cc3b662fa02c648d3356059" }, { "path": "package/dist/bin/production-setup-discovery.d.ts.map", - "size": 380, - "sha256": "2653132cef0238c13ed741547dce19697dde825ba8fcb96828c97ed418150862" + "size": 619, + "sha256": "08e7d66b2af5c4905d2f0878513778c57cd6d7b4550a17c010cc89752d76d62a" }, { "path": "package/dist/bin/production-setup-discovery.js", - "size": 5565, - "sha256": "e9cbcb438252d239b409ffeb15281fc433f6112702074df65d5782a88272cab5" + "size": 7139, + "sha256": "d51492f34f106786ba1f1b3fb25af128b979fff488c021ad43e37a071bd37a5f" }, { "path": "package/dist/bin/production-setup-discovery.js.map", - "size": 4287, - "sha256": "303d57e59368db584bd0fff7286d0c9d60a58d17d011d80a7a0d2561d9491ae2" + "size": 5725, + "sha256": "9f5b68d841e0b21865fa1a2a3344fcb5d927eedc5f96d21b6571c1911d7ee77a" }, { "path": "package/dist/bin/production-setup-persistence.d.ts", @@ -5352,23 +5572,23 @@ }, { "path": "package/dist/bin/production-setup.d.ts", - "size": 1095, - "sha256": "4d9deb966ae3643d4a75a1a867e195f1731de2cd9764ff9625018e9591c0d86d" + "size": 1125, + "sha256": "b8b5e2e4d37695567d5e94ce51160264e37338ce1d8b96e90f091e6755c4b4e0" }, { "path": "package/dist/bin/production-setup.d.ts.map", - "size": 672, - "sha256": "a3d2d87a844a1f7b4b8445ccfc896d3641a7c49339b67028fc97fb86585e77ad" + "size": 685, + "sha256": "edd7317567d271c8f3dfb098e0a91f3afdb44347c0ae4f3a0851a2aeb2874967" }, { "path": "package/dist/bin/production-setup.js", - "size": 641, - "sha256": "a5d23f89ccfdc7ca5a7bdd295e6fa886e4594c3bce2b06b600f4c61763af2ee1" + "size": 671, + "sha256": "3e757a8400aec44715a0a504ee164bb6ce8b41f6651166203c6a7bf11324e75a" }, { "path": "package/dist/bin/production-setup.js.map", - "size": 436, - "sha256": "a97528b50e4309b3cb6b285e64bde372e188cf9824fb18ce096dd774f12e92ed" + "size": 449, + "sha256": "6db5757d1ff9a3d64cdce8080040c728b68e49dbd73b4e3ca1eb830174a0ede1" }, { "path": "package/dist/bin/production-startup.d.ts", @@ -5452,23 +5672,23 @@ }, { "path": "package/dist/domain/bounded-structured.d.ts", - "size": 1242, - "sha256": "c00eb1dee987ad332d84c0ed91b6e3789a4c739c52f8aaad2c8bfd1d64f26177" + "size": 1465, + "sha256": "1169d295444248fe82d43674ca39fe6b07d1f1a7498de36efe4d0dbddf0a5f5d" }, { "path": "package/dist/domain/bounded-structured.d.ts.map", - "size": 1071, - "sha256": "733695198bb90a9d029d14b371b2d4a490c879f8b5a7726f3c18f431bd6bc9c5" + "size": 1279, + "sha256": "b180692bc0c8c7566ee0f17b663d9b55709b917c2f677d63249ba5c92b6d8abb" }, { "path": "package/dist/domain/bounded-structured.js", - "size": 10074, - "sha256": "b7433d80f28f37b6eeb497f151b601c5c67c00e42b8ae1595b0571210bc5abf8" + "size": 11599, + "sha256": "dce7322dc3f22fdbd05e2c5069e863e8a5b2d69e336d318d9bfcdb232acca25b" }, { "path": "package/dist/domain/bounded-structured.js.map", - "size": 9701, - "sha256": "26061eb93fb8befe259c1016bddd5ec41e55ba6022d8f2a47ffdb45cc2996b85" + "size": 10890, + "sha256": "a9f3c554c65b34f6e63185c44d35c9ef742f2aa770369cd4630e984da5e53cc3" }, { "path": "package/dist/domain/canonical.d.ts", @@ -5572,13 +5792,13 @@ }, { "path": "package/dist/domain/entities-core.d.ts", - "size": 7714, - "sha256": "a86a30dacfa7b414814684f0da83aa880222137a5394bcb8829536a7ffa2fd92" + "size": 7822, + "sha256": "f244bc742395e04b62b7cd10ea485c4fd5d4fdc25fb9c216b095137ee3ec6bab" }, { "path": "package/dist/domain/entities-core.d.ts.map", - "size": 7245, - "sha256": "86fc7c1133859c9cf833a63d17be6990e37e84d388a995a3be0ae7a3a3f0e83d" + "size": 7299, + "sha256": "7c3a792a8a4e1cc7a6fb27453f1c7f310480cb3763805c3faaad7194ffb8032b" }, { "path": "package/dist/domain/entities-core.js", @@ -5712,13 +5932,13 @@ }, { "path": "package/dist/domain/events.d.ts", - "size": 10915, - "sha256": "a023354ea589d1dcbf57f076e6c96917d50cc1bc6376458021a6ceaa3de5fdf0" + "size": 11050, + "sha256": "d59980dcbe393a7f6f99fae6138bc6062b0795e78a904b1b83cf2860aac354a8" }, { "path": "package/dist/domain/events.d.ts.map", - "size": 9305, - "sha256": "5fd29707ec98f6c0e456b368983dd80e843c5d616ecec2cddcc93b848af6be5a" + "size": 9398, + "sha256": "fd804fd0a9ab0db59b7d0f475dff498d6045a4be76c80b09db5d183279e2f131" }, { "path": "package/dist/domain/events.js", @@ -5728,7 +5948,7 @@ { "path": "package/dist/domain/events.js.map", "size": 2604, - "sha256": "7d6be6c1142180ef8786149942cbc75d0dbddfb785f1751a526ec05c2ee08063" + "sha256": "b086602c935370c6e0b2eccf32fa9b6ca04294a4f44395da464777b53fd13038" }, { "path": "package/dist/domain/execution-observation.d.ts", @@ -5773,22 +5993,22 @@ { "path": "package/dist/domain/ids-core.d.ts", "size": 983, - "sha256": "85a7874a71d6977ddf0e54407c2e066b61ab083fa651662655b6e62e8c352335" + "sha256": "655f946520855885bbbdb67f17e44b5642998b52dd0f5cbf675f113565b351f5" }, { "path": "package/dist/domain/ids-core.d.ts.map", "size": 1051, - "sha256": "2e20b83ebd6af9ec927c1a797b7f86772e95838341e3f103836b96b25d8b2a46" + "sha256": "ae9e021d423a46d64c068c001660b302537d011ee2fbb0c8dce64c25faa2261c" }, { "path": "package/dist/domain/ids-core.js", - "size": 2686, - "sha256": "2608de80a04a04873b9d81db98d44eb35c2afb82563c8a016d1b5fab5ebdf836" + "size": 2843, + "sha256": "77d886a4472c1eb54731d390c1cd5bbbc17be1a58c56444f94014566ed4ee603" }, { "path": "package/dist/domain/ids-core.js.map", - "size": 2982, - "sha256": "28701752c0777c271c90e9bc47ddcab89bd175bc09765f26504c4a6170a979f0" + "size": 3103, + "sha256": "a121f1d8b3c0a2503b3122e0b475cde1f0af10c5656b919a1cd4fd3b2b828426" }, { "path": "package/dist/domain/ids-types.d.ts", @@ -5938,17 +6158,17 @@ { "path": "package/dist/domain/invariants-run.d.ts.map", "size": 500, - "sha256": "8800a718feef7a33bc63ceb9673a91a247ec9f723c2fc69750601005b4b68d00" + "sha256": "8606affef36b7d1dc20dd0f34628687202c577a1f2e123874f81dc3e687be91f" }, { "path": "package/dist/domain/invariants-run.js", - "size": 16670, - "sha256": "9ff634670b056638a11956b0e79ef89b3895a748992f79f721f5b44a08b8f83f" + "size": 17174, + "sha256": "e505efab4ab0de5064cd302e366c0fded9e9cab8abe862b68038ddaf38c5f37e" }, { "path": "package/dist/domain/invariants-run.js.map", - "size": 13735, - "sha256": "61c45a6ce4599f7ebeaad342a709d522f2e339d0bd81f9fb18fbb27453686517" + "size": 14111, + "sha256": "e4d1e8fe0b14f30194d89d391e376ce503747f4677e10e583ec62c883233b0b0" }, { "path": "package/dist/domain/invariants-runtime.d.ts", @@ -6010,6 +6230,26 @@ "size": 258, "sha256": "6ac24b20c28c1424fd3b41445dca9434ce78b79e6354690ba5f20b534d17a652" }, + { + "path": "package/dist/domain/materialization-receipt.d.ts", + "size": 258, + "sha256": "4ca5df75be2d2615f82ba7ac945c3e1e9e704badbb1283734c89f620146757d3" + }, + { + "path": "package/dist/domain/materialization-receipt.d.ts.map", + "size": 299, + "sha256": "5b6650ce34d936a6d3701e4e5d948f553776d30fae975786d65815f55720c713" + }, + { + "path": "package/dist/domain/materialization-receipt.js", + "size": 701, + "sha256": "87e9912af44ceda6c3eca6cd5f57cc179ff3c71bd46de6ffc34e8b28d82c5808" + }, + { + "path": "package/dist/domain/materialization-receipt.js.map", + "size": 748, + "sha256": "65fdb0dbc3bf92a72820519d99923453736bbd954a1f47c194a275502dfb2b85" + }, { "path": "package/dist/domain/materialized-state-snapshot.d.ts", "size": 4158, @@ -6098,17 +6338,17 @@ { "path": "package/dist/domain/receipts.d.ts.map", "size": 1892, - "sha256": "b9e6adea392bd81f68ecb172d4db044e1d973c12fd9f291830fedf788fe0d84f" + "sha256": "9921cb6fdff9e9454031203aa2093512eb77e4c4e883d895e62561ee666c7191" }, { "path": "package/dist/domain/receipts.js", - "size": 6841, - "sha256": "83e4434c6436892a2e878b16fee695a8cc1ef008dba436e3880a13537672e3e0" + "size": 6816, + "sha256": "e655d858e65319acb789fc013fee2d49e35d752c406dab3f3c4ae321b64fc082" }, { "path": "package/dist/domain/receipts.js.map", - "size": 6392, - "sha256": "fda544b8b688b55df622b8b39ded98c25c96b9b94cab52520438b51db446ccbe" + "size": 6344, + "sha256": "099baa16d284c7e446315bbe7d1e8719db32805302fa47418addc10f1ed0bdc2" }, { "path": "package/dist/domain/redaction.d.ts", @@ -6198,17 +6438,17 @@ { "path": "package/dist/domain/reducer-execution-observation.d.ts.map", "size": 448, - "sha256": "271c5e020be4f29d21775a8eed3e01bffd748c6575fa3b3f9c07f2b5dbbb9c94" + "sha256": "5bd9a2c56076abf6f7f0891b63b49786a6e4abb1949c168b265eda9809c18d75" }, { "path": "package/dist/domain/reducer-execution-observation.js", - "size": 6584, - "sha256": "1f96f316a6559e861696c925ae596a86031eb409bbf9f6ca77924d812339ea77" + "size": 6706, + "sha256": "02ea87c13495c4cf87ed18ab41d4a869c9b02e047b36784665440ffd90775014" }, { "path": "package/dist/domain/reducer-execution-observation.js.map", - "size": 5930, - "sha256": "2d4cbf2b77b922432e112f1c3708c2bffa857d4de8b52c7407bc204cc25c9d23" + "size": 6077, + "sha256": "035217fef5820dca11b1da32556f13cc6ab7a300f9fb65f9411f71ad87f17608" }, { "path": "package/dist/domain/reducer-helpers.d.ts", @@ -6282,13 +6522,13 @@ }, { "path": "package/dist/domain/reducer-lifecycle.js", - "size": 10760, - "sha256": "e63a47b64d3984b13cf130478c709b00b9384fd06ffe44f92330989efcbd171b" + "size": 10739, + "sha256": "949745a9febbc8c0818823a1f2799694d44813235f9f9091399a2ea0468ecaea" }, { "path": "package/dist/domain/reducer-lifecycle.js.map", - "size": 9876, - "sha256": "13a96ad3ad27caed73f7468e3bf1c4b76172aec1d83b42782ad49c6ba20f32b0" + "size": 9856, + "sha256": "de9d4cb9f443f9afadfc0ea4f0d6c988555359e79c60d475b3cb4184611f984d" }, { "path": "package/dist/domain/reducer-replay.d.ts", @@ -6418,27 +6658,47 @@ { "path": "package/dist/domain/run-usage.d.ts.map", "size": 447, - "sha256": "940a0bf7ac20f37ac99046bd9c84e8d1f8b1d499bc138cb64837711aaef2fce5" + "sha256": "de6db478111b367413ef78a57d0bec897b69c4a1b3e794194989db7ba6cdc925" }, { "path": "package/dist/domain/run-usage.js", - "size": 5158, - "sha256": "419acbc8a859b062522d65e37f30cb81b760ba06cb9d6912065b4f0a950e6419" + "size": 5359, + "sha256": "8bab41931bf1dce6bb672ab77237f163021ba14cb34ec920d3cfcd85ec7cfe2c" }, { "path": "package/dist/domain/run-usage.js.map", - "size": 6226, - "sha256": "63b2c768ab8524cfa4a0e70f76998129a7c6d3ff259b76dd8d3a73702ca7d1b1" + "size": 6509, + "sha256": "8f406df273f6b248b1508a4d6d9a7d3882b4cf921d90c46309d0b1b289effa1f" + }, + { + "path": "package/dist/domain/runtime-diagnostics.d.ts", + "size": 328, + "sha256": "43800e61de893b1146c8266a097f7f8d8ff64cffc6fbd5ee0f7782af18410353" + }, + { + "path": "package/dist/domain/runtime-diagnostics.d.ts.map", + "size": 287, + "sha256": "e4ab48a8baaab8c0ed133b9eb10e2da9722f71be7d0fecc481f8449e1dd85bdb" + }, + { + "path": "package/dist/domain/runtime-diagnostics.js", + "size": 760, + "sha256": "0531032e3fe1429e2ca32f4c26e94a208e9ba936de7976a56042f3ff870cbb3d" + }, + { + "path": "package/dist/domain/runtime-diagnostics.js.map", + "size": 566, + "sha256": "82bc46084d602ce754d4cd0b5f7d42bb214da1b14de3f16d0a80f3c1dce72d17" }, { "path": "package/dist/domain/runtime-events.d.ts", - "size": 1650, - "sha256": "f8f2185443903ade26accd696a340ca453d1145d97c0213fb9851a983315771e" + "size": 1726, + "sha256": "8d5a7aa707feeea9604989955559e02157f50187edf453863d8547413425b56d" }, { "path": "package/dist/domain/runtime-events.d.ts.map", - "size": 1561, - "sha256": "a31681e69675c56e22becfff3860a4c76ca1158dffdc2946b904ed926b5d1541" + "size": 1618, + "sha256": "81b953b54eb5a49752a7272bd603270de99934a401c852e322f5c70b16085583" }, { "path": "package/dist/domain/runtime-events.js", @@ -6448,17 +6708,17 @@ { "path": "package/dist/domain/runtime-events.js.map", "size": 724, - "sha256": "81e02ebf73162cd2ea64f11b7c871c7cd25356bcfb8a7a7b6789cb3867db462e" + "sha256": "831d9c3592ca05cc52cdd6690a0a9404570e34fae629bc41116da0a20ff6a8dc" }, { "path": "package/dist/domain/runtime-projection.d.ts", - "size": 3221, - "sha256": "60594eeeca5c6306efa366c9f6050f0683c91f17d89c2b7052ccb50161658979" + "size": 3297, + "sha256": "b67fca63ab602f704097611c9dbdf9aec30abb2eda0c8bc143c708afc322642e" }, { "path": "package/dist/domain/runtime-projection.d.ts.map", - "size": 3287, - "sha256": "6d8bd8c3c4918b7b4a44d317823f19940e792f01bd1d8f0731632e3d7f96cd75" + "size": 3345, + "sha256": "d7f4a61322c15560d96fdf635ab69e84a75f7c10f15eee4e185b235f1f7ef85f" }, { "path": "package/dist/domain/runtime-projection.js", @@ -6468,7 +6728,7 @@ { "path": "package/dist/domain/runtime-projection.js.map", "size": 591, - "sha256": "ffd87f23a6ead2bb877ad1fc99183ed8d65cd841563af95416f863f947d5f775" + "sha256": "511ae95ce031367d03c8ea81498bab4d4773b2b5160b91388d6791e90b9c5cb0" }, { "path": "package/dist/domain/secret-sanitizer.d.ts", @@ -6810,6 +7070,26 @@ "size": 1809, "sha256": "d9d266b5627959e6ad0f052ce0c66d6c8cce7dd7312abda4cd4cad27ad447f87" }, + { + "path": "package/dist/installed-package-version.d.ts", + "size": 232, + "sha256": "edbe9f99acaaf36b0e77490a6f0f6197475d855469f985526dddf4d285b5d71b" + }, + { + "path": "package/dist/installed-package-version.d.ts.map", + "size": 212, + "sha256": "f10e1eca21d8bfdbecebe0f99e6ba6502f81443941dc6e0b1e1806365c9ffb86" + }, + { + "path": "package/dist/installed-package-version.js", + "size": 1370, + "sha256": "f9a99f95706b906c3609dc87595436faebf338e5594cad247dde1af16305334e" + }, + { + "path": "package/dist/installed-package-version.js.map", + "size": 1245, + "sha256": "de4d8bd81f10d55119b0e287ee1549e5b39bee7dbe22084754e5a20aaabd32ec" + }, { "path": "package/dist/ports/clock.d.ts", "size": 273, @@ -6872,13 +7152,13 @@ }, { "path": "package/dist/ports/effect-storage.d.ts", - "size": 2327, - "sha256": "e40dfc118120143b1054045f983eb9dc7b20e243b11ec24f27908813f35bfcd6" + "size": 2565, + "sha256": "cdd46123a63af8b90f5f2eafa6eea2db02951a814bbb72cb691d22dc79d69199" }, { "path": "package/dist/ports/effect-storage.d.ts.map", - "size": 1869, - "sha256": "17287bcb3af108b9d0095e86c34263c9deb7bc5716597023ef791904ff4875bf" + "size": 2048, + "sha256": "ff74f7c5bb13bd6ae2137a540d2c8c514849dbee58a329c05af3c750d0207648" }, { "path": "package/dist/ports/effect-storage.js", @@ -6892,13 +7172,13 @@ }, { "path": "package/dist/ports/execution.d.ts", - "size": 4508, - "sha256": "74935d32fc82c25c142ad46c45fc3288932d52801032a19a6bbe1892763b8449" + "size": 5003, + "sha256": "dba8cc6189e7a955b9ad5b5aa975c3f49904d5a57f83e1d315cbac2b86b9574c" }, { "path": "package/dist/ports/execution.d.ts.map", - "size": 4291, - "sha256": "f73fd7e5ba868a8d3df1ef04105a0ddccd42bdb32b1ad73d6b17a410803b8d3f" + "size": 4730, + "sha256": "1ec897402a04c96d5a22f9ab515e06f9cd20fbbbe9dfde11e0cc75c3c444b7e4" }, { "path": "package/dist/ports/execution.js", @@ -6908,7 +7188,7 @@ { "path": "package/dist/ports/execution.js.map", "size": 1610, - "sha256": "e38f5fc664c37c796e258f244a3cda4d7d701182893b358411f752d886840662" + "sha256": "dc872eeac8ef326ea7b856c85e8111ed15d821471f4df9948d358cfb52d598c4" }, { "path": "package/dist/ports/ids.d.ts", @@ -6978,17 +7258,17 @@ { "path": "package/dist/startup/preview-runtime.d.ts.map", "size": 2312, - "sha256": "1f467f22dd5edd9c57571f93bdf8aa99c271449477d1e5b7735b5c93e6c05279" + "sha256": "5233c02daeb3e194a36d308a398284b3fdcda949b990e1424dbceccc9f555b83" }, { "path": "package/dist/startup/preview-runtime.js", - "size": 7158, - "sha256": "1e1395a3c64a6eacec96018f73fe53e54b4e5993837d340c08bf10ef016d41f2" + "size": 6716, + "sha256": "ff6e9adf0994cb2c4606c052768015243cd31c5ca3de89702db5a7030394c75f" }, { "path": "package/dist/startup/preview-runtime.js.map", - "size": 7364, - "sha256": "40f419101bc5f2b023f179dd13a2d2fcd4b9018dd5964e5bc04f53a4c03e4a5c" + "size": 7039, + "sha256": "d68c488efc6a92aa3811f5d97d8142f8ad7ad418ef533d0f46924957c9a45425" }, { "path": "package/dist/startup/terminal-runtime.d.ts", @@ -7042,13 +7322,13 @@ }, { "path": "package/dist/testing/deterministic-backend.js", - "size": 2705, - "sha256": "54fe11f57dcdc1283114acbc7fa771c8f066b14d62090ddbbd33ad6cfc5ef811" + "size": 2717, + "sha256": "7eed8c661b8d716b525a822a71e1b0233af4d505d7d66e7e6482874c1e3f4368" }, { "path": "package/dist/testing/deterministic-backend.js.map", "size": 2085, - "sha256": "0a8f62480c0fbdc3ec8ec4793d47ed324a63e1f11f40324e1d5d48fd4dfc26a6" + "sha256": "10ac1fd79253054d390d67272e49066257474ea00ab0df41efad5481e7d6f104" }, { "path": "package/dist/testing/product-demo-fixture.d.ts", @@ -7092,23 +7372,23 @@ }, { "path": "package/dist/version.d.ts", - "size": 83, - "sha256": "23297dff76d954009bc2debf7186260b547c876247fbea2990f1b12365d8f9e1" + "size": 81, + "sha256": "becbc11ce0975aa4331693a73fa92feeb2a7704147bb36d7cbee3c421a232419" }, { "path": "package/dist/version.d.ts.map", - "size": 137, - "sha256": "21087c1c7cc48e99b88deb49a94205047cd9899aee1627fba5b9c27f1daa5a8b" + "size": 139, + "sha256": "4a7776b5c9d8120b09c35782c63f2dfb9fd5d093f92dffbd144a7994e9da955a" }, { "path": "package/dist/version.js", - "size": 73, - "sha256": "3b587242055e6f5f9b98c112e46c6300a9fc3bd4d15edf99e175c52fc297805d" + "size": 832, + "sha256": "54364fc9c48d2329fc036ac1390513f1df6f721888c9fac6a4d9b17c775d4687" }, { "path": "package/dist/version.js.map", - "size": 145, - "sha256": "f3eaec21281bb26b9f2f9f8775277020ca56a08ac9ba204212957eb3749cb066" + "size": 832, + "sha256": "3026409fab0cd831c75e9e295809ace3f2450e8e084cc06e6e715bb9ea28665f" }, { "path": "package/dist/views/headless/bounded-output.d.ts", @@ -7242,13 +7522,13 @@ }, { "path": "package/dist/views/headless/rpc-parser.js", - "size": 19606, - "sha256": "0fc420d28805893818bd6ecd6983329ea3f69114938eb022bdd3d55ece97b40f" + "size": 19612, + "sha256": "2fd4b49f12659567bbe332e48ec4232c3215a57771335ad2430e815b859cfd92" }, { "path": "package/dist/views/headless/rpc-parser.js.map", - "size": 17416, - "sha256": "29c57688dec4c5be316552aaab8d0233ca5c946d92c24e5a51ed18ced87353f7" + "size": 17427, + "sha256": "f02d886edf261975fd084dce2810c092b95d86028b073b69bfa130721975b039" }, { "path": "package/dist/views/headless/rpc-types.d.ts", @@ -7302,13 +7582,13 @@ }, { "path": "package/dist/views/shared/analysis-model-call-presentation.js", - "size": 5029, - "sha256": "71b5c7366c01d512432e1496482b7d6713a3b46075a98125530ee39b24ebdd55" + "size": 5049, + "sha256": "d29ee5a0098c5eec5c46cf221f790c1b7c5e97b7b656fe8a284556a2217ac18f" }, { "path": "package/dist/views/shared/analysis-model-call-presentation.js.map", - "size": 6155, - "sha256": "300c22df57612e5295ba92eff6557c9213e38b8d8d7b1b27d1b14cc7ff68ddfc" + "size": 6223, + "sha256": "e04fc2ba51d190ce0b23bae2fa141213a767dc3d129d87e0dc38523b12e6f9c7" }, { "path": "package/dist/views/shared/analysis-presentation.d.ts", @@ -7318,17 +7598,17 @@ { "path": "package/dist/views/shared/analysis-presentation.d.ts.map", "size": 582, - "sha256": "fe26e3354a80e0546aaf573ad425974610b7484bcea355a35487c83d75efd185" + "sha256": "c469aec64e78160fdef11187da0f04e71208033d8a3174666eef264f58dc8612" }, { "path": "package/dist/views/shared/analysis-presentation.js", - "size": 9523, - "sha256": "b013472c0822b9d203b204f4325ed877227005b213e84447ee3c1217ec5d694b" + "size": 9874, + "sha256": "27099533612d7ed54033258ae713d7f30ecc28590e9d0f37ff0ace393e63e41a" }, { "path": "package/dist/views/shared/analysis-presentation.js.map", - "size": 9631, - "sha256": "8af39d41a5c1fde3b60326137ae0b32500216d01ca7163ccbfb74193d74f37d9" + "size": 10000, + "sha256": "b1acbd69bd0e02a8ca7df7afe2acc05f275e8853807773fb4598bf28f01c6900" }, { "path": "package/dist/views/shared/appearance.d.ts", @@ -7372,43 +7652,43 @@ }, { "path": "package/dist/views/shared/command-registry.d.ts", - "size": 2806, - "sha256": "b5f836bc6daad23e3a27107fa370b6bce963fa770ecf8d6fd60dd2a92fa9db3c" + "size": 2845, + "sha256": "96b42a814582b1d37c0f4fb6f9eccf582812c415055307ff7520303ec165d7c0" }, { "path": "package/dist/views/shared/command-registry.d.ts.map", "size": 2422, - "sha256": "2d603554498ca2cdfa4dee592150aff917d029ac09cdc566466b265c85af1496" + "sha256": "b835b143e56798a81a8271be02ba333cc632fb97529e65290129d4f1b0c1ce29" }, { "path": "package/dist/views/shared/command-registry.js", - "size": 11197, - "sha256": "94ca41bf84635b422ccb871088daae0d13fb2689352fd138d92e683dd8662aa3" + "size": 11839, + "sha256": "51b1ea27b4d83789f35836153d67e5f8689c6ee5c9fe8a89ca316269661f70f0" }, { "path": "package/dist/views/shared/command-registry.js.map", - "size": 10381, - "sha256": "79e5dfa3036f57197719bfc71edb4e9877d513cde1aeed64eb3b371507aabcd1" + "size": 10723, + "sha256": "1c804bfe8ff760669100285b1478c522334e94c8993d08d0be8087cf7054ae08" }, { "path": "package/dist/views/shared/command-table.d.ts", - "size": 5899, - "sha256": "dfbd104b22a4a586ab2f62bc83062c543c264767c8d8616e93da239fef621ccf" + "size": 6475, + "sha256": "5fa6e857e65fd3a89ce78daa793b9b3119881be8020ab22e09d5411d56c0598d" }, { "path": "package/dist/views/shared/command-table.d.ts.map", - "size": 2791, - "sha256": "e3ae87e01b43d82dcb16b0b6be600807827b582c81356e09c2d904c9007d8f0e" + "size": 3033, + "sha256": "7cc2c0c6a1b75649b71b00765751c018eaf9fb02a9318228cf9ab74351d3b0ee" }, { "path": "package/dist/views/shared/command-table.js", - "size": 5282, - "sha256": "0207028a84e71282d3ec08fadaa4e0d7ed56cebb411c9de86cb0ec026d32d3ea" + "size": 5768, + "sha256": "ba7b9ef2e43fb47451f7290de903a554c9333a1e5240b51ec85bc39867f2a006" }, { "path": "package/dist/views/shared/command-table.js.map", - "size": 3787, - "sha256": "49c3153690d3dea73a7ecde03028b8362e118143a569f3cde347bf1093928268" + "size": 4146, + "sha256": "5145a8277dd9fa846006ff2b79ff9c79693aae1a687006d5b31da1ec1abff5ed" }, { "path": "package/dist/views/shared/comparison-presentation.d.ts", @@ -7532,13 +7812,13 @@ }, { "path": "package/dist/views/shared/models.d.ts", - "size": 21243, - "sha256": "90231a0e00ecd279ff586997a4270183eb59bc52ceac95234934de986b00fa0d" + "size": 21550, + "sha256": "f0d8a1066ab7f90527dd13f65ce286f0942e93d547698a4208a2f99f22746394" }, { "path": "package/dist/views/shared/models.d.ts.map", - "size": 21923, - "sha256": "0b81a0084add06f6aff63b58c83388cd87262d51a9a07dd24ba1737012f023bc" + "size": 22195, + "sha256": "3f9a137c1ef7a3ea44b2dc631311b1b88c5992d4f6a64b2ce1cd0a58afb4d361" }, { "path": "package/dist/views/shared/models.js", @@ -7548,7 +7828,7 @@ { "path": "package/dist/views/shared/models.js.map", "size": 595, - "sha256": "2ed4a35e1b2a3eeded62a316def2d6ba7fddf050b6e93a7af4584de594b9aa56" + "sha256": "bacf326846406bde657e44461a6a022678a0c2fcf451fa4cd24ff07a81b82291" }, { "path": "package/dist/views/shared/plain-accessibility.d.ts", @@ -7638,17 +7918,17 @@ { "path": "package/dist/views/shared/semantic-details.d.ts.map", "size": 565, - "sha256": "360e62b165434f81eec9cdc78cd0ce88212a1fb27df7c02742a20edba6d9d7c2" + "sha256": "f27b908205ed5455f63c5edbd8f383c2694b9c57c3bffe4dbb4c0fd408de7241" }, { "path": "package/dist/views/shared/semantic-details.js", - "size": 21051, - "sha256": "c804dbb2f1e17ae44edff7f787a8226b04d480f40dbe74c93c4a14c3ac7a1a9b" + "size": 21448, + "sha256": "046221f5ba1e2100070b088e8ace61d83508e88b3b10a592a22eba650975e7ba" }, { "path": "package/dist/views/shared/semantic-details.js.map", - "size": 20853, - "sha256": "81c9a97b27442c6c5de182efe2f3e79d27fed300d24a026755a2a3fc0f7190e3" + "size": 21143, + "sha256": "014b08afbbdbbd4f8f6aeb12090ab2c1637fc1362fddca379febc132b9cfccfb" }, { "path": "package/dist/views/shared/semantic-graph-edges.d.ts", @@ -7817,18 +8097,18 @@ }, { "path": "package/dist/views/tui/activity-browser.d.ts.map", - "size": 1072, - "sha256": "fb60d8df22624af275281d9583a55a5d6873d114b10b08e623e1ba79c572d765" + "size": 1073, + "sha256": "68a8bf96aed9305cf545bace4008a320f74b96f63b4b05c6610aa5f68ad77e4f" }, { "path": "package/dist/views/tui/activity-browser.js", - "size": 13175, - "sha256": "6c734afdab876c92adf153c3964560bd377151d8191fa426abfc1e63a780d806" + "size": 9220, + "sha256": "b189226e485bc6734f73724bd7d13a27a24ccd1c51b0286f3339aae01cf7622e" }, { "path": "package/dist/views/tui/activity-browser.js.map", - "size": 15251, - "sha256": "0f567c7c5393759271b1fce2fbe44e27bea669191bf25589603f816f325017a6" + "size": 10509, + "sha256": "1e698ef83201edf318ccb90751058889786fa75b7ec4a95ea6fca3da8db18678" }, { "path": "package/dist/views/tui/activity-document.d.ts", @@ -7857,18 +8137,18 @@ }, { "path": "package/dist/views/tui/activity.d.ts.map", - "size": 374, - "sha256": "cdf8ba57b0ca7a69ff6c927e5c9e03414ab749c53b57a0e55b1b066d17fc6770" + "size": 373, + "sha256": "41dcd3fd8b67e9442825c47a0e9d7fca00ccd163ae557a15b9912a81885cbdf0" }, { "path": "package/dist/views/tui/activity.js", - "size": 2020, - "sha256": "579f4cc0d73e870c9ee6e290a1203178f5b1dfc8fbcd1904423986086f7b6e94" + "size": 1815, + "sha256": "def2e40d6c0bdfd2c51fb74039e9d351372b80072213f20daa956ca9ad13cac0" }, { "path": "package/dist/views/tui/activity.js.map", - "size": 2312, - "sha256": "7f499ba8f26fcb53970b8211325d365e5c48c6f177ab0a58e8f4186650185fb6" + "size": 2174, + "sha256": "3d738471d101855e6402e695b42d709d97108bb85b2746ace39556b2a4c93e80" }, { "path": "package/dist/views/tui/analysis.d.ts", @@ -8032,23 +8312,23 @@ }, { "path": "package/dist/views/tui/composer-view.d.ts", - "size": 1320, - "sha256": "61c72dd0b038304ec923e106ac4b3328a0f9f4b3afedb7b4f1595bb40502bed9" + "size": 1435, + "sha256": "fdcc804f223cf3890246ac06df779ce18f4468363654ac476c4e6d3a0cecbebf" }, { "path": "package/dist/views/tui/composer-view.d.ts.map", - "size": 1157, - "sha256": "dfb2ffd5d67dc13f05047c8788f0242e73f0cf296f66b667cf04794e4e3c0c07" + "size": 1273, + "sha256": "44be45a1e247ba253a000fa35864099f2967886dc43a6e36f90b8aaac880c948" }, { "path": "package/dist/views/tui/composer-view.js", - "size": 5040, - "sha256": "b598a5d2cae877ec4ed100f1439a6ba84419766e1e447d949f64529e62432a96" + "size": 6265, + "sha256": "8a2fa6a4fc73841166f04623fb6d37222f718bbb57cdf94686336e0d651ee640" }, { "path": "package/dist/views/tui/composer-view.js.map", - "size": 5824, - "sha256": "529ca63ec1c6a762e787aa96bc2fad405749f63e360532f79849e5139620c374" + "size": 7111, + "sha256": "399b90b823a37679a28901efb3a6b9e69547b47cfb83416dd8185dbc119f03e6" }, { "path": "package/dist/views/tui/configuration-credential.d.ts", @@ -8557,18 +8837,18 @@ }, { "path": "package/dist/views/tui/fork-preview.d.ts.map", - "size": 812, - "sha256": "a1d39c9c4afc31a6d22f9e8f229b293623a7fd9f4cf9d68ab1b0511f51a055d1" + "size": 815, + "sha256": "4601b033e395a0ce72b8f4dee7343b532179a0c2127393fdd7e3ba0cd60fdb06" }, { "path": "package/dist/views/tui/fork-preview.js", - "size": 4709, - "sha256": "dddb26ffdd028e58bb923c52a6ebce61d5c0fe8982a3267da3daf5a84d14b59d" + "size": 6398, + "sha256": "e7f3b69b765416bd46af47f5bf4c749eed03cd18c7a71e160f3f9938fc209f90" }, { "path": "package/dist/views/tui/fork-preview.js.map", - "size": 4845, - "sha256": "1ac5096550a395860534274cfbe85a3a27cc62f615c3783c0a60bc9ba3283bc1" + "size": 6579, + "sha256": "8d1e301838e84c8e9a38a2009a729baf656258132f3e9089013a2cf6f2712c2b" }, { "path": "package/dist/views/tui/graph.d.ts", @@ -8677,18 +8957,18 @@ }, { "path": "package/dist/views/tui/interaction-presentation.d.ts.map", - "size": 1380, - "sha256": "3339698ae537392176f081c23b2214312b1b1cdedfc4f1c9b1a25bd8fb02f29e" + "size": 1379, + "sha256": "e762fe68783020751784c859265dd3cbca533d9555cc46995bcbea07ead65172" }, { "path": "package/dist/views/tui/interaction-presentation.js", - "size": 7063, - "sha256": "37577de8b7c567a05752c4f86e5f62425fcdbe954ac8d6f8f2fa5d353d1fde0b" + "size": 7074, + "sha256": "3ed65c58c1712101054781d0d121127828ceca8d9c36b7d9cb42a525e7f9846f" }, { "path": "package/dist/views/tui/interaction-presentation.js.map", - "size": 7603, - "sha256": "0141433b0de29bb02bbf805950fad28e428f7630bd1496bc650081774b771b51" + "size": 7634, + "sha256": "3b60b31b49e17d7ddf4fa69d2500bf5986167f3637e784636b4b9c988be7d6bf" }, { "path": "package/dist/views/tui/interaction.d.ts", @@ -8712,23 +8992,23 @@ }, { "path": "package/dist/views/tui/keyboard.d.ts", - "size": 940, - "sha256": "7f2db743aa90cae4ce023d8f5ef761fc069ed920eac2814b22713ab829dd9e8c" + "size": 956, + "sha256": "db87fb1e2085390407cf017da105a90c1c7d1acd7f93e10fba2d13bf0ba5c082" }, { "path": "package/dist/views/tui/keyboard.d.ts.map", - "size": 895, - "sha256": "770077b1a85bb7bc0d5555d3a8758cfa3b2ece3b7270674777ab0a40d6d5ce5e" + "size": 905, + "sha256": "fc44fcaade2b02cf66091702588b7ec1e2cf14cd50c52d866fa0d3bd2bb15420" }, { "path": "package/dist/views/tui/keyboard.js", - "size": 7231, - "sha256": "7befa215911b3e010cf0903526d66f903bfac63f07a9b13a985c48f3ffb4926b" + "size": 7341, + "sha256": "ce63745e2eb0560d893d835c55f6cd3944345e0d2882088b787b42efbb4fc6d3" }, { "path": "package/dist/views/tui/keyboard.js.map", - "size": 7798, - "sha256": "7f9df6d7b2671407d8a7eedd79a5b867237bf64a68b162b1b6fe4e84297f8ea7" + "size": 7896, + "sha256": "8ef94a69feb8e4c2e49e88556b6e40e86fd3ff309eb1c2ca83cba834b8338055" }, { "path": "package/dist/views/tui/layout.d.ts", @@ -8898,17 +9178,17 @@ { "path": "package/dist/views/tui/selector.d.ts.map", "size": 1410, - "sha256": "bf70f76b416f121d7f22e3ff48c49fd548d05e57cce60ded6e500054aad028f6" + "sha256": "6ea1fcccd25f116cf763b9255ba5d2520cc0a1d4e3133c0373e9d873208ce8ba" }, { "path": "package/dist/views/tui/selector.js", - "size": 6634, - "sha256": "de8019676cf123cf7a8cec8ef9bc82d550a56d3d66081e69c5301821d50751d4" + "size": 6727, + "sha256": "1be97125de7a586c7f18d7e57ffddf15ef27a8066314bf89810c6acf449b967c" }, { "path": "package/dist/views/tui/selector.js.map", - "size": 7598, - "sha256": "1a29f7ad8d741b2497b987281f5d22b46f49ef71a9e23043d0c85ac6021050c9" + "size": 7704, + "sha256": "85e8f52cda69c3fec75ee5cb456bc656d7ef91527fa9ff77b5012ab174a4de70" }, { "path": "package/dist/views/tui/terminal-app.d.ts", @@ -8918,57 +9198,57 @@ { "path": "package/dist/views/tui/terminal-app.d.ts.map", "size": 1253, - "sha256": "8edcc4cf42f5f2af3891436b590a99967e0de4cba3f4765a6ea19df8d2a53b25" + "sha256": "93f8a7b562e6b7cf66baecb241d9ed51ae6055125a48f63398d8f1fddb044c57" }, { "path": "package/dist/views/tui/terminal-app.js", - "size": 9324, - "sha256": "f38fcbdb97528d50848bc41165c387f91f2c87767e410dc404818af5560d5907" + "size": 9382, + "sha256": "2f2948e7c35e1574c09fd8d36be3c3469ac03d73d2442f1ad5a93d5b6327f107" }, { "path": "package/dist/views/tui/terminal-app.js.map", - "size": 9170, - "sha256": "53cd4989c379f35b8b0a74c90aa927871e179a2ccd9348fcaac4369405ed65dc" + "size": 9226, + "sha256": "65e356ba75a123467aa9384e70382e694bc337e611bec2f70735c296ba26f334" }, { "path": "package/dist/views/tui/terminal-chrome.d.ts", - "size": 771, - "sha256": "57f1cab056e159f6542f4c9ad3db03a60fa79fcce58ad970476eeba7787f5899" + "size": 869, + "sha256": "28449a80a9326a85678b24a6d0f80d159b40d85a99bbf4a03ed79e3c587744da" }, { "path": "package/dist/views/tui/terminal-chrome.d.ts.map", - "size": 759, - "sha256": "e4533425b3fa2c8e207127f6b6a6bf923fe4c6ba41d656afffa3a36b8893a6ba" + "size": 842, + "sha256": "8a9bce7e603dfc8f868738ed9420b01c972b05684a889f9366416313dc28c09e" }, { "path": "package/dist/views/tui/terminal-chrome.js", - "size": 4684, - "sha256": "d6e288bfcd4e952bd8d4db6c4faa51052b9f124e1df683a9fca7a01a5b815f1b" + "size": 6757, + "sha256": "b09523bc0681c1500757728f449631d85bb08bf03814a8084da62f582243e2c6" }, { "path": "package/dist/views/tui/terminal-chrome.js.map", - "size": 4828, - "sha256": "812c94430535e3ca1c309c4c26538687d8898633faafc3e1478dc99d9910e2b7" + "size": 6514, + "sha256": "5484f11fb007f2e0399e5c257558c45f5e256d886e3f4573137d03db89edf9d9" }, { "path": "package/dist/views/tui/terminal-command-controller.d.ts", - "size": 1160, - "sha256": "87ce08c15a2937df75240af322bd8854d5d0f4fac7a316c16b8917a6279f7ec3" + "size": 1263, + "sha256": "d75dd7e91d9ef95348c055a8dc39f5d2b03d422175ba03fedb222d137a25cacf" }, { "path": "package/dist/views/tui/terminal-command-controller.d.ts.map", - "size": 1030, - "sha256": "bf68e88b2f3b5d1ba0a8ca3c0791ad10b731b02161f8f14a4027517daec95dbb" + "size": 1119, + "sha256": "8e5a644592e1d3c20e6c1eb33875fcead161a5108bcf061519b8cdaa4376c3ac" }, { "path": "package/dist/views/tui/terminal-command-controller.js", - "size": 8716, - "sha256": "5b896302093d1bc74614952ca980f51a2081caec6043c602fc3e419389d5ec6e" + "size": 8982, + "sha256": "0e4133269e59b912e299687b292a79bacb3244029d81ac73ef8ca69f001fc899" }, { "path": "package/dist/views/tui/terminal-command-controller.js.map", - "size": 8792, - "sha256": "b39f03c3867c28bf49487eb298bf4c63b34de09e5d0a66da3963debc2c02a0e6" + "size": 9028, + "sha256": "b6c788d576dccd76afe1a8f614a7480b0d00820484b992ab77348826678be99f" }, { "path": "package/dist/views/tui/terminal-compatibility.d.ts", @@ -9012,43 +9292,43 @@ }, { "path": "package/dist/views/tui/terminal-identity.d.ts", - "size": 1050, - "sha256": "e6d2dde0f1413082810636a0795ce70134efa74bb34c15d69f4a019e7cb689c7" + "size": 1337, + "sha256": "22ced0c49b5656bfeddaafc099df67520375998fd243871d33929a3852a00b52" }, { "path": "package/dist/views/tui/terminal-identity.d.ts.map", - "size": 1144, - "sha256": "59ecb6f5481a039ec752448ab0a17efb5f11e65c34dec851f67ab78ecd138f3e" + "size": 1349, + "sha256": "f4d6016a1319c1eca4449cc91251d47ca6fcbe25a2fef3c47679a48b4b5ff0ad" }, { "path": "package/dist/views/tui/terminal-identity.js", - "size": 6448, - "sha256": "c02cb383ce3d01389d2e697b9b0d7248b80c684a0bf83e3806769dcbacd93d0b" + "size": 4507, + "sha256": "c87fa96acc5f9d5c77ae209ab87fbccfd29918449fda84166a72222a0d273454" }, { "path": "package/dist/views/tui/terminal-identity.js.map", - "size": 7363, - "sha256": "d28cf8f2535c9245496947aee0b286ded09801984e1fe5d14feecf438370e9b2" + "size": 5016, + "sha256": "2c37382ea0953d317f84ab84397dd903092db3e29a5df291aa35e2f87175d77b" }, { "path": "package/dist/views/tui/terminal-input-controller.d.ts", - "size": 1679, - "sha256": "1e799e3dcf6c437adfddbf8cec8fff7ec6f2a59c8456c72bc3ff152c1aaadec8" + "size": 1774, + "sha256": "eba017acbd5844fe8e711ed074a9deb857445c371f65d6dc356ecac983db62d2" }, { "path": "package/dist/views/tui/terminal-input-controller.d.ts.map", - "size": 1511, - "sha256": "f3d902f888b0d5e59dbc3300189afb4b2718033c0f680610c3693c4d82e6adeb" + "size": 1590, + "sha256": "15333bb9fae8b5ac56a5e476145a6ea6edf90d8c6d72ee94f6a7196c1de64b4d" }, { "path": "package/dist/views/tui/terminal-input-controller.js", - "size": 6268, - "sha256": "f6c41faa17cbea4c1305e193a577b4e10da3cc1abc6204179cb31666ac0cb8e5" + "size": 7149, + "sha256": "a75a730f3e1b973f610b9378134461eec11914c8b7e92eb1e1f2ae3330497ace" }, { "path": "package/dist/views/tui/terminal-input-controller.js.map", - "size": 6479, - "sha256": "abb9eef7cf731be358691dd77572a526b11c463b88de30c58a3d636ef3d4ad4a" + "size": 7312, + "sha256": "48abefa24d94ffb8e33fd158485ca0c7d9dfb896a89b42af1251189e056cfbba" }, { "path": "package/dist/views/tui/terminal-interaction-controller.d.ts", @@ -9092,23 +9372,23 @@ }, { "path": "package/dist/views/tui/terminal-shell.d.ts", - "size": 913, - "sha256": "721f4341b7a05d22c5d8a5fb5269bfd9a61cc394f2e98f0395f73ac2c5c716ac" + "size": 1016, + "sha256": "f448e99165410b8f5ccd28fc0ee2368e4b0900c9e9aec69ffab0b96506ee635e" }, { "path": "package/dist/views/tui/terminal-shell.d.ts.map", - "size": 978, - "sha256": "41f6cd973f6a125368bc5fac5db57900266095bdfb24ec66481ea77b64fbefc1" + "size": 1073, + "sha256": "01c8c49f7b913206a0404773b3a7768f7e10ff0e370411ba8f3c1881e272b0a0" }, { "path": "package/dist/views/tui/terminal-shell.js", - "size": 5712, - "sha256": "92cc21876a8ac19d5202af1342f81725048c1a143a2ea3607c61c26068510b8b" + "size": 6043, + "sha256": "07e887228026b9af3ff1d17fa3e9df22b0e0f30b59178e79579fa3d67e367996" }, { "path": "package/dist/views/tui/terminal-shell.js.map", - "size": 6118, - "sha256": "f583b31fb1af3cc197d637218ab9dab5541b69737ce211e0132cd4d0e929d160" + "size": 6444, + "sha256": "0dbc6de43a96699935957f9a6504d83763b433b07ebf493cf9304705ae8b5d11" }, { "path": "package/dist/views/tui/terminal-surface-overlays.d.ts", @@ -9132,23 +9412,23 @@ }, { "path": "package/dist/views/tui/terminal-usage.d.ts", - "size": 171, - "sha256": "d62964ef5d3b175687877841996691a42c5ad7a699d6c7b84b1b59a4a5134f55" + "size": 239, + "sha256": "4748d7b0c205ab7c17197b96b1fb430a1518c8876552967af80dcc2f06cdeb5e" }, { "path": "package/dist/views/tui/terminal-usage.d.ts.map", - "size": 244, - "sha256": "bbf6ffeebf28d5955470e770c47f8db5a31b1ff0be35aba8b5bc1c20bf21c75f" + "size": 257, + "sha256": "53815711294ef5083bfcae05656ad6d7eac681795e25d4b796b0d455e25ce6d2" }, { "path": "package/dist/views/tui/terminal-usage.js", - "size": 6276, - "sha256": "c644dd41d1a2831a5998138c2a2d9335c4eaa796d535fe46cf202ce624203222" + "size": 2640, + "sha256": "21ad03a1f3168fe482a932aaf94b24735c6716520ce2a5491a0988f2894e0f5b" }, { "path": "package/dist/views/tui/terminal-usage.js.map", - "size": 7105, - "sha256": "628356453cc64021fb73e2bfb04ec334db53255b390727de84103cfc5cc4c1aa" + "size": 3042, + "sha256": "8195d5323ede16fbaa049b5f1b4857b5ec0766f341ce6809994de134a6d2d509" }, { "path": "package/dist/views/tui/theme.d.ts", @@ -9178,25 +9458,25 @@ { "path": "package/dist/views/tui/transcript.d.ts.map", "size": 789, - "sha256": "0845503282e4832edd85b444f40acd617c1ee2a4f677a248d3874f655744c833" + "sha256": "7136604006c007ff224961c82ff89ca82fe6b405115fe05e5161cfe8ea699ada" }, { "path": "package/dist/views/tui/transcript.js", - "size": 13627, - "sha256": "81e4cc2e5434293b4d54e78f6a7d7a5fe49ba943f6b0480cf116adae989a421b" + "size": 13459, + "sha256": "1c3f19194a2056ef51333efe12f556551fb6e56cde78c47eb56968500c65338b" }, { "path": "package/dist/views/tui/transcript.js.map", - "size": 15144, - "sha256": "61eb20bb66768d102fe503524ac7caabeddd6d7f1798128099ff540258b14824" + "size": 15010, + "sha256": "f609c12af31425d1dc8b911bfd8751a32ce39ab47f1e7744713ad9b09257c5fb" }, { "path": "package/package.json", - "size": 5625, - "sha256": "e5c4c9bdfcd3a24fedc17db7b33786715fdce60f0f659727cb2bb47ceb9db907" + "size": 5673, + "sha256": "70fd8ae1e0a9cb9b77230dfc2b23164a7818936b3169eedfded195b5e5fe9935" } ], - "digest": "67c137e3644e07fc1fc469d47251b011ac9613a36ab30386771e12855963ee4b" + "digest": "e95f73298dd6bb3e59e4c238995985212ed879ae5daa34f6bec19d9cf89736ae" }, "rpcRecords": 34, "referenceSizes": [ diff --git a/artifacts/verification/w6/raw/braid-demo.cast b/artifacts/verification/w6/raw/braid-demo.cast index 90d9d86..446c98a 100644 --- a/artifacts/verification/w6/raw/braid-demo.cast +++ b/artifacts/verification/w6/raw/braid-demo.cast @@ -1,336 +1,375 @@ -{"version":2,"width":120,"height":18,"timestamp":1786424908,"duration":7.426261,"idle_time_limit":1,"command":"packed braid --fixture deterministic","title":"Braid product demo","env":{"TERM":"xterm-256color"},"stdin":true} -[0.01,"o","\u001b[?2004h"] -[0.01023,"o","\u001b[>7u\u001b[?u\u001b[c"] -[0.010248,"o","\u001b[?1049h"] -[0.010253,"o","\u001b[2J\u001b[H"] -[0.010271,"o","\u001b[?25l"] -[0.020903,"o","\u001b[?2026h\u001b[2J\u001b[H\u001b[3J\u001b[1m\u001b[38;5;153mbraid\u001b[39m\u001b[22m \u001b[38;5;145mcwd\u001b[39m \u001b[38;5;195magent-sdk\u001b[39m · \u001b[38;5;145msession\u001b[39m \u001b[38;5;195mNew conversation\u001b[39m · \u001b[38;5;145mbranch\u001b[39m \u001b[38;5;195mbranch-1\u001b[39m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[38;5;195mAgentProfile Release engineer\u001b[39m · \u001b[38;5;195mrunner pi · model openai-codex/gpt-5.6-luna\u001b[39m \u001b[38;5;195mno connection\u001b[39m · \u001b[38;5;195mthinking high\u001b[39m\u001b[0m\u001b]8;;\u0007\r\r\n \u001b[0m\u001b]8;;\u0007\r\r\n Send a task to Release engineer, or press Ctrl+P for commands. \u001b[0m\u001b]8;;\u0007\r\r\n \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[38;5;145m────────────────────────────────────────────────────────\u001b[39m\u001b[38;5;111m › send \u001b[39m\u001b[38;5;145m────────────────────────────────────────────────────────\u001b[39m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[38;5;111m›\u001b[39m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[38;5;145m──────────────────────────────────────────────\u001b[39m\u001b[38;5;111m alt+enter newline · paste \u001b[39m\u001b[38;5;145m───────────────────────────────────────────────\u001b[39m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[38;5;115mready for a message\u001b[39m \u001b[38;5;195mCtrl+P commands\u001b[39m\u001b[0m\u001b]8;;\u0007\u001b[?2026l"] -[0.020961,"o","\u001b[4A\u001b[2G\u001b[?25l"] -[0.064337,"o","\u001b]0;Braid — /tmp/braid-product-demo-iraCD9/agent-sdk\u0007"] -[0.068263,"o","\u001b[?2026h\u001b[12A\r\u001b[2K\u001b[38;5;195mAgentProfile Release engineer\u001b[39m · \u001b[38;5;195mrunner pi · model openai-codex/gpt-5.6-luna\u001b[39m \u001b[38;5;195mLocal CLI Bridge\u001b[39m · \u001b[38;5;195mthinking high\u001b[39m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145mSend a task to Release engineer, or press Ctrl+P for commands.\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;145m────────────────────────────────────────────────────────\u001b[39m\u001b[38;5;111m › send \u001b[39m\u001b[38;5;145m────────────────────────────────────────────────────────\u001b[39m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;111m›\u001b[39m\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] -[0.068298,"o","\u001b[2A\u001b[2G\u001b[?25l"] -[0.788499,"i","/"] -[0.792227,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m/\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[3G\u001b[?25l"] -[0.808059,"o","\u001b[?2026h\u001b[3A\r\u001b[2K\u001b[38;5;145m────────────────────────────────────────────────────────\u001b[39m\u001b[38;5;111m › send \u001b[39m\u001b[38;5;145m────────────────────────────────────────────────────────\u001b[39m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;111m›\u001b[39m/\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[1m\u001b[38;5;111m→ new Create an empty conversation\u001b[39m\u001b[22m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K open\u001b[38;5;145m Search and open conversations\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K profile\u001b[38;5;145m Inspect, select, import, or edit a profile\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[4A\u001b[3G\u001b[?25l"] -[0.818484,"i","p"] -[0.820531,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m/p\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] -[0.82057,"o","\u001b[4G\u001b[?25l"] -[0.835464,"o","\u001b[?2026h\u001b[2B\r\u001b[2K \u001b[1m\u001b[38;5;111m→ profile Inspect, select, import, or edit a profile\u001b[39m\u001b[22m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K open\u001b[38;5;145m Search and open conversations\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K approve\u001b[38;5;145m unavailable — Interaction response is not exposed by the current runtime adapter\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[4A\u001b[4G\u001b[?25l"] -[0.848829,"i","r"] -[0.850076,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m/pr\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[5G"] -[0.85012,"o","\u001b[?25l"] -[0.865627,"o","\u001b[?2026h\u001b[3B\r\u001b[2K approve\u001b[38;5;145m unavailable — Interaction response is not exposed by the current runtime adapter\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K export\u001b[38;5;145m Export selected redacted data\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[4A\u001b[5G\u001b[?25l"] -[0.878979,"i","o"] -[0.880692,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m/pro\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] -[0.880723,"o","\u001b[6G\u001b[?25l"] -[0.896447,"o","\u001b[?2026h\u001b[1A\r\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;145m────────────────────────────────────────────────────────\u001b[39m\u001b[38;5;111m › send \u001b[39m\u001b[38;5;145m────────────────────────────────────────────────────────\u001b[39m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;111m›\u001b[39m/pro\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[1m\u001b[38;5;111m→ profile Inspect, select, import, or edit a profile\u001b[39m\u001b[22m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] -[0.896496,"o","\u001b[2A\u001b[6G\u001b[?25l"] -[0.908882,"i","f"] -[0.910232,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m/prof\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[7G"] -[0.91027,"o","\u001b[?25l"] -[0.925805,"o","\u001b[?2026h\u001b[2B\r\u001b[2K \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[2A\u001b[7G\u001b[?25l"] -[0.939151,"i","i"] -[0.940368,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m/profi\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] -[0.940404,"o","\u001b[8G\u001b[?25l"] -[0.956714,"o","\u001b[8G\u001b[?25l"] -[0.969065,"i","l"] -[0.970273,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m/profil\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[9G\u001b[?25l"] -[0.985764,"o","\u001b[9G\u001b[?25l"] -[0.999103,"i","e"] -[1.00065,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m/profile\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] -[1.00069,"o","\u001b[10G\u001b[?25l"] -[1.016316,"o","\u001b[10G\u001b[?25l"] -[1.028674,"i","\r"] -[1.031415,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[1A\u001b[2G"] -[1.031465,"o","\u001b[?25l"] -[1.032506,"o","\u001b[?25l"] -[1.061474,"o","\u001b[?2026h\u001b[10A\r\u001b[2K \u001b[38;5;145mSend a\u001b[0m\u001b]8;;\u0007 Active profile · Release engineer \u001b[0m\u001b]8;;\u0007 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007 \u001b[38;5;145msource Release engineer · trusted · read-only\u001b[39m \u001b[0m\u001b]8;;\u0007 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007 \u001b[38;5;145mrunner pi · model openai-codex/gpt-5.6-luna\u001b[39m \u001b[0m\u001b]8;;\u0007 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007 \u001b[38;5;145mthinking high · max output provider default\u001b[39m \u001b[0m\u001b]8;;\u0007 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007 \u001b[38;5;145mdigest sha256:0419cd01…31378043\u001b[39m \u001b[0m\u001b]8;;\u0007 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007 \u001b[1m\u001b[38;5;153mprofiles\u001b[39m\u001b[22m \u001b[0m\u001b]8;;\u0007 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\u001b[38;5;145m─────────────────────────────────────────────────────────────────────────────────────────────────────────\u001b[0m\u001b]8;;\u0007 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007> \u001b[7m \u001b[27m \u001b[0m\u001b]8;;\u0007 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\u001b[1m\u001b[38;5;111m→ ✓ Release engineer trusted · read-only · Release engineer · pi · openai-codex/gpt-5.6-lu\u001b[0m\u001b[39m\u001b[22m \u001b[0m\u001b]8;;\u0007 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\u001b[38;5;145m─────────────────────────────────────────────────────────────────────────────────────────────────────────\u001b[0m\u001b]8;;\u0007 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007 \u001b[38;5;145menter · ^V valid · ^S save · esc\u001b[39m \u001b[0m\u001b]8;;\u0007 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[6A\u001b[10G\u001b[?25l"] -[2.070027,"i","\u001b"] -[2.081216,"o","\u001b[?25l"] -[2.083011,"o","\u001b[?2026h\u001b[7A\r\u001b[2K \u001b[38;5;145mSend a task to Release engineer, or press Ctrl+P for commands.\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;145m────────────────────────────────────────────────────────\u001b[39m\u001b[38;5;111m › send \u001b[39m\u001b[38;5;145m────────────────────────────────────────────────────────\u001b[39m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;111m›\u001b[39m\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;145m──────────────────────────────────────────────\u001b[39m\u001b[38;5;111m alt+enter newline · paste \u001b[39m\u001b[38;5;145m───────────────────────────────────────────────\u001b[39m\u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[3A\u001b[2G\u001b[?25l"] -[2.440608,"i","T"] -[2.442288,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mT\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[3G"] -[2.442329,"o","\u001b[?25l"] -[2.460657,"i","r"] -[2.461949,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mTr\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[4G\u001b[?25l"] -[2.481282,"i","a"] -[2.482645,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mTra\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] -[2.482682,"o","\u001b[5G\u001b[?25l"] -[2.501544,"i","c"] -[2.502917,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mTrac\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[6G\u001b[?25l"] -[2.522262,"i","e"] -[2.523391,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mTrace\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[7G\u001b[?25l"] -[2.542702,"i"," "] -[2.543963,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mTrace \u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[8G\u001b[?25l"] -[2.563414,"i","t"] -[2.564885,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mTrace t\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[9G\u001b[?25l"] -[2.584251,"i","h"] -[2.585694,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mTrace th\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[10G\u001b[?25l"] -[2.605069,"i","i"] -[2.606206,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mTrace thi\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[11G\u001b[?25l"] -[2.624543,"i","s"] -[2.625796,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mTrace this\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] -[2.625845,"o","\u001b[12G\u001b[?25l"] -[2.645249,"i"," "] -[2.646569,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mTrace this \u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[13G\u001b[?25l"] -[2.665958,"i","r"] -[2.667303,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mTrace this r\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[14G\u001b[?25l"] -[2.685681,"i","e"] -[2.687022,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mTrace this re\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[15G\u001b[?25l"] -[2.706422,"i","q"] -[2.707572,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mTrace this req\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[16G\u001b[?25l"] -[2.726958,"i","u"] -[2.728395,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mTrace this requ\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[17G\u001b[?25l"] -[2.746787,"i","e"] -[2.748006,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mTrace this reque\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[18G\u001b[?25l"] -[2.767345,"i","s"] -[2.768413,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mTrace this reques\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[19G\u001b[?25l"] -[2.787802,"i","t"] -[2.788959,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mTrace this request\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[20G\u001b[?25l"] -[2.808349,"i"," "] -[2.809469,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mTrace this request \u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[21G\u001b[?25l"] -[2.828823,"i","f"] -[2.82988,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mTrace this request f\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[22G\u001b[?25l"] -[2.849249,"i","r"] -[2.850353,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mTrace this request fr\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] -[2.850415,"o","\u001b[23G\u001b[?25l"] -[2.869765,"i","o"] -[2.87077,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mTrace this request fro\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[24G\u001b[?25l"] -[2.890146,"i","m"] -[2.891189,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mTrace this request from\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[25G\u001b[?25l"] -[2.909535,"i"," "] -[2.910667,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mTrace this request from \u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[26G\u001b[?25l"] -[2.93005,"i","A"] -[2.931159,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mTrace this request from A\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[27G\u001b[?25l"] -[2.949536,"i","g"] -[2.950587,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mTrace this request from Ag\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[28G\u001b[?25l"] -[2.969968,"i","e"] -[2.971032,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mTrace this request from Age\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[29G\u001b[?25l"] -[2.989416,"i","n"] -[2.990846,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mTrace this request from Agen\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[30G\u001b[?25l"] -[3.010222,"i","t"] -[3.011402,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mTrace this request from Agent\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[31G\u001b[?25l"] -[3.030842,"i","P"] -[3.032125,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mTrace this request from AgentP\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[32G\u001b[?25l"] -[3.050539,"i","r"] -[3.051734,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mTrace this request from AgentPr\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[33G\u001b[?25l"] -[3.071134,"i","o"] -[3.07223,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mTrace this request from AgentPro\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[34G\u001b[?25l"] -[3.090536,"i","f"] -[3.091586,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mTrace this request from AgentProf\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[35G\u001b[?25l"] -[3.110963,"i","i"] -[3.112294,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mTrace this request from AgentProfi\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[36G"] -[3.112343,"o","\u001b[?25l"] -[3.130673,"i","l"] -[3.131776,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mTrace this request from AgentProfil\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[37G"] -[3.131818,"o","\u001b[?25l"] -[3.151146,"i","e"] -[3.152567,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mTrace this request from AgentProfile\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[38G"] -[3.152614,"o","\u001b[?25l"] -[3.17098,"i"," "] -[3.172559,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mTrace this request from AgentProfile \u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[39G\u001b[?25l"] -[3.19095,"i","t"] -[3.192484,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mTrace this request from AgentProfile t\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[40G\u001b[?25l"] -[3.210944,"i","o"] -[3.212565,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mTrace this request from AgentProfile to\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] -[3.21261,"o","\u001b[41G\u001b[?25l"] -[3.23092,"i"," "] -[3.23206,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mTrace this request from AgentProfile to \u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] -[3.232091,"o","\u001b[42G\u001b[?25l"] -[3.251338,"i","t"] -[3.2523,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mTrace this request from AgentProfile to t\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[43G"] -[3.252362,"o","\u001b[?25l"] -[3.271792,"i","h"] -[3.272978,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mTrace this request from AgentProfile to th\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[44G\u001b[?25l"] -[3.292316,"i","e"] -[3.293432,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mTrace this request from AgentProfile to the\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[45G\u001b[?25l"] -[3.312751,"i"," "] -[3.313849,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mTrace this request from AgentProfile to the \u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[46G"] -[3.313882,"o","\u001b[?25l"] -[3.333194,"i","s"] -[3.33425,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mTrace this request from AgentProfile to the s\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[47G"] -[3.334386,"o","\u001b[?25l"] -[3.353565,"i","e"] -[3.354624,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mTrace this request from AgentProfile to the se\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[48G\u001b[?25l"] -[3.373952,"i","l"] -[3.374963,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mTrace this request from AgentProfile to the sel\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[49G"] -[3.375002,"o","\u001b[?25l"] -[3.394309,"i","e"] -[3.395468,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mTrace this request from AgentProfile to the sele\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[50G\u001b[?25l"] -[3.414786,"i","c"] -[3.415966,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mTrace this request from AgentProfile to the selec\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[51G\u001b[?25l"] -[3.435269,"i","t"] -[3.436433,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mTrace this request from AgentProfile to the select\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[52G\u001b[?25l"] -[3.45578,"i","e"] -[3.45699,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mTrace this request from AgentProfile to the selecte\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[53G\u001b[?25l"] -[3.476341,"i","d"] -[3.477501,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mTrace this request from AgentProfile to the selected\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[54G\u001b[?25l"] -[3.496861,"i"," "] -[3.498053,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mTrace this request from AgentProfile to the selected \u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[55G\u001b[?25l"] -[3.517351,"i","r"] -[3.518456,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mTrace this request from AgentProfile to the selected r\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[56G\u001b[?25l"] -[3.537774,"i","u"] -[3.538835,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mTrace this request from AgentProfile to the selected ru\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[57G\u001b[?25l"] -[3.558163,"i","n"] -[3.559244,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mTrace this request from AgentProfile to the selected run\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[58G\u001b[?25l"] -[3.577478,"i","n"] -[3.578886,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mTrace this request from AgentProfile to the selected runn\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[59G\u001b[?25l"] -[3.598168,"i","e"] -[3.599681,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mTrace this request from AgentProfile to the selected runne\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[60G\u001b[?25l"] -[3.618107,"i","r"] -[3.619335,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mTrace this request from AgentProfile to the selected runner\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] -[3.619375,"o","\u001b[61G\u001b[?25l"] -[3.637665,"i","."] -[3.638735,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mTrace this request from AgentProfile to the selected runner.\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[62G\u001b[?25l"] -[3.658046,"i","\r"] -[3.659436,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[2G\u001b[?25l"] -[3.71677,"o","\u001b[?2026h\u001b[11A\r\u001b[2K\u001b[48;5;59m Trace this request from AgentProfile to the selected runner. \u001b[49m\u001b[38;5;145m│\u001b[39m \u001b[1m\u001b[38;5;153mactivity\u001b[39m\u001b[22m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m│\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;222mworking…\u001b[39m \u001b[38;5;145m│\u001b[39m \u001b[38;5;222m> running\u001b[39m run run-000001 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m│\u001b[39m \u001b[38;5;222m> running\u001b[39m admitted · pr\u001b[0m...\u001b[0m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m│\u001b[39m \u001b[38;5;222m> running\u001b[39m event backend\u001b[0m...\u001b[0m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m│\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m│\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m│\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m│\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m│\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;145m───────────────────────────────────────────────────\u001b[39m\u001b[38;5;111m › queue next #1 \u001b[39m\u001b[38;5;145m────────────────────────────────────────────────────\u001b[39m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;111m›\u001b[39m\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;145m──────────────────────────────────────────────\u001b[39m\u001b[38;5;111m alt+enter newline · paste \u001b[39m\u001b[38;5;145m───────────────────────────────────────────────\u001b[39m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;222mstreaming · Ctrl+C cancel\u001b[39m \u001b[38;5;195mEnter queues input\u001b[39m · \u001b[38;5;195mturns usage unknown · cost unknown · calls 0 · model unknown (1 missing)\u001b[39m\u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[4A\u001b[2G\u001b[?25l"] -[4.92014,"o","\u001b[?2026h\u001b[11A\r\u001b[2K\u001b[48;5;59m Trace this request from AgentProfile to the selected runner. \u001b[49m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Route confirmed. Your AgentProfile stays intact while agent-runtime sends this turn through Pi over Local CLI Bridge. \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Braid keeps the conversation, forks, approvals, and trace analysis in one place. \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;145m────────────────────────────────────────────────────────\u001b[39m\u001b[38;5;111m › send \u001b[39m\u001b[38;5;145m────────────────────────────────────────────────────────\u001b[39m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;111m›\u001b[39m\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;145m──────────────────────────────────────────────\u001b[39m\u001b[38;5;111m alt+enter newline · paste \u001b[39m\u001b[38;5;145m───────────────────────────────────────────────\u001b[39m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;115mcompleted\u001b[39m \u001b[38;5;195mCtrl+E detail\u001b[39m · \u001b[38;5;195mturns in 1.3k · out 96 · ~$0.0026 · calls 1 · model 1193ms\u001b[39m\u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[4A\u001b[2G\u001b[?25l"] -[5.907412,"i","\u001bOQ"] -[5.913458,"o","\u001b[?2026h\u001b[11A\r\u001b[2K\u001b[48;5;59m Trace this request from AgentProfile to the selected runner. \u001b[49m\u001b[38;5;145m│\u001b[39m \u001b[1m\u001b[38;5;153mactivity\u001b[39m\u001b[22m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m│\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[38;5;145m│\u001b[39m \u001b[38;5;115mok completed\u001b[39m run run-00\u001b[0m...\u001b[0m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Route confirmed. Your AgentProfile stays intact while agent-runtime sends this turn \u001b[38;5;145m│\u001b[39m \u001b[38;5;115mok completed\u001b[39m admitted ·\u001b[0m...\u001b[0m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K through Pi over Local CLI Bridge. Braid keeps the conversation, forks, approvals, and \u001b[38;5;145m│\u001b[39m \u001b[38;5;115mok completed\u001b[39m event back\u001b[0m...\u001b[0m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K trace analysis in one place. \u001b[38;5;145m│\u001b[39m \u001b[38;5;115mok completed\u001b[39m artifact \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m│\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m│\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m│\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m│\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[2B\u001b[2G\u001b[?25l"] -[6.628344,"i","\u001bOQ"] -[6.632935,"o","\u001b[?2026h\u001b[11A\r\u001b[2K\u001b[48;5;59m Trace this request from AgentProfile to the selected runner. \u001b[49m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Route confirmed. Your AgentProfile stays intact while agent-runtime sends this turn through Pi over Local CLI Bridge. \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Braid keeps the conversation, forks, approvals, and trace analysis in one place. \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[2B\u001b[2G\u001b[?25l"] -[6.649414,"i","/"] -[6.651521,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m/\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[3G\u001b[?25l"] -[6.667229,"o","\u001b[?2026h\u001b[3A\r\u001b[2K\u001b[38;5;145m────────────────────────────────────────────────────────\u001b[39m\u001b[38;5;111m › send \u001b[39m\u001b[38;5;145m────────────────────────────────────────────────────────\u001b[39m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;111m›\u001b[39m/\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[1m\u001b[38;5;111m→ new Create an empty conversation\u001b[39m\u001b[22m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K open\u001b[38;5;145m Search and open conversations\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K profile\u001b[38;5;145m Inspect, select, import, or edit a profile\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[4A\u001b[3G\u001b[?25l"] -[6.667323,"i","a"] -[6.668719,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m/a\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] -[6.668757,"o","\u001b[4G\u001b[?25l"] -[6.685171,"o","\u001b[?2026h\u001b[2B\r\u001b[2K \u001b[1m\u001b[38;5;111m→ ask Analyze a frozen run with citations\u001b[39m\u001b[22m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K analyze\u001b[38;5;145m Run a named trace-analysis recipe\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K approve\u001b[38;5;145m unavailable — Interaction response is not exposed by the current runtime adapter\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[4A\u001b[4G\u001b[?25l"] -[6.686308,"i","s"] -[6.687811,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m/as\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] -[6.687846,"o","\u001b[5G\u001b[?25l"] -[6.704086,"o","\u001b[?2026h\u001b[1A\r\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;145m────────────────────────────────────────────────────────\u001b[39m\u001b[38;5;111m › send \u001b[39m\u001b[38;5;145m────────────────────────────────────────────────────────\u001b[39m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;111m›\u001b[39m/as\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[38;5;111m─\u001b[39m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[2A\u001b[5G\u001b[?25l"] -[6.705257,"i","k"] -[6.706234,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m/ask\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[6G\u001b[?25l"] -[6.72217,"o","\u001b[6G\u001b[?25l"] -[6.723441,"i"," "] -[6.724998,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m/ask \u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] -[6.725037,"o","\u001b[7G\u001b[?25l"] -[6.741191,"o","\u001b[?2026h\u001b[1B\r\u001b[2K \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[1A\u001b[7G\u001b[?25l"] -[6.742303,"i","W"] -[6.74367,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m/ask W\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] -[6.74371,"o","\u001b[8G\u001b[?25l"] -[6.760216,"o","\u001b[8G\u001b[?25l"] -[6.760321,"i","h"] -[6.761122,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m/ask Wh\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[9G\u001b[?25l"] -[6.778182,"o","\u001b[9G\u001b[?25l"] -[6.779316,"i","a"] -[6.78015,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m/ask Wha\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[10G\u001b[?25l"] -[6.797181,"o","\u001b[10G\u001b[?25l"] -[6.798302,"i","t"] -[6.799139,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m/ask What\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[11G\u001b[?25l"] -[6.815605,"o","\u001b[11G\u001b[?25l"] -[6.816761,"i"," "] -[6.817621,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m/ask What \u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[12G\u001b[?25l"] -[6.834937,"i","s"] -[6.836227,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m/ask What s\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[13G\u001b[?25l"] -[6.851773,"o","\u001b[13G\u001b[?25l"] -[6.852909,"i","h"] -[6.85388,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m/ask What sh\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] -[6.853907,"o","\u001b[14G\u001b[?25l"] -[6.869536,"o","\u001b[14G\u001b[?25l"] -[6.870673,"i","o"] -[6.872042,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m/ask What sho\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[15G"] -[6.872074,"o","\u001b[?25l"] -[6.887545,"o","\u001b[15G\u001b[?25l"] -[6.888675,"i","u"] -[6.88981,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m/ask What shou\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] -[6.889831,"o","\u001b[16G\u001b[?25l"] -[6.905768,"o","\u001b[16G\u001b[?25l"] -[6.906889,"i","l"] -[6.908049,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m/ask What shoul\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] -[6.908074,"o","\u001b[17G\u001b[?25l"] -[6.924996,"o","\u001b[17G\u001b[?25l"] -[6.925103,"i","d"] -[6.926086,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m/ask What should\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[18G\u001b[?25l"] -[6.942291,"o","\u001b[18G\u001b[?25l"] -[6.942385,"i"," "] -[6.943628,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m/ask What should \u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[19G\u001b[?25l"] -[6.960973,"i","t"] -[6.962635,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m/ask What should t\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] -[6.962676,"o","\u001b[20G\u001b[?25l"] -[6.978466,"o","\u001b[20G\u001b[?25l"] -[6.978545,"i","h"] -[6.979396,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m/ask What should th\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[21G\u001b[?25l"] -[6.995124,"o","\u001b[21G\u001b[?25l"] -[6.996453,"i","i"] -[6.998291,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m/ask What should thi\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[22G\u001b[?25l"] -[7.013831,"o","\u001b[22G\u001b[?25l"] -[7.01497,"i","s"] -[7.01587,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m/ask What should this\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[23G"] -[7.015888,"o","\u001b[?25l"] -[7.031431,"o","\u001b[23G\u001b[?25l"] -[7.032551,"i"," "] -[7.033382,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m/ask What should this \u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[24G"] -[7.033402,"o","\u001b[?25l"] -[7.050676,"i","a"] -[7.051919,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m/ask What should this a\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[25G\u001b[?25l"] -[7.0674,"o","\u001b[25G\u001b[?25l"] -[7.068549,"i","g"] -[7.06936,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m/ask What should this ag\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[26G\u001b[?25l"] -[7.085942,"o","\u001b[26G\u001b[?25l"] -[7.087093,"i","e"] -[7.088259,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m/ask What should this age\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[27G\u001b[?25l"] -[7.10395,"o","\u001b[27G\u001b[?25l"] -[7.105085,"i","n"] -[7.105951,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m/ask What should this agen\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[28G\u001b[?25l"] -[7.122643,"o","\u001b[28G\u001b[?25l"] -[7.122761,"i","t"] -[7.123997,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m/ask What should this agent\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[29G\u001b[?25l"] -[7.139805,"o","\u001b[29G\u001b[?25l"] -[7.141008,"i"," "] -[7.141869,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m/ask What should this agent \u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[30G\u001b[?25l"] -[7.159246,"i","i"] -[7.160447,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m/ask What should this agent i\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[31G\u001b[?25l"] -[7.175878,"o","\u001b[31G\u001b[?25l"] -[7.178136,"i","m"] -[7.17896,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m/ask What should this agent im\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[32G"] -[7.178976,"o","\u001b[?25l"] -[7.195463,"o","\u001b[32G\u001b[?25l"] -[7.195524,"i","p"] -[7.196408,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m/ask What should this agent imp\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] -[7.196518,"o","\u001b[33G\u001b[?25l"] -[7.212315,"o","\u001b[33G\u001b[?25l"] -[7.213435,"i","r"] -[7.214304,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m/ask What should this agent impr\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] -[7.214313,"o","\u001b[34G\u001b[?25l"] -[7.229936,"o","\u001b[34G\u001b[?25l"] -[7.23218,"i","o"] -[7.23339,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m/ask What should this agent impro\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[35G"] -[7.233402,"o","\u001b[?25l"] -[7.248912,"o","\u001b[35G\u001b[?25l"] -[7.250063,"i","v"] -[7.250822,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m/ask What should this agent improv\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[36G\u001b[?25l"] -[7.267468,"o","\u001b[36G\u001b[?25l"] -[7.267549,"i","e"] -[7.268374,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m/ask What should this agent improve\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] -[7.268517,"o","\u001b[37G\u001b[?25l"] -[7.284071,"o","\u001b[37G\u001b[?25l"] -[7.286341,"i"," "] -[7.287309,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m/ask What should this agent improve \u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] -[7.287326,"o","\u001b[38G\u001b[?25l"] -[7.30465,"i","n"] -[7.305902,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m/ask What should this agent improve n\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[39G\u001b[?25l"] -[7.321319,"o","\u001b[39G\u001b[?25l"] -[7.322449,"i","e"] -[7.323311,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m/ask What should this agent improve ne\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[40G\u001b[?25l"] -[7.3399,"o","\u001b[40G\u001b[?25l"] -[7.34103,"i","x"] -[7.341795,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m/ask What should this agent improve nex\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[41G\u001b[?25l"] -[7.358915,"o","\u001b[41G\u001b[?25l"] -[7.359003,"i","t"] -[7.360221,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m/ask What should this agent improve next\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[42G"] -[7.360233,"o","\u001b[?25l"] -[7.376352,"o","\u001b[42G\u001b[?25l"] -[7.376421,"i","?"] -[7.377636,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m/ask What should this agent improve next?\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] -[7.377648,"o","\u001b[43G\u001b[?25l"] -[7.39486,"i","\r"] -[7.396595,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[2G\u001b[?25l"] -[7.402919,"o","\u001b[?25l"] -[7.405653,"o","\u001b[?25l"] -[7.405686,"o","\u001b[?25l"] -[7.416227,"o","\u001b[?2026h\u001b[13A\r\u001b[2K\u001b[0m\u001b]8;;\u0007 \u001b[1m\u001b[38;5;153mactivity · analyses\u001b[39m\u001b[22m \u001b[38;5;145m›\u001b[39m \u001b[38;5;111m/ask · frozen question\u001b[39m \u001b[38;5;145m· complete · 1/1\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007\u001b[38;5;145m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────\u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 \u001b[38;5;145msource run run-000001 · Release engineer · pi · openai-codex/gpt-5.6-luna · Local CLI Bridge\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 source: a9ed803bccc9148…26b84d74 · frozen \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 analyst: profile-trace-reviewer · completed \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 \u001b[38;5;111m── question\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 What should this agent improve next? \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 \u001b[38;5;111m── findings\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 1. [1] The route was explicit, but the final answer did not cite its materialization receipt. · medium · confidence \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 0.93 \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 \u001b[38;5;145m↳ [1] runtime.route completed: Local CLI Bridge → Pi · event event-runtime-route\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 2. [2] Profile inspection and route resolution ran serially even though their inputs were independent. · low · \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 confidence 0.86 \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 \u001b[38;5;145m↳ [2] profile.inspect completed before runtime.route started · event event-profile-b…re-route\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 \u001b[38;5;111m── next\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 next: ask a narrower question, or use /compare against another frozen run \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007\u001b[38;5;145m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────\u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 \u001b[38;5;145mPgUp/PgDn detail · ←/esc close · page 1/2 · tab filter: analyses\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\u001b[?2026l"] -[7.416261,"o","\u001b[?25l"] -[7.426261,"o","\u001b[0m"] +{"version":2,"width":120,"height":18,"timestamp":1786538290,"duration":11.512167,"idle_time_limit":1,"command":"packed braid --fixture deterministic","title":"Braid product demo","env":{"TERM":"xterm-256color"},"stdin":true} +[0.01,"o","\u001b[?2026h\u001b[2J\u001b[H\u001b[3J \u001b[0m\u001b]8;;\u0007\r\r\n \u001b[38;5;145mAsk Release engineer anything.\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n \u001b[0m\u001b]8;;\u0007\r\r\n \u001b[0m\u001b]8;;\u0007 Active profile · Release engineer \u001b[0m\u001b]8;;\u0007 \u001b[0m\u001b]8;;\u0007\r\r\n \u001b[0m\u001b]8;;\u0007 \u001b[38;5;145msource Release engineer · trusted · read-only\u001b[39m \u001b[0m\u001b]8;;\u0007 \u001b[0m\u001b]8;;\u0007\r\r\n \u001b[0m\u001b]8;;\u0007 \u001b[38;5;145mrunner pi · model openai-codex/gpt-5.6-luna\u001b[39m \u001b[0m\u001b]8;;\u0007 \u001b[0m\u001b]8;;\u0007\r\r\n \u001b[0m\u001b]8;;\u0007 \u001b[38;5;145mthinking high · max output provider default\u001b[39m \u001b[0m\u001b]8;;\u0007 \u001b[0m\u001b]8;;\u0007\r\r\n \u001b[0m\u001b]8;;\u0007 \u001b[38;5;145mdigest sha256:0419cd01…31378043\u001b[39m \u001b[0m\u001b]8;;\u0007 \u001b[0m\u001b]8;;\u0007\r\r\n \u001b[0m\u001b]8;;\u0007 \u001b[1m\u001b[38;5;153mprofiles\u001b[39m\u001b[22m \u001b[0m\u001b]8;;\u0007 \u001b[0m\u001b]8;;\u0007\r\r\n \u001b[0m\u001b]8;;\u0007\u001b[38;5;145m─────────────────────────────────────────────────────────────────────────────────────────────────────────\u001b[0m\u001b]8;;\u0007 \u001b[0m\u001b]8;;\u0007\r\r\n \u001b[0m\u001b]8;;\u0007> \u001b[7m \u001b[27m \u001b[0m\u001b]8;;\u0007 \u001b[0m\u001b]8;;\u0007\r\r\n \u001b[0m\u001b]8;;\u0007\u001b[1m\u001b[38;5;111m→ ✓ Release engineer trusted · read-only · Release engineer · pi · openai-codex/gpt-5.6-lu\u001b[0m\u001b[39m\u001b[22m \u001b[0m\u001b]8;;\u0007 \u001b[0m\u001b]8;;\u0007\r\r\n \u001b[0m\u001b]8;;\u0007\u001b[38;5;145m─────────────────────────────────────────────────────────────────────────────────────────────────────────\u001b[0m\u001b]8;;\u0007 \u001b[0m\u001b]8;;\u0007\r\r\n \u001b[0m\u001b]8;;\u0007 \u001b[38;5;145menter · ^V valid · ^S save · esc\u001b[39m \u001b[0m\u001b]8;;\u0007 \u001b[0m\u001b]8;;\u0007\r\r\n \u001b[0m\u001b]8;;\u0007\r\r\n \u001b[0m\u001b]8;;\u0007\r\r\n \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[38;5;195mprofile Release engineer\u001b[39m · \u001b[38;5;195mpi / openai-codex/gpt-5.6-luna\u001b[39m · \u001b[38;5;195mLocal CLI Bridge\u001b[39m · \u001b[38;5;195mhigh\u001b[39m \u001b[38;5;195mCtrl+P commands\u001b[39m\u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[0.010052,"o","\u001b[7A\u001b[10G\u001b[?25l"] +[1.007919,"i","\u001b"] +[1.019211,"o","\u001b[?25l"] +[1.020923,"o","\u001b[?2026h\u001b[7A\r\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;111m›\u001b[39m\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[2G"] +[1.02095,"o","\u001b[?25l"] +[1.37875,"i","W"] +[1.38029,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mW\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[3G\u001b[?25l"] +[1.39968,"i","h"] +[1.400949,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mWh\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[4G\u001b[?25l"] +[1.419366,"i","i"] +[1.4204,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mWhi\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[5G\u001b[?25l"] +[1.439808,"i","c"] +[1.44092,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mWhic\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[6G\u001b[?25l"] +[1.460401,"i","h"] +[1.461423,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mWhich\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[7G\u001b[?25l"] +[1.480802,"i"," "] +[1.481784,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mWhich \u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[8G\u001b[?25l"] +[1.501065,"i","c"] +[1.502198,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mWhich c\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[1.502235,"o","\u001b[9G\u001b[?25l"] +[1.521563,"i","o"] +[1.52279,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mWhich co\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[10G\u001b[?25l"] +[1.541063,"i","d"] +[1.542318,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mWhich cod\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[11G"] +[1.542359,"o","\u001b[?25l"] +[1.561681,"i","i"] +[1.562789,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mWhich codi\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[12G\u001b[?25l"] +[1.581083,"i","n"] +[1.582125,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mWhich codin\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[13G\u001b[?25l"] +[1.601461,"i","g"] +[1.602574,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mWhich coding\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[14G\u001b[?25l"] +[1.621915,"i"," "] +[1.623102,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mWhich coding \u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[15G\u001b[?25l"] +[1.642439,"i","a"] +[1.643904,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mWhich coding a\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[16G\u001b[?25l"] +[1.662342,"i","g"] +[1.663667,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mWhich coding ag\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[17G\u001b[?25l"] +[1.682028,"i","e"] +[1.683271,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mWhich coding age\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[18G\u001b[?25l"] +[1.702885,"i","n"] +[1.704166,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mWhich coding agen\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[19G\u001b[?25l"] +[1.723488,"i","t"] +[1.724708,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mWhich coding agent\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[1.724746,"o","\u001b[20G\u001b[?25l"] +[1.743048,"i"," "] +[1.744244,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mWhich coding agent \u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[1.74428,"o","\u001b[21G\u001b[?25l"] +[1.763575,"i","w"] +[1.76478,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mWhich coding agent w\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[1.76482,"o","\u001b[22G\u001b[?25l"] +[1.783122,"i","i"] +[1.784374,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mWhich coding agent wi\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[23G"] +[1.784406,"o","\u001b[?25l"] +[1.803709,"i","l"] +[1.804928,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mWhich coding agent wil\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[1.804963,"o","\u001b[24G\u001b[?25l"] +[1.823275,"i","l"] +[1.824366,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mWhich coding agent will\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[25G"] +[1.824402,"o","\u001b[?25l"] +[1.843725,"i"," "] +[1.844852,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mWhich coding agent will \u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[26G\u001b[?25l"] +[1.863225,"i","h"] +[1.864288,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mWhich coding agent will h\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[1.864327,"o","\u001b[27G\u001b[?25l"] +[1.88365,"i","a"] +[1.884721,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mWhich coding agent will ha\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[1.884761,"o","\u001b[28G\u001b[?25l"] +[1.903134,"i","n"] +[1.904115,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mWhich coding agent will han\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[1.904153,"o","\u001b[29G\u001b[?25l"] +[1.923459,"i","d"] +[1.924488,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mWhich coding agent will hand\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[1.924528,"o","\u001b[30G\u001b[?25l"] +[1.943046,"i","l"] +[1.944298,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mWhich coding agent will handl\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[31G\u001b[?25l"] +[1.963638,"i","e"] +[1.964927,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mWhich coding agent will handle\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[32G"] +[1.964963,"o","\u001b[?25l"] +[1.983286,"i"," "] +[1.984511,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mWhich coding agent will handle \u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[33G\u001b[?25l"] +[2.003872,"i","m"] +[2.005113,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mWhich coding agent will handle m\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[2.005163,"o","\u001b[34G\u001b[?25l"] +[2.024507,"i","y"] +[2.025575,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mWhich coding agent will handle my\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[35G"] +[2.025611,"o","\u001b[?25l"] +[2.04401,"i"," "] +[2.045016,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mWhich coding agent will handle my \u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[36G\u001b[?25l"] +[2.064357,"i","n"] +[2.065583,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mWhich coding agent will handle my n\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[2.065621,"o","\u001b[37G\u001b[?25l"] +[2.084923,"i","e"] +[2.086192,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mWhich coding agent will handle my ne\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[2.086239,"o","\u001b[38G\u001b[?25l"] +[2.105588,"i","x"] +[2.106847,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mWhich coding agent will handle my nex\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[39G\u001b[?25l"] +[2.12522,"i","t"] +[2.126289,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mWhich coding agent will handle my next\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[40G\u001b[?25l"] +[2.145676,"i"," "] +[2.146818,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mWhich coding agent will handle my next \u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[2.146864,"o","\u001b[41G\u001b[?25l"] +[2.165176,"i","t"] +[2.166128,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mWhich coding agent will handle my next t\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[42G\u001b[?25l"] +[2.185428,"i","a"] +[2.186372,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mWhich coding agent will handle my next ta\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[43G\u001b[?25l"] +[2.205675,"i","s"] +[2.206701,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mWhich coding agent will handle my next tas\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[44G"] +[2.206738,"o","\u001b[?25l"] +[2.225094,"i","k"] +[2.226305,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mWhich coding agent will handle my next task\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[2.226342,"o","\u001b[45G\u001b[?25l"] +[2.245815,"i",","] +[2.246847,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mWhich coding agent will handle my next task,\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[46G\u001b[?25l"] +[2.266163,"i"," "] +[2.267361,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mWhich coding agent will handle my next task, \u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[2.267419,"o","\u001b[47G\u001b[?25l"] +[2.286751,"i","a"] +[2.287793,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mWhich coding agent will handle my next task, a\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[48G"] +[2.287946,"o","\u001b[?25l"] +[2.307167,"i","n"] +[2.308411,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mWhich coding agent will handle my next task, an\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[2.308455,"o","\u001b[49G\u001b[?25l"] +[2.327785,"i","d"] +[2.328864,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mWhich coding agent will handle my next task, and\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[2.328904,"o","\u001b[50G\u001b[?25l"] +[2.348214,"i"," "] +[2.349631,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mWhich coding agent will handle my next task, and \u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[2.349789,"o","\u001b[51G\u001b[?25l"] +[2.368077,"i","w"] +[2.369302,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mWhich coding agent will handle my next task, and w\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[2.369343,"o","\u001b[52G\u001b[?25l"] +[2.388666,"i","h"] +[2.389907,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mWhich coding agent will handle my next task, and wh\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[53G\u001b[?25l"] +[2.408242,"i","e"] +[2.409389,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mWhich coding agent will handle my next task, and whe\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[54G\u001b[?25l"] +[2.428726,"i","r"] +[2.429774,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mWhich coding agent will handle my next task, and wher\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[2.429806,"o","\u001b[55G\u001b[?25l"] +[2.448065,"i","e"] +[2.449127,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mWhich coding agent will handle my next task, and where\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[2.449167,"o","\u001b[56G\u001b[?25l"] +[2.468454,"i"," "] +[2.46945,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mWhich coding agent will handle my next task, and where \u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[57G"] +[2.469486,"o","\u001b[?25l"] +[2.488889,"i","w"] +[2.489859,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mWhich coding agent will handle my next task, and where w\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[58G"] +[2.489897,"o","\u001b[?25l"] +[2.509482,"i","i"] +[2.510555,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mWhich coding agent will handle my next task, and where wi\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[59G\u001b[?25l"] +[2.529925,"i","l"] +[2.531365,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mWhich coding agent will handle my next task, and where wil\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[60G\u001b[?25l"] +[2.550706,"i","l"] +[2.551794,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mWhich coding agent will handle my next task, and where will\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[61G\u001b[?25l"] +[2.570097,"i"," "] +[2.571145,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mWhich coding agent will handle my next task, and where will \u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[62G"] +[2.571178,"o","\u001b[?25l"] +[2.590443,"i","i"] +[2.591426,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mWhich coding agent will handle my next task, and where will i\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[63G"] +[2.591461,"o","\u001b[?25l"] +[2.610784,"i","t"] +[2.612078,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mWhich coding agent will handle my next task, and where will it\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[64G\u001b[?25l"] +[2.631481,"i"," "] +[2.632599,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mWhich coding agent will handle my next task, and where will it \u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[65G\u001b[?25l"] +[2.650983,"i","r"] +[2.652069,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mWhich coding agent will handle my next task, and where will it r\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[66G"] +[2.652113,"o","\u001b[?25l"] +[2.671508,"i","u"] +[2.672531,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mWhich coding agent will handle my next task, and where will it ru\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[67G\u001b[?25l"] +[2.691845,"i","n"] +[2.692781,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mWhich coding agent will handle my next task, and where will it run\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[2.692819,"o","\u001b[68G\u001b[?25l"] +[2.712084,"i","?"] +[2.713761,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mWhich coding agent will handle my next task, and where will it run?\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[69G\u001b[?25l"] +[2.73204,"i","\r"] +[2.733371,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[2G\u001b[?25l"] +[2.761756,"o","\u001b[?2026h\u001b[14A\r\u001b[2K \u001b[38;5;111m› \u001b[39mWhich coding agent will handle my next task, and where will it run? \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;222mworking…\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;222m› queue #1\u001b[39m \u001b[7m \u001b[0m \u001b[0m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;195mprofile Release engineer\u001b[39m · \u001b[38;5;195mpi / openai-codex/gpt-5.6-luna\u001b[39m · \u001b[38;5;195mLocal CLI Bridge\u001b[39m \u001b[38;5;222mworking · Ctrl+C cancel\u001b[39m · \u001b[38;5;195mEnter queues\u001b[39m\u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[3A\u001b[12G\u001b[?25l"] +[2.772195,"i","\u001bOQ"] +[2.77715,"o","\u001b[?2026h\u001b[14A\r\u001b[2K \u001b[38;5;111m› \u001b[39mWhich coding agent will handle my next task, and where will it run? \u001b[38;5;145m│\u001b[39m \u001b[1m\u001b[38;5;153mlive work\u001b[39m\u001b[22m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m│\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;222mworking…\u001b[39m \u001b[38;5;145m│\u001b[39m \u001b[38;5;222m> running\u001b[39m run run-000001 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m│\u001b[39m \u001b[38;5;222m> running\u001b[39m admitted · pr\u001b[0m...\u001b[0m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m│\u001b[39m \u001b[38;5;222m> running\u001b[39m event backend\u001b[0m...\u001b[0m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m│\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m│\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m│\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m│\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m│\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m│\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m│\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m│\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m│\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[1B\u001b[12G\u001b[?25l"] +[8.188954,"o","\u001b[?2026h\u001b[12A\r\u001b[2K \u001b[38;5;145m· run result\u001b[39m\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[38;5;145m│\u001b[39m \u001b[38;5;145mNo live work.\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Pi will run the task through Local CLI Bridge with gpt-5.6-luna. The Release engineer \u001b[38;5;145m│\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K AgentProfile supplies its instructions and permissions. Braid keeps the transcript, \u001b[38;5;145m│\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K approvals, branches, and trace analysis. \u001b[38;5;145m│\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m│\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m│\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m│\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m│\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m│\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m│\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m│\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m│\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;111m›\u001b[39m\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;195mprofile Release engineer\u001b[39m · \u001b[38;5;195mpi / openai-codex/gpt-5.6-luna\u001b[39m · \u001b[38;5;195mLocal CLI Bridge\u001b[39m · \u001b[38;5;195mhigh\u001b[39m \u001b[38;5;195min 1.3k\u001b[39m · \u001b[38;5;195mout 96\u001b[39m · \u001b[38;5;195m~$0.0026\u001b[39m\u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[3A\u001b[2G\u001b[?25l"] +[8.209899,"i","\u001bOQ"] +[8.218787,"o","\u001b[?2026h\u001b[14A\r\u001b[2K \u001b[38;5;111m› \u001b[39mWhich coding agent will handle my next task, and where will it run? \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Pi will run the task through Local CLI Bridge with gpt-5.6-luna. The Release engineer AgentProfile supplies its \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K instructions and permissions. Braid keeps the transcript, approvals, branches, and trace analysis. \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[1B\u001b[2G\u001b[?25l"] +[9.21187,"i","/"] +[9.213679,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m/\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[3G"] +[9.213713,"o","\u001b[?25l"] +[9.229516,"o","\u001b[?2026h\u001b[7A\r\u001b[2K\u001b[38;5;111m›\u001b[39m/\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;145m──────────────────────────────────────────────\u001b[39m\u001b[38;5;111m alt+enter newline · paste \u001b[39m\u001b[38;5;145m───────────────────────────────────────────────\u001b[39m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[1m\u001b[38;5;111m→ new Create an empty conversation\u001b[39m\u001b[22m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K open\u001b[38;5;145m Search and open conversations\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K profile\u001b[38;5;145m Inspect, select, import, or edit a profile\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K connection\u001b[38;5;145m Inspect, create, test, select, or remove a connection\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K runner\u001b[38;5;145m unavailable — Runner overrides require the current profile compatibility helpers\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m (1/29)\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[9A\u001b[3G\u001b[?25l"] +[9.242926,"i","a"] +[9.244557,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m/a\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[4G"] +[9.244608,"o","\u001b[?25l"] +[9.260491,"o","\u001b[?2026h\u001b[4B\r\u001b[2K \u001b[1m\u001b[38;5;111m→ ask Analyze a frozen run with citations\u001b[39m\u001b[22m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K analyze\u001b[38;5;145m Run a named trace-analysis recipe\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K approve\u001b[38;5;145m unavailable — Interaction response is not exposed by the current runtime adapter\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K automate\u001b[38;5;145m unavailable — Interaction automation requires the shared response contract\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K activity\u001b[38;5;145m Open run, tool, worker, and usage activity\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m (1/10)\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[9A\u001b[4G\u001b[?25l"] +[9.273862,"i","c"] +[9.275417,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m/ac\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[5G\u001b[?25l"] +[9.291342,"o","\u001b[?2026h\r\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;111m›\u001b[39m/ac\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;145m──────────────────────────────────────────────\u001b[39m\u001b[38;5;111m alt+enter newline · paste \u001b[39m\u001b[38;5;145m───────────────────────────────────────────────\u001b[39m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[1m\u001b[38;5;111m→ activity Open run, tool, worker, and usage activity\u001b[39m\u001b[22m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K detach\u001b[38;5;145m unavailable — There is no active run\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K cancel\u001b[38;5;145m unavailable — There is no active run to cancel\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K branch\u001b[38;5;145m Create a branch at a message boundary\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[7A\u001b[5G\u001b[?25l"] +[9.304906,"i","t"] +[9.306439,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m/act\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[6G\u001b[?25l"] +[9.322113,"o","\u001b[?2026h\r\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;111m›\u001b[39m/act\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;145m──────────────────────────────────────────────\u001b[39m\u001b[38;5;111m alt+enter newline · paste \u001b[39m\u001b[38;5;145m───────────────────────────────────────────────\u001b[39m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[1m\u001b[38;5;111m→ activity Open run, tool, worker, and usage activity\u001b[39m\u001b[22m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[4A\u001b[6G\u001b[?25l"] +[9.33542,"i","i"] +[9.336863,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m/acti\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[9.337011,"o","\u001b[7G\u001b[?25l"] +[9.352719,"o","\u001b[7G\u001b[?25l"] +[9.365113,"i","v"] +[9.366414,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m/activ\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[8G\u001b[?25l"] +[9.382407,"o","\u001b[8G\u001b[?25l"] +[9.395739,"i","i"] +[9.396991,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m/activi\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[9G\u001b[?25l"] +[9.412613,"o","\u001b[9G\u001b[?25l"] +[9.425877,"i","t"] +[9.427217,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m/activit\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[10G"] +[9.42725,"o","\u001b[?25l"] +[9.442787,"o","\u001b[10G\u001b[?25l"] +[9.456011,"i","y"] +[9.457222,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m/activity\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[11G"] +[9.45725,"o","\u001b[?25l"] +[9.473271,"o","\u001b[11G\u001b[?25l"] +[9.486542,"i","\r"] +[9.488313,"o","\u001b[?2026h\r\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;111m›\u001b[39m\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[9.488343,"o","\u001b[2A\u001b[2G\u001b[?25l"] +[9.492447,"o","\u001b[?25l"] +[9.506727,"o","\u001b[?2026h\u001b[14A\r\u001b[2K\u001b[0m\u001b]8;;\u0007 \u001b[1m\u001b[38;5;153mactivity\u001b[39m\u001b[22m \u001b[38;5;145m›\u001b[39m \u001b[38;5;111martifact\u001b[39m \u001b[38;5;145m· completed · 1/4\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007\u001b[38;5;145m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────\u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 \u001b[38;5;111m›\u001b[39m \u001b[38;5;115m✓\u001b[39m \u001b[38;5;111martifact\u001b[39m\u001b[38;5;145m · system\u001b[39m \u001b[38;5;145m│\u001b[39m time: 2026-08-12T12:38:04.554Z \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 \u001b[38;5;115m✓\u001b[39m \u001b[38;5;195mevent backend_start\u001b[39m\u001b[38;5;145m · system · seq\u001b[0m…\u001b[0m\u001b[38;5;145m│\u001b[39m run: run-000001 \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 \u001b[38;5;115m✓\u001b[39m \u001b[38;5;195madmitted\u001b[39m\u001b[38;5;145m · system · profile b6e862\u001b[0m…\u001b[0m\u001b[38;5;145m│\u001b[39m source event: run-000001:runtime:3 \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 \u001b[38;5;115m✓\u001b[39m \u001b[38;5;195mrun run-000001\u001b[39m\u001b[38;5;145m · run\u001b[39m \u001b[38;5;145m│\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 \u001b[38;5;145m│\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 \u001b[38;5;145m│\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 \u001b[38;5;145m│\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 \u001b[38;5;145m│\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 \u001b[38;5;145m│\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 \u001b[38;5;145m│\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 \u001b[38;5;145m│\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 \u001b[38;5;145m│\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 \u001b[38;5;145m│\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 \u001b[38;5;145m│\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007\u001b[38;5;145m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────\u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 \u001b[38;5;145m↑↓ select · PgUp/PgDn detail · home/end jump · ←/esc close · tab filter: all\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[?25l"] +[10.386924,"i","\u001b"] +[10.400874,"o","\u001b[?25l"] +[10.403566,"o","\u001b[?2026h\u001b[17A\r\u001b[2K \u001b[38;5;111m› \u001b[39mWhich coding agent will handle my next task, and where will it run? \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Pi will run the task through Local CLI Bridge with gpt-5.6-luna. The Release engineer AgentProfile supplies its \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K instructions and permissions. Braid keeps the transcript, approvals, branches, and trace analysis. \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;111m›\u001b[39m\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;195mprofile Release engineer\u001b[39m · \u001b[38;5;195mpi / openai-codex/gpt-5.6-luna\u001b[39m · \u001b[38;5;195mLocal CLI Bridge\u001b[39m · \u001b[38;5;195mhigh\u001b[39m \u001b[38;5;195min 1.3k\u001b[39m · \u001b[38;5;195mout 96\u001b[39m · \u001b[38;5;195m~$0.0026\u001b[39m\u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[3A\u001b[2G\u001b[?25l"] +[10.737947,"i","/"] +[10.739269,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m/\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[3G\u001b[?25l"] +[10.754872,"o","\u001b[?2026h\u001b[7A\r\u001b[2K\u001b[38;5;111m›\u001b[39m/\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;145m──────────────────────────────────────────────\u001b[39m\u001b[38;5;111m alt+enter newline · paste \u001b[39m\u001b[38;5;145m───────────────────────────────────────────────\u001b[39m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[1m\u001b[38;5;111m→ new Create an empty conversation\u001b[39m\u001b[22m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K open\u001b[38;5;145m Search and open conversations\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K profile\u001b[38;5;145m Inspect, select, import, or edit a profile\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K connection\u001b[38;5;145m Inspect, create, test, select, or remove a connection\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K runner\u001b[38;5;145m unavailable — Runner overrides require the current profile compatibility helpers\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m (1/29)\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[9A\u001b[3G\u001b[?25l"] +[10.756006,"i","a"] +[10.756824,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m/a\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[4G"] +[10.756847,"o","\u001b[?25l"] +[10.773767,"o","\u001b[?2026h\u001b[4B\r\u001b[2K \u001b[1m\u001b[38;5;111m→ ask Analyze a frozen run with citations\u001b[39m\u001b[22m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K analyze\u001b[38;5;145m Run a named trace-analysis recipe\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K approve\u001b[38;5;145m unavailable — Interaction response is not exposed by the current runtime adapter\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K automate\u001b[38;5;145m unavailable — Interaction automation requires the shared response contract\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K activity\u001b[38;5;145m Open run, tool, worker, and usage activity\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m (1/10)\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[9A\u001b[4G\u001b[?25l"] +[10.773853,"i","s"] +[10.775146,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m/as\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[5G"] +[10.775173,"o","\u001b[?25l"] +[10.790235,"o","\u001b[?2026h\r\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;111m›\u001b[39m/as\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;145m──────────────────────────────────────────────\u001b[39m\u001b[38;5;111m alt+enter newline · paste \u001b[39m\u001b[38;5;145m───────────────────────────────────────────────\u001b[39m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[1m\u001b[38;5;111m→ ask Analyze a frozen run with citations\u001b[39m\u001b[22m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[4A\u001b[5G\u001b[?25l"] +[10.792545,"i","k"] +[10.793735,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m/ask\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[10.793753,"o","\u001b[6G\u001b[?25l"] +[10.809502,"o","\u001b[6G\u001b[?25l"] +[10.810632,"i"," "] +[10.811588,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m/ask \u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[7G\u001b[?25l"] +[10.82723,"o","\u001b[?2026h\r\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;111m›\u001b[39m/ask \u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[2A\u001b[7G\u001b[?25l"] +[10.828422,"i","W"] +[10.829204,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m/ask W\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[8G"] +[10.829227,"o","\u001b[?25l"] +[10.845726,"o","\u001b[8G\u001b[?25l"] +[10.84686,"i","h"] +[10.847715,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m/ask Wh\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[10.847737,"o","\u001b[9G\u001b[?25l"] +[10.86329,"o","\u001b[9G\u001b[?25l"] +[10.865554,"i","a"] +[10.866634,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m/ask Wha\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[10G\u001b[?25l"] +[10.882212,"o","\u001b[10G\u001b[?25l"] +[10.883367,"i","t"] +[10.884367,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m/ask What\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[10.884384,"o","\u001b[11G\u001b[?25l"] +[10.900179,"o","\u001b[11G\u001b[?25l"] +[10.901365,"i"," "] +[10.902434,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m/ask What \u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[10.902459,"o","\u001b[12G\u001b[?25l"] +[10.919714,"i","s"] +[10.921148,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m/ask What s\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[10.921184,"o","\u001b[13G\u001b[?25l"] +[10.936695,"o","\u001b[13G\u001b[?25l"] +[10.937847,"i","h"] +[10.938657,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m/ask What sh\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[10.938669,"o","\u001b[14G\u001b[?25l"] +[10.955371,"o","\u001b[14G"] +[10.955428,"o","\u001b[?25l"] +[10.956508,"i","o"] +[10.957597,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m/ask What sho\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[10.957605,"o","\u001b[15G\u001b[?25l"] +[10.973259,"o","\u001b[15G\u001b[?25l"] +[10.974413,"i","u"] +[10.975309,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m/ask What shou\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[16G"] +[10.975326,"o","\u001b[?25l"] +[10.99106,"o","\u001b[16G"] +[10.991093,"o","\u001b[?25l"] +[10.992175,"i","l"] +[10.99306,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m/ask What shoul\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[10.993076,"o","\u001b[17G\u001b[?25l"] +[11.009817,"o","\u001b[17G\u001b[?25l"] +[11.009918,"i","d"] +[11.010834,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m/ask What should\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[18G\u001b[?25l"] +[11.027361,"o","\u001b[18G\u001b[?25l"] +[11.028492,"i"," "] +[11.029308,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m/ask What should \u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[19G\u001b[?25l"] +[11.046835,"i","t"] +[11.048416,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m/ask What should t\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[20G\u001b[?25l"] +[11.064089,"o","\u001b[20G\u001b[?25l"] +[11.065215,"i","h"] +[11.066237,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m/ask What should th\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[11.066267,"o","\u001b[21G\u001b[?25l"] +[11.080912,"o","\u001b[21G\u001b[?25l"] +[11.083166,"i","i"] +[11.084367,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m/ask What should thi\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[22G\u001b[?25l"] +[11.100278,"o","\u001b[22G\u001b[?25l"] +[11.101429,"i","s"] +[11.102474,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m/ask What should this\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[11.102491,"o","\u001b[23G\u001b[?25l"] +[11.118838,"o","\u001b[23G\u001b[?25l"] +[11.118915,"i"," "] +[11.119835,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m/ask What should this \u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[24G"] +[11.119958,"o","\u001b[?25l"] +[11.137341,"i","a"] +[11.138669,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m/ask What should this a\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[25G\u001b[?25l"] +[11.15385,"o","\u001b[25G\u001b[?25l"] +[11.154984,"i","g"] +[11.156133,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m/ask What should this ag\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[11.156179,"o","\u001b[26G"] +[11.156187,"o","\u001b[?25l"] +[11.17156,"o","\u001b[26G\u001b[?25l"] +[11.173836,"i","e"] +[11.174713,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m/ask What should this age\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[11.174741,"o","\u001b[27G\u001b[?25l"] +[11.191166,"o","\u001b[27G\u001b[?25l"] +[11.192291,"i","n"] +[11.192983,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m/ask What should this agen\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[28G"] +[11.192992,"o","\u001b[?25l"] +[11.209278,"o","\u001b[28G\u001b[?25l"] +[11.210397,"i","t"] +[11.211125,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m/ask What should this agent\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[29G"] +[11.211143,"o","\u001b[?25l"] +[11.227603,"o","\u001b[29G\u001b[?25l"] +[11.228732,"i"," "] +[11.229482,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m/ask What should this agent \u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[11.229497,"o","\u001b[30G\u001b[?25l"] +[11.246775,"i","i"] +[11.247992,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m/ask What should this agent i\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[11.248023,"o","\u001b[31G\u001b[?25l"] +[11.263554,"o","\u001b[31G\u001b[?25l"] +[11.265817,"i","m"] +[11.266901,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m/ask What should this agent im\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[32G\u001b[?25l"] +[11.282489,"o","\u001b[32G\u001b[?25l"] +[11.283847,"i","p"] +[11.284559,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m/ask What should this agent imp\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[33G\u001b[?25l"] +[11.301185,"o","\u001b[33G\u001b[?25l"] +[11.302303,"i","r"] +[11.303038,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m/ask What should this agent impr\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[11.303046,"o","\u001b[34G\u001b[?25l"] +[11.319547,"o","\u001b[34G\u001b[?25l"] +[11.320666,"i","o"] +[11.321999,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m/ask What should this agent impro\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[35G\u001b[?25l"] +[11.337531,"o","\u001b[35G\u001b[?25l"] +[11.338637,"i","v"] +[11.340119,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m/ask What should this agent improv\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[11.340157,"o","\u001b[36G"] +[11.340171,"o","\u001b[?25l"] +[11.356342,"o","\u001b[36G\u001b[?25l"] +[11.356427,"i","e"] +[11.357511,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m/ask What should this agent improve\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[37G\u001b[?25l"] +[11.373499,"o","\u001b[37G\u001b[?25l"] +[11.374654,"i"," "] +[11.37582,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m/ask What should this agent improve \u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[38G"] +[11.375883,"o","\u001b[?25l"] +[11.392078,"i","n"] +[11.39348,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m/ask What should this agent improve n\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[39G\u001b[?25l"] +[11.409252,"o","\u001b[39G\u001b[?25l"] +[11.410409,"i","e"] +[11.411455,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m/ask What should this agent improve ne\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[40G\u001b[?25l"] +[11.427174,"o","\u001b[40G\u001b[?25l"] +[11.428329,"i","x"] +[11.429291,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m/ask What should this agent improve nex\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[41G\u001b[?25l"] +[11.44387,"o","\u001b[41G\u001b[?25l"] +[11.446185,"i","t"] +[11.447412,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m/ask What should this agent improve next\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[42G\u001b[?25l"] +[11.463246,"o","\u001b[42G\u001b[?25l"] +[11.464372,"i","?"] +[11.469172,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m/ask What should this agent improve next?\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[43G\u001b[?25l"] +[11.482497,"i","\r"] +[11.484067,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[2G\u001b[?25l"] +[11.485477,"o","\u001b[?25l"] +[11.489179,"o","\u001b[?25l"] +[11.489215,"o","\u001b[?25l"] +[11.502167,"o","\u001b[?2026h\u001b[14A\r\u001b[2K\u001b[0m\u001b]8;;\u0007 \u001b[1m\u001b[38;5;153mactivity · analyses\u001b[39m\u001b[22m \u001b[38;5;145m›\u001b[39m \u001b[38;5;111m/ask · frozen question\u001b[39m \u001b[38;5;145m· complete · 1/1\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007\u001b[38;5;145m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────\u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 \u001b[38;5;145msource run run-000001 · Release engineer · pi · openai-codex/gpt-5.6-luna · Local CLI Bridge\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 source: a9ed803bccc9148…26b84d74 · frozen \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 analyst: profile-trace-reviewer · completed \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 \u001b[38;5;111m── question\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 What should this agent improve next? \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 \u001b[38;5;111m── findings\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 1. [1] The route was explicit, but the final answer did not cite its materialization receipt. · medium · confidence \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 0.93 \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 \u001b[38;5;145m↳ [1] runtime.route completed: Local CLI Bridge → Pi · event event-runtime-route\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 2. [2] Profile inspection and route resolution ran serially even though their inputs were independent. · low · \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 confidence 0.86 \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 \u001b[38;5;145m↳ [2] profile.inspect completed before runtime.route started · event event-profile-b…re-route\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 \u001b[38;5;111m── next\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 next: ask a narrower question, or use /compare against another frozen run \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007\u001b[38;5;145m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────\u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[0m\u001b]8;;\u0007 \u001b[38;5;145mPgUp/PgDn detail · ←/esc close · page 1/2 · tab filter: analyses\u001b[39m \u001b[0m\u001b]8;;\u0007\u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[?25l"] +[11.512167,"o","\u001b[0m"] diff --git a/artifacts/verification/w6/raw/transcript-keyboard.cast b/artifacts/verification/w6/raw/transcript-keyboard.cast index 372accb..733c485 100644 --- a/artifacts/verification/w6/raw/transcript-keyboard.cast +++ b/artifacts/verification/w6/raw/transcript-keyboard.cast @@ -1,74 +1,84 @@ -{"version":2,"width":80,"height":24,"timestamp":1786424911,"duration":1.787511,"idle_time_limit":1,"command":"packed braid --fixture deterministic","title":"Braid transcript keyboard navigation","env":{"TERM":"xterm-256color"},"stdin":true} -[0.279517,"o","\u001b[?2004h"] -[0.279645,"o","\u001b[>7u\u001b[?u\u001b[c"] -[0.279653,"o","\u001b[?1049h"] -[0.279694,"o","\u001b[2J\u001b[H"] -[0.27971,"o","\u001b[?25l"] -[0.289315,"o","\u001b[?2026h\u001b[2J\u001b[H\u001b[3J\u001b[1m\u001b[38;5;153mbraid\u001b[39m\u001b[22m \u001b[38;5;145mcwd\u001b[39m \u001b[38;5;195mbraid-release-ux-final-wt\u001b[39m · \u001b[38;5;145msession\u001b[39m \u001b[38;5;195mNew conversation\u001b[39m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[38;5;195mAgentProfile Braid starter\u001b[39m · \u001b[38;5;195mpi / deterministic\u001b[39m \u001b[38;5;195mno connection\u001b[39m · \u001b[38;5;195mthinking none\u001b[39m\u001b[0m\u001b]8;;\u0007\r\r\n \u001b[0m\u001b]8;;\u0007\r\r\n Send a task to Braid starter, or press Ctrl+P for commands. \u001b[0m\u001b]8;;\u0007\r\r\n \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[38;5;145m────────────────────────────────────\u001b[39m\u001b[38;5;111m › send \u001b[39m\u001b[38;5;145m────────────────────────────────────\u001b[39m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[38;5;111m›\u001b[39m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[38;5;145m──────────────────────────\u001b[39m\u001b[38;5;111m alt+enter newline · paste \u001b[39m\u001b[38;5;145m───────────────────────────\u001b[39m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[38;5;115mready for a message\u001b[39m \u001b[38;5;195mCtrl+P commands\u001b[39m\u001b[0m\u001b]8;;\u0007\u001b[?2026l"] -[0.28937,"o","\u001b[4A\u001b[2G\u001b[?25l"] -[0.316342,"o","\u001b]0;Braid — /home/drew/code/braid-release-ux-final-wt\u0007"] -[0.321947,"i","keyboard-flow-01"] -[0.32203,"i","\r"] -[0.326896,"o","\u001b[?2026h\u001b[18A\r\u001b[2K\u001b[38;5;195mAgentProfile Braid starter\u001b[39m · \u001b[38;5;195mpi / deterministic\u001b[39m \u001b[38;5;195mdeterministic fixture\u001b[39m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145mSend a task to Braid starter, or press Ctrl+P for commands.\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;145m────────────────────────────────────\u001b[39m\u001b[38;5;111m › send \u001b[39m\u001b[38;5;145m────────────────────────────────────\u001b[39m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;111m›\u001b[39m\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] -[0.326985,"o","\u001b[2A\u001b[2G\u001b[?25l"] -[0.332566,"o","\u001b[2G\u001b[?25l"] -[0.388547,"o","\u001b[?2026h\u001b[18A\r\u001b[2K\u001b[38;5;195mAgentProfile Braid starter\u001b[39m · \u001b[38;5;195mpi / deterministic\u001b[39m \u001b[38;5;195mnot connected\u001b[39m · \u001b[38;5;195mthinking none\u001b[39m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[48;5;59m keyboard-flow-01 \u001b[49m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;222mworking…\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;145m───────────────────────────────\u001b[39m\u001b[38;5;111m › queue next #1 \u001b[39m\u001b[38;5;145m────────────────────────────────\u001b[39m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;111m›\u001b[39m\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;145m──────────────────────────\u001b[39m\u001b[38;5;111m alt+enter newline · paste \u001b[39m\u001b[38;5;145m───────────────────────────\u001b[39m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;222mstreaming · Ctrl+C cancel\u001b[39m \u001b[38;5;195mEnter queues input\u001b[39m\u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[4A\u001b[2G\u001b[?25l"] -[0.444138,"o","\u001b[?2026h\u001b[18A\r\u001b[2K\u001b[38;5;195mAgentProfile Braid starter\u001b[39m · \u001b[38;5;195mpi / deterministic\u001b[39m \u001b[38;5;195mdeterministic fixture\u001b[39m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[48;5;59m keyboard-flow-01 \u001b[49m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-01 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;145m────────────────────────────────────\u001b[39m\u001b[38;5;111m › send \u001b[39m\u001b[38;5;145m────────────────────────────────────\u001b[39m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;111m›\u001b[39m\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;145m──────────────────────────\u001b[39m\u001b[38;5;111m alt+enter newline · paste \u001b[39m\u001b[38;5;145m───────────────────────────\u001b[39m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;115mcompleted\u001b[39m \u001b[38;5;195mCtrl+E detail\u001b[39m\u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[4A\u001b[2G\u001b[?25l"] -[0.526116,"i","keyboard-flow-02"] -[0.526219,"i","\r"] -[0.528942,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mkeyboard-flow-02\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[18G\u001b[?25l"] -[0.531001,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[2G"] -[0.531035,"o","\u001b[?25l"] -[0.547109,"o","\u001b[?2026h\u001b[18A\r\u001b[2K\u001b[38;5;195mAgentProfile Braid starter\u001b[39m · \u001b[38;5;195mpi / deterministic\u001b[39m \u001b[38;5;195mnot connected\u001b[39m · \u001b[38;5;195mthinking none\u001b[39m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[48;5;59m keyboard-flow-01 \u001b[49m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-01 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[48;5;59m keyboard-flow-02 \u001b[49m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;222mworking…\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;145m───────────────────────────────\u001b[39m\u001b[38;5;111m › queue next #1 \u001b[39m\u001b[38;5;145m────────────────────────────────\u001b[39m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;111m›\u001b[39m\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;145m──────────────────────────\u001b[39m\u001b[38;5;111m alt+enter newline · paste \u001b[39m\u001b[38;5;145m───────────────────────────\u001b[39m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;222mstreaming · Ctrl+C cancel\u001b[39m \u001b[38;5;195mEnter queues input\u001b[39m\u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[4A\u001b[2G\u001b[?25l"] -[0.606914,"o","\u001b[?2026h\u001b[18A\r\u001b[2K\u001b[38;5;195mAgentProfile Braid starter\u001b[39m · \u001b[38;5;195mpi / deterministic\u001b[39m \u001b[38;5;195mdeterministic fixture\u001b[39m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[48;5;59m keyboard-flow-01 \u001b[49m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m 1/2\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-01 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[48;5;59m keyboard-flow-02 \u001b[49m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m 2/2\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-02 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;145m────────────────────────────────────\u001b[39m\u001b[38;5;111m › send \u001b[39m\u001b[38;5;145m────────────────────────────────────\u001b[39m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;111m›\u001b[39m\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;145m──────────────────────────\u001b[39m\u001b[38;5;111m alt+enter newline · paste \u001b[39m\u001b[38;5;145m───────────────────────────\u001b[39m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;115mcompleted\u001b[39m \u001b[38;5;195mCtrl+E next detail (2/2)\u001b[39m\u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[4A\u001b[2G"] -[0.60696,"o","\u001b[?25l"] -[0.686786,"i","keyboard-flow-03"] -[0.686863,"i","\r"] -[0.689652,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mkeyboard-flow-03\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[18G\u001b[?25l"] -[0.691411,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[2G\u001b[?25l"] -[0.709382,"o","\u001b[?2026h\u001b[18A\r\u001b[2K\u001b[38;5;195mAgentProfile Braid starter\u001b[39m · \u001b[38;5;195mpi / deterministic\u001b[39m \u001b[38;5;195mnot connected\u001b[39m · \u001b[38;5;195mthinking none\u001b[39m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[48;5;59m keyboard-flow-01 \u001b[49m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m 1/2\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-01 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[48;5;59m keyboard-flow-02 \u001b[49m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m 2/2\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-02 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[48;5;59m keyboard-flow-03 \u001b[49m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;222mworking…\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;145m───────────────────────────────\u001b[39m\u001b[38;5;111m › queue next #1 \u001b[39m\u001b[38;5;145m────────────────────────────────\u001b[39m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;111m›\u001b[39m\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;145m──────────────────────────\u001b[39m\u001b[38;5;111m alt+enter newline · paste \u001b[39m\u001b[38;5;145m───────────────────────────\u001b[39m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;222mstreaming · Ctrl+C cancel\u001b[39m \u001b[38;5;195mEnter queues input\u001b[39m\u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[4A\u001b[2G\u001b[?25l"] -[0.764116,"o","\u001b[?2026h\u001b[18A\r\u001b[2K\u001b[38;5;195mAgentProfile Braid starter\u001b[39m · \u001b[38;5;195mpi / deterministic\u001b[39m \u001b[38;5;195mdeterministic fixture\u001b[39m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[48;5;59m keyboard-flow-01 \u001b[49m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m 1/3\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-01 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[48;5;59m keyboard-flow-02 \u001b[49m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m 2/3\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-02 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[48;5;59m keyboard-flow-03 \u001b[49m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m 3/3\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-03 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;145m────────────────────────────────────\u001b[39m\u001b[38;5;111m › send \u001b[39m\u001b[38;5;145m────────────────────────────────────\u001b[39m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;111m›\u001b[39m\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;145m──────────────────────────\u001b[39m\u001b[38;5;111m alt+enter newline · paste \u001b[39m\u001b[38;5;145m───────────────────────────\u001b[39m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;115mcompleted\u001b[39m \u001b[38;5;195mCtrl+E next detail (3/3)\u001b[39m\u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[4A\u001b[2G\u001b[?25l"] -[0.847061,"i","keyboard-flow-04"] -[0.84714,"i","\r"] -[0.849016,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mkeyboard-flow-04\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[18G"] -[0.849064,"o","\u001b[?25l"] -[0.850647,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] -[0.850653,"o","\u001b[2G\u001b[?25l"] -[0.867257,"o","\u001b[?2026h\u001b[18A\r\u001b[2K\u001b[38;5;195mAgentProfile Braid starter\u001b[39m · \u001b[38;5;195mpi / deterministic\u001b[39m \u001b[38;5;195mnot connected\u001b[39m · \u001b[38;5;195mthinking none\u001b[39m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[48;5;59m keyboard-flow-01 \u001b[49m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m 1/3\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-01 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[48;5;59m keyboard-flow-02 \u001b[49m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m 2/3\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-02 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[48;5;59m keyboard-flow-03 \u001b[49m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m 3/3\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-03 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[48;5;59m keyboard-flow-04 \u001b[49m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;222mworking…\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;145m───────────────────────────────\u001b[39m\u001b[38;5;111m › queue next #1 \u001b[39m\u001b[38;5;145m────────────────────────────────\u001b[39m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;111m›\u001b[39m\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;145m──────────────────────────\u001b[39m\u001b[38;5;111m alt+enter newline · paste \u001b[39m\u001b[38;5;145m───────────────────────────\u001b[39m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;222mstreaming · Ctrl+C cancel\u001b[39m \u001b[38;5;195mEnter queues input\u001b[39m\u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[4A\u001b[2G\u001b[?25l"] -[0.93014,"o","\u001b[?2026h\u001b[18A\r\u001b[2K\u001b[38;5;195mAgentProfile Braid starter\u001b[39m · \u001b[38;5;195mpi / deterministic\u001b[39m \u001b[38;5;195mdeterministic fixture\u001b[39m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[48;5;59m keyboard-flow-01 \u001b[49m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m 1/4\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-01 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[48;5;59m keyboard-flow-02 \u001b[49m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m 2/4\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-02 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[48;5;59m keyboard-flow-03 \u001b[49m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m 3/4\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-03 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[48;5;59m keyboard-flow-04 \u001b[49m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m 4/4\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-04 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;145m────────────────────────────────────\u001b[39m\u001b[38;5;111m › send \u001b[39m\u001b[38;5;145m────────────────────────────────────\u001b[39m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;111m›\u001b[39m\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;145m──────────────────────────\u001b[39m\u001b[38;5;111m alt+enter newline · paste \u001b[39m\u001b[38;5;145m───────────────────────────\u001b[39m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;115mcompleted\u001b[39m \u001b[38;5;195mCtrl+E next detail (4/4)\u001b[39m\u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[4A\u001b[2G\u001b[?25l"] -[1.007008,"i","keyboard-flow-05"] -[1.007092,"i","\r"] -[1.008943,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mkeyboard-flow-05\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[18G\u001b[?25l"] -[1.01095,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[2G\u001b[?25l"] -[1.027521,"o","\u001b[?2026h\u001b[18A\r\u001b[2K\u001b[38;5;195mAgentProfile Braid starter\u001b[39m · \u001b[38;5;195mpi / deterministic\u001b[39m \u001b[38;5;195mnot connected\u001b[39m · \u001b[38;5;195mthinking none\u001b[39m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-01 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[48;5;59m keyboard-flow-02 \u001b[49m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m 2/4\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-02 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[48;5;59m keyboard-flow-03 \u001b[49m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m 3/4\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-03 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[48;5;59m keyboard-flow-04 \u001b[49m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m 4/4\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-04 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[48;5;59m keyboard-flow-05 \u001b[49m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;222mworking…\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;145m───────────────────────────────\u001b[39m\u001b[38;5;111m › queue next #1 \u001b[39m\u001b[38;5;145m────────────────────────────────\u001b[39m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;111m›\u001b[39m\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;145m──────────────────────────\u001b[39m\u001b[38;5;111m alt+enter newline · paste \u001b[39m\u001b[38;5;145m───────────────────────────\u001b[39m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;222mstreaming · Ctrl+C cancel\u001b[39m \u001b[38;5;195mEnter queues input\u001b[39m\u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[4A\u001b[2G\u001b[?25l"] -[1.091216,"o","\u001b[?2026h\u001b[18A\r\u001b[2K\u001b[38;5;195mAgentProfile Braid starter\u001b[39m · \u001b[38;5;195mpi / deterministic\u001b[39m \u001b[38;5;195mdeterministic fixture\u001b[39m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[48;5;59m keyboard-flow-02 \u001b[49m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m 2/5\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-02 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[48;5;59m keyboard-flow-03 \u001b[49m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m 3/5\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-03 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[48;5;59m keyboard-flow-04 \u001b[49m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m 4/5\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-04 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[48;5;59m keyboard-flow-05 \u001b[49m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m 5/5\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-05 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;145m────────────────────────────────────\u001b[39m\u001b[38;5;111m › send \u001b[39m\u001b[38;5;145m────────────────────────────────────\u001b[39m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;111m›\u001b[39m\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;145m──────────────────────────\u001b[39m\u001b[38;5;111m alt+enter newline · paste \u001b[39m\u001b[38;5;145m───────────────────────────\u001b[39m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;115mcompleted\u001b[39m \u001b[38;5;195mCtrl+E next detail (5/5)\u001b[39m\u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[4A\u001b[2G\u001b[?25l"] -[1.169152,"i","keyboard-flow-06"] -[1.169239,"i","\r"] -[1.173574,"o","\u001b[2G\u001b[?25l"] -[1.191709,"o","\u001b[?2026h\u001b[18A\r\u001b[2K\u001b[38;5;195mAgentProfile Braid starter\u001b[39m · \u001b[38;5;195mpi / deterministic\u001b[39m \u001b[38;5;195mnot connected\u001b[39m · \u001b[38;5;195mthinking none\u001b[39m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-02 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[48;5;59m keyboard-flow-03 \u001b[49m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m 3/5\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-03 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[48;5;59m keyboard-flow-04 \u001b[49m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m 4/5\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-04 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[48;5;59m keyboard-flow-05 \u001b[49m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m 5/5\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-05 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[48;5;59m keyboard-flow-06 \u001b[49m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;222mworking…\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;145m───────────────────────────────\u001b[39m\u001b[38;5;111m › queue next #1 \u001b[39m\u001b[38;5;145m────────────────────────────────\u001b[39m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;111m›\u001b[39m\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;145m──────────────────────────\u001b[39m\u001b[38;5;111m alt+enter newline · paste \u001b[39m\u001b[38;5;145m───────────────────────────\u001b[39m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;222mstreaming · Ctrl+C cancel\u001b[39m \u001b[38;5;195mEnter queues input\u001b[39m\u001b[0m\u001b]8;;\u0007\u001b[?2026l"] -[1.19178,"o","\u001b[4A\u001b[2G\u001b[?25l"] -[1.254464,"o","\u001b[?2026h\u001b[18A\r\u001b[2K\u001b[38;5;195mAgentProfile Braid starter\u001b[39m · \u001b[38;5;195mpi / deterministic\u001b[39m \u001b[38;5;195mdeterministic fixture\u001b[39m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[48;5;59m keyboard-flow-03 \u001b[49m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m 3/6\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-03 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[48;5;59m keyboard-flow-04 \u001b[49m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m 4/6\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-04 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[48;5;59m keyboard-flow-05 \u001b[49m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m 5/6\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-05 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[48;5;59m keyboard-flow-06 \u001b[49m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m 6/6\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-06 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;145m────────────────────────────────────\u001b[39m\u001b[38;5;111m › send \u001b[39m\u001b[38;5;145m────────────────────────────────────\u001b[39m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;111m›\u001b[39m\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;145m──────────────────────────\u001b[39m\u001b[38;5;111m alt+enter newline · paste \u001b[39m\u001b[38;5;145m───────────────────────────\u001b[39m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;115mcompleted\u001b[39m \u001b[38;5;195mCtrl+E next detail (6/6)\u001b[39m\u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[4A\u001b[2G\u001b[?25l"] -[1.331551,"i","keyboard-flow-07"] -[1.331632,"i","\r"] -[1.333909,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mkeyboard-flow-07\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] -[1.333973,"o","\u001b[18G\u001b[?25l"] -[1.336673,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[2G"] -[1.336719,"o","\u001b[?25l"] -[1.363128,"o","\u001b[?2026h\u001b[18A\r\u001b[2K\u001b[38;5;195mAgentProfile Braid starter\u001b[39m · \u001b[38;5;195mpi / deterministic\u001b[39m \u001b[38;5;195mnot connected\u001b[39m · \u001b[38;5;195mthinking none\u001b[39m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-03 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[48;5;59m keyboard-flow-04 \u001b[49m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m 4/6\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-04 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[48;5;59m keyboard-flow-05 \u001b[49m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m 5/6\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-05 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[48;5;59m keyboard-flow-06 \u001b[49m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m 6/6\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-06 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[48;5;59m keyboard-flow-07 \u001b[49m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;222mworking…\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;145m───────────────────────────────\u001b[39m\u001b[38;5;111m › queue next #1 \u001b[39m\u001b[38;5;145m────────────────────────────────\u001b[39m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;111m›\u001b[39m\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;145m──────────────────────────\u001b[39m\u001b[38;5;111m alt+enter newline · paste \u001b[39m\u001b[38;5;145m───────────────────────────\u001b[39m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;222mstreaming · Ctrl+C cancel\u001b[39m \u001b[38;5;195mEnter queues input\u001b[39m\u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[4A\u001b[2G\u001b[?25l"] -[1.431064,"o","\u001b[?2026h\u001b[18A\r\u001b[2K\u001b[38;5;195mAgentProfile Braid starter\u001b[39m · \u001b[38;5;195mpi / deterministic\u001b[39m \u001b[38;5;195mdeterministic fixture\u001b[39m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[48;5;59m keyboard-flow-04 \u001b[49m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m 4/7\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-04 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[48;5;59m keyboard-flow-05 \u001b[49m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m 5/7\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-05 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[48;5;59m keyboard-flow-06 \u001b[49m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m 6/7\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-06 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[48;5;59m keyboard-flow-07 \u001b[49m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m 7/7\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-07 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;145m────────────────────────────────────\u001b[39m\u001b[38;5;111m › send \u001b[39m\u001b[38;5;145m────────────────────────────────────\u001b[39m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;111m›\u001b[39m\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;145m──────────────────────────\u001b[39m\u001b[38;5;111m alt+enter newline · paste \u001b[39m\u001b[38;5;145m───────────────────────────\u001b[39m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;115mcompleted\u001b[39m \u001b[38;5;195mCtrl+E next detail (7/7)\u001b[39m\u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[4A\u001b[2G\u001b[?25l"] -[1.513009,"i","keyboard-flow-08"] -[1.51309,"i","\r"] -[1.515591,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mkeyboard-flow-08\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[18G\u001b[?25l"] -[1.518676,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] -[1.518717,"o","\u001b[2G\u001b[?25l"] -[1.543967,"o","\u001b[?2026h\u001b[18A\r\u001b[2K\u001b[38;5;195mAgentProfile Braid starter\u001b[39m · \u001b[38;5;195mpi / deterministic\u001b[39m \u001b[38;5;195mnot connected\u001b[39m · \u001b[38;5;195mthinking none\u001b[39m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-04 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[48;5;59m keyboard-flow-05 \u001b[49m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m 5/7\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-05 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[48;5;59m keyboard-flow-06 \u001b[49m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m 6/7\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-06 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[48;5;59m keyboard-flow-07 \u001b[49m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m 7/7\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-07 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[48;5;59m keyboard-flow-08 \u001b[49m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;222mworking…\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;145m───────────────────────────────\u001b[39m\u001b[38;5;111m › queue next #1 \u001b[39m\u001b[38;5;145m────────────────────────────────\u001b[39m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;111m›\u001b[39m\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;145m──────────────────────────\u001b[39m\u001b[38;5;111m alt+enter newline · paste \u001b[39m\u001b[38;5;145m───────────────────────────\u001b[39m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;222mstreaming · Ctrl+C cancel\u001b[39m \u001b[38;5;195mEnter queues input\u001b[39m\u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[4A\u001b[2G\u001b[?25l"] -[1.608524,"o","\u001b[?2026h\u001b[18A\r\u001b[2K\u001b[38;5;195mAgentProfile Braid starter\u001b[39m · \u001b[38;5;195mpi / deterministic\u001b[39m \u001b[38;5;195mdeterministic fixture\u001b[39m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[48;5;59m keyboard-flow-05 \u001b[49m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m 5/8\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-05 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[48;5;59m keyboard-flow-06 \u001b[49m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m 6/8\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-06 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[48;5;59m keyboard-flow-07 \u001b[49m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m 7/8\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-07 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[48;5;59m keyboard-flow-08 \u001b[49m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m 8/8\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-08 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;145m────────────────────────────────────\u001b[39m\u001b[38;5;111m › send \u001b[39m\u001b[38;5;145m────────────────────────────────────\u001b[39m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;111m›\u001b[39m\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;145m──────────────────────────\u001b[39m\u001b[38;5;111m alt+enter newline · paste \u001b[39m\u001b[38;5;145m───────────────────────────\u001b[39m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;115mcompleted\u001b[39m \u001b[38;5;195mCtrl+E next detail (8/8)\u001b[39m\u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[4A\u001b[2G\u001b[?25l"] -[1.693493,"i","\u001b[5~"] -[1.693582,"i","\u001b[1;3H"] -[1.696795,"o","\u001b[?2026h\u001b[17A\r\u001b[2K \u001b[38;5;145m· run result\u001b[39m 1/8\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-01 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[48;5;59m keyboard-flow-02 \u001b[49m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m 2/8\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-02 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[48;5;59m keyboard-flow-03 \u001b[49m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m 3/8\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-03 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[48;5;59m keyboard-flow-04 \u001b[49m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m 4/8\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-04 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[48;5;59m keyboard-flow-05 \u001b[49m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;145m────────────────────────────────────\u001b[39m\u001b[38;5;111m › send \u001b[39m\u001b[38;5;145m────────────────────────────────────\u001b[39m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;111m›\u001b[39m\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;145m──────────────────────────\u001b[39m\u001b[38;5;111m alt+enter newline · paste \u001b[39m\u001b[38;5;145m───────────────────────────\u001b[39m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;115mcompleted\u001b[39m \u001b[38;5;195mPgUp/PgDn history · End follow\u001b[39m\u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[4A\u001b[2G"] -[1.696853,"o","\u001b[?25l"] -[1.774833,"i","\u001b[6~"] -[1.774914,"i","\u001b[1;3F"] -[1.777472,"o","\u001b[?2026h\u001b[17A\r\u001b[2K\u001b[48;5;59m keyboard-flow-05 \u001b[49m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m 5/8\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-05 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[48;5;59m keyboard-flow-06 \u001b[49m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m 6/8\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-06 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[48;5;59m keyboard-flow-07 \u001b[49m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m 7/8\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-07 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[48;5;59m keyboard-flow-08 \u001b[49m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m 8/8\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-08 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;145m────────────────────────────────────\u001b[39m\u001b[38;5;111m › send \u001b[39m\u001b[38;5;145m────────────────────────────────────\u001b[39m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;111m›\u001b[39m\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;145m──────────────────────────\u001b[39m\u001b[38;5;111m alt+enter newline · paste \u001b[39m\u001b[38;5;145m───────────────────────────\u001b[39m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;115mcompleted\u001b[39m \u001b[38;5;195mCtrl+E next detail (8/8)\u001b[39m\u001b[0m\u001b]8;;\u0007\u001b[?2026l"] -[1.777511,"o","\u001b[4A\u001b[2G\u001b[?25l"] -[1.787511,"o","\u001b[0m"] +{"version":2,"width":80,"height":24,"timestamp":1786538293,"duration":2.028706,"idle_time_limit":1,"command":"packed braid --fixture deterministic","title":"Braid transcript keyboard navigation","env":{"TERM":"xterm-256color"},"stdin":true} +[0.455262,"o","\u001b[?2004h"] +[0.455437,"o","\u001b[>7u\u001b[?u\u001b[c"] +[0.45545,"o","\u001b[?1049h"] +[0.455461,"o","\u001b[2J\u001b[H"] +[0.455476,"o","\u001b[?25l"] +[0.466018,"o","\u001b[?2026h\u001b[2J\u001b[H\u001b[3J \u001b[0m\u001b]8;;\u0007\r\r\n Ask Braid starter anything. \u001b[0m\u001b]8;;\u0007\r\r\n \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[38;5;111m›\u001b[39m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[38;5;195mprofile Braid starter\u001b[39m · \u001b[38;5;195mpi / deterministic\u001b[39m · \u001b[38;5;195mno connection\u001b[39m \u001b[38;5;115mready\u001b[39m\u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[0.46606,"o","\u001b[3A\u001b[2G\u001b[?25l"] +[0.505436,"o","\u001b]0;Braid — /home/drew/code/braid-transcript-first-wt\u0007"] +[0.508782,"o","\u001b[?2026h\u001b[19A\r\u001b[2K \u001b[38;5;145mAsk Braid starter anything.\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n"] +[0.508801,"o","\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;111m›\u001b[39m\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;195mprofile Braid starter\u001b[39m · \u001b[38;5;195mpi / deterministic\u001b[39m \u001b[38;5;195mCtrl+P commands\u001b[39m\u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[0.508807,"o","\u001b[3A\u001b[2G"] +[0.508812,"o","\u001b[?25l"] +[0.523264,"i","keyboard-flow-01"] +[0.523342,"i","\r"] +[0.538154,"o","\u001b[2G\u001b[?25l"] +[0.566513,"o","\u001b[?2026h\u001b[20A\r\u001b[2K \u001b[38;5;111m› \u001b[39mkeyboard-flow-01 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;222mworking…\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;222m› queue #1\u001b[39m \u001b[7m \u001b[0m \u001b[0m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;195mprofile Braid starter\u001b[39m \u001b[38;5;222mworking · Ctrl+C cancel\u001b[39m · \u001b[38;5;195mEnter queues\u001b[39m\u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[3A\u001b[12G\u001b[?25l"] +[0.634528,"o","\u001b[?2026h\u001b[18A\r\u001b[2K \u001b[38;5;145m· run result\u001b[39m\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-01 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;111m›\u001b[39m\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;195mprofile Braid starter\u001b[39m · \u001b[38;5;195mpi / deterministic\u001b[39m \u001b[38;5;195mCtrl+E detail\u001b[39m\u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[3A\u001b[2G\u001b[?25l"] +[0.724794,"i","keyboard-flow-02"] +[0.724876,"i","\r"] +[0.729119,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mkeyboard-flow-02\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[18G\u001b[?25l"] +[0.730757,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[0.730777,"o","\u001b[2G\u001b[?25l"] +[0.746311,"o","\u001b[?2026h\u001b[16A\r\u001b[2K \u001b[38;5;111m› \u001b[39mkeyboard-flow-02 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;222mworking…\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;222m› queue #1\u001b[39m \u001b[7m \u001b[0m \u001b[0m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;195mprofile Braid starter\u001b[39m \u001b[38;5;222mworking · Ctrl+C cancel\u001b[39m · \u001b[38;5;195mEnter queues\u001b[39m\u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[3A\u001b[12G"] +[0.746365,"o","\u001b[?25l"] +[0.804871,"o","\u001b[?2026h\u001b[18A\r\u001b[2K \u001b[38;5;145m· run result\u001b[39m 1/2\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-01 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;111m› \u001b[39mkeyboard-flow-02 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m 2/2\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-02 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;111m›\u001b[39m\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;195mprofile Braid starter\u001b[39m · \u001b[38;5;195mpi / deterministic\u001b[39m \u001b[38;5;195mCtrl+E next detail (2/2)\u001b[39m\u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[3A\u001b[2G"] +[0.80497,"o","\u001b[?25l"] +[0.885845,"i","keyboard-flow-03"] +[0.88592,"i","\r"] +[0.887814,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mkeyboard-flow-03\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[0.887852,"o","\u001b[18G\u001b[?25l"] +[0.889393,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[0.889414,"o","\u001b[2G\u001b[?25l"] +[0.906086,"o","\u001b[?2026h\u001b[12A\r\u001b[2K \u001b[38;5;111m› \u001b[39mkeyboard-flow-03 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;222mworking…\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;222m› queue #1\u001b[39m \u001b[7m \u001b[0m \u001b[0m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;195mprofile Braid starter\u001b[39m \u001b[38;5;222mworking · Ctrl+C cancel\u001b[39m · \u001b[38;5;195mEnter queues\u001b[39m\u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[0.906118,"o","\u001b[3A\u001b[12G\u001b[?25l"] +[0.963025,"o","\u001b[?2026h\u001b[18A\r\u001b[2K \u001b[38;5;145m· run result\u001b[39m 1/3\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-01 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;111m› \u001b[39mkeyboard-flow-02 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m 2/3\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-02 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;111m› \u001b[39mkeyboard-flow-03 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m 3/3\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-03 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;111m›\u001b[39m\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;195mprofile Braid starter\u001b[39m · \u001b[38;5;195mpi / deterministic\u001b[39m \u001b[38;5;195mCtrl+E next detail (3/3)\u001b[39m\u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[3A\u001b[2G\u001b[?25l"] +[1.045937,"i","keyboard-flow-04"] +[1.046011,"i","\r"] +[1.047766,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mkeyboard-flow-04\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[18G\u001b[?25l"] +[1.050081,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[2G"] +[1.050115,"o","\u001b[?25l"] +[1.067575,"o","\u001b[?2026h\u001b[8A\r\u001b[2K \u001b[38;5;111m› \u001b[39mkeyboard-flow-04 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;222mworking…\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;222m› queue #1\u001b[39m \u001b[7m \u001b[0m \u001b[0m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;195mprofile Braid starter\u001b[39m \u001b[38;5;222mworking · Ctrl+C cancel\u001b[39m · \u001b[38;5;195mEnter queues\u001b[39m\u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[3A\u001b[12G\u001b[?25l"] +[1.132689,"o","\u001b[?2026h\u001b[18A\r\u001b[2K \u001b[38;5;145m· run result\u001b[39m 1/4\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-01 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;111m› \u001b[39mkeyboard-flow-02 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m 2/4\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-02 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;111m› \u001b[39mkeyboard-flow-03 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m 3/4\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-03 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;111m› \u001b[39mkeyboard-flow-04 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m 4/4\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-04 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;111m›\u001b[39m\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;195mprofile Braid starter\u001b[39m · \u001b[38;5;195mpi / deterministic\u001b[39m \u001b[38;5;195mCtrl+E next detail (4/4)\u001b[39m\u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[3A\u001b[2G\u001b[?25l"] +[1.227601,"i","keyboard-flow-05"] +[1.227671,"i","\r"] +[1.229798,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mkeyboard-flow-05\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[18G\u001b[?25l"] +[1.231758,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[1.231789,"o","\u001b[2G\u001b[?25l"] +[1.249551,"o","\u001b[?2026h\u001b[4A\r\u001b[2K \u001b[38;5;111m› \u001b[39mkeyboard-flow-05 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;222mworking…\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;222m› queue #1\u001b[39m \u001b[7m \u001b[0m \u001b[0m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;195mprofile Braid starter\u001b[39m \u001b[38;5;222mworking · Ctrl+C cancel\u001b[39m · \u001b[38;5;195mEnter queues\u001b[39m\u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[3A\u001b[12G\u001b[?25l"] +[1.313758,"o","\u001b[?2026h\u001b[18A\r\u001b[2K \u001b[38;5;145m· run result\u001b[39m 1/5\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-01 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;111m› \u001b[39mkeyboard-flow-02 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m 2/5\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-02 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;111m› \u001b[39mkeyboard-flow-03 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m 3/5\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-03 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;111m› \u001b[39mkeyboard-flow-04 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m 4/5\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-04 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;111m› \u001b[39mkeyboard-flow-05 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m 5/5\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-05 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;111m›\u001b[39m\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;195mprofile Braid starter\u001b[39m · \u001b[38;5;195mpi / deterministic\u001b[39m \u001b[38;5;195mCtrl+E next detail (5/5)\u001b[39m\u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[1.313801,"o","\u001b[3A\u001b[2G\u001b[?25l"] +[1.389686,"i","keyboard-flow-06"] +[1.389776,"i","\r"] +[1.39166,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mkeyboard-flow-06\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[18G\u001b[?25l"] +[1.393519,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[2G"] +[1.393542,"o","\u001b[?25l"] +[1.412422,"o","\u001b[?2026h\u001b[20A\r\u001b[2K Fixture response through pi: keyboard-flow-01 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;111m› \u001b[39mkeyboard-flow-02 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m 2/5\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-02 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;111m› \u001b[39mkeyboard-flow-03 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m 3/5\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-03 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;111m› \u001b[39mkeyboard-flow-04 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m 4/5\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-04 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;111m› \u001b[39mkeyboard-flow-05 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m 5/5\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-05 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;111m› \u001b[39mkeyboard-flow-06 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;222mworking…\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;222m› queue #1\u001b[39m \u001b[7m \u001b[0m \u001b[0m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;195mprofile Braid starter\u001b[39m \u001b[38;5;222mworking · Ctrl+C cancel\u001b[39m · \u001b[38;5;195mEnter queues\u001b[39m\u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[3A\u001b[12G"] +[1.412462,"o","\u001b[?25l"] +[1.483081,"o","\u001b[?2026h\u001b[20A\r\u001b[2K \u001b[38;5;111m› \u001b[39mkeyboard-flow-02 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m 2/6\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-02 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;111m› \u001b[39mkeyboard-flow-03 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m 3/6\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-03 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;111m› \u001b[39mkeyboard-flow-04 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m 4/6\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-04 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;111m› \u001b[39mkeyboard-flow-05 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m 5/6\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-05 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;111m› \u001b[39mkeyboard-flow-06 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m 6/6\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-06 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;111m›\u001b[39m\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;195mprofile Braid starter\u001b[39m · \u001b[38;5;195mpi / deterministic\u001b[39m \u001b[38;5;195mCtrl+E next detail (6/6)\u001b[39m\u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[3A\u001b[2G\u001b[?25l"] +[1.57018,"i","keyboard-flow-07"] +[1.570255,"i","\r"] +[1.574224,"o","\u001b[2G\u001b[?25l"] +[1.592731,"o","\u001b[?2026h\u001b[20A\r\u001b[2K Fixture response through pi: keyboard-flow-02 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;111m› \u001b[39mkeyboard-flow-03 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m 3/6\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-03 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;111m› \u001b[39mkeyboard-flow-04 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m 4/6\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-04 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;111m› \u001b[39mkeyboard-flow-05 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m 5/6\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-05 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;111m› \u001b[39mkeyboard-flow-06 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m 6/6\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-06 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;111m› \u001b[39mkeyboard-flow-07 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;222mworking…\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;222m› queue #1\u001b[39m \u001b[7m \u001b[0m \u001b[0m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;195mprofile Braid starter\u001b[39m \u001b[38;5;222mworking · Ctrl+C cancel\u001b[39m · \u001b[38;5;195mEnter queues\u001b[39m\u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[1.592771,"o","\u001b[3A\u001b[12G\u001b[?25l"] +[1.658569,"o","\u001b[?2026h\u001b[20A\r\u001b[2K \u001b[38;5;111m› \u001b[39mkeyboard-flow-03 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m 3/7\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-03 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;111m› \u001b[39mkeyboard-flow-04 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m 4/7\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-04 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;111m› \u001b[39mkeyboard-flow-05 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m 5/7\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-05 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;111m› \u001b[39mkeyboard-flow-06 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m 6/7\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-06 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;111m› \u001b[39mkeyboard-flow-07 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m 7/7\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-07 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;111m›\u001b[39m\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;195mprofile Braid starter\u001b[39m · \u001b[38;5;195mpi / deterministic\u001b[39m \u001b[38;5;195mCtrl+E next detail (7/7)\u001b[39m\u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[1.658659,"o","\u001b[3A\u001b[2G\u001b[?25l"] +[1.752568,"i","keyboard-flow-08"] +[1.752641,"i","\r"] +[1.755149,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39mkeyboard-flow-08\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[18G"] +[1.755192,"o","\u001b[?25l"] +[1.75801,"o","\u001b[?2026h\r\u001b[2K\u001b[38;5;111m›\u001b[39m\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[1.758024,"o","\u001b[2G\u001b[?25l"] +[1.778488,"o","\u001b[?2026h\u001b[20A\r\u001b[2K Fixture response through pi: keyboard-flow-03 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;111m› \u001b[39mkeyboard-flow-04 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m 4/7\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-04 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;111m› \u001b[39mkeyboard-flow-05 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m 5/7\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-05 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;111m› \u001b[39mkeyboard-flow-06 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m 6/7\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-06 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;111m› \u001b[39mkeyboard-flow-07 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m 7/7\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-07 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;111m› \u001b[39mkeyboard-flow-08 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;222mworking…\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;222m› queue #1\u001b[39m \u001b[7m \u001b[0m \u001b[0m\u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;195mprofile Braid starter\u001b[39m \u001b[38;5;222mworking · Ctrl+C cancel\u001b[39m · \u001b[38;5;195mEnter queues\u001b[39m\u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[3A\u001b[12G"] +[1.778526,"o","\u001b[?25l"] +[1.845392,"o","\u001b[?2026h\u001b[20A\r\u001b[2K \u001b[38;5;111m› \u001b[39mkeyboard-flow-04 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m 4/8\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-04 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;111m› \u001b[39mkeyboard-flow-05 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m 5/8\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-05 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;111m› \u001b[39mkeyboard-flow-06 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m 6/8\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-06 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;111m› \u001b[39mkeyboard-flow-07 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m 7/8\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-07 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;111m› \u001b[39mkeyboard-flow-08 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m 8/8\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-08 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;111m›\u001b[39m\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;195mprofile Braid starter\u001b[39m · \u001b[38;5;195mpi / deterministic\u001b[39m \u001b[38;5;195mCtrl+E next detail (8/8)\u001b[39m\u001b[0m\u001b]8;;\u0007\u001b[?2026l\u001b[3A\u001b[2G\u001b[?25l"] +[1.934374,"i","\u001b[5~"] +[1.934451,"i","\u001b[1;3H"] +[1.937925,"o","\u001b[?2026h\u001b[20A\r\u001b[2K \u001b[38;5;111m› \u001b[39mkeyboard-flow-01 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m 1/8\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-01 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;111m› \u001b[39mkeyboard-flow-02 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m 2/8\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-02 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;111m› \u001b[39mkeyboard-flow-03 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m 3/8\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-03 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;111m› \u001b[39mkeyboard-flow-04 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m 4/8\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-04 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;111m› \u001b[39mkeyboard-flow-05 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m 5/8\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-05 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;111m›\u001b[39m\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;195mprofile Braid starter\u001b[39m · \u001b[38;5;195mpi / deterministic\u001b[39m \u001b[38;5;195mPgUp/PgDn history · End follow\u001b[39m\u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[1.937964,"o","\u001b[3A\u001b[2G\u001b[?25l"] +[2.015744,"i","\u001b[6~"] +[2.015826,"i","\u001b[1;3F"] +[2.018668,"o","\u001b[?2026h\u001b[20A\r\u001b[2K \u001b[38;5;111m› \u001b[39mkeyboard-flow-04 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m 4/8\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-04 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;111m› \u001b[39mkeyboard-flow-05 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m 5/8\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-05 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;111m› \u001b[39mkeyboard-flow-06 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m 6/8\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-06 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;111m› \u001b[39mkeyboard-flow-07 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m 7/8\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-07 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;111m› \u001b[39mkeyboard-flow-08 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[38;5;145m· run result\u001b[39m 8/8\u001b[38;5;145m · Ctrl+E details\u001b[39m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K Fixture response through pi: keyboard-flow-08 \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;111m›\u001b[39m\u001b[7m \u001b[0m \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K \u001b[0m\u001b]8;;\u0007\r\r\n\u001b[2K\u001b[38;5;195mprofile Braid starter\u001b[39m · \u001b[38;5;195mpi / deterministic\u001b[39m \u001b[38;5;195mCtrl+E next detail (8/8)\u001b[39m\u001b[0m\u001b]8;;\u0007\u001b[?2026l"] +[2.018706,"o","\u001b[3A\u001b[2G\u001b[?25l"] +[2.028706,"o","\u001b[0m"] diff --git a/artifacts/verification/w6/states/active-streaming.json b/artifacts/verification/w6/states/active-streaming.json index e1fc37f..b5d8deb 100644 --- a/artifacts/verification/w6/states/active-streaming.json +++ b/artifacts/verification/w6/states/active-streaming.json @@ -4,8 +4,8 @@ "source": { "binary": "packed real binary from clean npm install", "binarySha256": "141288e0fe917635d723b4b70d464dc49baff14cc356f54de1d3f8faa5d8254f", - "tarball": "tangle-network-braid-0.1.0.tgz", - "tarballSha256": "32f19ba707aa659f776a604fe241111fe2bfb68928ac6f005014d81bd924182c" + "tarball": "tangle-network-braid-0.1.1.tgz", + "tarballSha256": "9bbd7b5b9f78b3d202dbb09d4375970edfd4965ffc5ad94b04bd7fa6ce01c4c7" }, "dimensions": { "columns": 80, @@ -30,7 +30,7 @@ }, "capturePhase": "atomic-signal-frame", "captureRevision": 5, - "frame": "braid cwd braid-release-ux-final-wt · session New conversation\nAgentProfile Braid starter · pi / deterministic not connected · thinking none\n W6 active streaming\n\n working…\n\n\n\n\n\n\n\n\n\n\n\n\n\n─────────────────────────────── › queue next #1 ────────────────────────────────\n›\n\n\n────────────────────────── alt+enter newline · paste ───────────────────────────\nstreaming · Ctrl+C cancel Enter queues input\n", + "frame": " › W6 active streaming\n\n working…\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n› queue #1\n\n\nprofile Braid starter working · Ctrl+C cancel · Enter queues\n", "packedState": { "schemaVersion": 2, "capturePhase": "atomic-signal-frame", @@ -38,7 +38,7 @@ "schemaVersion": 2, "revision": 5, "sequence": 5, - "workspace": "/home/drew/code/braid-release-ux-final-wt", + "workspace": "/home/drew/code/braid-transcript-first-wt", "conversationId": "conv-1", "branchId": "branch-1", "conversations": [ @@ -95,7 +95,6 @@ "outputTokens": 0, "tokenStatus": "unknown", "costStatus": "unknown", - "llmCalls": 0, "model": "fixture/deterministic", "provider": "agent-runtime", "runner": "pi", @@ -110,12 +109,11 @@ "output": 0, "tokenStatus": "unknown", "costStatus": "unknown", - "llmCalls": 0, "unknownTokenSources": 1, "unknownCostSources": 1, - "callStatus": "complete", + "callStatus": "unknown", "latencyStatus": "unknown", - "unknownCallSources": 0, + "unknownCallSources": 1, "unknownLatencySources": 1 }, "analyses": { @@ -154,7 +152,7 @@ }, "view": { "revision": 5, - "workspace": "/home/drew/code/braid-release-ux-final-wt", + "workspace": "/home/drew/code/braid-transcript-first-wt", "profileName": "Braid starter", "profileDigest": "44ed91c937fd4079fe01c49f6bc76413656886e5aa8b2351590d3a888cfa0a14", "runner": "pi", @@ -218,7 +216,6 @@ "output": 0, "tokenStatus": "unknown", "costStatus": "unknown", - "llmCalls": 0, "model": "fixture/deterministic" }, "runner": "pi", @@ -233,12 +230,11 @@ "output": 0, "tokenStatus": "unknown", "costStatus": "unknown", - "llmCalls": 0, "unknownTokenSources": 1, "unknownCostSources": 1, - "callStatus": "complete", + "callStatus": "unknown", "latencyStatus": "unknown", - "unknownCallSources": 0, + "unknownCallSources": 1, "unknownLatencySources": 1 }, "analyses": { @@ -301,7 +297,7 @@ "title": "event backend_start", "status": "running", "detail": "sequence 1", - "occurredAt": "2026-08-11T05:08:10.659Z", + "occurredAt": "2026-08-12T12:37:37.459Z", "sourceEventId": "run-000001:runtime:1", "runId": "run-000001", "entityType": "run", @@ -510,12 +506,12 @@ "run.reconnect": { "available": false, "source": "provider", - "reason": "The current runtime does not report replay support" + "reason": "Detach the active run before reconnecting it" }, "run.reconcile": { "available": false, "source": "provider", - "reason": "The current runtime does not report status reconciliation" + "reason": "Detach the active run before reconciling it" } }, "draft": "", @@ -534,7 +530,7 @@ "payload": { "value": { "kind": "workspace.opened", - "workspace": "/home/drew/code/braid-release-ux-final-wt" + "workspace": "/home/drew/code/braid-transcript-first-wt" } } }, @@ -644,10 +640,10 @@ "value": { "kind": "effect.upserted", "effect": { - "id": "effect-op-terminal-000002-5de0e82b220f27cb6f90a090", + "id": "effect-op-terminal-000002-0a838249b94207b11007ccae", "operationId": "op-terminal-000002", "effectKind": "run.execute", - "requestDigest": "5de0e82b220f27cb6f90a090fa6f46e8a81a0c7d101654cfed0273b31bd2510c", + "requestDigest": "0a838249b94207b11007ccae8a84f3baa7e3b080a7f8fd0a909dbab19fa7e6a1", "kind": "send", "status": "pending", "attempt": 1, @@ -665,13 +661,13 @@ "source": { "eventId": "run-000001:runtime:1", "providerSequence": 1, - "occurredAt": "2026-08-11T05:08:10.659Z", + "occurredAt": "2026-08-12T12:37:37.459Z", "receivedAt": "2026-08-01T00:00:00.000Z" }, "unknown": { "type": "backend_start", "task": { - "id": "turn-f47c74fa-ae4c-47d3-a2b3-8503aff616f1", + "id": "turn-db68a0cb-0c7c-4589-8347-fb36879b38e3", "intent": "W6 active streaming", "metadata": { "identity": { @@ -681,11 +677,11 @@ } }, "session": { - "id": "c6ca70af-e24b-4ad7-b1e4-c8a21cff9efd", + "id": "590229a9-1f09-4c04-ba48-6a314df1a398", "backend": "executor", "status": "active", - "createdAt": "2026-08-11T05:08:10.659Z", - "updatedAt": "2026-08-11T05:08:10.659Z", + "createdAt": "2026-08-12T12:37:37.459Z", + "updatedAt": "2026-08-12T12:37:37.459Z", "metadata": "[unavailable]" }, "backend": "router", @@ -707,16 +703,16 @@ }, "execution": { "kind": "request", - "id": "turn-f47c74fa-ae4c-47d3-a2b3-8503aff616f1" + "id": "turn-db68a0cb-0c7c-4589-8347-fb36879b38e3" }, "materializer": "router-prompt-model" }, "executionBindings": [ { "status": "known", - "attemptId": "turn-f47c74fa-ae4c-47d3-a2b3-8503aff616f1:attempt:23a3c309-2515-4c12-be07-83ea488bd05d", - "materializationReceiptDigest": "sha256:964f6efa1b1f7c64895d43ba7752c4811d9c445c18d706e9580dad8e6452fe19", - "bindingDigest": "sha256:4a47ef0529bd415c75c8a3af276f9c6e8655f211c5a9e91c10111803e0991784", + "attemptId": "turn-db68a0cb-0c7c-4589-8347-fb36879b38e3:attempt:5654b7d0-b218-4427-95c6-361d6dd76cc8", + "materializationReceiptDigest": "sha256:dad1132b1c7dd7c6fe79575ac4fc5da786d05d0b35d84d97698e1d43c21ba925", + "bindingDigest": "sha256:3ec6106218348b41e13fc244f20dbeae88da12fec1825bfb19090e1a343467c1", "descriptor": { "kind": "router-request", "transport": "http", @@ -728,10 +724,10 @@ "timeoutMs": null }, "timing": { - "startedAt": "2026-08-11T05:08:10.657Z" + "startedAt": "2026-08-12T12:37:37.455Z" } }, - "timestamp": "2026-08-11T05:08:10.659Z" + "timestamp": "2026-08-12T12:37:37.459Z" } } } diff --git a/artifacts/verification/w6/states/active-streaming.png b/artifacts/verification/w6/states/active-streaming.png index 265a6e4..e6f36a7 100644 Binary files a/artifacts/verification/w6/states/active-streaming.png and b/artifacts/verification/w6/states/active-streaming.png differ diff --git a/artifacts/verification/w6/states/active-streaming.txt b/artifacts/verification/w6/states/active-streaming.txt index e2097d0..5aab693 100644 --- a/artifacts/verification/w6/states/active-streaming.txt +++ b/artifacts/verification/w6/states/active-streaming.txt @@ -1,6 +1,4 @@ -braid cwd braid-release-ux-final-wt · session New conversation -AgentProfile Braid starter · pi / deterministic not connected · thinking none - W6 active streaming + › W6 active streaming working… @@ -16,9 +14,11 @@ AgentProfile Braid starter · pi / deterministic not connected · thinking no -─────────────────────────────── › queue next #1 ──────────────────────────────── -› -────────────────────────── alt+enter newline · paste ─────────────────────────── -streaming · Ctrl+C cancel Enter queues input + + +› queue #1 + + +profile Braid starter working · Ctrl+C cancel · Enter queues diff --git a/artifacts/verification/w6/states/analysis.json b/artifacts/verification/w6/states/analysis.json index bc9ffe8..0b99f85 100644 --- a/artifacts/verification/w6/states/analysis.json +++ b/artifacts/verification/w6/states/analysis.json @@ -4,8 +4,8 @@ "source": { "binary": "packed real binary from clean npm install", "binarySha256": "141288e0fe917635d723b4b70d464dc49baff14cc356f54de1d3f8faa5d8254f", - "tarball": "tangle-network-braid-0.1.0.tgz", - "tarballSha256": "32f19ba707aa659f776a604fe241111fe2bfb68928ac6f005014d81bd924182c" + "tarball": "tangle-network-braid-0.1.1.tgz", + "tarballSha256": "9bbd7b5b9f78b3d202dbb09d4375970edfd4965ffc5ad94b04bd7fa6ce01c4c7" }, "dimensions": { "columns": 80, @@ -38,7 +38,7 @@ "schemaVersion": 2, "revision": 1, "sequence": 1, - "workspace": "/home/drew/code/braid-release-ux-final-wt", + "workspace": "/home/drew/code/braid-transcript-first-wt", "conversationId": "conv-1", "branchId": "branch-1", "conversations": [ @@ -113,7 +113,7 @@ }, "view": { "revision": 1, - "workspace": "/home/drew/code/braid-release-ux-final-wt", + "workspace": "/home/drew/code/braid-transcript-first-wt", "profileName": "Braid starter", "profileDigest": "44ed91c937fd4079fe01c49f6bc76413656886e5aa8b2351590d3a888cfa0a14", "runner": "pi", @@ -244,6 +244,8 @@ "analysis time: 1240ms" ], "analysisFindingCount": 2, + "analysisSupportedFindingCount": 2, + "analysisCitationSupport": "unavailable", "analysisExecution": { "observedModels": [], "wallTimeMs": 1240 @@ -377,12 +379,12 @@ "run.reconnect": { "available": false, "source": "provider", - "reason": "There is no active run" + "reason": "There is no detached run to reconnect" }, "run.reconcile": { "available": false, "source": "provider", - "reason": "There is no active run" + "reason": "There is no detached run to reconcile" } }, "draft": "", @@ -402,7 +404,7 @@ "payload": { "value": { "kind": "workspace.opened", - "workspace": "/home/drew/code/braid-release-ux-final-wt" + "workspace": "/home/drew/code/braid-transcript-first-wt" } } } diff --git a/artifacts/verification/w6/states/analysis.png b/artifacts/verification/w6/states/analysis.png index 80d5c57..a8955e1 100644 Binary files a/artifacts/verification/w6/states/analysis.png and b/artifacts/verification/w6/states/analysis.png differ diff --git a/artifacts/verification/w6/states/automation.json b/artifacts/verification/w6/states/automation.json index 2be4b41..e691d9d 100644 --- a/artifacts/verification/w6/states/automation.json +++ b/artifacts/verification/w6/states/automation.json @@ -4,8 +4,8 @@ "source": { "binary": "packed real binary from clean npm install", "binarySha256": "141288e0fe917635d723b4b70d464dc49baff14cc356f54de1d3f8faa5d8254f", - "tarball": "tangle-network-braid-0.1.0.tgz", - "tarballSha256": "32f19ba707aa659f776a604fe241111fe2bfb68928ac6f005014d81bd924182c" + "tarball": "tangle-network-braid-0.1.1.tgz", + "tarballSha256": "9bbd7b5b9f78b3d202dbb09d4375970edfd4965ffc5ad94b04bd7fa6ce01c4c7" }, "dimensions": { "columns": 80, @@ -30,7 +30,7 @@ }, "capturePhase": "atomic-signal-frame", "captureRevision": 1, - "frame": "braid cwd braid-release-ux-final-wt · session New conversation\nAgentProfile Braid starter · pi / deterministic deterministic fixture\n\n Send a task to Braid starter, or press Ctrl+P for commands.\n\n\n\n\n\n automation rules\n ──────────────────────────────────────────────────────────────────────\n No saved automation rules\n ──────────────────────────────────────────────────────────────────────\n create from a pending request with Alt+A · esc close\n\n\n\n\n\n\n\n\n\nNo automation rules Ctrl+P commands\n", + "frame": "\n Ask Braid starter anything.\n\n\n\n\n\n\n\n automation rules\n ──────────────────────────────────────────────────────────────────────\n No saved automation rules\n ──────────────────────────────────────────────────────────────────────\n create from a pending request with Alt+A · esc close\n\n\n\n\n\n\n\n\n\nNo automation rules\n", "packedState": { "schemaVersion": 2, "capturePhase": "atomic-signal-frame", @@ -38,7 +38,7 @@ "schemaVersion": 2, "revision": 1, "sequence": 1, - "workspace": "/home/drew/code/braid-release-ux-final-wt", + "workspace": "/home/drew/code/braid-transcript-first-wt", "conversationId": "conv-1", "branchId": "branch-1", "conversations": [ @@ -113,7 +113,7 @@ }, "view": { "revision": 1, - "workspace": "/home/drew/code/braid-release-ux-final-wt", + "workspace": "/home/drew/code/braid-transcript-first-wt", "profileName": "Braid starter", "profileDigest": "44ed91c937fd4079fe01c49f6bc76413656886e5aa8b2351590d3a888cfa0a14", "runner": "pi", @@ -334,12 +334,12 @@ "run.reconnect": { "available": false, "source": "provider", - "reason": "There is no active run" + "reason": "There is no detached run to reconnect" }, "run.reconcile": { "available": false, "source": "provider", - "reason": "There is no active run" + "reason": "There is no detached run to reconcile" } }, "draft": "", @@ -359,7 +359,7 @@ "payload": { "value": { "kind": "workspace.opened", - "workspace": "/home/drew/code/braid-release-ux-final-wt" + "workspace": "/home/drew/code/braid-transcript-first-wt" } } } diff --git a/artifacts/verification/w6/states/automation.png b/artifacts/verification/w6/states/automation.png index b9f59e0..5e922ce 100644 Binary files a/artifacts/verification/w6/states/automation.png and b/artifacts/verification/w6/states/automation.png differ diff --git a/artifacts/verification/w6/states/automation.txt b/artifacts/verification/w6/states/automation.txt index ee9fd39..4bc0c2a 100644 --- a/artifacts/verification/w6/states/automation.txt +++ b/artifacts/verification/w6/states/automation.txt @@ -1,7 +1,7 @@ -braid cwd braid-release-ux-final-wt · session New conversation -AgentProfile Braid starter · pi / deterministic deterministic fixture - Send a task to Braid starter, or press Ctrl+P for commands. + Ask Braid starter anything. + + @@ -21,4 +21,4 @@ AgentProfile Braid starter · pi / deterministic deterministic fixtur -No automation rules Ctrl+P commands +No automation rules diff --git a/artifacts/verification/w6/states/comparison.json b/artifacts/verification/w6/states/comparison.json index 3f459f6..a50e2be 100644 --- a/artifacts/verification/w6/states/comparison.json +++ b/artifacts/verification/w6/states/comparison.json @@ -4,8 +4,8 @@ "source": { "binary": "packed real binary from clean npm install", "binarySha256": "141288e0fe917635d723b4b70d464dc49baff14cc356f54de1d3f8faa5d8254f", - "tarball": "tangle-network-braid-0.1.0.tgz", - "tarballSha256": "32f19ba707aa659f776a604fe241111fe2bfb68928ac6f005014d81bd924182c" + "tarball": "tangle-network-braid-0.1.1.tgz", + "tarballSha256": "9bbd7b5b9f78b3d202dbb09d4375970edfd4965ffc5ad94b04bd7fa6ce01c4c7" }, "dimensions": { "columns": 80, @@ -38,7 +38,7 @@ "schemaVersion": 2, "revision": 1, "sequence": 1, - "workspace": "/home/drew/code/braid-release-ux-final-wt", + "workspace": "/home/drew/code/braid-transcript-first-wt", "conversationId": "conv-1", "branchId": "branch-1", "conversations": [ @@ -113,7 +113,7 @@ }, "view": { "revision": 1, - "workspace": "/home/drew/code/braid-release-ux-final-wt", + "workspace": "/home/drew/code/braid-transcript-first-wt", "profileName": "Braid starter", "profileDigest": "44ed91c937fd4079fe01c49f6bc76413656886e5aa8b2351590d3a888cfa0a14", "runner": "pi", @@ -421,12 +421,12 @@ "run.reconnect": { "available": false, "source": "provider", - "reason": "There is no active run" + "reason": "There is no detached run to reconnect" }, "run.reconcile": { "available": false, "source": "provider", - "reason": "There is no active run" + "reason": "There is no detached run to reconcile" } }, "draft": "", @@ -446,7 +446,7 @@ "payload": { "value": { "kind": "workspace.opened", - "workspace": "/home/drew/code/braid-release-ux-final-wt" + "workspace": "/home/drew/code/braid-transcript-first-wt" } } } diff --git a/artifacts/verification/w6/states/comparison.png b/artifacts/verification/w6/states/comparison.png index a1fdf39..b31c3d2 100644 Binary files a/artifacts/verification/w6/states/comparison.png and b/artifacts/verification/w6/states/comparison.png differ diff --git a/artifacts/verification/w6/states/empty.json b/artifacts/verification/w6/states/empty.json index a8b8548..4663069 100644 --- a/artifacts/verification/w6/states/empty.json +++ b/artifacts/verification/w6/states/empty.json @@ -4,8 +4,8 @@ "source": { "binary": "packed real binary from clean npm install", "binarySha256": "141288e0fe917635d723b4b70d464dc49baff14cc356f54de1d3f8faa5d8254f", - "tarball": "tangle-network-braid-0.1.0.tgz", - "tarballSha256": "32f19ba707aa659f776a604fe241111fe2bfb68928ac6f005014d81bd924182c" + "tarball": "tangle-network-braid-0.1.1.tgz", + "tarballSha256": "9bbd7b5b9f78b3d202dbb09d4375970edfd4965ffc5ad94b04bd7fa6ce01c4c7" }, "dimensions": { "columns": 80, @@ -30,7 +30,7 @@ }, "capturePhase": "atomic-signal-frame", "captureRevision": 1, - "frame": "braid cwd braid-release-ux-final-wt · session New conversation\nAgentProfile Braid starter · pi / deterministic deterministic fixture\n\n Send a task to Braid starter, or press Ctrl+P for commands.\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n──────────────────────────────────── › send ────────────────────────────────────\n›\n\n\n────────────────────────── alt+enter newline · paste ───────────────────────────\nready for a message Ctrl+P commands\n", + "frame": "\n Ask Braid starter anything.\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n›\n\n\nprofile Braid starter · pi / deterministic Ctrl+P commands\n", "packedState": { "schemaVersion": 2, "capturePhase": "atomic-signal-frame", @@ -38,7 +38,7 @@ "schemaVersion": 2, "revision": 1, "sequence": 1, - "workspace": "/home/drew/code/braid-release-ux-final-wt", + "workspace": "/home/drew/code/braid-transcript-first-wt", "conversationId": "conv-1", "branchId": "branch-1", "conversations": [ @@ -113,7 +113,7 @@ }, "view": { "revision": 1, - "workspace": "/home/drew/code/braid-release-ux-final-wt", + "workspace": "/home/drew/code/braid-transcript-first-wt", "profileName": "Braid starter", "profileDigest": "44ed91c937fd4079fe01c49f6bc76413656886e5aa8b2351590d3a888cfa0a14", "runner": "pi", @@ -334,12 +334,12 @@ "run.reconnect": { "available": false, "source": "provider", - "reason": "There is no active run" + "reason": "There is no detached run to reconnect" }, "run.reconcile": { "available": false, "source": "provider", - "reason": "There is no active run" + "reason": "There is no detached run to reconcile" } }, "draft": "", @@ -358,7 +358,7 @@ "payload": { "value": { "kind": "workspace.opened", - "workspace": "/home/drew/code/braid-release-ux-final-wt" + "workspace": "/home/drew/code/braid-transcript-first-wt" } } } diff --git a/artifacts/verification/w6/states/empty.png b/artifacts/verification/w6/states/empty.png index 8678ecb..b93509e 100644 Binary files a/artifacts/verification/w6/states/empty.png and b/artifacts/verification/w6/states/empty.png differ diff --git a/artifacts/verification/w6/states/empty.txt b/artifacts/verification/w6/states/empty.txt index e95ad2d..fdd18e5 100644 --- a/artifacts/verification/w6/states/empty.txt +++ b/artifacts/verification/w6/states/empty.txt @@ -1,7 +1,9 @@ -braid cwd braid-release-ux-final-wt · session New conversation -AgentProfile Braid starter · pi / deterministic deterministic fixture - Send a task to Braid starter, or press Ctrl+P for commands. + Ask Braid starter anything. + + + + @@ -16,9 +18,7 @@ AgentProfile Braid starter · pi / deterministic deterministic fixtur -──────────────────────────────────── › send ──────────────────────────────────── › -────────────────────────── alt+enter newline · paste ─────────────────────────── -ready for a message Ctrl+P commands +profile Braid starter · pi / deterministic Ctrl+P commands diff --git a/artifacts/verification/w6/states/failure-or-reconnect.json b/artifacts/verification/w6/states/failure-or-reconnect.json index 4733a8a..a9be76c 100644 --- a/artifacts/verification/w6/states/failure-or-reconnect.json +++ b/artifacts/verification/w6/states/failure-or-reconnect.json @@ -4,8 +4,8 @@ "source": { "binary": "packed real binary from clean npm install", "binarySha256": "141288e0fe917635d723b4b70d464dc49baff14cc356f54de1d3f8faa5d8254f", - "tarball": "tangle-network-braid-0.1.0.tgz", - "tarballSha256": "32f19ba707aa659f776a604fe241111fe2bfb68928ac6f005014d81bd924182c" + "tarball": "tangle-network-braid-0.1.1.tgz", + "tarballSha256": "9bbd7b5b9f78b3d202dbb09d4375970edfd4965ffc5ad94b04bd7fa6ce01c4c7" }, "dimensions": { "columns": 80, @@ -30,7 +30,7 @@ }, "capturePhase": "atomic-signal-frame", "captureRevision": 8, - "frame": "braid cwd braid-release-ux-final-wt · session New conversation\nAgentProfile Braid starter · pi / deterministic deterministic fixture\n W6 failure state\n\n error · unknown\n RUNTIME_BACKEND_ERROR\n failed\n\n\n\n\n\n\n\n\n\n\n\n──────────────────────────────────── › send ────────────────────────────────────\n›\n\n\n────────────────────────── alt+enter newline · paste ───────────────────────────\noutcome failed · operation op-terminal-000002 /export preserve · /new continue\n", + "frame": " › W6 failure state\n\n error · unknown\n RUNTIME_BACKEND_ERROR\n failed\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n›\n\n\nprofile Braid starter · pi / deterministic failed · /activity details · /new\n", "packedState": { "schemaVersion": 2, "capturePhase": "atomic-signal-frame", @@ -38,7 +38,7 @@ "schemaVersion": 2, "revision": 8, "sequence": 8, - "workspace": "/home/drew/code/braid-release-ux-final-wt", + "workspace": "/home/drew/code/braid-transcript-first-wt", "conversationId": "conv-1", "branchId": "branch-1", "conversations": [ @@ -102,7 +102,6 @@ "outputTokens": 0, "tokenStatus": "unknown", "costStatus": "unknown", - "llmCalls": 0, "model": "fixture/deterministic", "provider": "agent-runtime", "runner": "pi", @@ -119,12 +118,11 @@ "output": 0, "tokenStatus": "unknown", "costStatus": "unknown", - "llmCalls": 0, "unknownTokenSources": 1, "unknownCostSources": 1, - "callStatus": "complete", + "callStatus": "unknown", "latencyStatus": "unknown", - "unknownCallSources": 0, + "unknownCallSources": 1, "unknownLatencySources": 1 }, "analyses": { @@ -163,7 +161,7 @@ }, "view": { "revision": 8, - "workspace": "/home/drew/code/braid-release-ux-final-wt", + "workspace": "/home/drew/code/braid-transcript-first-wt", "profileName": "Braid starter", "profileDigest": "44ed91c937fd4079fe01c49f6bc76413656886e5aa8b2351590d3a888cfa0a14", "runner": "pi", @@ -235,7 +233,6 @@ "output": 0, "tokenStatus": "unknown", "costStatus": "unknown", - "llmCalls": 0, "model": "fixture/deterministic" }, "runner": "pi", @@ -251,12 +248,11 @@ "output": 0, "tokenStatus": "unknown", "costStatus": "unknown", - "llmCalls": 0, "unknownTokenSources": 1, "unknownCostSources": 1, - "callStatus": "complete", + "callStatus": "unknown", "latencyStatus": "unknown", - "unknownCallSources": 0, + "unknownCallSources": 1, "unknownLatencySources": 1 }, "analyses": { @@ -319,7 +315,7 @@ "title": "event backend_start", "status": "failed", "detail": "sequence 1", - "occurredAt": "2026-08-11T05:08:18.981Z", + "occurredAt": "2026-08-12T12:37:54.196Z", "sourceEventId": "run-000001:runtime:1", "runId": "run-000001", "entityType": "run", @@ -331,7 +327,7 @@ "title": "error", "status": "failed", "detail": "RUNTIME_BACKEND_ERROR", - "occurredAt": "2026-08-11T05:08:18.986Z", + "occurredAt": "2026-08-12T12:37:54.216Z", "sourceEventId": "run-000001:runtime:2", "runId": "run-000001", "entityType": "run", @@ -533,12 +529,12 @@ "run.reconnect": { "available": false, "source": "provider", - "reason": "There is no active run" + "reason": "There is no detached run to reconnect" }, "run.reconcile": { "available": false, "source": "provider", - "reason": "There is no active run" + "reason": "There is no detached run to reconcile" } }, "draft": "", @@ -557,7 +553,7 @@ "payload": { "value": { "kind": "workspace.opened", - "workspace": "/home/drew/code/braid-release-ux-final-wt" + "workspace": "/home/drew/code/braid-transcript-first-wt" } } }, @@ -667,10 +663,10 @@ "value": { "kind": "effect.upserted", "effect": { - "id": "effect-op-terminal-000002-d1e2c03899b0c97c425569d7", + "id": "effect-op-terminal-000002-cf99cee4622510fbb428f652", "operationId": "op-terminal-000002", "effectKind": "run.execute", - "requestDigest": "d1e2c03899b0c97c425569d764d47bfed6374d758a3da01e422726781f6e6e82", + "requestDigest": "cf99cee4622510fbb428f6528d8bf27f2101fdaf5757203e738e46db132e8ca6", "kind": "send", "status": "pending", "attempt": 1, @@ -688,13 +684,13 @@ "source": { "eventId": "run-000001:runtime:1", "providerSequence": 1, - "occurredAt": "2026-08-11T05:08:18.981Z", + "occurredAt": "2026-08-12T12:37:54.196Z", "receivedAt": "2026-08-01T00:00:00.000Z" }, "unknown": { "type": "backend_start", "task": { - "id": "turn-efc4717c-fe6d-4399-9726-034e963e307a", + "id": "turn-1139ed2e-9b39-4c46-995c-10007663d712", "intent": "W6 failure state", "metadata": { "identity": { @@ -704,11 +700,11 @@ } }, "session": { - "id": "536d264c-145b-42ae-b0e4-a79c0d418713", + "id": "badfe62c-65d2-4dc1-87ac-ac9276afc5e5", "backend": "executor", "status": "active", - "createdAt": "2026-08-11T05:08:18.981Z", - "updatedAt": "2026-08-11T05:08:18.981Z", + "createdAt": "2026-08-12T12:37:54.196Z", + "updatedAt": "2026-08-12T12:37:54.196Z", "metadata": "[unavailable]" }, "backend": "router", @@ -730,16 +726,16 @@ }, "execution": { "kind": "request", - "id": "turn-efc4717c-fe6d-4399-9726-034e963e307a" + "id": "turn-1139ed2e-9b39-4c46-995c-10007663d712" }, "materializer": "router-prompt-model" }, "executionBindings": [ { "status": "known", - "attemptId": "turn-efc4717c-fe6d-4399-9726-034e963e307a:attempt:f6751c91-823a-40f3-b533-26db3bc0bb17", - "materializationReceiptDigest": "sha256:1fb8ed7e103c92c66f31a53ca73cd721769de4603a772c778d2b9121d6338acd", - "bindingDigest": "sha256:e0fef4d274d803ed382cea1f0ba4ff073a7b8c741ddb58b58257524177b82073", + "attemptId": "turn-1139ed2e-9b39-4c46-995c-10007663d712:attempt:87b9a1e3-6bae-4569-9634-2a92fd9dd874", + "materializationReceiptDigest": "sha256:901b372639b868da687202e07ac113cc883a6518f868e1db3b4975fc58657f76", + "bindingDigest": "sha256:ffd96086c5ed9c2a2b810e793bb42c06e25cedafe8e1b75879cb80887661a659", "descriptor": { "kind": "router-request", "transport": "http", @@ -751,10 +747,10 @@ "timeoutMs": null }, "timing": { - "startedAt": "2026-08-11T05:08:18.979Z" + "startedAt": "2026-08-12T12:37:54.194Z" } }, - "timestamp": "2026-08-11T05:08:18.981Z" + "timestamp": "2026-08-12T12:37:54.196Z" } } }, @@ -766,7 +762,7 @@ "source": { "eventId": "run-000001:runtime:2", "providerSequence": 2, - "occurredAt": "2026-08-11T05:08:18.986Z", + "occurredAt": "2026-08-12T12:37:54.216Z", "receivedAt": "2026-08-01T00:00:00.000Z" }, "error": { @@ -784,7 +780,7 @@ "source": { "eventId": "run-000001:runtime:3", "providerSequence": 3, - "occurredAt": "2026-08-11T05:08:18.988Z", + "occurredAt": "2026-08-12T12:37:54.221Z", "receivedAt": "2026-08-01T00:00:00.000Z" }, "status": "failed", @@ -808,10 +804,10 @@ "value": { "kind": "effect.upserted", "effect": { - "id": "effect-op-terminal-000002-d1e2c03899b0c97c425569d7", + "id": "effect-op-terminal-000002-cf99cee4622510fbb428f652", "operationId": "op-terminal-000002", "effectKind": "run.execute", - "requestDigest": "d1e2c03899b0c97c425569d764d47bfed6374d758a3da01e422726781f6e6e82", + "requestDigest": "cf99cee4622510fbb428f6528d8bf27f2101fdaf5757203e738e46db132e8ca6", "kind": "send", "status": "failed", "attempt": 1, diff --git a/artifacts/verification/w6/states/failure-or-reconnect.png b/artifacts/verification/w6/states/failure-or-reconnect.png index 47c7b19..b1adc61 100644 Binary files a/artifacts/verification/w6/states/failure-or-reconnect.png and b/artifacts/verification/w6/states/failure-or-reconnect.png differ diff --git a/artifacts/verification/w6/states/failure-or-reconnect.txt b/artifacts/verification/w6/states/failure-or-reconnect.txt index acd1778..4f589ca 100644 --- a/artifacts/verification/w6/states/failure-or-reconnect.txt +++ b/artifacts/verification/w6/states/failure-or-reconnect.txt @@ -1,6 +1,4 @@ -braid cwd braid-release-ux-final-wt · session New conversation -AgentProfile Braid starter · pi / deterministic deterministic fixture - W6 failure state + › W6 failure state error · unknown RUNTIME_BACKEND_ERROR @@ -16,9 +14,11 @@ AgentProfile Braid starter · pi / deterministic deterministic fixtur -──────────────────────────────────── › send ──────────────────────────────────── + + + + › -────────────────────────── alt+enter newline · paste ─────────────────────────── -outcome failed · operation op-terminal-000002 /export preserve · /new continue +profile Braid starter · pi / deterministic failed · /activity details · /new diff --git a/artifacts/verification/w6/states/fork-preview.json b/artifacts/verification/w6/states/fork-preview.json index 124aba4..e0bff81 100644 --- a/artifacts/verification/w6/states/fork-preview.json +++ b/artifacts/verification/w6/states/fork-preview.json @@ -4,8 +4,8 @@ "source": { "binary": "packed real binary from clean npm install", "binarySha256": "141288e0fe917635d723b4b70d464dc49baff14cc356f54de1d3f8faa5d8254f", - "tarball": "tangle-network-braid-0.1.0.tgz", - "tarballSha256": "32f19ba707aa659f776a604fe241111fe2bfb68928ac6f005014d81bd924182c" + "tarball": "tangle-network-braid-0.1.1.tgz", + "tarballSha256": "9bbd7b5b9f78b3d202dbb09d4375970edfd4965ffc5ad94b04bd7fa6ce01c4c7" }, "dimensions": { "columns": 80, @@ -30,7 +30,7 @@ }, "capturePhase": "atomic-signal-frame", "captureRevision": 1, - "frame": " fork preview workspace\n────────────────────────────────────────────────────────────────────────────────\n will create a new workspace from this source\n source: workspace:/workspace\n destination: workspace:/workspace-fork\n boundary: conv-1 / branch-1 → conv-fork-1 / branch-1\n profile snapshot: digest:fixture-source → digest:fixture-copy\n workspace state: checkpoint:fixture-1 → checkpoint:fixture-1\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n────────────────────────────────────────────────────────────────────────────────\n enter/y create fork · ←/esc cancel\n", + "frame": " fork preview workspace\n────────────────────────────────────────────────────────────────────────────────\n will create a new workspace from this source\n source: workspace:/workspace\n destination: workspace:/workspace-fork\n boundary: conv-1 / branch-1 → conv-fork-1 / branch-1\n profile snapshot: digest:fixture-source → digest:fixture-copy\n workspace state: checkpoint:fixture-1 → checkpoint:fixture-1\n operation id: operation-fixture-fork → operation-fixture-fork\n plan digest: digest:fixture-fork-plan → digest:fixture-fork-plan\n\n\n\n\n\n\n\n\n\n\n\n\n────────────────────────────────────────────────────────────────────────────────\n enter/y create fork · ←/esc cancel\n", "packedState": { "schemaVersion": 2, "capturePhase": "atomic-signal-frame", @@ -38,7 +38,7 @@ "schemaVersion": 2, "revision": 1, "sequence": 1, - "workspace": "/home/drew/code/braid-release-ux-final-wt", + "workspace": "/home/drew/code/braid-transcript-first-wt", "conversationId": "conv-1", "branchId": "branch-1", "conversations": [ @@ -113,7 +113,7 @@ }, "view": { "revision": 1, - "workspace": "/home/drew/code/braid-release-ux-final-wt", + "workspace": "/home/drew/code/braid-transcript-first-wt", "profileName": "Braid starter", "profileDigest": "44ed91c937fd4079fe01c49f6bc76413656886e5aa8b2351590d3a888cfa0a14", "runner": "pi", @@ -334,12 +334,12 @@ "run.reconnect": { "available": false, "source": "provider", - "reason": "There is no active run" + "reason": "There is no detached run to reconnect" }, "run.reconcile": { "available": false, "source": "provider", - "reason": "There is no active run" + "reason": "There is no detached run to reconcile" } }, "draft": "", @@ -391,7 +391,7 @@ "payload": { "value": { "kind": "workspace.opened", - "workspace": "/home/drew/code/braid-release-ux-final-wt" + "workspace": "/home/drew/code/braid-transcript-first-wt" } } } diff --git a/artifacts/verification/w6/states/fork-preview.png b/artifacts/verification/w6/states/fork-preview.png index 4ae5d9d..ae93f8e 100644 Binary files a/artifacts/verification/w6/states/fork-preview.png and b/artifacts/verification/w6/states/fork-preview.png differ diff --git a/artifacts/verification/w6/states/fork-preview.txt b/artifacts/verification/w6/states/fork-preview.txt index c06bef4..8f069ea 100644 --- a/artifacts/verification/w6/states/fork-preview.txt +++ b/artifacts/verification/w6/states/fork-preview.txt @@ -6,8 +6,8 @@ boundary: conv-1 / branch-1 → conv-fork-1 / branch-1 profile snapshot: digest:fixture-source → digest:fixture-copy workspace state: checkpoint:fixture-1 → checkpoint:fixture-1 - - + operation id: operation-fixture-fork → operation-fixture-fork + plan digest: digest:fixture-fork-plan → digest:fixture-fork-plan diff --git a/artifacts/verification/w6/states/graph-or-analysis.json b/artifacts/verification/w6/states/graph-or-analysis.json index 007e250..f20e5f2 100644 --- a/artifacts/verification/w6/states/graph-or-analysis.json +++ b/artifacts/verification/w6/states/graph-or-analysis.json @@ -4,8 +4,8 @@ "source": { "binary": "packed real binary from clean npm install", "binarySha256": "141288e0fe917635d723b4b70d464dc49baff14cc356f54de1d3f8faa5d8254f", - "tarball": "tangle-network-braid-0.1.0.tgz", - "tarballSha256": "32f19ba707aa659f776a604fe241111fe2bfb68928ac6f005014d81bd924182c" + "tarball": "tangle-network-braid-0.1.1.tgz", + "tarballSha256": "9bbd7b5b9f78b3d202dbb09d4375970edfd4965ffc5ad94b04bd7fa6ce01c4c7" }, "dimensions": { "columns": 80, @@ -30,7 +30,7 @@ }, "capturePhase": "atomic-signal-frame", "captureRevision": 1, - "frame": " conversation graph · 2 Braid starter · pi · fixture/deterministic\n────────────────────────────────────────────────────────────────────────────────\n ! runtime activity unavailable; showing last saved state: The runtime returned…\n ✓ New conversation · conversation · complete · ─attached→\n › └ ✓ Main · branch · complete · current · ─attached→\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n────────────────────────────────────────────────────────────────────────────────\n ↑↓ move · enter/→ open · ←/esc close\n", + "frame": " conversation graph · 2 Braid starter · pi · fixture/deterministic\n────────────────────────────────────────────────────────────────────────────────\n ✓ New conversation · conversation · complete · ─attached→\n › └ ✓ Main · branch · complete · current · ─attached→\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n────────────────────────────────────────────────────────────────────────────────\n ↑↓ move · enter/→ open · ←/esc close\n", "packedState": { "schemaVersion": 2, "capturePhase": "atomic-signal-frame", @@ -38,7 +38,7 @@ "schemaVersion": 2, "revision": 1, "sequence": 1, - "workspace": "/home/drew/code/braid-release-ux-final-wt", + "workspace": "/home/drew/code/braid-transcript-first-wt", "conversationId": "conv-1", "branchId": "branch-1", "conversations": [ @@ -113,7 +113,7 @@ }, "view": { "revision": 1, - "workspace": "/home/drew/code/braid-release-ux-final-wt", + "workspace": "/home/drew/code/braid-transcript-first-wt", "profileName": "Braid starter", "profileDigest": "44ed91c937fd4079fe01c49f6bc76413656886e5aa8b2351590d3a888cfa0a14", "runner": "pi", @@ -334,12 +334,12 @@ "run.reconnect": { "available": false, "source": "provider", - "reason": "There is no active run" + "reason": "There is no detached run to reconnect" }, "run.reconcile": { "available": false, "source": "provider", - "reason": "There is no active run" + "reason": "There is no detached run to reconcile" } }, "draft": "", @@ -358,7 +358,7 @@ "payload": { "value": { "kind": "workspace.opened", - "workspace": "/home/drew/code/braid-release-ux-final-wt" + "workspace": "/home/drew/code/braid-transcript-first-wt" } } } diff --git a/artifacts/verification/w6/states/graph-or-analysis.png b/artifacts/verification/w6/states/graph-or-analysis.png index 76ae18b..0e6302d 100644 Binary files a/artifacts/verification/w6/states/graph-or-analysis.png and b/artifacts/verification/w6/states/graph-or-analysis.png differ diff --git a/artifacts/verification/w6/states/graph-or-analysis.txt b/artifacts/verification/w6/states/graph-or-analysis.txt index 5742f9b..273207a 100644 --- a/artifacts/verification/w6/states/graph-or-analysis.txt +++ b/artifacts/verification/w6/states/graph-or-analysis.txt @@ -1,6 +1,5 @@ conversation graph · 2 Braid starter · pi · fixture/deterministic ──────────────────────────────────────────────────────────────────────────────── - ! runtime activity unavailable; showing last saved state: The runtime returned… ✓ New conversation · conversation · complete · ─attached→ › └ ✓ Main · branch · complete · current · ─attached→ @@ -20,5 +19,6 @@ + ──────────────────────────────────────────────────────────────────────────────── ↑↓ move · enter/→ open · ←/esc close diff --git a/artifacts/verification/w6/states/interaction.json b/artifacts/verification/w6/states/interaction.json index c8f883d..5539211 100644 --- a/artifacts/verification/w6/states/interaction.json +++ b/artifacts/verification/w6/states/interaction.json @@ -4,8 +4,8 @@ "source": { "binary": "packed real binary from clean npm install", "binarySha256": "141288e0fe917635d723b4b70d464dc49baff14cc356f54de1d3f8faa5d8254f", - "tarball": "tangle-network-braid-0.1.0.tgz", - "tarballSha256": "32f19ba707aa659f776a604fe241111fe2bfb68928ac6f005014d81bd924182c" + "tarball": "tangle-network-braid-0.1.1.tgz", + "tarballSha256": "9bbd7b5b9f78b3d202dbb09d4375970edfd4965ffc5ad94b04bd7fa6ce01c4c7" }, "dimensions": { "columns": 80, @@ -38,7 +38,7 @@ "schemaVersion": 2, "revision": 1, "sequence": 1, - "workspace": "/home/drew/code/braid-release-ux-final-wt", + "workspace": "/home/drew/code/braid-transcript-first-wt", "conversationId": "conv-1", "branchId": "branch-1", "conversations": [ @@ -113,7 +113,7 @@ }, "view": { "revision": 1, - "workspace": "/home/drew/code/braid-release-ux-final-wt", + "workspace": "/home/drew/code/braid-transcript-first-wt", "profileName": "Braid starter", "profileDigest": "44ed91c937fd4079fe01c49f6bc76413656886e5aa8b2351590d3a888cfa0a14", "runner": "pi", @@ -216,6 +216,7 @@ "session" ], "queuePosition": 0, + "queueTotal": 1, "secret": false } ], @@ -368,12 +369,12 @@ "run.reconnect": { "available": false, "source": "provider", - "reason": "There is no active run" + "reason": "There is no detached run to reconnect" }, "run.reconcile": { "available": false, "source": "provider", - "reason": "There is no active run" + "reason": "There is no detached run to reconcile" } }, "draft": "", @@ -392,7 +393,7 @@ "payload": { "value": { "kind": "workspace.opened", - "workspace": "/home/drew/code/braid-release-ux-final-wt" + "workspace": "/home/drew/code/braid-transcript-first-wt" } } } diff --git a/artifacts/verification/w6/states/interaction.png b/artifacts/verification/w6/states/interaction.png index 3186975..6937c6a 100644 Binary files a/artifacts/verification/w6/states/interaction.png and b/artifacts/verification/w6/states/interaction.png differ diff --git a/artifacts/verification/w6/states/narrow.json b/artifacts/verification/w6/states/narrow.json index 36fdf53..5d3bf4f 100644 --- a/artifacts/verification/w6/states/narrow.json +++ b/artifacts/verification/w6/states/narrow.json @@ -4,8 +4,8 @@ "source": { "binary": "packed real binary from clean npm install", "binarySha256": "141288e0fe917635d723b4b70d464dc49baff14cc356f54de1d3f8faa5d8254f", - "tarball": "tangle-network-braid-0.1.0.tgz", - "tarballSha256": "32f19ba707aa659f776a604fe241111fe2bfb68928ac6f005014d81bd924182c" + "tarball": "tangle-network-braid-0.1.1.tgz", + "tarballSha256": "9bbd7b5b9f78b3d202dbb09d4375970edfd4965ffc5ad94b04bd7fa6ce01c4c7" }, "dimensions": { "columns": 40, @@ -30,7 +30,7 @@ }, "capturePhase": "atomic-signal-frame", "captureRevision": 1, - "frame": "braid AgentProfile Braid starter\npi / deterministic · fixture\n\n Send a task to Braid starter, or press\n Ctrl+P for commands.\n\n──────────────── › send ────────────────\n›\n\n\n────── alt+enter newline · paste ───────\nready for a message Ctrl+P commands\n", + "frame": "\n Ask Braid starter anything.\n\n\n\n\n\n\n›\n\n\nBraid starter Ctrl+P commands\n", "packedState": { "schemaVersion": 2, "capturePhase": "atomic-signal-frame", @@ -38,7 +38,7 @@ "schemaVersion": 2, "revision": 1, "sequence": 1, - "workspace": "/home/drew/code/braid-release-ux-final-wt", + "workspace": "/home/drew/code/braid-transcript-first-wt", "conversationId": "conv-1", "branchId": "branch-1", "conversations": [ @@ -113,7 +113,7 @@ }, "view": { "revision": 1, - "workspace": "/home/drew/code/braid-release-ux-final-wt", + "workspace": "/home/drew/code/braid-transcript-first-wt", "profileName": "Braid starter", "profileDigest": "44ed91c937fd4079fe01c49f6bc76413656886e5aa8b2351590d3a888cfa0a14", "runner": "pi", @@ -334,12 +334,12 @@ "run.reconnect": { "available": false, "source": "provider", - "reason": "There is no active run" + "reason": "There is no detached run to reconnect" }, "run.reconcile": { "available": false, "source": "provider", - "reason": "There is no active run" + "reason": "There is no detached run to reconcile" } }, "draft": "", @@ -358,7 +358,7 @@ "payload": { "value": { "kind": "workspace.opened", - "workspace": "/home/drew/code/braid-release-ux-final-wt" + "workspace": "/home/drew/code/braid-transcript-first-wt" } } } diff --git a/artifacts/verification/w6/states/narrow.png b/artifacts/verification/w6/states/narrow.png index 6f59b80..442393c 100644 Binary files a/artifacts/verification/w6/states/narrow.png and b/artifacts/verification/w6/states/narrow.png differ diff --git a/artifacts/verification/w6/states/narrow.txt b/artifacts/verification/w6/states/narrow.txt index 88e4322..913d8d5 100644 --- a/artifacts/verification/w6/states/narrow.txt +++ b/artifacts/verification/w6/states/narrow.txt @@ -1,12 +1,12 @@ -braid AgentProfile Braid starter -pi / deterministic · fixture - Send a task to Braid starter, or press - Ctrl+P for commands. + Ask Braid starter anything. + + + + + -──────────────── › send ──────────────── › -────── alt+enter newline · paste ─────── -ready for a message Ctrl+P commands +Braid starter Ctrl+P commands diff --git a/artifacts/verification/w6/states/profile.json b/artifacts/verification/w6/states/profile.json index d47912a..903ced5 100644 --- a/artifacts/verification/w6/states/profile.json +++ b/artifacts/verification/w6/states/profile.json @@ -4,8 +4,8 @@ "source": { "binary": "packed real binary from clean npm install", "binarySha256": "141288e0fe917635d723b4b70d464dc49baff14cc356f54de1d3f8faa5d8254f", - "tarball": "tangle-network-braid-0.1.0.tgz", - "tarballSha256": "32f19ba707aa659f776a604fe241111fe2bfb68928ac6f005014d81bd924182c" + "tarball": "tangle-network-braid-0.1.1.tgz", + "tarballSha256": "9bbd7b5b9f78b3d202dbb09d4375970edfd4965ffc5ad94b04bd7fa6ce01c4c7" }, "dimensions": { "columns": 80, @@ -30,7 +30,7 @@ }, "capturePhase": "atomic-signal-frame", "captureRevision": 1, - "frame": "braid cwd braid-release-ux-final-wt · session New conversation\nAgentProfile Braid starter · pi / deterministic deterministic fixture\n\n Send a task to Braid starter, or press Ctrl+P for commands.\n\n\n Active profile · Braid starter\n source Braid starter · trusted · read-only\n runner pi · model fixture/deterministic\n thinking none · max output provider default\n digest sha256:44ed91c9…8cfa0a14\n profiles\n ──────────────────────────────────────────────────────────────────────\n >\n → ✓ Braid starter trusted · read-only · Braid starte\n ──────────────────────────────────────────────────────────────────────\n enter · ^V valid · ^S save · esc\n\n\n\n\n\n\nready for a message Ctrl+P commands\n", + "frame": "\n Ask Braid starter anything.\n\n\n\n\n Active profile · Braid starter\n source Braid starter · trusted · read-only\n runner pi · model fixture/deterministic\n thinking none · max output provider default\n digest sha256:44ed91c9…8cfa0a14\n profiles\n ──────────────────────────────────────────────────────────────────────\n >\n → ✓ Braid starter trusted · read-only · Braid starte\n ──────────────────────────────────────────────────────────────────────\n enter · ^V valid · ^S save · esc\n\n\n\n\n\n\nprofile Braid starter · pi / deterministic Ctrl+P commands\n", "packedState": { "schemaVersion": 2, "capturePhase": "atomic-signal-frame", @@ -38,7 +38,7 @@ "schemaVersion": 2, "revision": 1, "sequence": 1, - "workspace": "/home/drew/code/braid-release-ux-final-wt", + "workspace": "/home/drew/code/braid-transcript-first-wt", "conversationId": "conv-1", "branchId": "branch-1", "conversations": [ @@ -113,7 +113,7 @@ }, "view": { "revision": 1, - "workspace": "/home/drew/code/braid-release-ux-final-wt", + "workspace": "/home/drew/code/braid-transcript-first-wt", "profileName": "Braid starter", "profileDigest": "44ed91c937fd4079fe01c49f6bc76413656886e5aa8b2351590d3a888cfa0a14", "runner": "pi", @@ -334,12 +334,12 @@ "run.reconnect": { "available": false, "source": "provider", - "reason": "There is no active run" + "reason": "There is no detached run to reconnect" }, "run.reconcile": { "available": false, "source": "provider", - "reason": "There is no active run" + "reason": "There is no detached run to reconcile" } }, "draft": "", @@ -358,7 +358,7 @@ "payload": { "value": { "kind": "workspace.opened", - "workspace": "/home/drew/code/braid-release-ux-final-wt" + "workspace": "/home/drew/code/braid-transcript-first-wt" } } } diff --git a/artifacts/verification/w6/states/profile.png b/artifacts/verification/w6/states/profile.png index f346e3a..1491f88 100644 Binary files a/artifacts/verification/w6/states/profile.png and b/artifacts/verification/w6/states/profile.png differ diff --git a/artifacts/verification/w6/states/profile.txt b/artifacts/verification/w6/states/profile.txt index 68ab9dd..2e6be89 100644 --- a/artifacts/verification/w6/states/profile.txt +++ b/artifacts/verification/w6/states/profile.txt @@ -1,7 +1,7 @@ -braid cwd braid-release-ux-final-wt · session New conversation -AgentProfile Braid starter · pi / deterministic deterministic fixture - Send a task to Braid starter, or press Ctrl+P for commands. + Ask Braid starter anything. + + Active profile · Braid starter @@ -21,4 +21,4 @@ AgentProfile Braid starter · pi / deterministic deterministic fixtur -ready for a message Ctrl+P commands +profile Braid starter · pi / deterministic Ctrl+P commands diff --git a/docs/02-experience-specification.md b/docs/02-experience-specification.md index a0da14a..e9f5dc0 100644 --- a/docs/02-experience-specification.md +++ b/docs/02-experience-specification.md @@ -75,9 +75,6 @@ If a connection is unavailable, the conversation opens offline and retains full ### Standard layout from 80 to 119 columns ```text - Braid Release engineer runner:pi model:tangle-router/glm-5.2 thinking:high out:≤16k - Local CLI Bridge exec:local workspace -─────────────────────────────────────────────────────────────────────────────── user Explain the failing integration test. agent I found the failure in the session replay path. @@ -86,33 +83,47 @@ If a connection is unavailable, the conversation opens offline and retains full The reconnect starts one event too early … -─────────────────────────────────────────────────────────────────────────────── - > _ -─────────────────────────────────────────────────────────────────────────────── - main • running 12.4k in / 1.8k out $0.08 00:37 queue:0 ctrl+p commands + › _ + + + profile Release engineer · pi / glm-5.2 · Local CLI Bridge Ctrl+P commands ``` -The header area identifies the product, profile, connection, runner, model, and effort in one scan. +The main shell has no persistent header. + +The transcript begins at the top of the viewport and remains visually primary. + +One quiet line below the composer identifies the profile, runner, model, and connection. -The execution line keeps the maximum output limit and local or remote execution location visible without folding them into the model name. +Remote or sandbox execution appears there only when location changes the user's mental model. -The transcript consumes all remaining vertical space above the composer. +The composer has no border during normal input and keeps at least three usable rows. -The composer grows from one line to at most 40% of terminal height, then scrolls internally. +Autocomplete adds one divider between input and results. -The status line identifies branch, run state, usage, elapsed time, queue count, and one contextual shortcut. +The composer grows to at most 40% of terminal height, then scrolls internally. -The status line omits unavailable values instead of displaying empty placeholders. +During work, the context line gives priority to state, cancel, queue, and steer controls. + +At rest, it gives priority to execution identity and measured usage. + +Transient confirmations temporarily replace the context line so they remain visible at 40 columns. + +The context line omits unavailable values instead of displaying placeholders. The activity view keeps direct turn totals, analysis totals, and worker-tree totals separate. -Each total identifies whether its tokens, cost, latency, and completion state were reported, estimated, observed as a floor, or unavailable. +Each visible total identifies reported values, estimates, or observed minimums without inventing zeroes. ### Usage and execution inspector -The status line shows direct conversation tokens and cost when at least one turn exists. +The wide context line shows direct conversation tokens and cost when measured values exist. + +It labels estimates and observed minimums explicitly. -It labels each total as reported, estimated, an observed minimum, or unknown. +It omits an unreported metric from the main shell and activity summary. + +Focused run details label the metric as not reported. It never displays missing spend as zero. @@ -132,28 +143,29 @@ Account sandbox spend and per-run model spend are never presented as the same fa The physical machine IP, effective resource allocation, and per-sandbox CPU, RAM, and storage cost remain unavailable when the provider does not report them. -Headless state contains the same usage groups and secret-free execution records as the terminal. +Headless state retains complete measurement status, including unavailable fields, for automation and audit. ### Wide layout at 120 columns and above ```text - Braid profile:reviewer cloud:tangle codex/gpt-5.6 xhigh -──────────────────────────────────────────────────────────────┬────────────────────── - transcript │ Activity - │ run streaming 00:37 - agent output │ tool read complete - │ worker test running - │ interaction 1 waiting - │ cost $0.08 -──────────────────────────────────────────────────────────────┴────────────────────── - > _ -───────────────────────────────────────────────────────────────────────────────────── - fix/replay • running env:sbx_… checkpoint:none queue:0 ctrl+g graph + user Fix the replay race and run the focused tests. │ live work + │ > running replay fix + agent I found the stale cursor update. │ > running test worker + I am applying the fix now. │ · waiting approval + + › _ + + + profile reviewer · codex / openai/gpt-5.6 · sandbox · xhigh working · Ctrl+C cancel ``` -The right pane is optional and defaults to activity while a run is active. +The right pane is hidden by default, including while a run is active. -The user may switch the pane among activity, graph, details, workspace, and hidden. +F2 toggles one live-work pane that contains only active, waiting, detached, or reconnecting work. + +Completed history remains available through `/activity` instead of accumulating in the pane. + +Graph, details, and workspace views use focused overlays. The pane never reduces the transcript below 72 columns; below that boundary it becomes an overlay. @@ -161,7 +173,9 @@ The pane never reduces the transcript below 72 columns; below that boundary it b The transcript, composer, and one compact status line remain visible. -The top line collapses to profile and run state. +There is no top line. + +The bottom context keeps the profile at rest and gives active controls priority during work. Selectors, interactions, details, activity, and graph occupy the full viewport as focused overlays. diff --git a/docs/04-runtime-contracts.md b/docs/04-runtime-contracts.md index 7487ec5..19eeea2 100644 --- a/docs/04-runtime-contracts.md +++ b/docs/04-runtime-contracts.md @@ -10,28 +10,28 @@ When a current package blocks a real Braid flow, Braid records the unavailable a ## Evidence baseline -The following published versions were queried from npm and their installed declarations were inspected directly on 2026-08-10. +The following published versions were queried from npm and their installed declarations were inspected directly on 2026-08-12. | Package | Installed version | Braid boundary | | --- | ---: | --- | -| [`@tangle-network/agent-interface`](https://github.com/tangle-network/agent-sdk/tree/main/packages/agent-interface) | `0.46.1` | Canonical profile, capabilities, environment, stream, portable context, and interaction contracts | -| [`@tangle-network/agent-runtime`](https://github.com/tangle-network/agent-runtime) | `0.131.7` | Sole execution layer; exact executor, environment-provider, and terminal-monitor exports | -| [`@tangle-network/agent-eval`](https://github.com/tangle-network/agent-eval) | `0.144.11` | Run records, judges, trace analysts, comparisons, and feedback trajectories | -| `@tangle-network/agent-provider-cli-bridge` | `0.5.0` | CLI Bridge environment adapter with live streaming, replay, retry-safe turns, and explicit cancel | -| `@tangle-network/agent-provider-tangle` | `0.6.2` | Tangle environment adapter over the sandbox client | -| `@tangle-network/sandbox` | `0.19.4` | Tangle cloud client used by the provider | +| [`@tangle-network/agent-interface`](https://github.com/tangle-network/agent-sdk/tree/main/packages/agent-interface) | `0.47.0` | Canonical profile, capabilities, environment, stream, portable context, and interaction contracts | +| [`@tangle-network/agent-runtime`](https://github.com/tangle-network/agent-runtime) | `0.132.9` | Sole execution layer; exact executor, retained-run, environment-provider, and terminal-monitor exports | +| [`@tangle-network/agent-eval`](https://github.com/tangle-network/agent-eval) | `0.145.1` | Run records, judges, trace analysts, comparisons, and feedback trajectories | +| `@tangle-network/agent-provider-cli-bridge` | `0.6.0` | CLI Bridge environment adapter with live streaming, replay, retry-safe turns, and explicit cancel | +| `@tangle-network/agent-provider-tangle` | `0.6.3` | Tangle environment adapter over the sandbox client | +| `@tangle-network/sandbox` | `0.19.6` | Tangle cloud client used by the provider | -The installed runtime publishes `agent-eval >=0.144.10 <0.145.0`, `agent-interface >=0.46.1 <0.47.0`, and optional `sandbox >=0.19.4 <0.20.0` as peer ranges. +The installed runtime publishes `agent-eval >=0.145.0 <0.146.0`, `agent-interface >=0.46.1 <0.47.0`, and optional `sandbox >=0.19.4 <0.20.0` as peer ranges. -Braid exercises runtime `0.131.7` with interface `0.46.1`, eval `0.144.11`, CLI Bridge adapter `0.5.0`, Tangle adapter `0.6.2`, and sandbox `0.19.4`. +Braid exercises runtime `0.132.9` with interface `0.47.0`, eval `0.145.1`, CLI Bridge adapter `0.6.0`, Tangle adapter `0.6.3`, and sandbox `0.19.6`. The lockfile pins the registry integrity for every installed package. -The published peer ranges accept the complete installed package set without a workspace exception. +Runtime `0.132.9` does not yet accept interface `0.47.x` in its published peer range. -The workspace override resolves every transitive `agent-interface` dependency to `0.46.1`; the package tests prove that this version retains every public symbol Braid uses from the older dependency graph. +The workspace override resolves every transitive `agent-interface` dependency to `0.47.0`; the package tests prove one compatible contract graph. -[Agent-runtime issue 746](https://github.com/tangle-network/agent-runtime/issues/746) is closed by the peer ranges retained in `0.131.7`. +[Agent-runtime issue 803](https://github.com/tangle-network/agent-runtime/issues/803) tracks the remaining published peer-range mismatch. Braid imports only the canonical root `agent-interface` entry point behind two local modules. @@ -140,7 +140,7 @@ A partial or inconsistent positive-input split must set `cacheBreakdownKnown: fa Braid rejects unknown fields and inconsistent totals under token-bearing record names. -Runtime `0.131.7` currently buffers executor output until the executor settles. +Runtime `0.132.9` currently buffers executor output until the executor settles. Braid therefore receives terminal text, tool calls, usage, and result evidence but cannot render provider text deltas live through this path. @@ -166,7 +166,7 @@ The capability is a typed Runtime-executor tag, not a provider-specific callback When the tag is present, `/cancel` calls the public Runtime `Executor.teardown('infinity')` operation and waits for its result before committing control state. -The installed Runtime `0.131.7` bridge executor implements that operation by posting `POST /v1/runs/:id/cancel` and waiting for a terminal bridge snapshot. +The installed Runtime `0.132.9` bridge executor implements that operation by posting `POST /v1/runs/:id/cancel` and waiting for a terminal bridge snapshot. `destroyed: true` becomes an accepted cancellation, while `destroyed: false`, a thrown error, or a control deadline becomes unknown. @@ -188,7 +188,7 @@ The upstream Runtime change required to expand this support is recorded below wi Title: `Expose typed, signal-aware provider cancellation acknowledgement from Executor` -Runtime `0.131.7` exposes `Executor.teardown(grace): Promise<{ destroyed: boolean }>` in its published declaration bundle. +Runtime `0.132.9` exposes `Executor.teardown(grace): Promise<{ destroyed: boolean }>` in its published declaration bundle. The bridge executor's `teardown('infinity')` posts `POST /v1/runs/:id/cancel` and waits for a terminal bridge snapshot in `dist/supervisor-BI6Z-8Yi.js:6973-6977,7529-7570`. @@ -387,6 +387,9 @@ The observation record never contains API keys, bearer tokens, SSH credentials, The following upstream issues own missing shared contracts: +- [Runtime issue 799](https://github.com/tangle-network/agent-runtime/issues/799) requires exact cleanup when retained dispatch fails after environment creation. +- [Runtime issue 800](https://github.com/tangle-network/agent-runtime/issues/800) requires crash-safe exact run admission or deterministic discovery. +- [Agent SDK issue 146](https://github.com/tangle-network/agent-sdk/issues/146) requires retained Tangle control, recovery, interactions, and workspace branching. - [Runtime issue 763](https://github.com/tangle-network/agent-runtime/issues/763) requests one stable execution tree with complete usage provenance. - [Agent SDK issue 136](https://github.com/tangle-network/agent-sdk/issues/136) requests normalized provider observations and account usage. - [Sandbox issue 5076](https://github.com/tangle-network/agent-dev-container/issues/5076) requests resolved placement, effective resources, and per-sandbox billing. @@ -401,15 +404,19 @@ Paired comparisons retain outcomes and costs across two frozen candidates. The current DSPy RLM engine accepts a caller-owned model function, stable public call reference, and execution recorder instead of a provider URL or credential. Braid invokes this engine through its bundled `uv` binary and an isolated managed Python 3.12 runtime. -The invocation pins `agent-eval-rpc[dspy]` to the installed Agent Eval version and fixes the dependency resolution cutoff. +The invocation pins `agent-eval-rpc[dspy]` to `0.144.11` and fixes the dependency resolution cutoff. Braid never sends model credentials to the managed Python process. Braid gives each analyst invocation one explicit runtime transport attempt by default so its recorded usage and cost cannot hide additional paid retries. Braid binds that function to the selected profile, connection, effective model, and runtime package version. -`agent-runtime` executes each canonical text-message request through `streamAgentTurn`; Braid returns normalized output, measured token usage, priced cost, terminal status, and finite redacted execution evidence to `agent-eval`. +For direct inference, `agent-runtime` executes each canonical text-message request through `streamAgentTurn`. -The callback rejects multimodal and request-level thinking controls because runtime `0.131.7` does not expose those fields on this exact turn input. +For CLI Bridge, Braid uses Runtime's `startRetainedRun` with the selected provider and exact run identity. + +Braid returns normalized output, measured token usage, priced cost, terminal status, and finite redacted execution evidence to `agent-eval`. + +The callback rejects multimodal and request-level thinking controls because runtime `0.132.9` does not expose those fields on this exact turn input. Reasoning remains an `AgentProfile` setting, and unsupported callback shapes fail before a provider call rather than being silently dropped. diff --git a/docs/08-verification.md b/docs/08-verification.md index de682da..6e7716f 100644 --- a/docs/08-verification.md +++ b/docs/08-verification.md @@ -374,6 +374,36 @@ The published package is downloaded from the registry after publication and its If a required live provider is unavailable, the release is blocked and the manifest reports the unavailable check rather than marking it skipped or simulated. +### Tangle Sandbox durability stress + +Run `pnpm test:live:tangle:sandbox:stress` against the public Sandbox endpoint. + +The command runs one canary before it starts the remaining cohort. + +The default cohort contains three proofs with at most two concurrent proofs. + +The command stops scheduling new proofs after any failure. + +Each proof creates one retained cloud environment through Braid and records the exact six-field cloud identity. + +The proof kills the first Braid process after a committed provider cursor. + +A fresh Braid process must reconnect to the same cloud execution without duplicate visible provider events. + +A follow-up turn must solve a hidden workspace continuity challenge in the same cloud session. + +Cancellation must become durable in the cloud and remain safe when the same request is retried. + +Every proof checks the execution account before and after cleanup. + +Every proof deletes only resources with its exact Braid ownership metadata. + +The cohort reports each result and the minimum, median, p90, and maximum latency for every observed phase. + +The cohort also reports observed tokens, costs, active-resource deltas, and explicit unavailable values. + +The command fails when a cloud identity repeats, an account changes, evidence is missing, or one owned resource remains. + ### Current core-path observations On 2026-08-09, the packed public setup, RPC dispatch, durable transcript, native continuation, process restart, and post-restart send passed separately against CLI Bridge commit `33695db` for Pi 0.83.0 with `tangle-router/glm-5.2` in 136.492 seconds and Codex CLI 0.147.0 with its default model in 43.462 seconds. @@ -386,6 +416,30 @@ An aggregate run proved that the current bridge cannot enable its process-wide P These observations prove the shared core flow only; they do not claim the broader interaction, tool, replay-cursor, cancellation, Tangle, or analysis rows in the required live matrix. +On 2026-08-12, a production Braid cloud-execution cohort completed 20 Tangle Sandbox jobs with OpenCode and `tangle-router/glm-5.2`. + +The cohort used four-way concurrency and completed in 198.554 seconds. + +Per-job latency was 24.671 seconds minimum, 27.981 seconds median, 34.738 seconds p90, and 44.578 seconds maximum. + +The jobs reported 10,180 input tokens, 460 output tokens, zero provider-reported model cost, and eight Sandbox compute-minutes. + +All 20 provider environment identifiers were unique. + +All 20 environments were absent after their turns, and active Sandbox count stayed at four. + +The artifact is `artifacts/verification/live/tangle-sandbox-braid-execution-stress-production-20260812.json`. + +Its SHA-256 is `1a38a26e97917073ef760525f7b18abbcca43fcf6faba54cf862e55e4886693c`. + +This proves production cloud execution, observation, concurrency, and cleanup only. + +It does not pass `LIVE-07` because the current provider cannot recover an exact retained run after a Braid process crash. + +Production and staging durability canaries both failed closed and left zero owned resources. + +[Runtime issue 800](https://github.com/tangle-network/agent-runtime/issues/800) and [Agent SDK issue 146](https://github.com/tangle-network/agent-sdk/issues/146) own the missing shared recovery path. + ## Runner conformance CLI Bridge may advertise a runner as interactive only after the following real flow passes at its pinned minimum version. diff --git a/docs/09-delivery-plan.md b/docs/09-delivery-plan.md index 30b0137..8730948 100644 --- a/docs/09-delivery-plan.md +++ b/docs/09-delivery-plan.md @@ -292,6 +292,8 @@ A repository search and dependency test must prove no Braid-owned runner/model m - Record one secret-free execution environment for local CLI Bridge, direct inference, and sandbox paths. - Implement shutdown behavior that distinguishes detachable and non-detachable runs. - Run the packed Braid binary against CLI Bridge, Tangle inference, and Tangle sandbox. +- Run one Tangle Sandbox canary before a bounded three-proof durability cohort. +- Record exact cleanup, account identity, latency distributions, tokens, costs, placement, and resource observations for every cloud proof. ### Done when @@ -299,6 +301,8 @@ A repository search and dependency test must prove no Braid-owned runner/model m The event ledger must show zero duplicate displayed part and zero lost committed event across forced disconnect and restart cases. +The Tangle Sandbox cohort must leave zero resources owned by its exact operation identities. + ## W9: Interactions and automation ### Repositories diff --git a/docs/10-upstream-strategy.md b/docs/10-upstream-strategy.md index 4d1b7c4..6848a42 100644 --- a/docs/10-upstream-strategy.md +++ b/docs/10-upstream-strategy.md @@ -37,7 +37,7 @@ The deciding question was how much source can be reused while preserving `AgentP | [OpenCode terminal packages](https://github.com/anomalyco/opencode/tree/dev/packages/tui) | `3a90639cb57619a21e59f544b3e8d23ffed56f48`; npm `1.18.16` | MIT | 50,341 lines across the TUI and run command | OpenTUI alternative and workflow reference | | [Codex terminal application](https://github.com/openai/codex/tree/main/codex-rs/tui) | repository `2cc9dbb`; TUI `9742cc8ed5def37a4575263733f70a01ca22047b`; CLI `0.147.0` | Apache-2.0 | 244,714 Rust lines | Composer, worker navigation, status, and snapshot-test reference | | [Hermes Agent terminal app](https://github.com/NousResearch/hermes-agent/tree/main/ui-tui) | `697f2896bf948731eb6fcb93caa7264478590843` | MIT | No size claim used | Client/runtime and workflow reference | -| [`agent-runtime` terminal monitor](https://github.com/tangle-network/agent-runtime/tree/main/src/tui) | `9c18cb48`; npm `0.131.7` | Project license | No refreshed size claim | Runtime-owned supervisor source, not app base | +| [`agent-runtime` terminal monitor](https://github.com/tangle-network/agent-runtime/tree/main/src/tui) | repository `9c18cb48`; npm `0.132.9` | Project license | No refreshed size claim | Runtime-owned supervisor source, not app base | The count commands selected the named TypeScript or Rust files from sparse clones and used `wc -l`. @@ -165,7 +165,7 @@ Braid uses Hermes as a workflow comparison and does not port its runtime protoco ## Runtime monitor -`agent-runtime` package version `0.131.7` exports a diagnostic terminal module and `agent-runtime-top` binary. +`agent-runtime` package version `0.132.9` exports a diagnostic terminal module and `agent-runtime-top` binary. The module understands runtime-owned supervisor files and shows worker state, spend, tokens, latency, logs, steering, shell action, and cancellation controls. diff --git a/package.json b/package.json index ec8b377..4248895 100644 --- a/package.json +++ b/package.json @@ -73,6 +73,8 @@ "test:live:bridge:matrix": "node scripts/live-bridge/matrix.mjs", "test:live:required:self": "pnpm run build && node --test scripts/live-required.test.mjs", "test:live:tangle": "node scripts/live-required.mjs live-tangle", + "test:live:tangle:sandbox:stress": "pnpm run build && node scripts/live-required/tangle-sandbox-braid-soak.mjs", + "test:live:tangle:sandbox:execution": "pnpm run build && node scripts/live-required/tangle-sandbox-braid-execution-soak.mjs", "test:live:supervisor": "node scripts/live-required.mjs live-supervisor", "test:live:analysis": "node scripts/live-required.mjs live-analysis", "verify:live-core": "node scripts/live-core/verify-pair.mjs", @@ -90,19 +92,19 @@ "release:smoke": "node scripts/release/smoke-package.mjs", "verify:candidate": "node scripts/verify-candidate.mjs", "verify:release": "node scripts/verify-release.mjs", - "proof:w0": "pnpm run build && node scripts/verify-package.mjs --record artifacts/verification/w0/package-proof.json && node scripts/capture-w0.mjs", - "proof:w6": "pnpm run build && node scripts/verify-package.mjs --record artifacts/verification/w6/package-proof.json && node scripts/capture-visual.mjs" + "proof:w0": "node scripts/proof-package.mjs w0", + "proof:w6": "node scripts/proof-package.mjs w6" }, "dependencies": { + "@dataiku/uv": "0.12.0", "@earendil-works/pi-tui": "0.84.1", "@napi-rs/keyring": "1.3.0", - "@dataiku/uv": "0.12.0", - "@tangle-network/agent-eval": "0.144.11", - "@tangle-network/agent-interface": "0.46.1", + "@tangle-network/agent-eval": "0.145.1", + "@tangle-network/agent-interface": "0.47.0", "@tangle-network/agent-provider-cli-bridge": "0.6.0", - "@tangle-network/agent-provider-tangle": "0.6.2", - "@tangle-network/agent-runtime": "0.132.0", - "@tangle-network/sandbox": "0.19.4", + "@tangle-network/agent-provider-tangle": "0.6.3", + "@tangle-network/agent-runtime": "0.132.10", + "@tangle-network/sandbox": "0.19.6", "better-sqlite3-multiple-ciphers": "13.0.3", "chalk": "6.0.0", "koffi": "3.1.4" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7d9c9d4..67cf064 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5,7 +5,7 @@ settings: excludeLinksFromLockfile: false overrides: - '@tangle-network/agent-interface': 0.46.1 + '@tangle-network/agent-interface': 0.47.0 hono: 4.13.0 undici: 7.29.0 @@ -23,23 +23,23 @@ importers: specifier: 1.3.0 version: 1.3.0 '@tangle-network/agent-eval': - specifier: 0.144.11 - version: 0.144.11 + specifier: 0.145.1 + version: 0.145.1 '@tangle-network/agent-interface': - specifier: 0.46.1 - version: 0.46.1 + specifier: 0.47.0 + version: 0.47.0 '@tangle-network/agent-provider-cli-bridge': specifier: 0.6.0 version: 0.6.0 '@tangle-network/agent-provider-tangle': - specifier: 0.6.2 - version: 0.6.2(@tangle-network/sandbox@0.19.4) + specifier: 0.6.3 + version: 0.6.3(@tangle-network/sandbox@0.19.6) '@tangle-network/agent-runtime': - specifier: 0.132.0 - version: 0.132.0(@tangle-network/agent-eval@0.144.11)(@tangle-network/agent-interface@0.46.1)(@tangle-network/sandbox@0.19.4) + specifier: 0.132.10 + version: 0.132.10(@tangle-network/agent-eval@0.145.1)(@tangle-network/agent-interface@0.47.0)(@tangle-network/sandbox@0.19.6) '@tangle-network/sandbox': - specifier: 0.19.4 - version: 0.19.4 + specifier: 0.19.6 + version: 0.19.6 better-sqlite3-multiple-ciphers: specifier: 13.0.3 version: 13.0.3 @@ -507,41 +507,45 @@ packages: '@tangle-network/agent-core@0.5.4': resolution: {integrity: sha512-k6gYv3BlagkfuWrGyTJH6mKUBgsLY6TXxizACqt0QF8a1/5uqy0UYc6R2Wo9nqQVJuaRxDnoiRf8YtsRVqA75g==} - '@tangle-network/agent-eval@0.144.11': - resolution: {integrity: sha512-rTZn6aRZzr8hZGGxG+iZDjifrQGO9Ghd4GvXdLHgPcwUJ3QHkRdFgp6ImTpjAW6ZVxrJlFv9wdN4OgDtbn5pCw==} + '@tangle-network/agent-core@0.6.0': + resolution: {integrity: sha512-GrSCn37gsIj8fYP3Yn4MLc3sjUN2LWrxD5dOp8G6tfcRhUdazqkZeo5zNAspfyhJdwDMlYk/gXr5xJ0GWxTnuw==} + + '@tangle-network/agent-eval@0.145.1': + resolution: {integrity: sha512-x4ze+gqR0Rr9HfW21ruThxXvKN+BukZ82jkV+XpvWKF3afECL6e3yERisviW/lJtl/uQB7HnLKt0f0YoRzsPng==} engines: {node: '>=20'} + hasBin: true - '@tangle-network/agent-interface@0.46.1': - resolution: {integrity: sha512-6a3GRkDxS+r6Bmlu8y6LQpiVE20oCYPzE2opb5o+AZeDzRW5KemyxreyYIprjgKfDrvBTMo0tgvC2JB51Sl84w==} + '@tangle-network/agent-interface@0.47.0': + resolution: {integrity: sha512-sCkr7FOT+SNut8EYtwh/x2JheI+Eer30V5IQVIrO8Fn6csC9pqlnLRuNDJh//pnk4+fsg4yLiiSW8/hL1l9e1w==} - '@tangle-network/agent-knowledge@7.2.0': - resolution: {integrity: sha512-DPpicRkEcRSd+5RBMmFg7uaoSQx4RpoTvrLo2Zm7m1NtDbQRKXgws/iaSPTWaOewUD5lI7OuJiXaBoLctGKn9g==} + '@tangle-network/agent-knowledge@7.2.3': + resolution: {integrity: sha512-pNt5FJT9RG3GazkBtTbgjHSkndbQDZZ4mmmzr3KRlgvaOucUfmvoN4b1dLmMmrOovK/CSNZYS0SF+X47dbpXzw==} engines: {node: '>=20.19.0'} hasBin: true peerDependencies: - '@tangle-network/agent-eval': '>=0.144.10 <0.145.0' - '@tangle-network/agent-interface': 0.46.1 + '@tangle-network/agent-eval': '>=0.145.0 <0.146.0' + '@tangle-network/agent-interface': 0.47.0 '@tangle-network/agent-profile-materialize@0.13.1': resolution: {integrity: sha512-2g/F8ABiJ6gB8lifTKKIMGPBgYjCyPHQF93HF3g7c5d0D5Rh8cEZ9WGRvggYjOm4HPi3/4PViG6VKEmr5sXwIg==} peerDependencies: - '@tangle-network/agent-interface': 0.46.1 + '@tangle-network/agent-interface': 0.47.0 '@tangle-network/agent-provider-cli-bridge@0.6.0': resolution: {integrity: sha512-eQdaQsM+TPwARU+wFGBIg7j1ScPzDJvy2A+YMcwP8NsiVLgH16t3pjao/eJSifKtHNipEdV2I5poNjHLmvTfJw==} - '@tangle-network/agent-provider-tangle@0.6.2': - resolution: {integrity: sha512-pzhrV3aczuC20Gl+kKn0D6tJkMFWKLVdzxTHBxY20gEi0DkH2kRCRCAzpSY6QzxCdekzalQiNvgmRoQPDy6HVQ==} + '@tangle-network/agent-provider-tangle@0.6.3': + resolution: {integrity: sha512-OO8HpPvqIdSPCeDLH8++Px2a/CtEu8O8ue18Yl4x7I45FWizaIRfidI7Jp9vIuWR1Os17jh6rgTXDGLKhU5oxw==} peerDependencies: '@tangle-network/sandbox': '>=0.17.0 <1.0.0' - '@tangle-network/agent-runtime@0.132.0': - resolution: {integrity: sha512-jw30sN+4X6njpyLE3Qhhfer0v9PMkXuMMad174Xos+729whig8uNjhfyiSf0h1eXrwkeFaZNjSnUOkwdxfJkZg==} + '@tangle-network/agent-runtime@0.132.10': + resolution: {integrity: sha512-Nj3Z+g6LjWo75rfve+RLN5FzNi22ypLPOYAXqGN3sSumJEJd8YOaH8v+AwAGiO8WrWlHkTTdDZXHW387LOQVvg==} engines: {node: '>=22.13.0'} hasBin: true peerDependencies: - '@tangle-network/agent-eval': '>=0.144.10 <0.145.0' - '@tangle-network/agent-interface': 0.46.1 + '@tangle-network/agent-eval': '>=0.145.0 <0.146.0' + '@tangle-network/agent-interface': 0.47.0 '@tangle-network/sandbox': '>=0.19.4 <0.20.0' peerDependenciesMeta: '@tangle-network/sandbox': @@ -550,8 +554,8 @@ packages: '@tangle-network/agent-trace-contract@1.0.2': resolution: {integrity: sha512-v7uMh56jkEp4vckevEU9xKsIatbs5dqzGPp69dFLSSXUVit0RP6VD6EANMXVlTCUk+6wVKBLHJx23XspVCEiIA==} - '@tangle-network/sandbox@0.19.4': - resolution: {integrity: sha512-rp4NGX7Em0ryNexEuoGYgX9jC5AyvYlDP/JLWBMCZRGdn+wE9HDcy9SsX8+geluyxpnW2Gg3BczefRYSnIcb2Q==} + '@tangle-network/sandbox@0.19.6': + resolution: {integrity: sha512-pEJEKiZm7ygxp/aXmCrTX/EUta7kwPjf9+a0ENTyr5lp1wRBiOyCs/67cnzS3xCR/w+4J46urkq6LisPmiXtXw==} peerDependencies: '@mastra/core': ^1.36.0 '@modelcontextprotocol/sdk': ^1.29.0 @@ -738,8 +742,8 @@ packages: bare-events: optional: true - bare-url@2.5.1: - resolution: {integrity: sha512-cD5ciQuKlx+eumTCfqbfiL+fhQm+dHbVNB/cX4+d+I/nx4JsVop9VoFEPAs5RJ6I84QR6bZIBjpd2kjDOYeWcg==} + bare-url@2.5.2: + resolution: {integrity: sha512-L13PCJzKG8RGvx8V1/DdMi12ERhC3tprr7/8a94BxpmnRsFqxh5XZNdhtMxu5HPkRshYOOWRGY8lDP7ZhpG9Cg==} better-sqlite3-multiple-ciphers@13.0.3: resolution: {integrity: sha512-UYabM82r1J84TLWc/SszoHs6XopWpl/2HCg3Nui1JUaFXg/VLswzkPowYiRhK/4CftI8dgtikwyZQecMldrGxQ==} @@ -872,6 +876,7 @@ packages: yaml@2.9.0: resolution: {integrity: sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==} engines: {node: '>= 14.6'} + hasBin: true zod@4.4.3: resolution: {integrity: sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==} @@ -1136,59 +1141,64 @@ snapshots: '@tangle-network/agent-core@0.5.4': dependencies: - '@tangle-network/agent-interface': 0.46.1 + '@tangle-network/agent-interface': 0.47.0 zod: 4.4.3 - '@tangle-network/agent-eval@0.144.11': + '@tangle-network/agent-core@0.6.0': + dependencies: + '@tangle-network/agent-interface': 0.47.0 + zod: 4.4.3 + + '@tangle-network/agent-eval@0.145.1': dependencies: '@asteasolutions/zod-to-openapi': 9.1.0(zod@4.4.3) '@hono/node-server': 2.1.0(hono@4.13.0) '@tangle-network/agent-core': 0.5.4 - '@tangle-network/agent-interface': 0.46.1 + '@tangle-network/agent-interface': 0.47.0 '@tangle-network/agent-trace-contract': 1.0.2 hono: 4.13.0 linear-sum-assignment: 1.0.9 re2js: 2.8.6 zod: 4.4.3 - '@tangle-network/agent-interface@0.46.1': + '@tangle-network/agent-interface@0.47.0': dependencies: '@noble/hashes': 1.8.0 spdx-expression-parse: 5.0.0 zod: 4.4.3 - '@tangle-network/agent-knowledge@7.2.0(@tangle-network/agent-eval@0.144.11)(@tangle-network/agent-interface@0.46.1)': + '@tangle-network/agent-knowledge@7.2.3(@tangle-network/agent-eval@0.145.1)(@tangle-network/agent-interface@0.47.0)': dependencies: - '@tangle-network/agent-eval': 0.144.11 - '@tangle-network/agent-interface': 0.46.1 + '@tangle-network/agent-eval': 0.145.1 + '@tangle-network/agent-interface': 0.47.0 proper-lockfile: 4.1.2 zod: 4.4.3 - '@tangle-network/agent-profile-materialize@0.13.1(@tangle-network/agent-interface@0.46.1)': + '@tangle-network/agent-profile-materialize@0.13.1(@tangle-network/agent-interface@0.47.0)': dependencies: - '@tangle-network/agent-interface': 0.46.1 + '@tangle-network/agent-interface': 0.47.0 '@tangle-network/agent-provider-cli-bridge@0.6.0': dependencies: - '@tangle-network/agent-interface': 0.46.1 + '@tangle-network/agent-interface': 0.47.0 undici: 7.29.0 - '@tangle-network/agent-provider-tangle@0.6.2(@tangle-network/sandbox@0.19.4)': + '@tangle-network/agent-provider-tangle@0.6.3(@tangle-network/sandbox@0.19.6)': dependencies: - '@tangle-network/agent-interface': 0.46.1 - '@tangle-network/sandbox': 0.19.4 + '@tangle-network/agent-interface': 0.47.0 + '@tangle-network/sandbox': 0.19.6 - '@tangle-network/agent-runtime@0.132.0(@tangle-network/agent-eval@0.144.11)(@tangle-network/agent-interface@0.46.1)(@tangle-network/sandbox@0.19.4)': + '@tangle-network/agent-runtime@0.132.10(@tangle-network/agent-eval@0.145.1)(@tangle-network/agent-interface@0.47.0)(@tangle-network/sandbox@0.19.6)': dependencies: '@tangle-network/agent-core': 0.5.4 - '@tangle-network/agent-eval': 0.144.11 - '@tangle-network/agent-interface': 0.46.1 - '@tangle-network/agent-knowledge': 7.2.0(@tangle-network/agent-eval@0.144.11)(@tangle-network/agent-interface@0.46.1) - '@tangle-network/agent-profile-materialize': 0.13.1(@tangle-network/agent-interface@0.46.1) + '@tangle-network/agent-eval': 0.145.1 + '@tangle-network/agent-interface': 0.47.0 + '@tangle-network/agent-knowledge': 7.2.3(@tangle-network/agent-eval@0.145.1)(@tangle-network/agent-interface@0.47.0) + '@tangle-network/agent-profile-materialize': 0.13.1(@tangle-network/agent-interface@0.47.0) '@tangle-network/agent-trace-contract': 1.0.2 tar-stream: 3.2.0 optionalDependencies: - '@tangle-network/sandbox': 0.19.4 + '@tangle-network/sandbox': 0.19.6 transitivePeerDependencies: - bare-abort-controller - bare-buffer @@ -1196,10 +1206,10 @@ snapshots: '@tangle-network/agent-trace-contract@1.0.2': {} - '@tangle-network/sandbox@0.19.4': + '@tangle-network/sandbox@0.19.6': dependencies: - '@tangle-network/agent-core': 0.5.4 - '@tangle-network/agent-interface': 0.46.1 + '@tangle-network/agent-core': 0.6.0 + '@tangle-network/agent-interface': 0.47.0 zod: 4.4.3 '@types/node@26.2.0': @@ -1277,7 +1287,7 @@ snapshots: bare-events: 2.9.1 bare-path: 3.1.1 bare-stream: 2.13.3(bare-events@2.9.1) - bare-url: 2.5.1 + bare-url: 2.5.2 fast-fifo: 1.3.2 transitivePeerDependencies: - bare-abort-controller @@ -1295,7 +1305,7 @@ snapshots: transitivePeerDependencies: - react-native-b4a - bare-url@2.5.1: + bare-url@2.5.2: dependencies: bare-path: 3.1.1 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index cdba875..2c1dfa6 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,5 +1,5 @@ overrides: - '@tangle-network/agent-interface': 0.46.1 + '@tangle-network/agent-interface': 0.47.0 hono: 4.13.0 undici: 7.29.0 allowBuilds: @@ -10,14 +10,14 @@ allowBuilds: node-pty: true ignoreScripts: false minimumReleaseAgeExclude: - - '@tangle-network/agent-runtime@0.126.0 || 0.128.0 || 0.131.0 || 0.131.1 || 0.131.2 || 0.131.3 || 0.131.4 || 0.131.5 || 0.131.6 || 0.131.7 || 0.132.0' - - '@tangle-network/agent-eval@0.143.0 || 0.144.0 || 0.144.1 || 0.144.3 || 0.144.4 || 0.144.6 || 0.144.8 || 0.144.10 || 0.144.11' - - '@tangle-network/agent-interface@0.46.0 || 0.46.1' - - '@tangle-network/agent-knowledge@7.0.8 || 7.1.2 || 7.1.3 || 7.2.0' + - '@tangle-network/agent-runtime@0.126.0 || 0.128.0 || 0.131.0 || 0.131.1 || 0.131.2 || 0.131.3 || 0.131.4 || 0.131.5 || 0.131.6 || 0.131.7 || 0.132.0 || 0.132.4 || 0.132.6 || 0.132.9 || 0.132.10' + - '@tangle-network/agent-eval@0.143.0 || 0.144.0 || 0.144.1 || 0.144.3 || 0.144.4 || 0.144.6 || 0.144.8 || 0.144.10 || 0.144.11 || 0.144.13 || 0.145.0 || 0.145.1' + - '@tangle-network/agent-interface@0.46.0 || 0.46.1 || 0.47.0' + - '@tangle-network/agent-knowledge@7.0.8 || 7.1.2 || 7.1.3 || 7.2.0 || 7.2.2 || 7.2.3' - '@tangle-network/agent-profile-materialize@0.10.2 || 0.13.1' - '@tangle-network/agent-provider-cli-bridge@0.4.2 || 0.4.3 || 0.5.0 || 0.6.0' - - '@tangle-network/agent-provider-tangle@0.6.0 || 0.6.1 || 0.6.2' - - '@tangle-network/sandbox@0.17.3 || 0.18.0 || 0.19.2 || 0.19.3 || 0.19.4' + - '@tangle-network/agent-provider-tangle@0.6.0 || 0.6.1 || 0.6.2 || 0.6.3' + - '@tangle-network/sandbox@0.17.3 || 0.18.0 || 0.19.2 || 0.19.3 || 0.19.4 || 0.19.5 || 0.19.6' - hono@4.13.0 - '@esbuild/aix-ppc64@0.28.2' - '@esbuild/android-arm64@0.28.2' @@ -46,4 +46,4 @@ minimumReleaseAgeExclude: - '@esbuild/win32-ia32@0.28.2' - '@esbuild/win32-x64@0.28.2' - esbuild@0.28.2 - - '@tangle-network/agent-core@0.5.4' + - '@tangle-network/agent-core@0.5.4 || 0.6.0' diff --git a/scripts/capture-connections.mjs b/scripts/capture-connections.mjs index 6198bd9..54410fc 100644 --- a/scripts/capture-connections.mjs +++ b/scripts/capture-connections.mjs @@ -224,7 +224,10 @@ async function spawnTerminal(binary, columns, rows, root) { } async function reachCredentialPrompt(terminal) { - await waitFor(() => normalized(terminal.screen()).includes('braid'), 'Braid header') + await waitFor( + () => normalized(terminal.screen()).includes('Braid starter'), + 'Braid conversation shell', + ) terminal.input('/connection create\r') await waitFor( () => normalized(terminal.screen()).includes('connection metadata'), diff --git a/scripts/capture-product-demo.mjs b/scripts/capture-product-demo.mjs index bb9fd92..d2d0868 100644 --- a/scripts/capture-product-demo.mjs +++ b/scripts/capture-product-demo.mjs @@ -34,7 +34,7 @@ export async function captureProductDemo({ spawnTerminal, normalized, castFor }) 'product-demo', COLUMNS, ROWS, - { BRAID_FIXTURE_CHUNK_DELAY_MS: '70' }, + { BRAID_FIXTURE_CHUNK_DELAY_MS: '300' }, 'product-demo', ['--connection', CONNECTION_ID, '--workspace', workspace], ) @@ -43,10 +43,10 @@ export async function captureProductDemo({ spawnTerminal, normalized, castFor }) await terminal.waitFor(() => { const screen = normalized(terminal.screen()) return ( - screen.includes('Release engineer') && - screen.includes('runner pi') && + screen.includes('profile Release engineer') && + screen.includes('pi / openai-codex/gpt-5.6-luna') && screen.includes('Local CLI Bridge') && - screen.includes('openai-codex/gpt-5.6-luna') + screen.includes('Ctrl+P commands') ) }, 'complete product configuration') await pause(700) @@ -60,6 +60,11 @@ export async function captureProductDemo({ spawnTerminal, normalized, castFor }) screen.includes('runner pi · model openai-codex/gpt-5.6-luna') ) }, 'AgentProfile panel') + terminal.resize(COLUMNS - 1, ROWS) + await terminal.waitForStable('pre-demo redraw') + const demoStartIndex = terminal.events.length + terminal.resize(COLUMNS, ROWS) + await terminal.waitForStable('demo redraw') await pause(1_000) terminal.input('\u001b') await terminal.waitFor( @@ -68,11 +73,22 @@ export async function captureProductDemo({ spawnTerminal, normalized, castFor }) ) await pause(350) - const prompt = 'Trace this request from AgentProfile to the selected runner.' + const prompt = 'Which coding agent will handle my next task, and where will it run?' await typeText(terminal, prompt) terminal.input('\r') await terminal.waitFor( - () => normalized(terminal.screen()).includes('Route confirmed.'), + () => normalized(terminal.screen()).includes('working'), + 'active run', + 15_000, + ) + terminal.input('\u001bOQ') + await terminal.waitFor( + () => normalized(terminal.screen()).includes('live work'), + 'live-work pane', + ) + await pause(500) + await terminal.waitFor( + () => normalized(terminal.screen()).includes('Pi will run the task'), 'routed response', 15_000, ) @@ -80,29 +96,30 @@ export async function captureProductDemo({ spawnTerminal, normalized, castFor }) () => { const screen = normalized(terminal.screen()) return ( - screen.includes('completed') && - screen.includes('model openai-codex/gpt-5.6-luna') && - !screen.includes('Ctrl+C cancel') + screen.includes('pi / openai-codex/gpt-5.6-luna') && !screen.includes('Ctrl+C cancel') ) }, 'completed run', 15_000, ) + terminal.input('\u001bOQ') + await terminal.waitFor( + () => !normalized(terminal.screen()).includes('live work'), + 'live-work pane close', + ) await terminal.waitForStable('product screenshot') const screenshot = terminal.snapshot() await pause(900) - terminal.input('\u001bOQ') + await typeText(terminal, '/activity', 30) + terminal.input('\r') await terminal.waitFor( () => normalized(terminal.screen()).includes('activity'), - 'activity pane', - ) - await pause(700) - terminal.input('\u001bOQ') - await terminal.waitFor( - () => !normalized(terminal.screen()).includes('activity'), - 'activity pane close', + 'retained activity', ) + await pause(900) + terminal.input('\u001b') + await pause(350) await typeText(terminal, '/ask What should this agent improve next?', 18) terminal.input('\r') @@ -126,13 +143,13 @@ export async function captureProductDemo({ spawnTerminal, normalized, castFor }) ), demoCast: castFor( terminal, - rebaseEvents(terminal.events.slice(0, demo.eventCount)), + rebaseEvents(terminal.events.slice(demoStartIndex, demo.eventCount)), 'Braid product demo', ), steps: [ 'Inspect AgentProfile', 'Run through Local CLI Bridge', - 'Inspect tool activity', + 'Inspect live work and the retained run', 'Analyze the frozen trace with /ask', 'Review the analysis list and details together', ], diff --git a/scripts/capture-visual-definitions.mjs b/scripts/capture-visual-definitions.mjs index f9e80f0..002b3f5 100644 --- a/scripts/capture-visual-definitions.mjs +++ b/scripts/capture-visual-definitions.mjs @@ -19,8 +19,8 @@ export function createStateDefinitions(normalized) { terminal.input('W6 active streaming') terminal.input('\r') await terminal.waitFor( - () => normalized(terminal.screen()).includes('streaming'), - 'streaming', + () => normalized(terminal.screen()).includes('working'), + 'active work', ) const { point, record } = await terminal.captureState() terminal.input('/cancel') @@ -28,7 +28,8 @@ export function createStateDefinitions(normalized) { await terminal.waitFor( () => normalized(terminal.screen()).includes('cancelled') || - normalized(terminal.screen()).includes('completed'), + normalized(terminal.screen()).includes('completed') || + normalized(terminal.screen()).includes('failed'), 'cancellation', ) await terminal.closeNormally() diff --git a/scripts/capture-visual.mjs b/scripts/capture-visual.mjs index 7873f05..b9744ff 100644 --- a/scripts/capture-visual.mjs +++ b/scripts/capture-visual.mjs @@ -138,6 +138,10 @@ async function spawnTerminal(name, columns, rows, extraEnvironment = {}, uiFixtu events.push([Number(((performance.now() - startedAt) / 1_000).toFixed(6)), 'i', data]) session.write(data) } + const resize = (nextColumns, nextRows) => { + emulator.resize(nextColumns, nextRows) + session.resize(nextColumns, nextRows) + } const snapshot = () => ({ screen: `${screen.replace(/[ \t]+$/gmu, '').replace(/\n+$/u, '')}\n`, output, @@ -152,13 +156,16 @@ async function spawnTerminal(name, columns, rows, extraEnvironment = {}, uiFixtu // A captured state may contain nested screens (for example the profile // selector inside the profile editor). Escape each possible layer before // exercising the shell's normal two-step Ctrl+C exit. - for (let layer = 0; layer < 3; layer += 1) { + for (let layer = 0; layer < 4; layer += 1) { input('\u001b') - await sleep(75) + // A bare Escape is decoded after a short ambiguity window. Keep each + // layer separate so a busy process cannot combine them into one sequence. + await sleep(250) + await waitForStable(`${name} escape layer ${layer + 1}`) } input('\u0003') await waitFor( - () => normalized(screen).includes('ctrl+c again to quit'), + () => normalized(screen).toLowerCase().includes('ctrl+c again to quit'), `${name} safe exit prompt`, ) input('\u0003') @@ -213,6 +220,7 @@ async function spawnTerminal(name, columns, rows, extraEnvironment = {}, uiFixtu rows, events, input, + resize, output: () => output, screen: () => screen, snapshot, @@ -311,7 +319,7 @@ async function baselineCapture(columns, rows) { const terminal = await spawnTerminal(`baseline-${columns}x${rows}`, columns, rows) try { await terminal.waitForInterface() - await terminal.waitFor(() => terminal.screen().includes('braid'), 'header') + await terminal.waitFor(() => terminal.screen().includes('Braid starter'), 'profile context') terminal.input('W6 visual proof') terminal.input('\r') await terminal.waitFor( @@ -320,8 +328,8 @@ async function baselineCapture(columns, rows) { ) await terminal.waitFor( () => - normalized(terminal.screen()).includes('completed') || - normalized(terminal.screen()).includes('ready for a message'), + normalized(terminal.screen()).includes('Braid starter') && + !normalized(terminal.screen()).includes('working'), 'final state', ) await terminal.waitForStable('baseline final frame') @@ -351,7 +359,7 @@ async function transcriptKeyboardCapture() { ) try { await terminal.waitForInterface() - await terminal.waitFor(() => terminal.screen().includes('braid'), 'header') + await terminal.waitFor(() => terminal.screen().includes('Braid starter'), 'profile context') for (const prompt of prompts) { terminal.input(prompt) terminal.input('\r') @@ -432,8 +440,8 @@ try { if (definition.name === 'active-streaming') { if (result.record.view?.status !== 'running') throw new Error('active-streaming frame and semantic state disagree') - if (!normalized(result.point.screen).includes('streaming')) - throw new Error('active-streaming frame is not streaming') + if (!normalized(result.point.screen).includes('working')) + throw new Error('active-streaming frame does not show active work') } if ( result.record.capturePhase !== 'atomic-signal-frame' || diff --git a/scripts/capture-w0.mjs b/scripts/capture-w0.mjs index 9da488e..45b041e 100644 --- a/scripts/capture-w0.mjs +++ b/scripts/capture-w0.mjs @@ -76,7 +76,7 @@ async function capture(columns, rows) { } const normalizedScreen = () => screen.replace(/\s+/gu, ' ').trim() - await waitFor(() => screen.includes('braid'), `${columns}x${rows} header`) + await waitFor(() => screen.includes('Braid starter'), `${columns}x${rows} conversation shell`) input('\u0010') await waitFor(() => screen.includes('Commands') && screen.includes('/help'), 'command overlay') input('q') @@ -87,15 +87,14 @@ async function capture(columns, rows) { input('\r') await waitFor( () => - normalizedScreen().includes("Fixture response through pi: Show Braid's first working turn") && - normalizedScreen().includes('ready'), + normalizedScreen().includes("Fixture response through pi: Show Braid's first working turn"), `${columns}x${rows} response`, ) const finalScreen = `${screen.replace(/[ \t]+$/gmu, '').replace(/\n+$/u, '')}\n` captureOutput = false session.write('\u0003') - await waitFor(() => screen.includes('press ctrl+c again to quit'), 'armed capture exit') + await waitFor(() => screen.toLowerCase().includes('ctrl+c again to quit'), 'armed capture exit') session.write('\u0003') const timeout = setTimeout(() => session.kill(), 5_000) const exit = await exited.finally(() => clearTimeout(timeout)) diff --git a/scripts/live-core/setup-tui.mjs b/scripts/live-core/setup-tui.mjs index f4f8e65..2418616 100644 --- a/scripts/live-core/setup-tui.mjs +++ b/scripts/live-core/setup-tui.mjs @@ -129,7 +129,12 @@ export async function configureWithPublicTui( session.write('\r') await sleep(250) session.write('\u0003') - await waitFor(() => output.value, 'ctrl+c again to quit', 'safe exit prompt', 15_000) + await waitFor( + () => output.value.toLowerCase(), + 'ctrl+c again to quit', + 'safe exit prompt', + 15_000, + ) session.write('\u0003') const result = await Promise.race([ exit, diff --git a/scripts/live-demo.mjs b/scripts/live-demo.mjs index f540a3a..9c3729e 100644 --- a/scripts/live-demo.mjs +++ b/scripts/live-demo.mjs @@ -9,18 +9,24 @@ import { promisify } from 'node:util' import { writeCastGif, writeRaster } from './capture-visual-support.mjs' import { configureWithPublicTui } from './live-core/setup-tui.mjs' import { jsonRequest } from './live-demo/http.mjs' -import { assertExactPackageProof, safeManifestAnalysis } from './live-demo/manifest.mjs' +import { + assertExactPackageProof, + packageTarballPath, + safeManifestAnalysis, +} from './live-demo/manifest.mjs' import { assertPublicCapture } from './live-demo/public-safety.mjs' import { castFor, createCapturedTerminal, pause, + terminalFailureDetail, terminalPageProgress, typeText, visibleModelCallNumbers, } from './live-demo/terminal.mjs' import { createLiveDemoWorkspace, + LIVE_DEMO_ANALYST_PROFILE, LIVE_DEMO_PROFILE, LIVE_DEMO_PROMPT, LIVE_DEMO_QUESTION, @@ -36,7 +42,8 @@ const outputRoot = process.env.BRAID_LIVE_DEMO_OUTPUT const packageProofPath = process.env.BRAID_LIVE_DEMO_PACKAGE_PROOF ? process.env.BRAID_LIVE_DEMO_PACKAGE_PROOF : join(repository, 'artifacts', 'verification', 'w6', 'package-proof.json') -const route = 'pi/tangle-router/glm-5.2' +const route = `${LIVE_DEMO_PROFILE.harness}/${LIVE_DEMO_PROFILE.model.default}` +const analysisRoute = `${LIVE_DEMO_ANALYST_PROFILE.harness}/${LIVE_DEMO_ANALYST_PROFILE.model.default}` const columns = 120 const rows = 30 @@ -63,13 +70,15 @@ async function bridgeProof(baseUrl) { jsonRequest(`${baseUrl}/health`), jsonRequest(`${baseUrl}/v1/models`), ]) - const backend = health.backends?.find((candidate) => candidate.name === 'pi') + const backend = health.backends?.find((candidate) => candidate.name === LIVE_DEMO_PROFILE.harness) assert.equal(health.status, 'ok', 'CLI Bridge is not healthy') - assert.equal(backend?.state, 'ready', 'Pi is not ready in CLI Bridge') - assert.ok( - models.data?.some((candidate) => candidate.id === route), - `${route} is not advertised by CLI Bridge`, - ) + assert.equal(backend?.state, 'ready', `${LIVE_DEMO_PROFILE.harness} is not ready in CLI Bridge`) + for (const requiredRoute of [route, analysisRoute]) { + assert.ok( + models.data?.some((candidate) => candidate.id === requiredRoute), + `${requiredRoute} is not advertised by CLI Bridge`, + ) + } return { health, backend } } @@ -88,7 +97,7 @@ async function waitForCompletedRun(terminal, timeoutMs = 300_000) { const terminalRun = lastRecord.state?.runs?.at(-1) if (terminalRun?.status === 'failed' || terminalRun?.status === 'unknown') { throw new Error( - `The coding turn ended ${terminalRun.status}: ${terminalRun.error ?? 'no error'}`, + `The coding turn ended ${terminalRun.status}: ${terminalFailureDetail(lastRecord)}`, ) } await pause(500) @@ -185,7 +194,8 @@ async function main() { } const temporaryRoot = await mkdtemp(join(tmpdir(), 'braid-live-demo-')) const packed = await installPackedBraid(repository, { - tarballPath: process.env.BRAID_RELEASE_TARBALL, + tarballPath: + process.env.BRAID_RELEASE_TARBALL ?? packageTarballPath(packageProofPath, packageProof), }) let terminal try { @@ -204,7 +214,7 @@ async function main() { workspace, keyFile, baseUrl, - 'pi', + LIVE_DEMO_PROFILE.harness, route, profilePath, ) @@ -241,9 +251,9 @@ async function main() { }) await terminal.waitForScreen( (screen) => - screen.includes('Product engineer') && - screen.includes('runner pi') && - screen.includes('tangle-router/glm-5.2') && + screen.includes(`profile ${LIVE_DEMO_PROFILE.name}`) && + screen.includes(LIVE_DEMO_PROFILE.harness) && + screen.includes(LIVE_DEMO_PROFILE.model.default) && screen.includes('Local CLI Bridge'), 'real AgentProfile route', 60_000, @@ -270,32 +280,67 @@ async function main() { await typeText(terminal, LIVE_DEMO_PROMPT, 9) terminal.input('\r') + await terminal.waitForScreen((screen) => screen.includes('working'), 'active coding turn') + terminal.input('\u001bOQ') + await terminal.waitForScreen((screen) => screen.includes('live work'), 'live-work pane') + await pause(900) + terminal.input('\u001bOQ') + await terminal.waitForScreen((screen) => !screen.includes('live work'), 'live-work pane close') const coding = await waitForCompletedRun(terminal) const transcript = transcriptEvidence(coding.record) assert.ok( transcript.assistantMessages.length > 0, 'The coding turn returned no visible assistant result', ) - assert.ok( - transcript.artifactParts.length > 0, - 'The coding turn retained no agent-runtime result artifact', - ) - assert.equal(coding.run.runner, 'pi') + assert.equal(coding.run.runner, LIVE_DEMO_PROFILE.harness) assert.equal(coding.run.model, route) const workspaceProof = await verifyWorkspace(workspace) await terminal.waitForStable('completed coding turn') await pause(800) - terminal.input('\u001bOQ') + await typeText(terminal, '/activity', 24) + terminal.input('\r') await terminal.waitForScreen((screen) => screen.includes('activity'), 'activity browser') await pause(900) - terminal.input('\u001bOQ') + terminal.input('\u001b') await terminal.waitForScreen( (screen) => !screen.includes('activity ·'), 'activity browser close', ) await pause(300) + await typeText(terminal, '/profile', 24) + terminal.input('\r') + await terminal.waitForScreen( + (screen) => + screen.includes('Active profile · Product engineer') && + screen.includes(LIVE_DEMO_ANALYST_PROFILE.name), + 'trace analyst profile choice', + ) + await typeText(terminal, LIVE_DEMO_ANALYST_PROFILE.name, 24) + await terminal.waitForScreen( + (screen) => + screen.includes(LIVE_DEMO_ANALYST_PROFILE.name) && + !screen.includes('CLI Bridge · claude-code · hai'), + 'filtered trace analyst profile', + ) + terminal.input('\r') + await terminal.waitForScreen( + (screen) => screen.includes(`Selected ${LIVE_DEMO_ANALYST_PROFILE.name} · next runs use it`), + 'trace analyst profile selection', + ) + terminal.input('\u001b') + await terminal.waitForScreen( + (screen) => + !screen.includes(`Selected ${LIVE_DEMO_ANALYST_PROFILE.name} · next runs use it`) && + screen.includes(`profile ${LIVE_DEMO_ANALYST_PROFILE.name}`) && + screen.includes( + `${LIVE_DEMO_ANALYST_PROFILE.harness} / ${LIVE_DEMO_ANALYST_PROFILE.model.default}`, + ), + 'trace analyst active route', + ) + await pause(500) + await typeText(terminal, `/ask ${LIVE_DEMO_QUESTION}`, 9) terminal.input('\r') await terminal.waitForScreen( @@ -343,7 +388,7 @@ async function main() { const heroScreen = hero.screen const cast = castFor( terminal, - 'Braid · AgentProfile to Pi, then trace analysis', + 'Braid · AgentProfile coding run, then trace analysis', 'braid --profile Product-engineer --connection Local-CLI-Bridge', hero.eventCount, ) @@ -351,12 +396,12 @@ async function main() { await terminal.closeNormally() await mkdir(outputRoot, { recursive: true }) - const castPath = join(outputRoot, 'braid-live-pi.cast') - const frameCastPath = join(temporaryRoot, 'braid-live-pi-frame.cast') - const gifPath = join(outputRoot, 'braid-live-pi.gif') - const pngPath = join(outputRoot, 'braid-live-pi.png') - const textPath = join(outputRoot, 'braid-live-pi.txt') - const manifestPath = join(outputRoot, 'braid-live-pi.json') + const castPath = join(outputRoot, 'braid-live.cast') + const frameCastPath = join(temporaryRoot, 'braid-live-frame.cast') + const gifPath = join(outputRoot, 'braid-live.gif') + const pngPath = join(outputRoot, 'braid-live.png') + const textPath = join(outputRoot, 'braid-live.txt') + const manifestPath = join(outputRoot, 'braid-live.json') const frameCast = castFor( { ...terminal, events: terminal.events.slice(0, hero.eventCount) }, 'Braid · completed trace analysis', @@ -383,16 +428,23 @@ async function main() { route: { connection: 'Local CLI Bridge', endpoint: baseUrl, - runner: 'pi', + runner: LIVE_DEMO_PROFILE.harness, runnerVersion: bridge.backend.version, - provider: 'tangle-router', - model: 'glm-5.2', + provider: LIVE_DEMO_PROFILE.model.provider ?? null, + model: LIVE_DEMO_PROFILE.model.default, }, profile: { name: LIVE_DEMO_PROFILE.name, reasoningEffort: LIVE_DEMO_PROFILE.model.reasoningEffort, maxOutputTokens: LIVE_DEMO_PROFILE.model.metadata.maxTokens, }, + analysisProfile: { + name: LIVE_DEMO_ANALYST_PROFILE.name, + runner: LIVE_DEMO_ANALYST_PROFILE.harness, + model: LIVE_DEMO_ANALYST_PROFILE.model.default, + reasoningEffort: LIVE_DEMO_ANALYST_PROFILE.model.reasoningEffort, + maxOutputTokens: LIVE_DEMO_ANALYST_PROFILE.model.metadata.maxTokens, + }, task: { prompt: LIVE_DEMO_PROMPT, runId: coding.run.id, diff --git a/scripts/live-demo.test.mjs b/scripts/live-demo.test.mjs index 5c31ce3..f2d59ec 100644 --- a/scripts/live-demo.test.mjs +++ b/scripts/live-demo.test.mjs @@ -7,15 +7,21 @@ import { join } from 'node:path' import test from 'node:test' import { jsonRequest } from './live-demo/http.mjs' -import { assertExactPackageProof, safeManifestAnalysis } from './live-demo/manifest.mjs' +import { + assertExactPackageProof, + packageTarballPath, + safeManifestAnalysis, +} from './live-demo/manifest.mjs' import { assertPublicCapture } from './live-demo/public-safety.mjs' import { castFor, createCapturedTerminal, presentationTimeline, + terminalFailureDetail, terminalPageProgress, visibleModelCallNumbers, } from './live-demo/terminal.mjs' +import { LIVE_DEMO_ANALYST_PROFILE, LIVE_DEMO_PROFILE } from './live-demo/workspace.mjs' function isAlive(pid) { try { @@ -102,6 +108,30 @@ test('live demo timeline starts on the configured screen and preserves captured ) }) +test('live demo profile leaves native tool policy with the selected harness', () => { + assert.equal(LIVE_DEMO_PROFILE.harness, 'claude-code') + assert.equal(LIVE_DEMO_PROFILE.model.default, 'opus') + assert.equal('tools' in LIVE_DEMO_PROFILE, false) + assert.equal('permissions' in LIVE_DEMO_PROFILE, false) + assert.equal(LIVE_DEMO_ANALYST_PROFILE.harness, 'claude-code') + assert.equal(LIVE_DEMO_ANALYST_PROFILE.model.default, 'sonnet') + assert.equal('tools' in LIVE_DEMO_ANALYST_PROFILE, false) + assert.equal('permissions' in LIVE_DEMO_ANALYST_PROFILE, false) +}) + +test('live demo failures preserve the sanitized Braid diagnostic', () => { + assert.equal( + terminalFailureDetail({ + state: { + lastError: 'portable profile rejected', + runs: [{ id: 'run-live', status: 'failed' }], + messages: [], + }, + }), + 'portable profile rejected', + ) +}) + test('jsonRequest aborts a response that never finishes', async () => { const server = createServer((_request, response) => { response.writeHead(200, { 'content-type': 'application/json' }) @@ -140,6 +170,8 @@ test('live manifest uses the typed public analysis execution view', () => { status: 'completed', lines: ['• [citation-1] Complete finding'], analysisFindingCount: 1, + analysisSupportedFindingCount: 1, + analysisCitationSupport: 'passed', analysisExecution: { configuredModel: 'tangle-router/glm-5.2', observedModels: ['glm-5.2'], @@ -175,6 +207,8 @@ test('live manifest uses the typed public analysis execution view', () => { id: 'analysis-live', status: 'completed', findings: 1, + supportedFindings: 1, + citationSupport: 'passed', configuredModel: 'tangle-router/glm-5.2', observedModels: ['glm-5.2'], modelCalls: 1, @@ -201,6 +235,27 @@ test('live manifest uses the typed public analysis execution view', () => { }) }) +test('live manifest rejects unsupported analysis findings', () => { + const record = { + state: { analyses: [] }, + view: { + activity: [{ kind: 'analysis', status: 'complete', entityId: 'analysis-live' }], + entityDetails: [ + { + entityType: 'analysis', + entityId: 'analysis-live', + status: 'completed', + lines: [], + analysisFindingCount: 1, + analysisSupportedFindingCount: 0, + analysisCitationSupport: 'failed', + }, + ], + }, + } + assert.throws(() => safeManifestAnalysis(record), /citation check did not pass/u) +}) + test('live demo accepts only the exact package proof', () => { const proof = { gitCommit: 'a'.repeat(40), @@ -229,6 +284,22 @@ test('live demo accepts only the exact package proof', () => { } }) +test('live demo installs the archive beside its package proof', () => { + const proofPath = join('/tmp', 'braid-proof', 'package-proof.json') + assert.equal( + packageTarballPath(proofPath, { tarball: 'tangle-network-braid-0.1.1.tgz' }), + join('/tmp', 'braid-proof', 'tangle-network-braid-0.1.1.tgz'), + ) + assert.throws( + () => packageTarballPath(proofPath, { tarball: '../different-build.tgz' }), + /must be a file name/u, + ) + assert.throws( + () => packageTarballPath(proofPath, { tarball: 'different-build.zip' }), + /gzip archive/u, + ) +}) + test('public capture rejects the credential patterns mirrored from the sanitizer', async (t) => { const filler = 'A'.repeat(32) const cases = [ diff --git a/scripts/live-demo/manifest.mjs b/scripts/live-demo/manifest.mjs index 2eccd28..039b0d1 100644 --- a/scripts/live-demo/manifest.mjs +++ b/scripts/live-demo/manifest.mjs @@ -1,4 +1,15 @@ import assert from 'node:assert/strict' +import { basename, dirname, resolve } from 'node:path' + +export function packageTarballPath(proofPath, proof) { + assert.equal( + basename(proof.tarball ?? ''), + proof.tarball, + 'The package proof tarball must be a file name', + ) + assert.match(proof.tarball, /\.tgz$/u, 'The package proof tarball must be a gzip archive') + return resolve(dirname(proofPath), proof.tarball) +} export function assertExactPackageProof(proof, expected) { assert.ok(proof && typeof proof === 'object', 'The package proof is not an object') @@ -23,6 +34,16 @@ export function safeManifestAnalysis(record) { const findings = detail.analysisFindingCount assert.ok(Number.isInteger(findings), 'The real /ask analysis omitted its typed finding count') assert.ok(findings > 0, 'The real /ask analysis returned no findings') + assert.equal( + detail.analysisCitationSupport, + 'passed', + 'The real /ask citation check did not pass', + ) + assert.equal( + detail.analysisSupportedFindingCount, + findings, + 'The real /ask analysis retained an unsupported finding', + ) const usage = record.view?.sessionUsage?.analyses assert.equal(usage?.sourceCount, 1, 'The live demo expected exactly one analysis usage source') const execution = detail.analysisExecution @@ -42,6 +63,8 @@ export function safeManifestAnalysis(record) { id: analysis.entityId, status: detail.status, findings, + supportedFindings: detail.analysisSupportedFindingCount, + citationSupport: detail.analysisCitationSupport, configuredModel: execution.configuredModel ?? null, observedModels: [...execution.observedModels], modelCalls: modelCalls?.length ?? null, diff --git a/scripts/live-demo/terminal.mjs b/scripts/live-demo/terminal.mjs index 1da090b..0ae992a 100644 --- a/scripts/live-demo/terminal.mjs +++ b/scripts/live-demo/terminal.mjs @@ -30,6 +30,20 @@ export function visibleModelCallNumbers(screen) { ].sort((left, right) => left - right) } +export function terminalFailureDetail(record) { + const run = record.state?.runs?.at(-1) + const errorPart = record.state?.messages + ?.findLast((message) => message.runId === run?.id) + ?.parts?.findLast((part) => part.kind === 'error' && part.text) + return ( + run?.error ?? + record.state?.lastError ?? + run?.terminalReason ?? + errorPart?.text ?? + 'no public error detail' + ) +} + export function pause(milliseconds) { return new Promise((resolve) => setTimeout(resolve, milliseconds)) } diff --git a/scripts/live-demo/workspace.mjs b/scripts/live-demo/workspace.mjs index f8f77af..a103a59 100644 --- a/scripts/live-demo/workspace.mjs +++ b/scripts/live-demo/workspace.mjs @@ -4,10 +4,9 @@ import { join } from 'node:path' export const LIVE_DEMO_PROFILE = Object.freeze({ name: 'Product engineer', description: 'Implements one bounded change and proves it with tests.', - harness: 'pi', + harness: 'claude-code', model: { - provider: 'tangle-router', - default: 'tangle-router/glm-5.2', + default: 'opus', reasoningEffort: 'high', metadata: { maxTokens: 32768 }, }, @@ -18,8 +17,24 @@ export const LIVE_DEMO_PROFILE = Object.freeze({ 'Run the relevant tests and report the exact result.', ], }, - tools: { read: true, write: true, shell: true }, - permissions: { read: 'allow', write: 'allow', shell: 'allow' }, +}) + +export const LIVE_DEMO_ANALYST_PROFILE = Object.freeze({ + name: 'Trace analyst', + description: 'Reviews a completed run and cites the retained execution evidence.', + harness: 'claude-code', + model: { + default: 'sonnet', + reasoningEffort: 'high', + metadata: { maxTokens: 16384 }, + }, + prompt: { + instructions: [ + 'Base every finding on the retained execution evidence.', + 'Cite the exact evidence that supports each finding.', + 'Separate verified facts from recommendations.', + ], + }, }) export const LIVE_DEMO_PROMPT = @@ -31,9 +46,11 @@ export async function createLiveDemoWorkspace(root) { const workspace = join(root, 'braid-demo') const sourceRoot = join(workspace, 'src') const testRoot = join(workspace, 'test') + const profileRoot = join(workspace, '.braid') await Promise.all([ mkdir(sourceRoot, { recursive: true, mode: 0o700 }), mkdir(testRoot, { recursive: true, mode: 0o700 }), + mkdir(profileRoot, { recursive: true, mode: 0o700 }), ]) const profilePath = join(workspace, 'braid.profile.json') await Promise.all([ @@ -76,6 +93,11 @@ export async function createLiveDemoWorkspace(root) { { mode: 0o600 }, ), writeFile(profilePath, `${JSON.stringify(LIVE_DEMO_PROFILE, null, 2)}\n`, { mode: 0o600 }), + writeFile( + join(profileRoot, 'profile.json'), + `${JSON.stringify(LIVE_DEMO_ANALYST_PROFILE, null, 2)}\n`, + { mode: 0o600 }, + ), ]) return { workspace, profilePath } } diff --git a/scripts/live-required.test.mjs b/scripts/live-required.test.mjs index 39b0dee..ee2cae4 100644 --- a/scripts/live-required.test.mjs +++ b/scripts/live-required.test.mjs @@ -1,6 +1,7 @@ import assert from 'node:assert/strict' import { execFileSync } from 'node:child_process' -import { access, chmod, mkdir, mkdtemp, rm, writeFile } from 'node:fs/promises' +import { EventEmitter } from 'node:events' +import { access, chmod, mkdir, mkdtemp, readFile, rm, writeFile } from 'node:fs/promises' import { tmpdir } from 'node:os' import { join, resolve } from 'node:path' import test from 'node:test' @@ -25,10 +26,34 @@ import { prepareProductionWorkspace, runHeadlessTurn, } from './live-required/headless.mjs' -import { runMatrixAdapter } from './live-required/tangle.mjs' +import { runMatrixAdapter, runSandbox } from './live-required/tangle.mjs' +import { executionLatencyDistribution } from './live-required/tangle-sandbox-braid-execution-soak.mjs' +import { + assertExactResumeEvidence, + cancellationReplayDetected, + cleanupProof, + duplicateTurnDetected, + PROOF_OWNER, + runStressProof, +} from './live-required/tangle-sandbox-stress.mjs' +import { runWorker } from './live-required/tangle-sandbox-worker.mjs' const repository = resolve(new URL('../', import.meta.url).pathname) +test('cloud execution stress reports exact small-sample latency distributions', () => { + assert.deepEqual( + executionLatencyDistribution([{ elapsedMs: 30 }, { elapsedMs: 10 }, { elapsedMs: 20 }]), + { n: 3, min: 10, median: 20, p90: 30, max: 30 }, + ) + assert.deepEqual(executionLatencyDistribution([]), { + n: 0, + min: null, + median: null, + p90: null, + max: null, + }) +}) + function protectedEnvironment() { const environment = { ...process.env } for (const name of [ @@ -63,6 +88,63 @@ function protectedEnvironment() { return environment } +const TANGLE_SANDBOX_CHECKS = [ + 'marker', + 'environment-id', + 'workspace-read-write-exec-git', + 'sigkill-reconnect', + 'exclusive-replay', + 'follow-up-session', + 'cancel-retry-conflict', + 'exact-resource-cleanup', +] + +function validTangleSandboxReceiptInput(overrides = {}) { + const cloudControl = { + provider: 'tangle-sandbox', + environmentId: 'sandbox-environment-1', + sessionId: 'sandbox-session-1', + executionId: 'sandbox-execution-1', + runId: 'sandbox-run-1', + requestDigest: `sha256:${'a'.repeat(64)}`, + } + return { + invocationId: 'live-required-tangle-sandbox-proof', + operation: PROOF_OPERATIONS.tangleSandbox, + status: 'passed', + startedAt: '2026-08-10T00:00:00.000Z', + completedAt: '2026-08-10T00:00:01.000Z', + config: { + endpoint: 'https://sandbox.tangle.tools', + connectionId: 'connection-live-07', + connectionKind: 'tangle-sandbox', + credentialConfigured: true, + model: 'glm-5.2', + runner: 'pi', + }, + runIds: ['local-run-1', 'local-run-follow-up', 'local-run-cancelled'], + environmentId: 'local-environment-1', + facts: { + environmentId: 'local-environment-1', + resumedRunId: 'local-run-1', + followUpRunId: 'local-run-follow-up', + cancelledRunId: 'local-run-cancelled', + resumeFromCursor: 'provider-cursor-before-kill', + finalCursor: 'provider-cursor-final', + cloudControl, + exactResource: true, + activeResourceDelta: 0, + }, + observations: { + phase: 'completed', + cloudControl, + usage: { inputTokens: 12, outputTokens: 8, costUsd: 0.001 }, + }, + checks: TANGLE_SANDBOX_CHECKS, + ...overrides, + } +} + function expectedFailureOutput(scope, environment, expectedStatus = 1) { let failure try { @@ -121,7 +203,12 @@ test('protected live scopes emit unavailable release evidence without credential /requires protected live-provider credentials\/adapters|live check requires/u, ) assert.match(output, /BRAID_RELEASE_RESULT_JSON=\{"status":"unavailable"/u) - if (scope === 'live-tangle') assert.match(output, /"status":"partial"/u) + if (scope === 'live-tangle') { + assert.match(output, /"row":"LIVE-07","status":"unavailable"/u) + assert.doesNotMatch(output, /"row":"LIVE-07","status":"partial"/u) + for (const row of ['LIVE-08', 'LIVE-09', 'LIVE-10']) + assert.match(output, new RegExp(`"row":"${row}","status":"unavailable"`, 'u')) + } assert.doesNotMatch(output, /\b(?:passed|success)\b/iu) return true }, @@ -248,7 +335,18 @@ test('receipts use a fixed schema and bind to one operation and invocation', () }, runIds: ['run-live-test'], environmentId: 'environment-live-test', - facts: { environmentId: 'environment-live-test' }, + facts: { + environmentId: 'environment-live-test', + resumedRunId: null, + followUpRunId: null, + cancelledRunId: null, + resumeFromCursor: null, + finalCursor: null, + cloudControl: null, + exactResource: null, + activeResourceDelta: null, + }, + observations: null, checks: ['marker', 'environment-id'], }) assert.equal(receipt.schema, PUBLIC_EVIDENCE_SCHEMA) @@ -258,6 +356,7 @@ test('receipts use a fixed schema and bind to one operation and invocation', () 'connection', 'facts', 'invocationId', + 'observations', 'operation', 'run', 'schema', @@ -285,6 +384,179 @@ test('receipts use a fixed schema and bind to one operation and invocation', () ) }) +test('passed Tangle Sandbox receipts preserve redacted observations and exact release proof', () => { + const secret = 'live-required-tangle-sandbox-observation-secret-7f3a' + const input = validTangleSandboxReceiptInput() + input.observations = { + ...input.observations, + apiKey: secret, + nested: { authorization: `Bearer ${secret}`, retained: true }, + } + const receipt = proofReceipt({ + ...input, + environment: { BRAID_TANGLE_SANDBOX_API_KEY: secret }, + }) + + assert.equal(receipt.status, 'passed') + assert.deepEqual(receipt.facts.cloudControl, input.facts.cloudControl) + assert.equal(receipt.facts.activeResourceDelta, 0) + assert.equal(receipt.facts.exactResource, true) + assert.equal(receipt.observations.phase, 'completed') + assert.equal(receipt.observations.apiKey, '[REDACTED]') + assert.equal(receipt.observations.nested.authorization, '[REDACTED]') + assert.deepEqual( + resultSummary('live-tangle', { status: 'passed', evidence: receipt }).evidence.observations, + receipt.observations, + ) +}) + +test('LIVE-07 wiring carries cloud identity, cleanup proof, and observations into evidence', async () => { + const input = validTangleSandboxReceiptInput() + const firstRun = { id: input.runIds[0], environmentId: input.environmentId } + const proof = { + status: 'passed', + config: input.config, + runs: { + first: firstRun, + resumed: firstRun, + followUp: { id: input.facts.followUpRunId, environmentId: input.environmentId }, + cancelled: { id: input.facts.cancelledRunId, environmentId: input.environmentId }, + }, + replay: { + resumeFromCursor: input.facts.resumeFromCursor, + finalCursor: input.facts.finalCursor, + }, + cleanup: { + exactResource: input.facts.exactResource, + activeResourceDelta: input.facts.activeResourceDelta, + }, + progress: { firstControlRef: input.facts.cloudControl }, + detailed: true, + } + const cohort = { + status: 'passed', + failures: [], + requestedRuns: 3, + attemptedRuns: 3, + concurrency: 2, + cleanup: { exactProofs: 3, exactResourcesRemaining: 0, activeResourceDelta: 0 }, + attempts: Array.from({ length: 3 }, (_, index) => ({ + index, + proof: { + ...proof, + proofId: `proof-${index}`, + progress: { + firstControlRef: { + ...proof.progress.firstControlRef, + environmentId: + index === 0 + ? proof.progress.firstControlRef.environmentId + : `cloud-environment-${index}`, + }, + }, + }, + })), + detailed: true, + } + const result = await runSandbox({ + repository, + environment: {}, + binary: 'unused-injected-binary', + invocationId: input.invocationId, + stressRunner: async () => cohort, + }) + + assert.equal(result.evidence.status, 'passed') + assert.deepEqual(result.evidence.facts.cloudControl, input.facts.cloudControl) + assert.equal(result.evidence.facts.exactResource, true) + assert.equal(result.evidence.facts.activeResourceDelta, 0) + assert.equal(result.evidence.observations.detailed, true) +}) + +test('passed Tangle Sandbox receipts reject null or forged acceptance facts', () => { + const valid = proofReceipt(validTangleSandboxReceiptInput()) + const cases = [ + [ + 'empty local run IDs', + { ...valid, run: { ...valid.run, ids: [] } }, + /at least one local run ID/u, + ], + [ + 'null cloud identity', + { ...valid, facts: { ...valid.facts, cloudControl: null } }, + /exact cloud control identity/u, + ], + [ + 'wrong cloud provider', + { + ...valid, + facts: { + ...valid.facts, + cloudControl: { ...valid.facts.cloudControl, provider: 'other-provider' }, + }, + }, + /wrong provider/u, + ], + [ + 'missing acknowledged cursor', + { ...valid, facts: { ...valid.facts, resumeFromCursor: null } }, + /facts\.resumeFromCursor/u, + ], + [ + 'missing model configuration', + { ...valid, connection: { ...valid.connection, model: null } }, + /connection\.model/u, + ], + [ + 'unconfirmed exact cleanup', + { ...valid, facts: { ...valid.facts, exactResource: false } }, + /exactResource=true/u, + ], + [ + 'non-zero resource delta', + { ...valid, facts: { ...valid.facts, activeResourceDelta: 1 } }, + /activeResourceDelta=0/u, + ], + ['missing observations', { ...valid, observations: null }, /redacted observations/u], + [ + 'unredacted observation credential', + { ...valid, observations: { apiKey: 'raw-secret' } }, + /observations\.apiKey must be redacted/u, + ], + ] + + for (const [label, forged, expected] of cases) + assert.throws(() => assertProofReceipt(forged), expected, label) + assert.throws(() => assertProofReceipt(null), /must be an object/u) +}) + +test('failed Tangle Sandbox receipts remain representable without passed-only facts', () => { + const receipt = proofReceipt({ + ...validTangleSandboxReceiptInput(), + status: 'failed', + runIds: [], + environmentId: null, + facts: { + environmentId: null, + resumedRunId: null, + followUpRunId: null, + cancelledRunId: null, + resumeFromCursor: null, + finalCursor: null, + cloudControl: null, + exactResource: null, + activeResourceDelta: null, + }, + observations: null, + checks: ['marker'], + }) + + assert.equal(receipt.status, 'failed') + assert.deepEqual(receipt.run.ids, []) + assert.equal(receipt.facts.cloudControl, null) + assert.equal(receipt.observations, null) +}) + test('passed trace-analysis receipts require complete checks and model-call evidence', () => { const receipt = proofReceipt({ invocationId: 'live-required-trace-proof', @@ -631,3 +903,621 @@ test('provider configuration names protected references and never accepts a miss /protected credential/u, ) }) + +class FakeWorkerChild extends EventEmitter { + constructor() { + super() + this.exitCode = null + this.signalCode = null + this.killSignals = [] + this.inputHandler = () => undefined + this.stdin = { + write: (value) => { + this.inputHandler(String(value)) + return true + }, + } + } + + finish(code = 0) { + if (this.exitCode !== null || this.signalCode !== null) return + this.exitCode = code + queueMicrotask(() => this.emit('exit', code, null)) + } + + kill(signal) { + this.killSignals.push(signal) + if (this.exitCode !== null || this.signalCode !== null) return false + this.signalCode = signal + queueMicrotask(() => this.emit('exit', null, signal)) + return true + } +} + +function fakeWorker(mode, records = []) { + const child = new FakeWorkerChild() + const waiters = new Set() + const worker = { + mode, + child, + records, + waiters, + stderr: [], + push(record) { + records.push(record) + for (const waiter of waiters) waiter(record) + }, + } + return worker +} + +function defaultResumedRecord(values) { + const executionId = values['execution-id'] + const cursor = values.cursor + return { + type: 'resumed', + environmentId: values['environment-id'], + sessionId: values['session-id'], + executionId, + reconnectToResultMs: 1, + replay: [ + { id: cursor, type: 'execution.started', executionId }, + { id: 'event-2', type: 'result', executionId }, + ], + cursorWasInclusive: true, + result: { + status: 'success', + executionId, + markerMatched: true, + usage: { inputTokens: 2, outputTokens: 1 }, + costUsd: 0.001, + }, + sessionStatus: 'completed', + completedTurn: { found: true, executionId }, + messageStates: [], + duplicate: { + dispatched: false, + executionId, + sameExecution: true, + alreadyExisted: true, + cancellation: null, + }, + workspaceRetained: true, + } +} + +function createFakeWorkerFactory({ + resumeRecord, + cancelRecord, + dispatchFailure, + cursorFailure, +} = {}) { + const workers = [] + let watcher + const factory = (mode, values) => { + const worker = fakeWorker(mode) + workers.push(worker) + if (mode === 'dispatch') { + if (dispatchFailure) { + worker.push({ type: 'error', message: dispatchFailure }) + } else { + worker.push({ + type: 'created', + environmentId: 'environment-1', + proofId: values['proof-id'], + sessionId: values['session-id'], + turnId: values['turn-id'], + marker: values.marker, + createdMs: 1, + workspaceMs: 1, + workspace: { + readMatched: true, + gitExitCode: 0, + gitCommit: 'a'.repeat(40), + resourceUsageReported: true, + }, + }) + worker.child.inputHandler = (value) => { + if (value !== 'dispatch\n') return + worker.push({ + type: 'admitted', + environmentId: 'environment-1', + sessionId: values['session-id'], + executionId: 'execution-1', + turnId: values['turn-id'], + marker: values.marker, + prompt: 'fake prompt', + dispatchMs: 1, + dispatched: true, + alreadyExisted: false, + }) + watcher?.push( + cursorFailure + ? { type: 'error', message: cursorFailure } + : { + type: 'cursor', + environmentId: 'environment-1', + sessionId: values['session-id'], + eventId: 'event-1', + eventType: 'execution.started', + executionId: 'execution-1', + }, + ) + } + } + } else if (mode === 'watch') { + watcher = worker + worker.push({ type: 'listening', environmentId: values['environment-id'] }) + } else if (mode === 'resume') { + worker.push(resumeRecord ?? defaultResumedRecord(values)) + queueMicrotask(() => worker.child.finish()) + } else if (mode === 'cancel') { + worker.push( + cancelRecord ?? { + type: 'cancelled', + environmentId: values['environment-id'], + sessionId: values['session-id'], + executionId: values['execution-id'], + first: { cancelled: true }, + second: { cancelled: false }, + replayDetected: true, + sessionStatus: 'cancelled', + }, + ) + queueMicrotask(() => worker.child.finish()) + } + return worker + } + return { factory, workers } +} + +function createFakeClient(proofId, { includeExact = true } = {}) { + const exact = { + id: 'environment-1', + name: proofId, + metadata: { owner: PROOF_OWNER, proofId }, + deleted: false, + deleteCalls: 0, + async dispatchPrompt() { + return { + sessionId: 'cancel-session', + executionId: 'cancel-execution', + status: 'running', + alreadyExisted: false, + dispatched: true, + } + }, + async delete() { + this.deleted = true + this.deleteCalls += 1 + }, + } + const other = { + id: 'environment-other', + name: proofId, + metadata: { owner: 'someone-else', proofId }, + deleted: false, + deleteCalls: 0, + async delete() { + this.deleted = true + this.deleteCalls += 1 + }, + } + let usageCalls = 0 + return { + exact, + other, + async usage() { + usageCalls += 1 + return usageCalls === 1 + ? { activeSandboxes: 1, totalSandboxes: 1, computeMinutes: 0 } + : { activeSandboxes: 0, totalSandboxes: 1, computeMinutes: 1 } + }, + async get(id) { + return id === exact.id && includeExact ? exact : null + }, + async list() { + return [...(includeExact && !exact.deleted ? [exact] : []), other] + }, + } +} + +function workerArguments(values) { + return [ + 'node', + 'tangle-sandbox-worker.mjs', + ...Object.entries(values).flatMap(([name, value]) => [`--${name}`, value]), + ] +} + +test('Tangle sandbox stress uses exact replay, duplicate, cancellation, and SIGKILL checks', async () => { + const proofId = 'proof-hardened-success' + const client = createFakeClient(proofId) + const fakeWorkers = createFakeWorkerFactory() + const proof = await runStressProof({ + client, + workerFactory: fakeWorkers.factory, + coordinates: { + proofId, + sessionId: 'session-1', + turnId: 'turn-1', + marker: 'MARKER-1', + }, + }) + + assert.equal(proof.status, 'passed') + assert.deepEqual(proof.gaps, []) + assert.equal(proof.checks.processKilledAfterDispatch, true) + assert.equal(proof.checks.processKilledAfterCursor, true) + assert.equal(proof.checks.exactResultIdentity, true) + assert.equal(proof.checks.turnIdempotency, true) + assert.equal(proof.checks.turnIdempotencyReceipt, true) + assert.equal(proof.checks.cancellationOperationReplay, true) + assert.deepEqual(proof.exactRun, { + sessionId: 'session-1', + executionId: 'execution-1', + turnId: 'turn-1', + }) + assert.deepEqual(proof.events, { + firstObservedId: 'event-1', + firstObservedType: 'execution.started', + replayCount: 2, + replayUniqueCount: 2, + providerCursorInclusive: true, + }) + assert.deepEqual( + fakeWorkers.workers + .filter((worker) => worker.mode === 'dispatch' || worker.mode === 'watch') + .map((worker) => worker.child.killSignals), + [['SIGKILL'], ['SIGKILL']], + ) + assert.equal(client.exact.deleteCalls, 1) + assert.equal(client.other.deleteCalls, 0) + assert.equal(duplicateTurnDetected(proof.idempotency.duplicate, 'execution-1'), true) + assert.equal( + duplicateTurnDetected( + { dispatched: false, executionId: 'execution-1', sameExecution: true }, + 'execution-1', + ), + false, + ) + const duplicateGapProofId = 'proof-duplicate-gap' + const duplicateGap = defaultResumedRecord({ + 'environment-id': 'environment-1', + 'session-id': 'session-gap', + 'execution-id': 'execution-1', + cursor: 'event-1', + }) + duplicateGap.duplicate.alreadyExisted = false + const duplicateGapProof = await runStressProof({ + client: createFakeClient(duplicateGapProofId), + workerFactory: createFakeWorkerFactory({ resumeRecord: duplicateGap }).factory, + coordinates: { + proofId: duplicateGapProofId, + sessionId: 'session-gap', + turnId: 'turn-gap', + marker: 'MARKER-GAP', + }, + }) + assert.equal(duplicateGapProof.status, 'passed-with-gaps') + assert.deepEqual(duplicateGapProof.gaps, ['turn-idempotency-receipt']) + assert.equal(duplicateGapProof.checks.turnIdempotency, false) + assert.equal(cancellationReplayDetected(proof.cancellation, 'cancel-execution'), true) + assert.equal( + cancellationReplayDetected( + { + executionId: 'cancel-execution', + first: { cancelled: false }, + second: { cancelled: false }, + }, + 'cancel-execution', + ), + false, + ) + assert.equal( + cancellationReplayDetected( + { + executionId: 'other-execution', + first: { cancelled: true }, + second: { cancelled: false }, + sessionStatus: 'cancelled', + }, + 'cancel-execution', + ), + false, + ) + assert.equal( + cancellationReplayDetected( + { + executionId: 'cancel-execution', + first: { cancelled: true }, + second: { cancelled: false }, + sessionStatus: 'running', + }, + 'cancel-execution', + ), + false, + ) +}) + +test('Tangle sandbox stress persists partial artifacts for assertion and worker failure', async () => { + const root = await mkdtemp(join(tmpdir(), 'braid-tangle-sandbox-partial-')) + try { + const assertionProofId = 'proof-failure' + const assertionClient = createFakeClient(assertionProofId) + const badResume = defaultResumedRecord({ + 'environment-id': 'environment-1', + 'session-id': 'session-1', + 'execution-id': 'execution-1', + cursor: 'event-1', + }) + badResume.result.executionId = 'wrong-execution' + const assertionWorkers = createFakeWorkerFactory({ resumeRecord: badResume }) + const assertionArtifact = join(root, 'assertion', 'proof.json') + await assert.rejects( + () => + runStressProof({ + client: assertionClient, + workerFactory: assertionWorkers.factory, + outputPath: assertionArtifact, + coordinates: { + proofId: assertionProofId, + sessionId: 'session-1', + turnId: 'turn-1', + marker: 'MARKER-1', + }, + }), + /resume result execution ID changed during replay/u, + ) + const assertionEvidence = JSON.parse(await readFile(assertionArtifact, 'utf8')) + assert.equal(assertionEvidence.status, 'failed') + assert.equal(assertionEvidence.failure.name, 'AssertionError') + assert.equal(assertionEvidence.environmentId, 'environment-1') + assert.equal(assertionEvidence.phase, 'resume') + assert.deepEqual(assertionEvidence.progress.dispatcherDeath, { + code: null, + signal: 'SIGKILL', + }) + assert.deepEqual(assertionEvidence.progress.watcherDeath, { + code: null, + signal: 'SIGKILL', + }) + assert.equal(assertionEvidence.progress.resumed.result.executionId, 'wrong-execution') + + const workerFailureProofId = 'proof-worker-failure' + const workerFailureArtifact = join(root, 'worker', 'proof.json') + const workerFailureClient = createFakeClient(workerFailureProofId, { includeExact: false }) + const workerFailureWorkers = createFakeWorkerFactory({ dispatchFailure: 'fake worker failed' }) + await assert.rejects( + () => + runStressProof({ + client: workerFailureClient, + workerFactory: workerFailureWorkers.factory, + outputPath: workerFailureArtifact, + coordinates: { + proofId: workerFailureProofId, + sessionId: 'session-2', + turnId: 'turn-2', + marker: 'MARKER-2', + }, + }), + /fake worker failed/u, + ) + const workerEvidence = JSON.parse(await readFile(workerFailureArtifact, 'utf8')) + assert.equal(workerEvidence.status, 'failed') + assert.equal(workerEvidence.failure.message, 'fake worker failed') + assert.equal(workerEvidence.phase, 'dispatch-create') + assert.equal(workerEvidence.cleanupConfirmed, true) + + const partialProofId = 'proof-partial-admission' + const partialArtifact = join(root, 'partial', 'proof.json') + const partialWorkers = createFakeWorkerFactory({ + cursorFailure: 'watcher failed after admission', + }) + await assert.rejects( + () => + runStressProof({ + client: createFakeClient(partialProofId), + workerFactory: partialWorkers.factory, + outputPath: partialArtifact, + coordinates: { + proofId: partialProofId, + sessionId: 'session-3', + turnId: 'turn-3', + marker: 'MARKER-3', + }, + }), + /watcher failed after admission/u, + ) + const partialEvidence = JSON.parse(await readFile(partialArtifact, 'utf8')) + assert.equal(partialEvidence.status, 'failed') + assert.equal(partialEvidence.phase, 'dispatch') + assert.equal(partialEvidence.progress.admitted.executionId, 'execution-1') + } finally { + await rm(root, { recursive: true, force: true }) + } +}) + +test('Tangle sandbox cleanup deletes only the exact proof tag', async () => { + const proofId = 'proof-exact-cleanup' + const exact = { + id: 'exact', + name: proofId, + metadata: { owner: PROOF_OWNER, proofId }, + deleteCalls: 0, + async delete() { + this.deleteCalls += 1 + }, + } + const wrongOwner = { + id: 'wrong-owner', + name: proofId, + metadata: { owner: 'not-braid', proofId }, + deleteCalls: 0, + async delete() { + this.deleteCalls += 1 + }, + } + const wrongProof = { + id: 'wrong-proof', + name: proofId, + metadata: { owner: PROOF_OWNER, proofId: 'other-proof' }, + deleteCalls: 0, + async delete() { + this.deleteCalls += 1 + }, + } + const pagedExact = { + id: 'exact-page-2', + name: proofId, + metadata: { owner: PROOF_OWNER, proofId }, + deleteCalls: 0, + async delete() { + this.deleteCalls += 1 + }, + } + const unrelated = Array.from({ length: 98 }, (_, index) => ({ + id: `unrelated-${index}`, + name: `unrelated-${index}`, + metadata: {}, + })) + const client = { + async get(id) { + assert.equal(id, exact.id) + return exact + }, + async list({ offset = 0 } = {}) { + if (offset === 0) return [wrongOwner, wrongProof, ...unrelated] + if (offset === 100 && exact.deleteCalls === 0) return [exact, pagedExact] + return [] + }, + } + + assert.equal(await cleanupProof(client, proofId, exact.id), true) + assert.equal(exact.deleteCalls, 1) + assert.equal(pagedExact.deleteCalls, 1) + assert.equal(wrongOwner.deleteCalls, 0) + assert.equal(wrongProof.deleteCalls, 0) +}) + +test('Tangle sandbox worker preserves exact replay and cancellation acknowledgements through fakes', async () => { + const replayRecords = [] + const interruptOptions = [] + const session = { + async *events(options) { + assert.deepEqual(options, { since: 'event-1', executionId: 'execution-1' }) + yield { id: 'event-1', type: 'execution.started', data: { executionId: 'execution-1' } } + yield { id: 'event-2', type: 'result', executionId: 'execution-1' } + }, + async result(options) { + assert.deepEqual(options, { executionId: 'execution-1' }) + return { + status: 'success', + executionId: 'execution-1', + response: 'MARKER-1', + usage: { inputTokens: 2, outputTokens: 1 }, + costUsd: 0.001, + } + }, + async status() { + return { status: interruptOptions.length === 0 ? 'completed' : 'cancelled' } + }, + async interrupt(options) { + interruptOptions.push(options) + return { cancelled: interruptOptions.length === 1 } + }, + } + const box = { + async read() { + return 'MARKER-1\n' + }, + session() { + return session + }, + async findCompletedTurn() { + return { result: { executionId: 'execution-1' } } + }, + async messages() { + return [{ role: 'assistant', metadata: { status: 'completed', turnId: 'turn-1' } }] + }, + async dispatchPrompt() { + return { + dispatched: false, + executionId: 'execution-1', + alreadyExisted: true, + } + }, + } + const client = { + async get() { + return box + }, + } + const argv = workerArguments({ + 'environment-id': 'environment-1', + 'session-id': 'session-1', + 'execution-id': 'execution-1', + 'turn-id': 'turn-1', + marker: 'MARKER-1', + cursor: 'event-1', + }) + + await runWorker('resume', { argv, client, write: (record) => replayRecords.push(record) }) + assert.equal(replayRecords.length, 1) + assert.deepEqual(replayRecords[0].replay, [ + { id: 'event-1', type: 'execution.started', executionId: 'execution-1' }, + { id: 'event-2', type: 'result', executionId: 'execution-1' }, + ]) + assert.deepEqual(replayRecords[0].result, { + status: 'success', + executionId: 'execution-1', + markerMatched: true, + usage: { inputTokens: 2, outputTokens: 1 }, + costUsd: 0.001, + }) + assert.deepEqual(replayRecords[0].duplicate, { + dispatched: false, + executionId: 'execution-1', + sameExecution: true, + alreadyExisted: true, + cancellation: null, + }) + + const cancellationRecords = [] + await runWorker('cancel', { + argv: workerArguments({ + 'environment-id': 'environment-1', + 'session-id': 'session-1', + 'execution-id': 'execution-1', + }), + client, + write: (record) => cancellationRecords.push(record), + }) + assert.deepEqual(interruptOptions, [ + { executionId: 'execution-1' }, + { executionId: 'execution-1' }, + ]) + assert.equal(cancellationRecords[0].replayDetected, true) + assert.equal(cancellationRecords[0].sessionStatus, 'cancelled') + assert.deepEqual(cancellationRecords[0].first, { cancelled: true }) + assert.deepEqual(cancellationRecords[0].second, { cancelled: false }) + assert.equal(cancellationReplayDetected(cancellationRecords[0], 'execution-1'), true) + assertExactResumeEvidence(replayRecords[0], { + executionId: 'execution-1', + cursorEventId: 'event-1', + }) + assert.throws( + () => + assertExactResumeEvidence( + { + ...replayRecords[0], + replay: [ + ...replayRecords[0].replay, + { id: 'foreign-event', type: 'result', executionId: 'execution-2' }, + ], + }, + { executionId: 'execution-1', cursorEventId: 'event-1' }, + ), + /replay included a different execution/u, + ) +}) diff --git a/scripts/live-required/contracts.mjs b/scripts/live-required/contracts.mjs index 9d055e0..c58121e 100644 --- a/scripts/live-required/contracts.mjs +++ b/scripts/live-required/contracts.mjs @@ -23,7 +23,16 @@ const PROOF_OPERATION_CHECKS = Object.freeze({ 'cancelled-turn', 'materialization-receipt', ]), - [PROOF_OPERATIONS.tangleSandbox]: Object.freeze(['marker', 'environment-id']), + [PROOF_OPERATIONS.tangleSandbox]: Object.freeze([ + 'marker', + 'environment-id', + 'workspace-read-write-exec-git', + 'sigkill-reconnect', + 'exclusive-replay', + 'follow-up-session', + 'cancel-retry-conflict', + 'exact-resource-cleanup', + ]), [PROOF_OPERATIONS.traceAnalysis]: Object.freeze([ 'source-frozen', 'cited-finding', @@ -35,7 +44,17 @@ const PROOF_OPERATION_CHECKS = Object.freeze({ const PROOF_OPERATION_FACT_KEYS = Object.freeze({ [PROOF_OPERATIONS.tangleInference]: Object.freeze(['normalRunId', 'cancelledRunId']), - [PROOF_OPERATIONS.tangleSandbox]: Object.freeze(['environmentId']), + [PROOF_OPERATIONS.tangleSandbox]: Object.freeze([ + 'environmentId', + 'resumedRunId', + 'followUpRunId', + 'cancelledRunId', + 'resumeFromCursor', + 'finalCursor', + 'cloudControl', + 'exactResource', + 'activeResourceDelta', + ]), [PROOF_OPERATIONS.traceAnalysis]: Object.freeze([ 'analysisId', 'findingCount', @@ -51,7 +70,7 @@ const PROOF_OPERATION_FACT_KEYS = Object.freeze({ ]), }) -const PROOF_STATUSES = new Set(['passed', 'partial', 'unavailable']) +const PROOF_STATUSES = new Set(['passed', 'partial', 'failed', 'unavailable']) const PROOF_EVIDENCE_KEYS = Object.freeze([ 'schema', 'invocationId', @@ -63,6 +82,7 @@ const PROOF_EVIDENCE_KEYS = Object.freeze([ 'run', 'facts', 'checks', + 'observations', ]) const PROOF_CONNECTION_KEYS = Object.freeze([ 'endpoint', @@ -73,6 +93,14 @@ const PROOF_CONNECTION_KEYS = Object.freeze([ 'runner', ]) const PROOF_RUN_KEYS = Object.freeze(['ids', 'environmentId', 'materializationDigest']) +const CLOUD_CONTROL_KEYS = Object.freeze([ + 'provider', + 'environmentId', + 'sessionId', + 'executionId', + 'runId', + 'requestDigest', +]) const CREDENTIAL_KEY_PATTERN = /(?:^|_)(?:access_key|api_key|auth|authorization|bearer|client_secret|cookie|credential|password|passwd|private_key|secret|session|token)(?:_|$)/u const MAX_PUBLIC_DEPTH = 8 @@ -235,6 +263,44 @@ function validNullableString(value, label) { throw new Error(`${label} must be a non-empty string or null`) } +function validRequiredString(value, label) { + if (typeof value !== 'string' || value.length === 0) + throw new Error(`${label} must be a non-empty string`) +} + +function validateCloudControl(value) { + if (value === null) return + if (typeof value !== 'object' || Array.isArray(value)) + throw new Error('Live proof cloudControl must be an object or null') + exactKeys(value, CLOUD_CONTROL_KEYS, 'Live proof cloudControl') + for (const key of CLOUD_CONTROL_KEYS) validNullableString(value[key], `Live proof ${key}`) +} + +function validateObservations(value) { + if (value !== null && (typeof value !== 'object' || Array.isArray(value))) + throw new Error('Live proof observations must be an object or null') + if (value === null) return + + const seen = new Set() + const visit = (candidate, path) => { + if (candidate === null || typeof candidate !== 'object') return + if (seen.has(candidate)) return + seen.add(candidate) + if (Array.isArray(candidate)) { + candidate.forEach((entry, index) => { + visit(entry, `${path}[${index}]`) + }) + return + } + for (const [key, nested] of Object.entries(candidate)) { + if (credentialKey(key) && nested !== '[REDACTED]') + throw new Error(`Live proof observations.${key} must be redacted`) + visit(nested, `${path}.${key}`) + } + } + visit(value, 'observations') +} + function validTimestamp(value, label) { if (typeof value !== 'string' || Number.isNaN(Date.parse(value))) throw new Error(`${label} must be an ISO timestamp`) @@ -274,6 +340,20 @@ function validateProofFacts(operation, status, facts) { if (typeof value !== 'boolean') throw new Error(`Live proof ${key} must be boolean`) continue } + if (key === 'exactResource') { + if (value !== null && typeof value !== 'boolean') + throw new Error('Live proof exactResource must be boolean or null') + continue + } + if (key === 'activeResourceDelta') { + if (value !== null && (typeof value !== 'number' || !Number.isFinite(value))) + throw new Error('Live proof activeResourceDelta must be a finite number or null') + continue + } + if (key === 'cloudControl') { + validateCloudControl(value) + continue + } validNullableString(value, `Live proof ${key}`) } if (operation === PROOF_OPERATIONS.traceAnalysis && status === 'passed') { @@ -288,6 +368,50 @@ function validateProofFacts(operation, status, facts) { } } +function validatePassedTangleSandboxReceipt(receipt) { + if (receipt.run.ids.length === 0) + throw new Error('Passed Tangle Sandbox proof requires at least one local run ID') + if (new Set(receipt.run.ids).size !== receipt.run.ids.length) + throw new Error('Passed Tangle Sandbox proof requires unique local run IDs') + validRequiredString(receipt.run.environmentId, 'Passed Tangle Sandbox local environmentId') + + const requiredConnectionFields = ['endpoint', 'connectionId', 'connectionKind', 'model', 'runner'] + for (const field of requiredConnectionFields) + validRequiredString(receipt.connection[field], `Passed Tangle Sandbox connection.${field}`) + if (receipt.connection.connectionKind !== 'tangle-sandbox') + throw new Error('Passed Tangle Sandbox proof requires a tangle-sandbox connection') + if (receipt.connection.credentialConfigured !== true) + throw new Error('Passed Tangle Sandbox proof requires configured credentials') + + const cloudControl = receipt.facts.cloudControl + if (cloudControl === null) { + throw new Error('Passed Tangle Sandbox proof requires exact cloud control identity') + } + for (const field of CLOUD_CONTROL_KEYS) + validRequiredString(cloudControl[field], `Passed Tangle Sandbox cloudControl.${field}`) + if (cloudControl.provider !== 'tangle-sandbox') + throw new Error('Passed Tangle Sandbox cloud control identity has the wrong provider') + if (!/^sha256:[0-9a-f]{64}$/u.test(cloudControl.requestDigest)) + throw new Error('Passed Tangle Sandbox cloud control requestDigest is not a SHA-256 digest') + + for (const field of ['resumedRunId', 'followUpRunId', 'cancelledRunId']) + validRequiredString(receipt.facts[field], `Passed Tangle Sandbox facts.${field}`) + if (receipt.facts.environmentId !== receipt.run.environmentId) + throw new Error('Passed Tangle Sandbox local environment identity is inconsistent') + for (const field of ['resumedRunId', 'followUpRunId', 'cancelledRunId']) { + if (!receipt.run.ids.includes(receipt.facts[field])) + throw new Error(`Passed Tangle Sandbox ${field} is missing from local run IDs`) + } + for (const field of ['resumeFromCursor', 'finalCursor']) + validRequiredString(receipt.facts[field], `Passed Tangle Sandbox facts.${field}`) + if (receipt.facts.exactResource !== true) + throw new Error('Passed Tangle Sandbox proof requires cleanup exactResource=true') + if (receipt.facts.activeResourceDelta !== 0) + throw new Error('Passed Tangle Sandbox proof requires numeric activeResourceDelta=0') + if (receipt.observations === null || Object.keys(receipt.observations).length === 0) + throw new Error('Passed Tangle Sandbox proof requires redacted observations') +} + export function proofInvocation(scope) { if (typeof scope !== 'string' || scope.trim().length === 0) throw new Error('Live proof scope must be a non-empty string') @@ -325,6 +449,7 @@ export function assertProofReceipt(receipt, { invocationId, operation } = {}) { throw new Error('Live proof credentialConfigured must be boolean or null') validNullableString(receipt.connection.model, 'Live proof model') validNullableString(receipt.connection.runner, 'Live proof runner') + validateObservations(receipt.observations) exactKeys(receipt.run, PROOF_RUN_KEYS, 'Live proof run') if ( !Array.isArray(receipt.run.ids) || @@ -352,6 +477,8 @@ export function assertProofReceipt(receipt, { invocationId, operation } = {}) { requiredChecks.some((check) => !receipt.checks.includes(check))) ) throw new Error('Passed live proof must include every check for the named operation') + if (receipt.operation === PROOF_OPERATIONS.tangleSandbox && receipt.status === 'passed') + validatePassedTangleSandboxReceipt(receipt) return receipt } @@ -367,7 +494,17 @@ export function proofReceipt({ materializationDigest = null, facts = {}, checks = [], + observations = null, + environment = process.env, }) { + const publicObservations = + observations === null + ? null + : sanitizePublicValue( + observations, + environment, + redactionSecretsFor(observations, environment), + ) const receipt = { schema: PUBLIC_EVIDENCE_SCHEMA, invocationId, @@ -391,6 +528,7 @@ export function proofReceipt({ }, facts, checks: [...checks], + observations: publicObservations, } assertProofReceipt(receipt, { invocationId, operation }) return Object.freeze({ @@ -399,6 +537,7 @@ export function proofReceipt({ run: Object.freeze({ ...receipt.run, ids: Object.freeze(receipt.run.ids) }), facts: Object.freeze({ ...receipt.facts }), checks: Object.freeze(receipt.checks), + observations: receipt.observations === null ? null : Object.freeze({ ...receipt.observations }), }) } diff --git a/scripts/live-required/headless.mjs b/scripts/live-required/headless.mjs index fa62cb2..ccb5469 100644 --- a/scripts/live-required/headless.mjs +++ b/scripts/live-required/headless.mjs @@ -167,6 +167,7 @@ export async function prepareProductionWorkspace({ model, runner, provider, + connectionName, credentialRef, credentialValue, credentialContextFactory, @@ -192,7 +193,7 @@ export async function prepareProductionWorkspace({ const connection = { id: `connection-live-${kindId}`, kind, - name: `Live ${kind}`, + name: connectionName ?? `Live ${kind}`, endpoint, ...(selectedCredentialId === undefined ? {} : { credentialRef: selectedCredentialId }), providerOptions: { transport: 'https' }, diff --git a/scripts/live-required/tangle-sandbox-braid-execution-soak.mjs b/scripts/live-required/tangle-sandbox-braid-execution-soak.mjs new file mode 100644 index 0000000..17df00b --- /dev/null +++ b/scripts/live-required/tangle-sandbox-braid-execution-soak.mjs @@ -0,0 +1,394 @@ +import assert from 'node:assert/strict' +import { createHash, randomUUID } from 'node:crypto' +import { mkdir, writeFile } from 'node:fs/promises' +import { dirname, resolve } from 'node:path' +import { fileURLToPath } from 'node:url' +import { Sandbox } from '@tangle-network/sandbox' +import { connectionConfiguration } from './configuration.mjs' +import { safeMessage } from './contracts.mjs' +import { + closeSession, + configEvidence, + prepareProductionWorkspace, + resolveBinary, + runHeadlessTurn, +} from './headless.mjs' +import { + environmentForRun, + resourceDelta, + runObservations, +} from './tangle-sandbox-braid-stress-support.mjs' + +const repository = resolve(dirname(fileURLToPath(import.meta.url)), '../..') +const DEFAULT_RUNS = 3 +const DEFAULT_CONCURRENCY = 2 +const MAX_RUNS = 20 +const MAX_CONCURRENCY = 4 + +function argument(name, argv = process.argv) { + const index = argv.indexOf(`--${name}`) + return index >= 0 && index + 1 < argv.length ? argv[index + 1] : undefined +} + +function boundedInteger(value, fallback, maximum, name) { + const parsed = value === undefined ? fallback : Number(value) + if (!Number.isSafeInteger(parsed) || parsed < 1 || parsed > maximum) { + throw new Error(`${name} must be an integer from 1 through ${maximum}`) + } + return parsed +} + +function percentile(sorted, ratio) { + if (sorted.length === 0) return null + return sorted[Math.max(0, Math.ceil(sorted.length * ratio) - 1)] +} + +export function executionLatencyDistribution(attempts) { + const sorted = attempts + .map((attempt) => attempt.elapsedMs) + .filter(Number.isFinite) + .toSorted((left, right) => left - right) + return { + n: sorted.length, + min: sorted[0] ?? null, + median: percentile(sorted, 0.5), + p90: percentile(sorted, 0.9), + max: sorted.at(-1) ?? null, + } +} + +function sandboxEnvironment(environment) { + return { + ...environment, + BRAID_TANGLE_SANDBOX_ENDPOINT: + environment.BRAID_TANGLE_SANDBOX_ENDPOINT?.trim() || 'https://sandbox.tangle.tools', + BRAID_TANGLE_SANDBOX_MODEL: + environment.BRAID_TANGLE_SANDBOX_MODEL?.trim() || 'tangle-router/glm-5.2', + BRAID_TANGLE_SANDBOX_RUNNER: environment.BRAID_TANGLE_SANDBOX_RUNNER?.trim() || 'opencode', + BRAID_TANGLE_SANDBOX_PROVIDER: + environment.BRAID_TANGLE_SANDBOX_PROVIDER?.trim() || 'tangle-router', + ...(!environment.BRAID_TANGLE_SANDBOX_API_KEY && environment.TANGLE_API_KEY + ? { BRAID_TANGLE_SANDBOX_API_KEY: environment.TANGLE_API_KEY } + : {}), + } +} + +function sandboxConfiguration(environment) { + return connectionConfiguration(environment, { + prefix: 'BRAID_TANGLE_SANDBOX', + kind: 'tangle-sandbox', + endpointNames: ['BRAID_TANGLE_ENDPOINT'], + modelNames: ['BRAID_TANGLE_MODEL'], + runnerNames: ['BRAID_TANGLE_RUNNER'], + providerNames: ['BRAID_TANGLE_SANDBOX_PROVIDER'], + }) +} + +async function listedByName(client, name) { + const matches = [] + let offset = 0 + for (;;) { + const page = await client.list({ limit: 100, offset }) + for (const box of page) if (box.name === name) matches.push(box) + if (page.length < 100) return matches + offset += page.length + } +} + +async function removeOwned(client, name) { + const owned = await listedByName(client, name) + for (const box of owned) await box.delete() + const remaining = await listedByName(client, name) + return { + matched: owned.map((box) => box.id), + remaining: remaining.map((box) => box.id), + } +} + +function publicError(error, environment) { + return { + name: error instanceof Error ? error.name : 'Error', + message: safeMessage(error, environment), + } +} + +function identityDigest(identity) { + return createHash('sha256') + .update(`${identity.customerId ?? ''}:${identity.billingOwnerId ?? ''}`) + .digest('hex') +} + +function publicUsage(value) { + if (!value || typeof value !== 'object') return value + const fields = [ + 'completeness', + 'computeMinutes', + 'gpuSeconds', + 'gpuCostUsd', + 'gpuProviderCostUsd', + 'activeSandboxes', + 'totalSandboxes', + 'maximumConcurrentSandboxes', + 'maximumCpuCores', + 'maximumRamGB', + 'maximumStorageGB', + 'periodStart', + 'periodEnd', + 'usagePeriodStart', + 'usagePeriodEnd', + 'sampledAt', + ] + return Object.fromEntries( + fields.filter((field) => value[field] !== undefined).map((field) => [field, value[field]]), + ) +} + +function publicObservations(run, state) { + const observations = runObservations(run, state) + const accountUsage = observations.environmentRecord?.accountUsage + const accountTelemetry = observations.environment.accountUsage + return { + ...observations, + environmentRecord: + observations.environmentRecord === null + ? null + : { + ...observations.environmentRecord, + ...(accountUsage === undefined ? {} : { accountUsage: publicUsage(accountUsage) }), + }, + environment: { + ...observations.environment, + ...(accountTelemetry?.status !== 'observed' + ? {} + : { accountUsage: { ...accountTelemetry, value: publicUsage(accountTelemetry.value) } }), + }, + } +} + +async function runAttempt({ index, proofId, client, binary, values, environment }) { + const started = performance.now() + const marker = `BRAID_CLOUD_EXECUTION_${proofId}_${index}_OK`.toUpperCase() + const connectionName = `braid-execution-${proofId}-${index}` + let config + let turn + let exactEnvironmentId + let automaticCleanup = false + let result + try { + config = await prepareProductionWorkspace({ + repository, + environment, + ...values, + connectionName, + }) + turn = await runHeadlessTurn({ + binary, + config, + marker, + prompt: [ + 'Use shell tools in the sandbox.', + `Create /workspace/${connectionName}.txt containing exactly ${marker} followed by a newline.`, + `Read /workspace/${connectionName}.txt and verify its exact contents.`, + 'Run git -C /workspace init if needed.', + 'Verify git -C /workspace rev-parse --is-inside-work-tree prints true.', + `Reply with exactly ${marker}.`, + ].join(' '), + }) + const state = turn.terminal.state + const run = state.runs.find((candidate) => candidate.id === turn.run.id) + assert.ok(run, 'terminal state omitted the admitted run') + const observed = environmentForRun(state, run) + assert.ok(observed, 'terminal state omitted the execution environment') + exactEnvironmentId = observed.providerEnvironmentId + assert.equal(observed.lifecycle, 'destroyed', 'Sandbox lifecycle was not destroyed') + assert.equal(observed.cleanup, 'delete-after-turn', 'Sandbox cleanup policy changed') + assert.equal(observed.continuity, 'unavailable', 'Ephemeral execution claimed continuity') + assert.equal(observed.location, 'remote', 'Sandbox execution was not remote') + assert.ok(exactEnvironmentId, 'Sandbox provider identity was unavailable') + automaticCleanup = (await client.get(exactEnvironmentId)) === null + assert.equal(automaticCleanup, true, 'Sandbox remained after the completed Braid turn') + result = { + index, + status: 'passed', + elapsedMs: performance.now() - started, + runId: run.id, + operationId: run.operationId, + model: run.model ?? null, + runner: run.runner ?? null, + message: turn.message.text, + environmentId: exactEnvironmentId, + automaticCleanup, + usage: { + tokens: + run.tokensKnown === false + ? { status: 'unavailable' } + : Number.isSafeInteger(run.inputTokens) && Number.isSafeInteger(run.outputTokens) + ? { status: 'observed', input: run.inputTokens, output: run.outputTokens } + : { status: 'missing' }, + cost: + run.usdKnown === false + ? { status: 'unavailable' } + : typeof run.costUsd === 'number' + ? { status: run.costStatus ?? 'observed', usd: run.costUsd } + : { status: 'missing' }, + }, + observation: publicObservations(run, state), + } + } catch (error) { + result = { + index, + status: 'failed', + elapsedMs: performance.now() - started, + environmentId: exactEnvironmentId ?? null, + automaticCleanup, + error: publicError(error, environment), + } + } finally { + if (turn?.session) await closeSession(turn.session).catch(() => undefined) + if (config) await config.cleanup().catch(() => undefined) + } + + try { + const cleanup = await removeOwned(client, connectionName) + if (cleanup.remaining.length > 0) { + throw new Error(`Exact cleanup left ${cleanup.remaining.length} owned Sandbox resource(s)`) + } + return { + ...result, + cleanup: { + status: 'passed', + matchedCount: cleanup.matched.length, + remainingCount: 0, + }, + } + } catch (error) { + const cleanupError = publicError(error, environment) + return { + ...result, + status: 'failed', + cleanup: { status: 'failed', error: cleanupError }, + error: + result.status === 'failed' + ? { + name: 'AggregateError', + message: `${result.error.message}; cleanup failed: ${cleanupError.message}`, + } + : cleanupError, + } + } +} + +export async function runBraidSandboxExecutionSoak({ + environment: sourceEnvironment = process.env, + runs = DEFAULT_RUNS, + concurrency = DEFAULT_CONCURRENCY, + attemptRunner = runAttempt, +} = {}) { + const requestedRuns = boundedInteger(runs, DEFAULT_RUNS, MAX_RUNS, 'runs') + const requestedConcurrency = Math.min( + boundedInteger(concurrency, DEFAULT_CONCURRENCY, MAX_CONCURRENCY, 'concurrency'), + requestedRuns, + ) + const environment = sandboxEnvironment(sourceEnvironment) + const values = sandboxConfiguration(environment) + const client = new Sandbox({ baseUrl: values.endpoint, apiKey: values.credentialValue }) + const binary = await resolveBinary(repository, environment) + const proofId = `${Date.now()}-${randomUUID().slice(0, 8)}` + const startedAt = new Date().toISOString() + const before = await client.usage() + const identityBefore = await client.getIdentity() + const attempts = [] + let nextIndex = 1 + let stop = false + + const input = { proofId, client, binary, values, environment } + const canary = await attemptRunner({ ...input, index: 0 }) + attempts.push(canary) + if (canary.status === 'passed') { + const workers = Array.from( + { length: Math.min(requestedConcurrency, requestedRuns - 1) }, + async () => { + while (!stop) { + const index = nextIndex + nextIndex += 1 + if (index >= requestedRuns) return + const attempt = await attemptRunner({ ...input, index }) + attempts.push(attempt) + if (attempt.status !== 'passed') stop = true + } + }, + ) + await Promise.all(workers) + } + attempts.sort((left, right) => left.index - right.index) + const after = await client.usage() + const identityAfter = await client.getIdentity() + const usageDelta = resourceDelta(after, before) + const failures = [] + if (attempts.length !== requestedRuns) + failures.push(`attempted ${attempts.length} of ${requestedRuns}`) + for (const attempt of attempts) { + if (attempt.status !== 'passed') failures.push(`run ${attempt.index + 1} failed`) + if (attempt.automaticCleanup !== true) + failures.push(`run ${attempt.index + 1} lacked automatic cleanup`) + } + if (usageDelta.activeSandboxes !== 0) { + failures.push(`active Sandbox delta was ${usageDelta.activeSandboxes ?? 'unavailable'}`) + } + if ( + identityBefore.customerId !== identityAfter.customerId || + identityBefore.billingOwnerId !== identityAfter.billingOwnerId + ) { + failures.push('Sandbox account identity changed during the cohort') + } + return { + schemaVersion: 'braid.tangle-sandbox-execution-soak.v1', + status: failures.length === 0 ? 'passed' : 'failed', + proofId, + startedAt, + completedAt: new Date().toISOString(), + requestedRuns, + attemptedRuns: attempts.length, + concurrency: requestedConcurrency, + failures, + config: { + ...configEvidence({ + endpoint: { + scheme: new URL(values.endpoint).protocol.slice(0, -1), + host: new URL(values.endpoint).host, + }, + connection: { id: 'connection-live-tangle-sandbox', kind: values.kind }, + credentialConfigured: true, + profile: { model: { default: values.model }, harness: values.runner }, + }), + provider: values.provider, + }, + account: { + stable: failures.includes('Sandbox account identity changed during the cohort') === false, + identityDigest: identityDigest(identityBefore), + }, + usage: { before: publicUsage(before), after: publicUsage(after), delta: usageDelta }, + latencyMs: executionLatencyDistribution(attempts), + attempts, + } +} + +async function main() { + const report = await runBraidSandboxExecutionSoak({ + runs: argument('runs'), + concurrency: argument('concurrency'), + }) + const output = `${JSON.stringify(report, null, 2)}\n` + const outputPath = argument('output') + if (outputPath) { + const target = resolve(outputPath) + await mkdir(dirname(target), { recursive: true }) + await writeFile(target, output, { mode: 0o600 }) + } + process.stdout.write(output) + if (report.status !== 'passed') process.exitCode = 1 +} + +if (process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url)) { + await main() +} diff --git a/scripts/live-required/tangle-sandbox-braid-soak.mjs b/scripts/live-required/tangle-sandbox-braid-soak.mjs new file mode 100644 index 0000000..7e54d92 --- /dev/null +++ b/scripts/live-required/tangle-sandbox-braid-soak.mjs @@ -0,0 +1,333 @@ +import { mkdir, writeFile } from 'node:fs/promises' +import { dirname, resolve } from 'node:path' +import { fileURLToPath } from 'node:url' +import { runBraidSandboxStress } from './tangle-sandbox-braid-stress.mjs' +import { resourceDelta } from './tangle-sandbox-braid-stress-support.mjs' + +const scriptPath = fileURLToPath(import.meta.url) +const repository = resolve(dirname(scriptPath), '../..') +const DEFAULT_RUNS = 3 +const DEFAULT_CONCURRENCY = 2 +const MAX_RUNS = 20 +const MAX_CONCURRENCY = 4 + +function argument(name, argv = process.argv) { + const index = argv.indexOf(`--${name}`) + return index >= 0 && index + 1 < argv.length ? argv[index + 1] : undefined +} + +function boundedInteger(value, fallback, maximum, label) { + const parsed = value === undefined ? fallback : Number(value) + if (!Number.isSafeInteger(parsed) || parsed < 1 || parsed > maximum) { + throw new Error(`${label} must be an integer from 1 through ${maximum}`) + } + return parsed +} + +function percentile(sorted, ratio) { + if (sorted.length === 0) return null + return sorted[Math.max(0, Math.ceil(sorted.length * ratio) - 1)] +} + +function distribution(values) { + const sorted = values.filter(Number.isFinite).toSorted((left, right) => left - right) + if (sorted.length === 0) return { n: 0, min: null, median: null, p90: null, max: null } + return { + n: sorted.length, + min: sorted[0], + median: percentile(sorted, 0.5), + p90: percentile(sorted, 0.9), + max: sorted.at(-1), + } +} + +function proofPassed(proof) { + return ( + proof?.status === 'passed' && + proof.cleanup?.exactResource === true && + proof.cleanup?.identity?.confirmed === true && + Array.isArray(proof.cleanup.identity.remainingIds) && + proof.cleanup.identity.remainingIds.length === 0 && + proof.accountIdentityConsistency?.stable === true + ) +} + +function proofIdentity(proof) { + return proof?.progress?.firstControlRef?.environmentId +} + +function accountKey(proof) { + const account = proof?.accountIdentityConsistency + return account?.stable === true ? `${account.customerId}:${account.billingOwnerId}` : undefined +} + +function latencySummary(attempts) { + const phases = new Set() + for (const attempt of attempts) { + for (const phase of Object.keys(attempt.proof?.timing ?? {})) { + if (phase !== 'totalMs') phases.add(phase) + } + } + return { + totalMs: distribution(attempts.map((attempt) => attempt.proof?.timing?.totalMs)), + phases: Object.fromEntries( + [...phases] + .sort() + .map((phase) => [ + phase, + distribution(attempts.map((attempt) => attempt.proof?.timing?.[phase]?.elapsedMs)), + ]), + ), + } +} + +function sessionSpend(attempts) { + const rows = attempts.flatMap((attempt) => + (attempt.proof?.spend?.rows ?? []).map((row) => ({ attempt: attempt.index, ...row })), + ) + const tokenRows = rows.map((row) => row.tokens) + const costRows = rows.map((row) => row.cost) + const observedTokens = tokenRows.filter((row) => row?.status === 'observed') + const observedCosts = costRows.filter((row) => row?.status === 'observed') + return { + scope: 'every unique local run in every cloud proof', + rows, + tokens: { + observedRuns: observedTokens.length, + unavailableRuns: tokenRows.filter((row) => row?.status === 'unavailable').length, + missingRuns: tokenRows.filter((row) => row?.status === 'missing').length, + input: observedTokens.reduce((total, row) => total + (row.input ?? 0), 0), + output: observedTokens.reduce((total, row) => total + (row.output ?? 0), 0), + }, + cost: { + observedRuns: observedCosts.length, + unavailableRuns: costRows.filter((row) => row?.status === 'unavailable').length, + missingRuns: costRows.filter((row) => row?.status === 'missing').length, + usd: observedCosts.reduce((total, row) => total + (row.usd ?? 0), 0), + }, + } +} + +function cohortUsage(attempts) { + const before = attempts[0]?.proof?.usage?.find((entry) => entry.phase === 'before')?.value + const finalAttempt = attempts + .toSorted((left, right) => left.completionSequence - right.completionSequence) + .at(-1) + const after = finalAttempt?.proof?.usage?.find((entry) => entry.phase === 'after')?.value + const delta = resourceDelta(after, before) + return { + complete: before !== undefined && before !== null && after !== undefined && after !== null, + before: before ?? null, + after: after ?? null, + delta, + } +} + +function cohortFailures(attempts, requestedRuns, usage) { + const failures = [] + if (attempts.length !== requestedRuns) { + failures.push(`attempted ${attempts.length} of ${requestedRuns} requested runs`) + } + for (const attempt of attempts) { + if (!proofPassed(attempt.proof)) + failures.push(`run ${attempt.index + 1} did not pass exact proof`) + } + + const uniqueField = (label, values) => { + if (values.some((value) => typeof value !== 'string' || value.length === 0)) { + failures.push(`${label} was missing`) + return + } + if (new Set(values).size !== values.length) failures.push(`${label} was reused across runs`) + } + uniqueField( + 'proof identity', + attempts.map((attempt) => attempt.proof?.proofId), + ) + uniqueField( + 'cloud environment identity', + attempts.map((attempt) => proofIdentity(attempt.proof)), + ) + + const binaries = attempts.map((attempt) => attempt.proof?.processes?.binarySha256) + if (binaries.some((value) => typeof value !== 'string' || value.length === 0)) { + failures.push('Braid binary digest was missing') + } else if (new Set(binaries).size !== 1) { + failures.push('Braid binary changed during the cohort') + } + + const accounts = attempts.map((attempt) => accountKey(attempt.proof)) + if (accounts.some((value) => value === undefined)) { + failures.push('Sandbox account identity was missing') + } else if (new Set(accounts).size !== 1) { + failures.push('Sandbox account identity changed during the cohort') + } + if (!usage.complete || usage.delta.activeSandboxes === null) { + failures.push('cohort active-resource usage was unavailable') + } else if (usage.delta.activeSandboxes !== 0) { + failures.push(`cohort active-resource delta was ${usage.delta.activeSandboxes}, expected 0`) + } + return [...new Set(failures)] +} + +function finish({ attempts, requestedRuns, concurrency, startedAt, stoppedAfterCanary }) { + const usage = cohortUsage(attempts) + const failures = cohortFailures(attempts, requestedRuns, usage) + const activeResourceDeltas = attempts.map( + (attempt) => attempt.proof?.cleanup?.activeResourceDelta ?? null, + ) + return { + schemaVersion: 'braid.tangle-sandbox-braid-soak.v1', + status: failures.length === 0 ? 'passed' : 'failed', + startedAt, + completedAt: new Date().toISOString(), + requestedRuns, + attemptedRuns: attempts.length, + concurrency, + stoppedAfterCanary, + failures, + cleanup: { + exactProofs: attempts.filter((attempt) => attempt.proof?.cleanup?.exactResource === true) + .length, + exactResourcesRemaining: attempts.reduce( + (total, attempt) => + total + + (Array.isArray(attempt.proof?.cleanup?.identity?.remainingIds) + ? attempt.proof.cleanup.identity.remainingIds.length + : 1), + 0, + ), + activeResourceDelta: usage.delta.activeSandboxes, + activeResourceDeltas, + }, + accountUsage: usage, + latency: latencySummary(attempts), + sessionSpend: sessionSpend(attempts), + attempts, + } +} + +export async function runBraidSandboxSoak({ + environment = process.env, + repository: suppliedRepository = repository, + binary, + runs = boundedInteger( + environment.BRAID_TANGLE_SANDBOX_STRESS_RUNS, + DEFAULT_RUNS, + MAX_RUNS, + 'runs', + ), + concurrency = boundedInteger( + environment.BRAID_TANGLE_SANDBOX_STRESS_CONCURRENCY, + DEFAULT_CONCURRENCY, + MAX_CONCURRENCY, + 'concurrency', + ), + stressRunner = runBraidSandboxStress, +} = {}) { + const requestedRuns = boundedInteger(runs, DEFAULT_RUNS, MAX_RUNS, 'runs') + const requestedConcurrency = Math.min( + boundedInteger(concurrency, DEFAULT_CONCURRENCY, MAX_CONCURRENCY, 'concurrency'), + requestedRuns, + ) + const startedAt = new Date().toISOString() + const attempts = [] + let completionSequence = 0 + + const attempt = async (index, requireZeroActiveResourceDelta) => { + const attemptStartedAt = new Date().toISOString() + let proof + try { + proof = await stressRunner({ + environment, + repository: suppliedRepository, + binary, + requireZeroActiveResourceDelta, + attemptIndex: index, + }) + } catch (error) { + if (error && typeof error === 'object' && error.unavailable === true) throw error + proof = { + status: 'failed', + failure: { + name: error instanceof Error ? error.name : 'Error', + message: error instanceof Error ? error.message : String(error), + }, + } + } + return { + index, + completionSequence: completionSequence++, + startedAt: attemptStartedAt, + completedAt: new Date().toISOString(), + requireZeroActiveResourceDelta, + proof, + } + } + + const canary = await attempt(0, true) + attempts.push(canary) + if (!proofPassed(canary.proof) || requestedRuns === 1) { + return finish({ + attempts, + requestedRuns, + concurrency: requestedConcurrency, + startedAt, + stoppedAfterCanary: !proofPassed(canary.proof), + }) + } + + let nextIndex = 1 + let stop = false + const workers = Array.from( + { length: Math.min(requestedConcurrency, requestedRuns - 1) }, + async () => { + while (!stop) { + const index = nextIndex + nextIndex += 1 + if (index >= requestedRuns) return + const completed = await attempt(index, false) + attempts.push(completed) + if (!proofPassed(completed.proof)) stop = true + } + }, + ) + await Promise.all(workers) + attempts.sort((left, right) => left.index - right.index) + return finish({ + attempts, + requestedRuns, + concurrency: requestedConcurrency, + startedAt, + stoppedAfterCanary: false, + }) +} + +async function writeOutput(path, value) { + const resolved = resolve(path) + await mkdir(dirname(resolved), { recursive: true, mode: 0o700 }) + await writeFile(resolved, `${JSON.stringify(value, null, 2)}\n`, { mode: 0o600 }) +} + +async function main() { + const result = await runBraidSandboxSoak({ + runs: boundedInteger(argument('runs'), DEFAULT_RUNS, MAX_RUNS, 'runs'), + concurrency: boundedInteger( + argument('concurrency'), + DEFAULT_CONCURRENCY, + MAX_CONCURRENCY, + 'concurrency', + ), + }) + const output = argument('output') + if (output) await writeOutput(output, result) + process.stdout.write(`${JSON.stringify(result, null, 2)}\n`) + if (result.status !== 'passed') process.exitCode = 1 +} + +if (resolve(process.argv[1] ?? '') === scriptPath) { + main().catch((error) => { + process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`) + process.exitCode = 1 + }) +} diff --git a/scripts/live-required/tangle-sandbox-braid-stress-support.mjs b/scripts/live-required/tangle-sandbox-braid-stress-support.mjs new file mode 100644 index 0000000..2063fa2 --- /dev/null +++ b/scripts/live-required/tangle-sandbox-braid-stress-support.mjs @@ -0,0 +1,604 @@ +import assert from 'node:assert/strict' +import { randomUUID } from 'node:crypto' +import { AgentExactRunControlRefSchema } from '@tangle-network/agent-interface' + +import { sleep } from '../live-bridge/process.mjs' +import { + requestBase, + responseForRequest, + runFromState, + stateForRun, +} from '../live-bridge/protocol.mjs' + +const TERMINAL_STATUSES = new Set([ + 'completed', + 'failed', + 'aborted', + 'cancelled', + 'expired', + 'blocked', + 'unknown', +]) +const NON_VISIBLE_KINDS = new Set([ + 'run.admitted', + 'run.started', + 'run.environment.observed', + 'run.reconnecting', + 'run.reconciled', + 'run.finished', + 'run.cancel.requested', + 'run.cancelled', + 'run.aborted', + 'run.failed', + 'run.status.changed', + 'replay.cursor.advanced', +]) + +export class MissingIntegrationError extends Error { + constructor(message, details = {}) { + super(message) + this.name = 'MissingIntegrationError' + this.code = 'BRAID_LIVE_INTEGRATION_MISSING' + this.details = details + } +} + +function record(value) { + return value !== null && typeof value === 'object' && !Array.isArray(value) ? value : undefined +} + +function nonEmptyString(value) { + return typeof value === 'string' && value.trim().length > 0 ? value : undefined +} + +function scalarCursor(value) { + return nonEmptyString(value) +} + +function eventParts(response) { + const event = record(response?.event) + if (!event) return undefined + const payload = record(event.payload) ?? event + return { event, payload } +} + +function eventRunId(response) { + const parts = eventParts(response) + return nonEmptyString(parts?.payload.runId) +} + +function rawControlRef(parts) { + const value = record(parts?.payload.value) + return ( + record(parts?.payload.controlRef) ?? + record(record(parts?.payload.observation)?.controlRef) ?? + record(value?.controlRef) ?? + record(parts?.event.controlRef) ?? + record(record(parts?.payload.source)?.controlRef) ?? + record(record(parts?.payload.provider)?.controlRef) + ) +} + +const CONTROL_REF_FIELDS = [ + 'provider', + 'environmentId', + 'sessionId', + 'executionId', + 'runId', + 'requestDigest', +] + +export function exactControlRef(value) { + const candidate = record(value) + if (!candidate) return undefined + const missing = CONTROL_REF_FIELDS.filter((field) => !nonEmptyString(candidate[field])) + if (missing.length > 0) return undefined + const parsed = AgentExactRunControlRefSchema.safeParse( + Object.fromEntries(CONTROL_REF_FIELDS.map((field) => [field, candidate[field]])), + ) + if (!parsed.success || parsed.data.provider !== 'tangle-sandbox') return undefined + if (!/^sha256:[0-9a-f]{64}$/u.test(parsed.data.requestDigest)) return undefined + return parsed.data +} + +export function controlRefFromEvent(response) { + return exactControlRef(rawControlRef(eventParts(response))) +} + +export function eventCursorFromEvent(response) { + const parts = eventParts(response) + return scalarCursor(record(parts?.payload.provider)?.cursor) +} + +export function observationFromResponses(responses, runId) { + let controlRef + let cursor + let observationEvent + for (const response of responses) { + if (!belongsToRun(response, runId, responses)) continue + controlRef ??= controlRefFromEvent(response) + const eventCursor = eventCursorFromEvent(response) + if (eventCursor !== undefined) cursor = eventCursor + if (controlRef && observationEvent === undefined) observationEvent = response + } + return controlRef || cursor !== undefined + ? { controlRef, cursor, event: observationEvent } + : undefined +} + +export function latestCursorFromResponses(responses, runId) { + let cursor + for (const response of responses) { + if (!belongsToRun(response, runId, responses)) continue + const eventCursor = eventCursorFromEvent(response) + if (eventCursor !== undefined) cursor = eventCursor + } + return cursor +} + +export async function waitForControlIdentity(session, runId, timeoutMs) { + const deadline = performance.now() + timeoutMs + for (;;) { + const observation = observationFromResponses(session.responses, runId) + if (observation?.controlRef && observation.cursor !== undefined) return observation + throwIfRunTerminated(session.responses, runId, 'exposing exact provider identity') + if (performance.now() >= deadline) { + const missing = [ + observation?.controlRef ? undefined : 'run.environment.observed.controlRef', + observation?.cursor === undefined ? 'provider event cursor' : undefined, + ].filter(Boolean) + throw new MissingIntegrationError( + `Braid RPC did not expose exact provider identity fields for run ${runId}`, + { runId, missing }, + ) + } + await sleep(Math.min(100, Math.max(10, deadline - performance.now()))) + } +} + +export async function waitForVisibleEvents(session, runId, timeoutMs, phase) { + const deadline = performance.now() + timeoutMs + for (;;) { + const visible = assertUniqueVisibleEvents(session.responses, runId, phase) + if (visible.count > 0) return visible + throwIfRunTerminated(session.responses, runId, 'emitting a stable visible provider event') + if (performance.now() >= deadline) { + throw new MissingIntegrationError( + `Braid RPC did not expose a stable visible provider event for run ${runId}`, + { runId, required: 'at least one stable visible provider event before SIGKILL' }, + ) + } + await sleep(Math.min(100, Math.max(10, deadline - performance.now()))) + } +} + +function throwIfRunTerminated(responses, runId, pendingProof) { + for (let index = responses.length - 1; index >= 0; index -= 1) { + const response = responses[index] + if (response?.type !== 'state') continue + const run = runFromState(response.state, runId) + const status = terminalStatus(run) + if (!status) continue + throw new MissingIntegrationError( + `Braid run ${runId} became ${status} before ${pendingProof}`, + { runId, status, required: pendingProof }, + ) + } +} + +export async function rpcRoundTrip(session, command, params = {}, operationId, label = command) { + const requestId = `braid-live-${command}-${randomUUID()}` + const request = { ...requestBase(requestId, command, operationId), params } + const started = performance.now() + session.send(request) + const response = await session.waitFor(label, responseForRequest(requestId)) + return { request, response, elapsedMs: performance.now() - started } +} + +export async function stateRoundTrip(session, projection = 'full') { + const result = await rpcRoundTrip(session, 'get_state', { projection }, undefined, 'state') + const response = result.response + if (response.type !== 'state') { + throw new Error(`get_state returned ${response.type} instead of state`) + } + return { ...result, state: response.state } +} + +export async function waitForTerminal(session, runId, timeoutMs) { + const deadline = performance.now() + timeoutMs + for (;;) { + const existing = [...session.responses] + .reverse() + .find((response) => stateForRun(response, runId)) + if (existing) return { response: existing, run: runFromState(existing.state, runId) } + const current = await stateRoundTrip(session) + if (stateForRun(current.response, runId)) { + return { response: current.response, run: runFromState(current.state, runId) } + } + if (performance.now() >= deadline) { + throw new Error(`run ${runId} did not reach a terminal state before the timeout`) + } + await sleep(Math.min(100, Math.max(10, deadline - performance.now()))) + } +} + +function providerEventMetadata(response) { + const parts = eventParts(response) + return record(parts?.payload.provider) +} + +function belongsToRun(response, runId) { + if (response?.type !== 'event') return false + const observedRunId = eventRunId(response) + return observedRunId === runId +} + +export function providerEventsForRun(responses, runId) { + const events = [] + for (const response of responses) { + if (!belongsToRun(response, runId, responses)) continue + const kind = eventParts(response)?.event.kind + if (typeof kind !== 'string') continue + const provider = providerEventMetadata(response) + if (!provider) { + if (!NON_VISIBLE_KINDS.has(kind)) { + throw new MissingIntegrationError( + `Braid emitted visible ${kind} without provider metadata`, + { runId, kind }, + ) + } + continue + } + const eventId = nonEmptyString(provider.eventId) + const providerSequence = provider.providerSequence + const cursor = nonEmptyString(provider.cursor) + if (!eventId) { + throw new MissingIntegrationError( + `Braid emitted ${kind} without a stable provider event identity`, + { runId, kind }, + ) + } + if (!Number.isSafeInteger(providerSequence) || providerSequence < 1) { + throw new MissingIntegrationError( + `Braid emitted ${kind} without a stable provider sequence`, + { runId, kind, eventId }, + ) + } + const payload = eventParts(response)?.payload + events.push({ + kind, + eventId, + providerSequence, + ...(cursor === undefined ? {} : { cursor }), + ...(record(payload?.part)?.kind === undefined + ? {} + : { partKind: record(payload?.part).kind }), + }) + } + return events +} + +export function visibleProviderEvents(responses, runId) { + return providerEventsForRun(responses, runId) + .filter((event) => !NON_VISIBLE_KINDS.has(event.kind)) + .map((event) => { + if (!event.cursor) { + throw new MissingIntegrationError( + `Braid emitted visible ${event.kind} without a provider cursor`, + { runId, kind: event.kind, eventId: event.eventId }, + ) + } + return event + }) +} + +export function visibleEventKeys(responses, runId) { + return visibleProviderEvents(responses, runId).map((event) => event.eventId) +} + +export function assertUniqueVisibleEvents(responses, runId, phase) { + const events = visibleProviderEvents(responses, runId) + const keys = events.map((event) => event.eventId) + const duplicates = keys.filter((key, index) => keys.indexOf(key) !== index) + assert.equal( + duplicates.length, + 0, + `${phase} replay contained duplicate visible provider events: ${duplicates.join(', ')}`, + ) + const sequences = events.map((event) => event.providerSequence) + const duplicateSequences = sequences.filter( + (sequence, index) => sequences.indexOf(sequence) !== index, + ) + assert.equal( + duplicateSequences.length, + 0, + `${phase} replay contained duplicate provider sequences: ${duplicateSequences.join(', ')}`, + ) + for (let index = 1; index < sequences.length; index += 1) { + assert.ok( + sequences[index] > sequences[index - 1], + `${phase} provider sequences were not strictly increasing`, + ) + } + return { count: keys.length, keys, events } +} + +export function exclusiveResumeIntersection(acknowledged, resumed) { + const acknowledgedSet = new Set(acknowledged) + return [...new Set(resumed)].filter((eventId) => acknowledgedSet.has(eventId)) +} + +export function assertExclusiveResume(acknowledged, resumed) { + assert.ok(resumed.length > 0, 'fresh process replay returned no visible provider events') + const intersection = exclusiveResumeIntersection(acknowledged, resumed) + assert.deepEqual( + intersection, + [], + 'fresh process replayed a provider event acknowledged before SIGKILL', + ) + return intersection +} + +export function assertProviderResumeProgress( + acknowledgedResponses, + resumedResponses, + runId, + cursor, +) { + const acknowledged = assertUniqueVisibleEvents(acknowledgedResponses, runId, 'pre-kill replay') + const resumed = assertUniqueVisibleEvents(resumedResponses, runId, 'fresh-process replay') + assertNonVacuousVisibleEvents(acknowledged, 'pre-kill replay') + assertNonVacuousVisibleEvents(resumed, 'fresh-process replay') + const cursorEvent = providerEventsForRun(acknowledgedResponses, runId).find( + (event) => event.cursor === cursor, + ) + if (!cursorEvent) { + throw new MissingIntegrationError( + 'The persisted reconnect cursor did not identify an acknowledged provider event', + { runId, cursor }, + ) + } + const first = resumed.events[0] + assert.ok( + first.providerSequence > cursorEvent.providerSequence, + 'fresh process replay did not advance beyond the acknowledged provider cursor', + ) + assert.notEqual(first.cursor, cursor, 'fresh process replay started at the acknowledged cursor') + return { + acknowledgedSequence: cursorEvent.providerSequence, + firstFreshSequence: first.providerSequence, + } +} + +export function assertNonTerminalRun(run, label = 'run') { + assert.ok(run && typeof run.status === 'string', `${label} was not present in persisted state`) + assert.equal(terminalStatus(run), undefined, `${label} was terminal before the forced restart`) + return run +} + +export function assertNonVacuousVisibleEvents(observation, label = 'pre-kill replay') { + assert.ok( + observation?.count >= 1, + `${label} had no stable visible provider events to compare across the restart`, + ) + return observation +} + +export function controlIdentity(ref) { + const exact = exactControlRef(ref) + if (!exact) throw new MissingIntegrationError('Provider control reference is incomplete', { ref }) + return { + provider: exact.provider, + environmentId: exact.environmentId, + sessionId: exact.sessionId, + executionId: exact.executionId, + runId: exact.runId, + requestDigest: exact.requestDigest, + } +} + +export function assertSameControlRef(left, right, label) { + assert.deepEqual( + controlIdentity(right), + controlIdentity(left), + `${label} changed provider control identity`, + ) +} + +export function assertSameCloudSession(left, right, label) { + const first = controlIdentity(left) + const second = controlIdentity(right) + assert.equal(second.provider, first.provider, `${label} changed provider`) + assert.equal(second.environmentId, first.environmentId, `${label} changed cloud environment`) + assert.equal(second.sessionId, first.sessionId, `${label} changed cloud session`) + assert.notEqual( + second.executionId, + first.executionId, + `${label} reused the prior execution identity`, + ) + assert.notEqual(second.runId, first.runId, `${label} reused the prior provider run identity`) +} + +const OBSERVATION_FIELDS = [ + 'inputTokens', + 'outputTokens', + 'reasoningTokens', + 'cacheReadTokens', + 'cacheWriteTokens', + 'costUsd', + 'costStatus', + 'usage', + 'cost', + 'tokensKnown', + 'usdKnown', + 'usageCompleteness', + 'latencyMs', + 'durationMs', + 'model', + 'provider', + 'runner', + 'operationId', + 'profileSnapshotId', + 'connectionId', + 'providerSessionId', + 'environmentId', + 'replayCursor', + 'lastCursor', + 'lastProviderSequence', + 'eventCount', + 'contentBytes', + 'contentTruncated', + 'missingSequence', + 'terminalReason', + 'materializationDigest', + 'complete', + 'status', + 'error', + 'startedAt', + 'updatedAt', + 'terminalAt', + 'placement', + 'resourceSample', + 'requestedResources', + 'machineId', + 'runtimeEndpointHost', + 'requestedRegion', + 'verifiedRegion', + 'storagePersistence', + 'gpu', +] + +const ENVIRONMENT_FIELDS = [ + 'kind', + 'providerEnvironmentId', + 'provider', + 'name', + 'lifecycle', + 'lifecycleMode', + 'cleanup', + 'continuity', + 'location', + 'region', + 'runtimeEndpointHost', + 'machineId', + 'requestedRegion', + 'verifiedRegion', + 'storagePersistence', + 'requestedResources', + 'resourceSample', + 'gpu', + 'accountUsage', + 'unavailableTelemetry', + 'placement', + 'repository', + 'gitRef', + 'workingDirectory', + 'image', + 'createdAt', + 'startedAt', + 'lastActivityAt', + 'expiresAt', + 'updatedAt', +] + +function telemetryField(source, field, unavailable) { + if (source && Object.hasOwn(source, field)) { + if (source[field] === null) return { status: 'unavailable', value: null } + if (source[field] === undefined) return { status: 'missing' } + return { status: 'observed', value: source[field] } + } + return unavailable.has(field) ? { status: 'unavailable', value: null } : { status: 'missing' } +} + +export function environmentForRun(state, run) { + return (state?.environments ?? []).find((environment) => environment.id === run?.environmentId) +} + +export function assertEnvironmentIdentity(run, state, controlRef, label) { + const environment = environmentForRun(state, run) + if (!environment) { + throw new MissingIntegrationError(`${label} has no persisted Braid environment record`, { + localEnvironmentId: run?.environmentId, + }) + } + if (!environment.providerEnvironmentId) { + throw new MissingIntegrationError(`${label} environment has no provider environment ID`, { + localEnvironmentId: environment.id, + }) + } + assert.equal( + controlRef.environmentId, + environment.providerEnvironmentId, + `${label} control reference does not match environment.providerEnvironmentId`, + ) + assert.notEqual( + run?.environmentId, + controlRef.environmentId, + `${label} collapsed the local Braid environment ID into the provider environment ID`, + ) + return environment +} + +export function runObservations(run, state) { + const environment = environmentForRun(state, run) + const unavailable = new Set(environment?.unavailableTelemetry ?? []) + const runSources = [record(run), record(run?.observation)].filter(Boolean) + const runTelemetry = Object.fromEntries( + OBSERVATION_FIELDS.map((field) => { + const source = runSources.find((candidate) => Object.hasOwn(candidate, field)) + return [field, telemetryField(source, field, new Set())] + }), + ) + const environmentTelemetry = Object.fromEntries( + ENVIRONMENT_FIELDS.map((field) => [field, telemetryField(environment, field, unavailable)]), + ) + return { + localEnvironmentId: run?.environmentId ?? null, + providerEnvironmentId: environment?.providerEnvironmentId ?? null, + environmentRecord: environment ?? null, + run: runTelemetry, + environment: environmentTelemetry, + } +} + +export function numericDelta(after, before, field) { + const left = after?.[field] + const right = before?.[field] + return typeof left === 'number' && typeof right === 'number' ? left - right : null +} + +export function resourceDelta(after, before) { + const fields = ['activeSandboxes', 'totalSandboxes', 'computeMinutes', 'gpuSeconds', 'gpuCostUsd'] + const delta = Object.fromEntries( + fields.map((field) => [field, numericDelta(after, before, field)]), + ) + return { + ...delta, + unknownFields: fields.filter((field) => delta[field] === null), + } +} + +export function proofCoordinates() { + const nonce = `${Date.now()}-${process.pid}-${randomUUID().slice(0, 8)}` + const safe = nonce.replaceAll('-', '_') + return { + proofId: `braid-cloud-stress-${nonce}`, + marker: `BRAID_CLOUD_${safe}`, + followUpMarker: `BRAID_FOLLOW_UP_${safe}`, + cancelMarker: `BRAID_CANCEL_${safe}`, + } +} + +export function errorDetails(error) { + return { + name: error instanceof Error ? error.name : 'Error', + message: error instanceof Error ? error.message : String(error), + ...(error instanceof MissingIntegrationError + ? { code: error.code, details: error.details } + : {}), + } +} + +export function terminalStatus(run) { + return run && TERMINAL_STATUSES.has(run.status) ? run.status : undefined +} diff --git a/scripts/live-required/tangle-sandbox-braid-stress.mjs b/scripts/live-required/tangle-sandbox-braid-stress.mjs new file mode 100644 index 0000000..2bcf903 --- /dev/null +++ b/scripts/live-required/tangle-sandbox-braid-stress.mjs @@ -0,0 +1,1510 @@ +import assert from 'node:assert/strict' +import { createHash, randomBytes } from 'node:crypto' +import { mkdir, readFile, writeFile } from 'node:fs/promises' +import { dirname, resolve } from 'node:path' +import { fileURLToPath } from 'node:url' +import { Sandbox } from '@tangle-network/sandbox' +import { sleep } from '../live-bridge/process.mjs' +import { connectionConfiguration } from './configuration.mjs' +import { + closeSession, + configEvidence, + initializedSession, + prepareProductionWorkspace, + resolveBinary, +} from './headless.mjs' +import { + assertEnvironmentIdentity, + assertExclusiveResume, + assertNonTerminalRun, + assertNonVacuousVisibleEvents, + assertProviderResumeProgress, + assertSameCloudSession, + assertSameControlRef, + assertUniqueVisibleEvents, + errorDetails, + latestCursorFromResponses, + MissingIntegrationError, + proofCoordinates, + resourceDelta, + rpcRoundTrip, + runObservations, + stateRoundTrip, + terminalStatus, + waitForControlIdentity, + waitForTerminal, + waitForVisibleEvents, +} from './tangle-sandbox-braid-stress-support.mjs' + +const scriptPath = fileURLToPath(import.meta.url) +const repository = resolve(dirname(scriptPath), '../..') +const DEFAULT_TIMEOUT_MS = 180_000 +const DEFAULT_HOLD_MS = 30_000 +const SANDBOX_LIST_PAGE_SIZE = 100 +const BRAID_RESOURCE_OWNER = 'braid' +const RETAINED_LIFECYCLE = 'retained' +const SECRET_ENVIRONMENT_NAMES = [ + 'BRAID_TANGLE_SANDBOX_AUTH', + 'BRAID_TANGLE_SANDBOX_API_KEY', + 'BRAID_TANGLE_SANDBOX_BEARER', + 'BRAID_TANGLE_SANDBOX_CLEANUP_API_KEY', + 'TANGLE_API_KEY', +] + +function argument(name, argv = process.argv) { + const index = argv.indexOf(`--${name}`) + return index >= 0 && index + 1 < argv.length ? argv[index + 1] : undefined +} + +function numberEnvironment(environment, name, fallback) { + const value = Number(environment[name]) + return Number.isFinite(value) && value > 0 ? value : fallback +} + +function sanitizedEnvironment(environment) { + const child = { ...environment } + for (const name of SECRET_ENVIRONMENT_NAMES) delete child[name] + return child +} + +function configurationEnvironment(environment) { + if ( + !environment.BRAID_TANGLE_SANDBOX_CREDENTIAL_REF && + !environment.BRAID_TANGLE_SANDBOX_AUTH && + !environment.BRAID_TANGLE_SANDBOX_API_KEY && + !environment.BRAID_TANGLE_SANDBOX_BEARER && + environment.TANGLE_API_KEY + ) { + return { ...environment, BRAID_TANGLE_SANDBOX_API_KEY: environment.TANGLE_API_KEY } + } + return environment +} + +function sandboxClient(values, environment) { + const apiKey = + values.credentialValue?.trim() || + environment.BRAID_TANGLE_SANDBOX_CLEANUP_API_KEY?.trim() || + environment.BRAID_TANGLE_SANDBOX_API_KEY?.trim() || + environment.TANGLE_API_KEY?.trim() + if (!apiKey) return undefined + return new Sandbox({ baseUrl: values.endpoint, apiKey }) +} + +function sandboxConfiguration(environment) { + return connectionConfiguration(configurationEnvironment(environment), { + prefix: 'BRAID_TANGLE_SANDBOX', + kind: 'tangle-sandbox', + endpointNames: ['BRAID_TANGLE_ENDPOINT'], + modelNames: ['BRAID_TANGLE_MODEL'], + runnerNames: ['BRAID_TANGLE_RUNNER'], + providerNames: ['BRAID_TANGLE_SANDBOX_PROVIDER'], + }) +} + +function workspaceMarkerPath(coordinates) { + return `/workspace/.braid-live/${coordinates.proofId}/marker.txt` +} + +function continuityChallengePath(coordinates) { + return `/workspace/.braid-live/${coordinates.proofId}/continuity-challenge.txt` +} + +function continuityResponsePath(coordinates) { + return `/workspace/.braid-live/${coordinates.proofId}/continuity-response.txt` +} + +function sha256(value) { + return createHash('sha256').update(value).digest('hex') +} + +function shellQuote(value) { + return `'${String(value).replaceAll("'", "'\\''")}'` +} + +function proofPrompts(coordinates, holdMs) { + const path = workspaceMarkerPath(coordinates) + const challengePath = continuityChallengePath(coordinates) + const responsePath = continuityResponsePath(coordinates) + const holdSeconds = Math.max(5, Math.ceil(holdMs / 1000)) + return { + first: [ + 'Use the Tangle Sandbox workspace for every command in this turn.', + `Create ${path} and write exactly ${coordinates.marker} followed by a newline.`, + `Read ${path}, print its contents, and prove the workspace is usable with a shell command.`, + 'Run git -C /workspace rev-parse --is-inside-work-tree. If it fails, run git -C /workspace init.', + 'Then run git -C /workspace rev-parse --is-inside-work-tree again and print its result.', + `Execute the shell command sleep ${holdSeconds} before the final response to keep the run active.`, + `Reply with exactly ${coordinates.marker}.`, + ].join(' '), + followUp: [ + 'Use a shell command in the existing Tangle Sandbox workspace.', + `Compute the SHA-256 digest of the exact bytes in ${challengePath}.`, + `Write only the lowercase 64-character digest followed by a newline to ${responsePath}.`, + `Read ${path} too, but do not include its contents in the response.`, + 'Reply with only the computed digest.', + ].join(' '), + cancel: [ + 'Keep working in the same Tangle Sandbox workspace.', + `Read ${path}, then execute the shell command sleep ${Math.max(60, holdSeconds * 2)} before replying.`, + `Reply with exactly ${coordinates.cancelMarker} only if cancellation does not arrive.`, + ].join(' '), + } +} + +async function retainedBox(client, controlRef, label) { + if (!client) { + throw new MissingIntegrationError(`${label} requires the Sandbox verification client`, { + required: 'BRAID_TANGLE_SANDBOX_API_KEY or TANGLE_API_KEY', + }) + } + const box = await client.get(controlRef.environmentId) + if (!box || !retainedResourceIdentity(box, controlRef)) { + throw new MissingIntegrationError(`${label} did not resolve the exact retained Sandbox`, { + environmentId: controlRef.environmentId, + }) + } + return box +} + +async function prepareContinuityChallenge(client, controlRef, coordinates) { + const box = await retainedBox(client, controlRef, 'Continuity challenge setup') + const challenge = randomBytes(32).toString('hex') + const bytes = `${challenge}\n` + const path = continuityChallengePath(coordinates) + const responsePath = continuityResponsePath(coordinates) + await box.write(path, bytes) + const readBack = await box.read(path) + assert.equal(readBack, bytes, 'continuity challenge did not persist before the follow-up') + return { + path, + responsePath, + expectedDigest: sha256(bytes), + bytes: Buffer.byteLength(bytes), + } +} + +async function listAllSandboxes(client) { + const boxes = [] + const seenIds = new Set() + let offset = 0 + for (;;) { + const page = await client.list({ limit: SANDBOX_LIST_PAGE_SIZE, offset }) + if (!Array.isArray(page)) throw new Error('Sandbox list returned an invalid page') + for (const box of page) { + if (typeof box?.id === 'string') { + if (seenIds.has(box.id)) throw new Error(`Sandbox list repeated ${box.id}`) + seenIds.add(box.id) + } + boxes.push(box) + } + if (page.length < SANDBOX_LIST_PAGE_SIZE) return boxes + offset += page.length + } +} + +async function verifyRetainedWorkspace(client, controlRef, coordinates, continuity) { + const box = await retainedBox(client, controlRef, 'Workspace verification') + const markerPath = workspaceMarkerPath(coordinates) + const expectedMarker = `${coordinates.marker}\n` + const [readValue, continuityResponse] = await Promise.all([ + box.read(markerPath), + box.read(continuity.responsePath), + ]) + const git = await box.exec( + `set -eu; test "$(cat -- ${shellQuote(markerPath)})" = ${shellQuote(coordinates.marker)}; test "$(cat -- ${shellQuote(continuity.responsePath)})" = ${shellQuote(continuity.expectedDigest)}; test "$(git -C /workspace rev-parse --is-inside-work-tree)" = true; git -C /workspace status --short --untracked-files=no`, + ) + const gitExitCode = Number.isInteger(git?.exitCode) + ? git.exitCode + : Number.isInteger(git?.code) + ? git.code + : undefined + if (!Number.isInteger(gitExitCode)) { + throw new MissingIntegrationError( + 'The Sandbox SDK did not expose the deterministic workspace Git exit code', + { required: 'Sandbox exec exitCode' }, + ) + } + let resourceSample + let resourceSampleError + try { + resourceSample = await box.resourceUsage() + } catch (error) { + resourceSampleError = errorDetails(error) + } + return { + environmentId: box.id, + markerPath, + readValue, + readMatched: readValue === expectedMarker, + continuity: { + challengePath: continuity.path, + responsePath: continuity.responsePath, + challengeBytes: continuity.bytes, + expectedDigest: continuity.expectedDigest, + responseDigest: continuityResponse.trim(), + matched: continuityResponse === `${continuity.expectedDigest}\n`, + }, + git: { + exitCode: gitExitCode, + stdout: typeof git?.stdout === 'string' ? git.stdout : null, + }, + resourceSample: + resourceSample === undefined + ? { status: 'missing' } + : resourceSample === null + ? { status: 'unavailable', value: null } + : { status: 'observed', value: resourceSample }, + ...(resourceSampleError ? { resourceSampleError } : {}), + } +} + +function operationIds(proofId) { + return { + first: `op-${proofId}-turn-1`, + reconnect: `op-${proofId}-reconnect-1`, + followUp: `op-${proofId}-follow-up-1`, + cancelSend: `op-${proofId}-cancel-send-1`, + cancel: `op-${proofId}-cancel-1`, + } +} + +function runSnapshot(run, state, observation) { + return { + id: run?.id, + operationId: run?.operationId, + status: terminalStatus(run) ?? run?.status, + environmentId: run?.environmentId, + providerSessionId: run?.providerSessionId, + materializationDigest: run?.materializationDigest, + cursor: run?.cursor ?? observation?.cursor, + controlRef: observation?.controlRef, + observations: runObservations(run, state), + } +} + +function diagnosticText(value) { + if (typeof value !== 'string') return undefined + const text = [...value] + .filter((character) => { + const code = character.codePointAt(0) + return code === 9 || code === 10 || code === 13 || (code >= 32 && code !== 127) + }) + .join('') + .trim() + if (text.length === 0) return undefined + return text + .replace( + /\b(authorization|bearer|token|api[-_]?key|password|passphrase|secret|credential)\b\s*[:=]?\s*(?:bearer\s+)?[^\s,;]+/giu, + '$1=[redacted]', + ) + .slice(0, 512) +} + +export function cloudFailureEventTimeline(responses, runId) { + return (responses ?? []) + .filter((response) => response?.type === 'event') + .flatMap((response) => { + const event = response.event + if (!event || typeof event !== 'object' || Array.isArray(event)) return [] + const payload = + event.payload && typeof event.payload === 'object' && !Array.isArray(event.payload) + ? event.payload + : event + const eventRunId = typeof payload.runId === 'string' ? payload.runId : undefined + if (eventRunId !== undefined && eventRunId !== runId) return [] + const kind = diagnosticText(event.kind) + if (kind === undefined) return [] + const fields = Object.fromEntries( + ['status', 'detail', 'error', 'reason', 'code'].flatMap((name) => { + const value = diagnosticText(payload[name]) + return value === undefined ? [] : [[name, value]] + }), + ) + return [ + { + ...(Number.isSafeInteger(response.sequence) ? { sequence: response.sequence } : {}), + kind, + ...(eventRunId === undefined ? {} : { runId: eventRunId }), + ...fields, + }, + ] + }) + .slice(-30) +} + +function failureDiagnostics(session, runId) { + if (!session) return undefined + const responses = session.responses ?? [] + const stateResponse = [...responses] + .reverse() + .find( + (response) => + response?.type === 'state' && response.state?.runs?.some((run) => run.id === runId), + ) + const run = stateResponse?.state?.runs?.find((candidate) => candidate.id === runId) + const errors = responses + .filter((response) => response?.type === 'error') + .slice(-5) + .map((response) => ({ + requestId: response.requestId, + code: response.code, + message: response.message, + })) + const stderr = session.stderr?.slice(-8_192).trim() + const eventTimeline = cloudFailureEventTimeline(responses, runId) + return { + responseCount: responses.length, + ...(run ? { run: runSnapshot(run, stateResponse.state) } : {}), + ...(errors.length > 0 ? { errors } : {}), + ...(eventTimeline.length > 0 ? { eventTimeline } : {}), + ...(stderr ? { stderr } : {}), + } +} + +function safeExecutionId(value) { + return value.replace(/[^A-Za-z0-9._:-]/gu, '-').slice(0, 128) || 'run' +} + +function expectedResourceIdentityForSession(providerSessionId, id) { + return { + ...(id === undefined ? {} : { id }), + name: `braid-${safeExecutionId(providerSessionId)}`, + metadata: { + owner: BRAID_RESOURCE_OWNER, + lifecycle: RETAINED_LIFECYCLE, + providerSessionId, + }, + } +} + +function matchesResourceIdentity(box, expected) { + return ( + typeof box?.id === 'string' && + (expected.id === undefined || box.id === expected.id) && + box.name === expected.name && + box.metadata?.owner === expected.metadata.owner && + box.metadata?.lifecycle === expected.metadata.lifecycle && + box.metadata?.providerSessionId === expected.metadata.providerSessionId + ) +} + +function retainedResourceIdentity(box, controlRef) { + return matchesResourceIdentity(box, expectedResourceIdentity(controlRef)) +} + +function expectedResourceIdentity(controlRef) { + return expectedResourceIdentityForSession(controlRef.sessionId, controlRef.environmentId) +} + +async function observeRetainedResource(client, controlRef) { + if (!client) { + throw new MissingIntegrationError( + 'No cleanup credential is available to verify the exact retained Braid Sandbox resource', + { required: 'BRAID_TANGLE_SANDBOX_CLEANUP_API_KEY or TANGLE_API_KEY' }, + ) + } + if (!controlRef?.environmentId || !controlRef.sessionId) { + throw new MissingIntegrationError( + 'Braid did not expose the exact retained Sandbox control identity', + { required: ['controlRef.environmentId', 'controlRef.sessionId'] }, + ) + } + const box = await client.get(controlRef.environmentId) + if (!box) { + throw new MissingIntegrationError( + `Sandbox environment ${controlRef.environmentId} was not visible to the cleanup client`, + { environmentId: controlRef.environmentId }, + ) + } + if (!retainedResourceIdentity(box, controlRef)) { + throw new MissingIntegrationError( + `Braid-created Sandbox ${controlRef.environmentId} does not match the retained Braid resource identity`, + { + expected: expectedResourceIdentity(controlRef), + observed: { id: box.id, name: box.name, metadata: box.metadata }, + }, + ) + } + return { observed: true, ...expectedResourceIdentity(controlRef) } +} + +export async function cleanupRetainedResourceByRunId(client, firstRunId) { + if (!client) { + throw new MissingIntegrationError( + 'Fail-safe retained Braid Sandbox cleanup has no cleanup client', + { required: 'BRAID_TANGLE_SANDBOX_CLEANUP_API_KEY or TANGLE_API_KEY' }, + ) + } + if (typeof firstRunId !== 'string' || firstRunId.length === 0) { + throw new MissingIntegrationError( + 'Fail-safe retained Braid Sandbox cleanup has no first local run ID', + { required: 'first local run ID' }, + ) + } + const providerSessionId = `session-braid-${safeExecutionId(firstRunId)}` + const expected = expectedResourceIdentityForSession(providerSessionId) + const matches = (await listAllSandboxes(client)).filter((box) => + matchesResourceIdentity(box, expected), + ) + if (matches.length !== 1) { + throw new MissingIntegrationError( + 'Fail-safe cleanup did not find exactly one Braid-owned retained Sandbox for the first local run', + { + expected, + matches: matches.map((box) => ({ id: box.id, name: box.name, metadata: box.metadata })), + }, + ) + } + const listed = matches[0] + const exact = await client.get(listed.id) + const exactExpected = expectedResourceIdentityForSession(providerSessionId, listed.id) + if (!exact || !matchesResourceIdentity(exact, exactExpected)) { + throw new MissingIntegrationError( + 'Fail-safe cleanup identity changed after the exact Sandbox list lookup', + { + expected: exactExpected, + observed: exact ? { id: exact.id, name: exact.name, metadata: exact.metadata } : null, + }, + ) + } + await exact.delete() + const remaining = await client.get(listed.id) + if (remaining !== null) { + throw new Error(`Fail-safe retained Braid Sandbox ${listed.id} remained after delete`) + } + return { + confirmed: true, + mode: 'first-run-identity-lookup', + ...exactExpected, + } +} + +function ownedByOperation(box, operationId) { + return ( + typeof operationId === 'string' && + box?.metadata?.owner === BRAID_RESOURCE_OWNER && + box.metadata?.lifecycle === RETAINED_LIFECYCLE && + box.metadata?.braidOperationId === operationId + ) +} + +async function deleteOwnedResource(client, box, predicate) { + const attempts = [] + for (let attempt = 1; attempt <= 3; attempt += 1) { + const exact = await client.get(box.id) + if (exact === null) return { id: box.id, attempts, confirmed: true } + if (!predicate(exact)) { + throw new MissingIntegrationError( + `Refusing to delete Sandbox ${box.id} because its exact ownership changed`, + { id: box.id }, + ) + } + try { + await exact.delete() + attempts.push({ attempt, deleted: true }) + } catch (error) { + attempts.push({ attempt, deleted: false, error: errorDetails(error) }) + } + const deadline = performance.now() + 5_000 + while (performance.now() < deadline) { + if ((await client.get(box.id)) === null) return { id: box.id, attempts, confirmed: true } + await sleep(100) + } + } + return { id: box.id, attempts, confirmed: false } +} + +async function cleanupOwnedRetainedResources(client, { controlRef, firstRunId, operationId }) { + if (!client) { + throw new MissingIntegrationError('Retained Sandbox cleanup has no authenticated client', { + required: 'the execution Sandbox credential', + }) + } + const expectedSessionId = + typeof firstRunId === 'string' ? `session-braid-${safeExecutionId(firstRunId)}` : undefined + const predicate = (box) => + (controlRef !== undefined && retainedResourceIdentity(box, controlRef)) || + (expectedSessionId !== undefined && + matchesResourceIdentity(box, expectedResourceIdentityForSession(expectedSessionId))) || + ownedByOperation(box, operationId) + const listed = (await listAllSandboxes(client)).filter(predicate) + if (controlRef?.environmentId) { + const exact = await client.get(controlRef.environmentId) + if (exact && !listed.some((box) => box.id === exact.id)) { + if (!predicate(exact)) { + throw new MissingIntegrationError( + 'The exact control resource failed ownership validation', + { + environmentId: controlRef.environmentId, + }, + ) + } + listed.push(exact) + } + } + if (firstRunId !== undefined && listed.length === 0) { + throw new MissingIntegrationError( + 'Cleanup found no retained Sandbox for the acknowledged Braid run', + { firstRunId, operationId, environmentId: controlRef?.environmentId }, + ) + } + const deletions = [] + for (const box of listed) deletions.push(await deleteOwnedResource(client, box, predicate)) + const remaining = (await listAllSandboxes(client)).filter(predicate) + const confirmed = deletions.every((entry) => entry.confirmed) && remaining.length === 0 + if (!confirmed) { + throw new MissingIntegrationError('Exact retained Sandbox cleanup did not converge', { + deletions, + remaining: remaining.map((box) => box.id), + }) + } + return { + confirmed, + mode: 'exact-owned-resource-set', + matchedCount: listed.length, + removedIds: listed.map((box) => box.id), + deletions, + remainingIds: [], + } +} + +async function usage(client, phase) { + if (!client) return { phase, value: undefined, error: undefined } + try { + return { phase, value: await client.usage(), error: undefined } + } catch (error) { + return { phase, value: undefined, error: errorDetails(error) } + } +} + +function publicAccountIdentity(value) { + if (!value || typeof value !== 'object') return undefined + const customerId = typeof value.customerId === 'string' ? value.customerId : undefined + const billingOwnerId = typeof value.billingOwnerId === 'string' ? value.billingOwnerId : undefined + if (!customerId || !billingOwnerId) return undefined + return { + customerId, + billingOwnerId, + ...(typeof value.billingDelegationAuthorized === 'boolean' + ? { billingDelegationAuthorized: value.billingDelegationAuthorized } + : {}), + } +} + +async function accountIdentity(client, phase) { + if (!client) return { phase, value: undefined, error: undefined } + try { + return { phase, value: publicAccountIdentity(await client.getIdentity()), error: undefined } + } catch (error) { + return { phase, value: undefined, error: errorDetails(error) } + } +} + +function assertStableAccountIdentity(records) { + const before = records.find((entry) => entry.phase === 'before') + const after = records.find((entry) => entry.phase === 'after') + if (!before?.value?.customerId || !before.value.billingOwnerId) { + throw new MissingIntegrationError('Sandbox account identity was unavailable before execution', { + observation: before, + }) + } + if (!after?.value?.customerId || !after.value.billingOwnerId) { + throw new MissingIntegrationError('Sandbox account identity was unavailable after cleanup', { + observation: after, + }) + } + assert.equal(after.value.customerId, before.value.customerId, 'Sandbox customer changed mid-run') + assert.equal( + after.value.billingOwnerId, + before.value.billingOwnerId, + 'Sandbox billing owner changed mid-run', + ) + return { + stable: true, + customerId: before.value.customerId, + billingOwnerId: before.value.billingOwnerId, + } +} + +function assertSameAccount(state, run, directIdentity) { + const environment = state.environments?.find((candidate) => candidate.id === run?.environmentId) + const observed = environment?.accountUsage + if (!directIdentity || !observed?.customerId || !observed.billingOwnerId) { + throw new MissingIntegrationError( + 'Braid did not expose the Sandbox account used for execution and cleanup', + { required: ['customerId', 'billingOwnerId'] }, + ) + } + assert.equal(observed.customerId, directIdentity.customerId, 'Sandbox customer identity changed') + assert.equal( + observed.billingOwnerId, + directIdentity.billingOwnerId, + 'Sandbox billing owner identity changed', + ) + return { + customerId: observed.customerId, + billingOwnerId: observed.billingOwnerId, + usage: { + computeMinutes: observed.computeMinutes ?? null, + gpuSeconds: observed.gpuSeconds ?? null, + gpuCostUsd: observed.gpuCostUsd ?? null, + activeSandboxes: observed.activeSandboxes ?? null, + totalSandboxes: observed.totalSandboxes ?? null, + }, + sampledAt: observed.sampledAt, + } +} + +function toolEvidence(observation, label) { + const events = observation.events.filter( + (event) => + event.kind === 'run.tool.call' || + event.kind === 'run.tool.result' || + (event.kind === 'run.part.updated' && + (event.partKind === 'tool-call' || event.partKind === 'tool-result')), + ) + if (events.length === 0) { + throw new MissingIntegrationError(`${label} exposed no provider-bound workspace tool event`, { + required: ['run.tool.call', 'run.tool.result', 'run.part.updated:tool'], + }) + } + return events +} + +async function verifyRemoteCancellation(client, controlRef, marker, timeoutMs) { + const box = await retainedBox(client, controlRef, 'Remote cancellation verification') + const session = box.session(controlRef.sessionId) + const deadline = performance.now() + Math.min(timeoutMs, 15_000) + const samples = [] + for (;;) { + const status = await session.status() + samples.push({ status: status?.status ?? null, observedAt: new Date().toISOString() }) + if (status?.status === 'cancelled') break + if (performance.now() >= deadline) { + throw new MissingIntegrationError('The exact cloud execution did not become cancelled', { + controlRef, + samples, + }) + } + await sleep(Math.min(250, Math.max(25, deadline - performance.now()))) + } + await sleep(1_000) + const [settledStatus, messages] = await Promise.all([ + session.status(), + box.messages({ sessionId: controlRef.sessionId, limit: 100 }), + ]) + assert.equal(settledStatus?.status, 'cancelled', 'remote cancellation did not remain terminal') + const lateResult = messages + .filter((message) => message?.role === 'assistant') + .some((message) => JSON.stringify(message).includes(marker)) + assert.equal(lateResult, false, 'a late provider result arrived after cancellation') + return { + controlRef, + samples, + settledStatus: settledStatus?.status ?? null, + messageCount: messages.length, + lateResult, + } +} + +function durationMs(run) { + const start = Date.parse(run?.startedAt ?? '') + const end = Date.parse(run?.terminalAt ?? run?.updatedAt ?? '') + return Number.isFinite(start) && Number.isFinite(end) && end >= start ? end - start : undefined +} + +function runSpend(run, label) { + const inputTokens = run?.inputTokens + const outputTokens = run?.outputTokens + const costUsd = run?.costUsd + const elapsed = durationMs(run) + const tokens = + run?.tokensKnown === false + ? { status: 'unavailable' } + : Number.isSafeInteger(inputTokens) && + inputTokens >= 0 && + Number.isSafeInteger(outputTokens) && + outputTokens >= 0 + ? { status: 'observed', input: inputTokens, output: outputTokens } + : { status: 'missing' } + const cost = + run?.usdKnown === false + ? { status: 'unavailable' } + : typeof costUsd === 'number' && Number.isFinite(costUsd) && costUsd >= 0 + ? { + status: 'observed', + usd: costUsd, + provenance: run?.costStatus ?? 'reported', + } + : { status: 'missing' } + return { + label, + runId: run?.id ?? null, + operationId: run?.operationId ?? null, + status: run?.status ?? null, + model: run?.model ?? null, + runner: run?.runner ?? null, + providerSessionId: run?.providerSessionId ?? null, + tokens, + cost, + duration: + elapsed === undefined ? { status: 'missing' } : { status: 'observed', milliseconds: elapsed }, + } +} + +export function spendDisclosure(runs) { + const rows = [ + runSpend(runs.resumed, 'resumed-first-turn'), + runSpend(runs.followUp, 'follow-up-turn'), + runSpend(runs.cancelled, 'cancelled-turn'), + ] + const observedTokens = rows.filter((row) => row.tokens.status === 'observed') + const observedCosts = rows.filter((row) => row.cost.status === 'observed') + return { + scope: 'all unique local runs in this cloud proof', + rows, + totals: { + tokens: { + observedRuns: observedTokens.length, + unavailableRuns: rows.filter((row) => row.tokens.status === 'unavailable').length, + missingRuns: rows.filter((row) => row.tokens.status === 'missing').length, + input: observedTokens.reduce((total, row) => total + row.tokens.input, 0), + output: observedTokens.reduce((total, row) => total + row.tokens.output, 0), + }, + cost: { + observedRuns: observedCosts.length, + unavailableRuns: rows.filter((row) => row.cost.status === 'unavailable').length, + missingRuns: rows.filter((row) => row.cost.status === 'missing').length, + usd: observedCosts.reduce((total, row) => total + row.cost.usd, 0), + }, + }, + } +} + +function telemetryDisclosure(run, state, workspaceVerification, account) { + const environment = state.environments?.find((candidate) => candidate.id === run?.environmentId) + const unavailable = environment?.unavailableTelemetry ?? [] + const unavailableByPrefix = (prefix) => unavailable.some((entry) => entry.startsWith(prefix)) + const elapsed = durationMs(run) + const fields = { + tokens: + run?.tokensKnown === false + ? { status: 'unavailable' } + : { status: 'observed', input: run?.inputTokens, output: run?.outputTokens }, + cost: + run?.usdKnown === false || run?.costUsd === undefined + ? { status: 'unavailable' } + : { status: 'observed', usd: run.costUsd }, + endToEndDuration: + elapsed === undefined ? { status: 'missing' } : { status: 'observed', milliseconds: elapsed }, + model: + typeof run?.model === 'string' + ? { status: 'observed', value: run.model } + : { status: 'missing' }, + environment: + typeof environment?.providerEnvironmentId === 'string' + ? { status: 'observed', value: environment.providerEnvironmentId } + : { status: 'missing' }, + runtimeEndpoint: + typeof environment?.runtimeEndpointHost === 'string' + ? { status: 'observed', host: environment.runtimeEndpointHost } + : { status: 'missing' }, + machine: + typeof environment?.machineId === 'string' + ? { status: 'observed', value: environment.machineId } + : unavailableByPrefix('machine-id:') + ? { status: 'unavailable' } + : { status: 'missing' }, + region: + typeof environment?.placement?.region === 'string' + ? { status: 'observed', value: environment.placement.region } + : unavailableByPrefix('verified-region:') + ? { status: 'unavailable' } + : { status: 'missing' }, + requestedResources: + environment?.requestedResources === undefined + ? { status: 'provider-default' } + : { status: 'observed', value: environment.requestedResources }, + resourceSample: workspaceVerification.resourceSample, + account: { status: 'observed', value: account }, + } + const missing = Object.entries(fields) + .filter(([, value]) => value.status === 'missing') + .map(([name]) => name) + if (missing.length > 0) { + throw new MissingIntegrationError('Required Sandbox telemetry was silently missing', { + missing, + unavailable, + }) + } + return { completeDisclosure: true, fields, unavailable } +} + +async function killFirstBraid(session) { + if (!session?.child) throw new Error('The first Braid process was not started') + const sent = session.child.kill('SIGKILL') + const exit = await session.exit + await session.close().catch(() => undefined) + assert.equal(sent, true, 'SIGKILL was not sent to the first Braid process') + assert.equal(exit.signal, 'SIGKILL', `first Braid process exited with ${JSON.stringify(exit)}`) + return { signal: exit.signal, code: exit.code, sent } +} + +function assertAck(result, command) { + if (result.response.type === 'error') { + throw new Error(`${command} failed: ${result.response.code}: ${result.response.message}`) + } + assert.equal(result.response.type, 'ack', `${command} did not return an acknowledgement`) + return result.response +} + +function localRunCount(state, runId) { + return (state?.runs ?? []).filter((run) => run.id === runId).length +} + +function assistantMarker(state, runId) { + return [...(state?.messages ?? [])] + .reverse() + .find((message) => message.runId === runId && message.role === 'assistant')?.text +} + +function collectIntegrationNeed(error, needs) { + if (error instanceof MissingIntegrationError) { + const fields = (value) => (Array.isArray(value) ? value : value === undefined ? [] : [value]) + for (const field of [...fields(error.details?.missing), ...fields(error.details?.required)]) + needs.push(String(field)) + needs.push(error.message) + } + if (error instanceof Error && /OPERATION_CONFLICT/iu.test(error.message)) { + needs.push('Braid must preserve operation-body conflict semantics for retry-safe cancellation') + } +} + +export async function runBraidSandboxStress({ + environment = process.env, + repository: suppliedRepository = repository, + binary: suppliedBinary, + requireZeroActiveResourceDelta = true, +} = {}) { + const startedAt = performance.now() + const coordinates = proofCoordinates() + const ids = operationIds(coordinates.proofId) + const holdMs = numberEnvironment( + environment, + 'BRAID_TANGLE_SANDBOX_STRESS_HOLD_MS', + DEFAULT_HOLD_MS, + ) + const timeoutMs = numberEnvironment( + environment, + 'BRAID_LIVE_REQUIRED_TIMEOUT_MS', + DEFAULT_TIMEOUT_MS, + ) + const prompts = proofPrompts(coordinates, holdMs) + const phases = {} + const unresolvedIntegrationNeeds = [] + const usageRecords = [] + const identityRecords = [] + const values = sandboxConfiguration(environment) + let config + let binary + let client + let firstSession + let freshSession + let firstRunId + let cancelRunId + let knownEnvironmentId + let firstObservation + let freshObservation + let firstResponses + let firstVisible + let freshVisible + let workspaceVerification + let continuity + let account + let telemetry + let followUpVisible + let remoteCancellation + let resumeFromCursor + let finalCursor + let resumeIntersection + let resumeProgress + let tagObservation + let accountIdentityConsistency + let accountIdentityError + let cleanupConfirmed = false + let cleanupMode + let cleanupIdentity + let cleanupError + let failure + let diagnostics + let finalUsage + let result + let binarySha256 + + const phase = async (name, task) => { + const start = performance.now() + try { + return await task() + } finally { + phases[name] = { elapsedMs: performance.now() - start } + } + } + + try { + client = sandboxClient(values, environment) + usageRecords.push(await usage(client, 'before')) + identityRecords.push(await accountIdentity(client, 'before')) + binary = suppliedBinary ?? (await resolveBinary(suppliedRepository, environment)) + binarySha256 = sha256(await readFile(binary)) + config = await phase('workspace', () => + prepareProductionWorkspace({ + repository: suppliedRepository, + environment: sanitizedEnvironment(environment), + ...values, + }), + ) + + const first = await phase('firstProcess.initialize', () => initializedSession(binary, config)) + firstSession = first.session + const initialState = first.state.state + const send = await phase('firstProcess.send', () => + rpcRoundTrip( + firstSession, + 'send', + { + conversationId: initialState.conversationId, + branchId: initialState.branchId, + text: prompts.first, + }, + ids.first, + 'first send acknowledgement', + ), + ) + const sendAck = assertAck(send, 'send') + firstRunId = sendAck.runId + assert.equal(typeof firstRunId, 'string', 'first send acknowledgement has no local run ID') + + firstObservation = await phase('firstProcess.observeControl', () => + waitForControlIdentity(firstSession, firstRunId, timeoutMs), + ) + knownEnvironmentId = firstObservation.controlRef.environmentId + try { + tagObservation = await observeRetainedResource(client, firstObservation.controlRef) + } catch (error) { + collectIntegrationNeed(error, unresolvedIntegrationNeeds) + tagObservation = { observed: false, error: errorDetails(error) } + } + + const firstStateBeforeVisible = await stateRoundTrip(firstSession) + const firstRunBeforeVisible = firstStateBeforeVisible.state.runs?.find( + (run) => run.id === firstRunId, + ) + assertNonTerminalRun(firstRunBeforeVisible, 'first Braid run') + assert.equal(localRunCount(firstStateBeforeVisible.state, firstRunId), 1) + await phase('firstProcess.waitVisible', () => + waitForVisibleEvents(firstSession, firstRunId, timeoutMs, 'first process'), + ) + const firstState = await stateRoundTrip(firstSession) + const firstRun = firstState.state.runs?.find((run) => run.id === firstRunId) + assertNonTerminalRun(firstRun, 'first Braid run') + assert.equal(localRunCount(firstState.state, firstRunId), 1) + const persistedCursor = firstRun?.lastCursor + const persistedCursorRecord = firstState.state.replayCursors?.find( + (record) => record.runId === firstRunId, + ) + resumeFromCursor = persistedCursor + if ( + resumeFromCursor === undefined || + persistedCursorRecord?.cursor !== resumeFromCursor || + !Number.isSafeInteger(persistedCursorRecord.committedSequence) || + persistedCursorRecord.committedSequence < 1 || + persistedCursorRecord.committedSequence > firstState.state.sequence + ) { + throw new MissingIntegrationError( + 'The pre-kill Braid process did not persist one acknowledged provider resume cursor', + { + runId: firstRunId, + required: ['run.lastCursor', 'state.replayCursors', 'committed journal sequence'], + }, + ) + } + assert.equal( + latestCursorFromResponses(firstSession.responses, firstRunId), + resumeFromCursor, + 'persisted cursor differs from the latest canonical provider cursor', + ) + firstObservation = { ...firstObservation, cursor: resumeFromCursor } + assertEnvironmentIdentity(firstRun, firstState.state, firstObservation.controlRef, 'first run') + const firstSnapshot = runSnapshot(firstRun, firstState.state, firstObservation) + firstResponses = [...firstSession.responses] + firstVisible = assertUniqueVisibleEvents(firstResponses, firstRunId, 'first process') + assertNonVacuousVisibleEvents(firstVisible, 'pre-kill replay') + toolEvidence(firstVisible, 'First process') + + const killed = await phase('firstProcess.sigkill', () => killFirstBraid(firstSession)) + firstSession = undefined + + const fresh = await phase('freshProcess.initialize', () => initializedSession(binary, config)) + freshSession = fresh.session + const freshInitialState = fresh.state.state + assert.equal( + localRunCount(freshInitialState, firstRunId), + 1, + 'fresh process created duplicate local runs', + ) + const reconnect = await phase('freshProcess.reconnect', () => + rpcRoundTrip( + freshSession, + 'reconnect', + { runId: firstRunId }, + ids.reconnect, + 'reconnect acknowledgement', + ), + ) + assertAck(reconnect, 'reconnect') + const freshTerminal = await phase('freshProcess.waitTerminal', () => + waitForTerminal(freshSession, firstRunId, timeoutMs), + ) + const freshRun = freshTerminal.run + assert.equal( + freshRun?.status, + 'completed', + `reconnected run ended ${freshRun?.status ?? 'missing'}`, + ) + assert.equal(assistantMarker(freshTerminal.response.state, firstRunId), coordinates.marker) + assert.equal(localRunCount(freshTerminal.response.state, firstRunId), 1) + freshObservation = await phase('freshProcess.observeControl', () => + waitForControlIdentity(freshSession, firstRunId, timeoutMs), + ) + assertSameControlRef(firstObservation.controlRef, freshObservation.controlRef, 'reconnect') + assertEnvironmentIdentity( + freshRun, + freshTerminal.response.state, + freshObservation.controlRef, + 'reconnected run', + ) + finalCursor = + latestCursorFromResponses(freshSession.responses, firstRunId) ?? + freshRun?.lastCursor ?? + freshObservation.cursor + freshVisible = assertUniqueVisibleEvents(freshSession.responses, firstRunId, 'fresh process') + assertNonVacuousVisibleEvents(freshVisible, 'fresh-process replay') + resumeIntersection = assertExclusiveResume(firstVisible.keys, freshVisible.keys) + resumeProgress = assertProviderResumeProgress( + firstResponses, + freshSession.responses, + firstRunId, + resumeFromCursor, + ) + const freshState = await stateRoundTrip(freshSession) + const firstRunCountAfterReplay = localRunCount(freshState.state, firstRunId) + assert.equal(firstRunCountAfterReplay, 1, 'reconnect left more than one local run') + + continuity = await phase('followUp.prepareContinuityChallenge', () => + prepareContinuityChallenge(client, freshObservation.controlRef, coordinates), + ) + + const followUp = await phase('followUp.send', () => + rpcRoundTrip( + freshSession, + 'send', + { + conversationId: freshState.state.conversationId, + branchId: freshState.state.branchId, + text: prompts.followUp, + }, + ids.followUp, + 'follow-up send acknowledgement', + ), + ) + const followUpAck = assertAck(followUp, 'follow-up send') + const followUpRunId = followUpAck.runId + assert.equal(typeof followUpRunId, 'string', 'follow-up send acknowledgement has no run ID') + const followUpTerminal = await phase('followUp.waitTerminal', () => + waitForTerminal(freshSession, followUpRunId, timeoutMs), + ) + assert.equal(followUpTerminal.run?.status, 'completed') + assert.equal( + assistantMarker(followUpTerminal.response.state, followUpRunId), + continuity.expectedDigest, + ) + const followUpObservation = await phase('followUp.observeControl', () => + waitForControlIdentity(freshSession, followUpRunId, timeoutMs), + ) + assertEnvironmentIdentity( + followUpTerminal.run, + followUpTerminal.response.state, + followUpObservation.controlRef, + 'follow-up run', + ) + assertSameCloudSession(firstObservation.controlRef, followUpObservation.controlRef, 'follow-up') + assert.equal( + followUpTerminal.run?.environmentId, + firstRun?.environmentId, + 'follow-up changed the local Braid environment record', + ) + assert.equal( + followUpTerminal.run?.providerSessionId, + firstRun?.providerSessionId, + 'follow-up changed the Braid provider session ID', + ) + followUpVisible = assertUniqueVisibleEvents( + freshSession.responses, + followUpRunId, + 'follow-up run', + ) + assertNonVacuousVisibleEvents(followUpVisible, 'follow-up run') + toolEvidence(followUpVisible, 'Follow-up run') + workspaceVerification = await phase('followUp.verifyWorkspace', () => + verifyRetainedWorkspace(client, followUpObservation.controlRef, coordinates, continuity), + ) + assert.equal(workspaceVerification.readMatched, true, 'SDK read did not match the marker') + assert.equal( + workspaceVerification.continuity.matched, + true, + 'follow-up did not materialize the hidden continuity challenge', + ) + assert.equal(workspaceVerification.git.exitCode, 0, 'SDK Git/read verification failed') + account = assertSameAccount( + followUpTerminal.response.state, + followUpTerminal.run, + identityRecords[0]?.value, + ) + telemetry = telemetryDisclosure( + followUpTerminal.run, + followUpTerminal.response.state, + workspaceVerification, + account, + ) + + const cancelSend = await phase('cancel.send', () => + rpcRoundTrip( + freshSession, + 'send', + { + conversationId: followUpTerminal.response.state.conversationId, + branchId: followUpTerminal.response.state.branchId, + text: prompts.cancel, + }, + ids.cancelSend, + 'cancel send acknowledgement', + ), + ) + const cancelSendAck = assertAck(cancelSend, 'cancel send') + cancelRunId = cancelSendAck.runId + assert.equal(typeof cancelRunId, 'string', 'cancel send acknowledgement has no run ID') + const cancelObservation = await phase('cancel.observeControl', () => + waitForControlIdentity(freshSession, cancelRunId, timeoutMs), + ) + const cancelState = await stateRoundTrip(freshSession) + const cancelRun = cancelState.state.runs?.find((run) => run.id === cancelRunId) + assertEnvironmentIdentity( + cancelRun, + cancelState.state, + cancelObservation.controlRef, + 'cancel run before cancellation', + ) + assertNonTerminalRun(cancelRun, 'cancel run before cancellation') + const cancel = await phase('cancel.first', () => + rpcRoundTrip( + freshSession, + 'cancel_run', + { runId: cancelRunId, reason: `Braid live cancellation ${coordinates.proofId}` }, + ids.cancel, + 'first cancel acknowledgement', + ), + ) + const cancelAck = assertAck(cancel, 'cancel_run') + assert.equal(cancelAck.runId, cancelRunId) + const cancelled = await phase('cancel.waitTerminal', () => + waitForTerminal(freshSession, cancelRunId, timeoutMs), + ) + assert.ok( + ['aborted', 'cancelled'].includes(cancelled.run?.status), + `cancel ended ${cancelled.run?.status}`, + ) + remoteCancellation = await phase('cancel.verifyRemote', () => + verifyRemoteCancellation( + client, + cancelObservation.controlRef, + coordinates.cancelMarker, + timeoutMs, + ), + ) + + const cancelRetry = await phase('cancel.retrySameBody', () => + rpcRoundTrip( + freshSession, + 'cancel_run', + { runId: cancelRunId, reason: `Braid live cancellation ${coordinates.proofId}` }, + ids.cancel, + 'same cancel retry acknowledgement', + ), + ) + const cancelRetryAck = assertAck(cancelRetry, 'same cancel retry') + assert.equal(cancelRetryAck.runId, cancelRunId) + if (cancelRetryAck.replayed !== true && cancelRetryAck.outcome !== 'already-applied') { + throw new MissingIntegrationError( + 'same cancellation retry did not expose replay-safe acknowledgement proof', + { + required: ['replayed:true', 'outcome:already-applied'], + response: cancelRetryAck, + }, + ) + } + + const cancelConflict = await phase('cancel.retryChangedBody', () => + rpcRoundTrip( + freshSession, + 'cancel_run', + { runId: cancelRunId, reason: `changed-body-${coordinates.proofId}` }, + ids.cancel, + 'changed cancel conflict response', + ), + ) + assert.equal(cancelConflict.response.type, 'error', 'changed cancel body was accepted') + assert.equal(cancelConflict.response.code, 'OPERATION_CONFLICT') + + const runRecords = { + first: firstSnapshot, + resumed: runSnapshot(freshRun, freshTerminal.response.state, freshObservation), + followUp: runSnapshot( + followUpTerminal.run, + followUpTerminal.response.state, + followUpObservation, + ), + cancelled: runSnapshot(cancelled.run, cancelled.response.state, cancelObservation), + } + const spend = spendDisclosure({ + resumed: freshRun, + followUp: followUpTerminal.run, + cancelled: cancelled.run, + }) + result = { + schemaVersion: 'braid.tangle-sandbox-braid-stress.v1', + status: 'passed', + proofId: coordinates.proofId, + resourceIdentity: tagObservation, + config: configEvidence(config), + processes: { + first: killed, + localRunCountAfterReconnect: firstRunCountAfterReplay, + binarySha256, + }, + runs: runRecords, + replay: { + firstVisibleEventCount: firstVisible.count, + freshVisibleEventCount: freshVisible.count, + freshVisibleEventIdsUnique: true, + resumeFromCursor, + finalCursor, + acknowledgedBeforeKillEventIds: firstVisible.keys, + freshVisibleEventIds: freshVisible.keys, + acknowledgedAndFreshIntersection: resumeIntersection, + progress: resumeProgress, + reconnectRequest: reconnect.request, + }, + cancellation: { + first: cancelAck, + sameBody: cancelRetryAck, + changedBody: cancelConflict.response, + remote: remoteCancellation, + }, + account, + telemetry, + spend, + followUpEvidence: { + visibleProviderEvents: followUpVisible.count, + continuity: workspaceVerification.continuity, + }, + workspaceVerification, + timing: phases, + } + } catch (error) { + failure = error + diagnostics = { + ...(firstSession ? { firstProcess: failureDiagnostics(firstSession, firstRunId) } : {}), + ...(freshSession + ? { + freshProcess: failureDiagnostics(freshSession, cancelRunId ?? firstRunId), + } + : {}), + } + collectIntegrationNeed(error, unresolvedIntegrationNeeds) + } finally { + if (freshSession) await closeSession(freshSession).catch((error) => (cleanupError ??= error)) + if (firstSession) await firstSession.close().catch((error) => (cleanupError ??= error)) + if (client) { + try { + const cleanup = await cleanupOwnedRetainedResources(client, { + controlRef: firstObservation?.controlRef, + firstRunId, + operationId: ids.first, + }) + cleanupConfirmed = cleanup.confirmed + cleanupMode = cleanup.mode + cleanupIdentity = cleanup + if (cleanup.matchedCount > 1) { + const error = new MissingIntegrationError( + 'One Braid turn created more than one retained Sandbox resource', + { matchedCount: cleanup.matchedCount, removedIds: cleanup.removedIds }, + ) + cleanupError ??= error + collectIntegrationNeed(error, unresolvedIntegrationNeeds) + } + if (!cleanupConfirmed) { + const error = new MissingIntegrationError( + 'Exact retained Braid Sandbox cleanup did not confirm deletion', + { environmentId: knownEnvironmentId }, + ) + cleanupError ??= error + collectIntegrationNeed(error, unresolvedIntegrationNeeds) + } + } catch (error) { + cleanupError ??= error + collectIntegrationNeed(error, unresolvedIntegrationNeeds) + } + finalUsage = await usage(client, 'after') + usageRecords.push(finalUsage) + identityRecords.push(await accountIdentity(client, 'after')) + } else { + const error = new MissingIntegrationError( + 'Exact-tag cleanup could not run because no Sandbox cleanup client was configured', + { required: 'BRAID_TANGLE_SANDBOX_CLEANUP_API_KEY or TANGLE_API_KEY' }, + ) + cleanupError ??= error + collectIntegrationNeed(error, unresolvedIntegrationNeeds) + finalUsage = { phase: 'after', value: undefined, error: errorDetails(error) } + usageRecords.push(finalUsage) + } + if (config) await config.cleanup().catch((error) => (cleanupError ??= error)) + } + + const beforeUsage = usageRecords.find((entry) => entry.phase === 'before') + const afterUsage = usageRecords.find((entry) => entry.phase === 'after') + const usageDelta = resourceDelta(afterUsage?.value, beforeUsage?.value) + const usageObservationComplete = + beforeUsage?.error === undefined && + beforeUsage?.value !== undefined && + beforeUsage.value !== null && + afterUsage?.error === undefined && + afterUsage?.value !== undefined && + afterUsage.value !== null && + usageDelta.activeSandboxes !== null + try { + accountIdentityConsistency = assertStableAccountIdentity(identityRecords) + } catch (error) { + accountIdentityError = error + collectIntegrationNeed(error, unresolvedIntegrationNeeds) + } + if (tagObservation?.observed !== true) { + const error = new MissingIntegrationError( + 'The live run did not prove the exact retained Braid Sandbox resource identity', + { proofId: coordinates.proofId, observation: tagObservation }, + ) + cleanupError ??= error + collectIntegrationNeed(error, unresolvedIntegrationNeeds) + } + if (requireZeroActiveResourceDelta && usageDelta.activeSandboxes !== 0) { + unresolvedIntegrationNeeds.push( + usageDelta.activeSandboxes === null + ? 'Sandbox usage must expose activeSandboxes before and after cleanup' + : `active Sandbox resource delta was ${usageDelta.activeSandboxes}, expected 0`, + ) + } + if (beforeUsage?.error) unresolvedIntegrationNeeds.push('Sandbox usage-before observation failed') + if (afterUsage?.error) unresolvedIntegrationNeeds.push('Sandbox usage-after observation failed') + if (!usageObservationComplete) { + unresolvedIntegrationNeeds.push( + 'Sandbox usage must expose activeSandboxes before execution and after cleanup', + ) + } + + const finished = { + ...(result ?? { + schemaVersion: 'braid.tangle-sandbox-braid-stress.v1', + proofId: coordinates.proofId, + config: config ? configEvidence(config) : undefined, + timing: phases, + }), + status: + failure === undefined && + cleanupError === undefined && + accountIdentityError === undefined && + usageObservationComplete && + (!requireZeroActiveResourceDelta || usageDelta.activeSandboxes === 0) + ? 'passed' + : 'failed', + proofId: coordinates.proofId, + cleanup: { + exactResource: cleanupConfirmed, + mode: cleanupMode, + identity: cleanupIdentity, + activeResourceDelta: usageDelta.activeSandboxes, + activeResourceDeltaRequired: requireZeroActiveResourceDelta, + usageObservationComplete, + usageDelta, + }, + usage: usageRecords.map((entry) => ({ + phase: entry.phase, + status: + entry.error !== undefined + ? 'unavailable' + : entry.value === undefined + ? 'missing' + : entry.value === null + ? 'unavailable' + : 'observed', + ...(entry.value === undefined ? {} : { value: entry.value }), + ...(entry.error ? { error: entry.error } : {}), + })), + accountIdentities: identityRecords.map((entry) => ({ + phase: entry.phase, + status: entry.error ? 'unavailable' : entry.value === undefined ? 'missing' : 'observed', + ...(entry.value === undefined ? {} : { value: entry.value }), + ...(entry.error ? { error: entry.error } : {}), + })), + accountIdentityConsistency: accountIdentityConsistency ?? null, + timing: { ...(result?.timing ?? {}), totalMs: performance.now() - startedAt }, + workspaceVerification: workspaceVerification ?? null, + progress: { + firstRunId, + cancelRunId, + providerEnvironmentId: knownEnvironmentId, + firstControlRef: firstObservation?.controlRef, + resumeFromCursor, + freshControlRef: freshObservation?.controlRef, + finalCursor, + }, + ...(failure ? { failure: errorDetails(failure) } : {}), + ...(diagnostics && Object.keys(diagnostics).length > 0 ? { diagnostics } : {}), + ...(cleanupError ? { cleanupFailure: errorDetails(cleanupError) } : {}), + ...(accountIdentityError ? { accountIdentityFailure: errorDetails(accountIdentityError) } : {}), + ...(unresolvedIntegrationNeeds.length > 0 + ? { unresolvedIntegrationNeeds: [...new Set(unresolvedIntegrationNeeds)] } + : {}), + } + return finished +} + +async function writeOutput(path, value) { + await mkdir(dirname(resolve(path)), { recursive: true }) + await writeFile(resolve(path), `${JSON.stringify(value, null, 2)}\n`, { mode: 0o600 }) +} + +async function main() { + const proof = await runBraidSandboxStress() + const outputPath = argument('output') + if (outputPath) await writeOutput(outputPath, proof) + process.stdout.write(`${JSON.stringify(proof, null, 2)}\n`) + if (proof.status !== 'passed') process.exitCode = 1 +} + +if (resolve(process.argv[1] ?? '') === scriptPath) { + main().catch((error) => { + process.stdout.write( + `${JSON.stringify({ status: 'failed', failure: errorDetails(error) }, null, 2)}\n`, + ) + process.exitCode = 1 + }) +} diff --git a/scripts/live-required/tangle-sandbox-stress.mjs b/scripts/live-required/tangle-sandbox-stress.mjs new file mode 100644 index 0000000..3823491 --- /dev/null +++ b/scripts/live-required/tangle-sandbox-stress.mjs @@ -0,0 +1,600 @@ +import assert from 'node:assert/strict' +import { spawn } from 'node:child_process' +import { mkdir, writeFile } from 'node:fs/promises' +import { dirname, resolve } from 'node:path' +import { createInterface } from 'node:readline' +import { fileURLToPath } from 'node:url' +import { Sandbox } from '@tangle-network/sandbox' + +const scriptDirectory = dirname(fileURLToPath(import.meta.url)) +const workerPath = resolve(scriptDirectory, 'tangle-sandbox-worker.mjs') +const DEFAULT_SANDBOX_ENDPOINT = 'https://sandbox.tangle.tools' +const DEFAULT_TIMEOUT_MS = 240_000 +const CLEANUP_PAGE_SIZE = 100 +export const PROOF_OWNER = 'braid-live-proof' + +function argument(name) { + const index = process.argv.indexOf(`--${name}`) + if (index === -1 || index + 1 >= process.argv.length) return undefined + return process.argv[index + 1] +} + +function requiredEnvironment(name, fallbacks = []) { + for (const candidate of [name, ...fallbacks]) { + const value = process.env[candidate]?.trim() + if (value) return value + } + throw new Error(`Missing ${name}`) +} + +function clientConfiguration() { + return { + baseUrl: process.env.BRAID_TANGLE_SANDBOX_ENDPOINT?.trim() || DEFAULT_SANDBOX_ENDPOINT, + apiKey: requiredEnvironment('BRAID_TANGLE_SANDBOX_API_KEY', ['TANGLE_API_KEY']), + } +} + +function proofCoordinates() { + const nonce = `${Date.now()}-${process.pid}` + return { + proofId: `braid-cloud-stress-${nonce}`, + sessionId: `braid-session-${nonce}`, + turnId: `braid-turn-${nonce}`, + marker: `BRAID_CLOUD_${nonce.replaceAll('-', '_')}`, + } +} + +function errorDetails(error) { + return { + name: error instanceof Error ? error.name : 'Error', + message: error instanceof Error ? error.message : String(error), + } +} + +function errorMessage(error) { + return error instanceof Error ? error.message : String(error) +} + +function combineErrors(primary, secondary) { + if (!primary) return secondary + if (!secondary) return primary + return new AggregateError( + [primary, secondary], + `${errorMessage(primary)}; ${errorMessage(secondary)}`, + ) +} + +function exactProofTag(box, proofId) { + return ( + box?.name === proofId && + box.metadata?.owner === PROOF_OWNER && + box.metadata?.proofId === proofId + ) +} + +export function duplicateTurnDetected(duplicate, executionId) { + return ( + typeof executionId === 'string' && + executionId.length > 0 && + duplicate?.dispatched === false && + duplicate?.sameExecution === true && + duplicate?.alreadyExisted === true && + duplicate?.executionId === executionId + ) +} + +export function cancellationReplayDetected(cancelled, executionId) { + return ( + typeof executionId === 'string' && + executionId.length > 0 && + cancelled?.executionId === executionId && + cancelled?.first?.cancelled === true && + cancelled?.second?.cancelled === false && + cancelled?.sessionStatus === 'cancelled' + ) +} + +export function assertExactResumeEvidence(resumed, { executionId, cursorEventId }) { + assert.equal(resumed.result.status, 'success', 'replayed result did not succeed') + assert.equal(resumed.executionId, executionId, 'resume record execution ID changed during replay') + assert.equal( + resumed.result.executionId, + executionId, + 'resume result execution ID changed during replay', + ) + assert.equal(resumed.result.markerMatched, true, 'replayed result marker changed') + assert.equal(resumed.sessionStatus, 'completed', 'replayed session did not complete') + assert.equal(resumed.completedTurn.found, true, 'completed turn was not retained') + assert.equal( + resumed.completedTurn.executionId, + executionId, + 'completed turn points to a different execution', + ) + assert.equal(resumed.cursorWasInclusive, true, 'replay did not include the acknowledged cursor') + assert.ok(Array.isArray(resumed.replay), 'replay did not return an event list') + assert.ok(resumed.replay.length > 1, 'replay did not return the complete event history') + assert.equal(resumed.replay[0]?.id, cursorEventId, 'replay started at the wrong event') + assert.equal( + new Set(resumed.replay.map((event) => event.id)).size, + resumed.replay.length, + 'replay returned a duplicate event ID', + ) + assert.ok( + resumed.replay.every( + (event) => + typeof event.id === 'string' && + event.id.length > 0 && + typeof event.type === 'string' && + event.type.length > 0, + ), + 'replay returned an incomplete event record', + ) + assert.ok( + resumed.replay.every( + (event) => event.executionId === undefined || event.executionId === executionId, + ), + 'replay included a different execution', + ) +} + +function spawnWorker(mode, values = {}) { + const args = [workerPath, mode] + for (const [name, value] of Object.entries(values)) args.push(`--${name}`, value) + const child = spawn(process.execPath, args, { + env: process.env, + stdio: ['pipe', 'pipe', 'pipe'], + }) + const records = [] + const waiters = new Set() + const stderr = [] + const output = createInterface({ input: child.stdout }) + const errors = createInterface({ input: child.stderr }) + + output.on('line', (line) => { + let record + try { + record = JSON.parse(line) + } catch { + record = { type: 'invalid-output', line } + } + records.push(record) + for (const waiter of waiters) waiter(record) + }) + errors.on('line', (line) => { + if (stderr.length < 20) stderr.push(line) + }) + + return { child, records, waiters, stderr } +} + +function waitForRecord(worker, type, timeoutMs = DEFAULT_TIMEOUT_MS) { + const prior = worker.records.find((record) => record.type === type) + if (prior) return Promise.resolve(prior) + const priorError = worker.records.find((record) => record.type === 'error') + if (priorError) return Promise.reject(new Error(priorError.message)) + return new Promise((resolvePromise, rejectPromise) => { + let settled = false + const finish = (operation) => { + if (settled) return + settled = true + clearTimeout(timeout) + worker.waiters.delete(onRecord) + worker.child.off('exit', onExit) + operation() + } + const onRecord = (record) => { + if (record.type === type) finish(() => resolvePromise(record)) + if (record.type === 'error') finish(() => rejectPromise(new Error(record.message))) + } + const onExit = (code, signal) => + finish(() => + rejectPromise( + new Error( + `Worker exited before ${type}: code=${String(code)} signal=${String(signal)} stderr=${worker.stderr.join(' | ')}`, + ), + ), + ) + const timeout = setTimeout( + () => + finish(() => { + worker.child.kill('SIGKILL') + rejectPromise(new Error(`Worker timed out waiting for ${type}`)) + }), + timeoutMs, + ) + worker.waiters.add(onRecord) + worker.child.once('exit', onExit) + }) +} + +function knownExit(child) { + if (child.exitCode !== null || child.signalCode !== null) + return { code: child.exitCode, signal: child.signalCode } + return null +} + +function waitForExit(child) { + return new Promise((resolvePromise) => { + const onExit = (code, signal) => { + child.off('exit', onExit) + resolvePromise({ code, signal }) + } + child.once('exit', onExit) + const existing = knownExit(child) + if (existing) { + child.off('exit', onExit) + resolvePromise(existing) + } + }) +} + +async function stopWorker(worker) { + const existing = knownExit(worker.child) + if (existing) return existing + const exit = waitForExit(worker.child) + if (!knownExit(worker.child)) worker.child.kill('SIGKILL') + return exit +} + +async function waitForWorkerExit(worker) { + return waitForExit(worker.child) +} + +async function listAllSandboxes(client) { + const boxes = [] + const seenIds = new Set() + let offset = 0 + for (;;) { + const page = await client.list({ limit: CLEANUP_PAGE_SIZE, offset }) + if (!Array.isArray(page)) throw new Error('Sandbox list returned an invalid page') + for (const box of page) { + if (typeof box?.id === 'string') { + if (seenIds.has(box.id)) throw new Error(`Sandbox list repeated ${box.id}`) + seenIds.add(box.id) + } + boxes.push(box) + } + if (page.length < CLEANUP_PAGE_SIZE) return boxes + offset += page.length + } +} + +function numericDifference(after, before, key) { + const left = after?.[key] + const right = before?.[key] + return typeof left === 'number' && typeof right === 'number' ? left - right : null +} + +export async function cleanupProof(client, proofId, knownEnvironmentId) { + const candidates = [] + if (knownEnvironmentId) { + const known = await client.get(knownEnvironmentId).catch(() => null) + if (known) { + if (!exactProofTag(known, proofId)) + throw new Error(`Refusing to delete sandbox ${known.id} without exact proof ownership`) + candidates.push(known) + } + } + const knownIds = new Set(candidates.map((box) => box.id)) + const listed = await listAllSandboxes(client) + candidates.push(...listed.filter((box) => exactProofTag(box, proofId) && !knownIds.has(box.id))) + for (const box of candidates) { + if (!exactProofTag(box, proofId)) + throw new Error(`Refusing to delete sandbox ${box.id} without exact proof ownership`) + await box.delete() + } + const remaining = await listAllSandboxes(client) + return !remaining.some((box) => exactProofTag(box, proofId)) +} + +function failureOutcome({ + coordinates, + environmentId, + outcome, + state, + failure, + cleanupError, + cleanupConfirmed, + usageBefore, + usageAfter, +}) { + return { + ...(outcome ?? { + schemaVersion: 'braid.tangle-sandbox-stress.v1', + proofId: coordinates.proofId, + }), + status: 'failed', + proofId: coordinates.proofId, + ...(environmentId ? { environmentId } : {}), + failure: errorDetails(failure ?? cleanupError), + ...(cleanupError ? { cleanupFailure: errorDetails(cleanupError) } : {}), + cleanupConfirmed, + phase: state.failurePhase ?? state.phase, + progress: { + created: state.created ?? null, + admitted: state.admitted ?? null, + cursor: state.cursor ?? null, + dispatcherDeath: state.dispatcherDeath ?? null, + watcherDeath: state.watcherDeath ?? null, + resumed: state.resumed ?? null, + cancelled: state.cancelled ?? null, + }, + accountDelta: { + activeSandboxes: numericDifference(usageAfter, usageBefore, 'activeSandboxes'), + totalSandboxes: numericDifference(usageAfter, usageBefore, 'totalSandboxes'), + computeMinutes: numericDifference(usageAfter, usageBefore, 'computeMinutes'), + gpuSeconds: numericDifference(usageAfter, usageBefore, 'gpuSeconds'), + gpuCostUsd: numericDifference(usageAfter, usageBefore, 'gpuCostUsd'), + }, + } +} + +export async function writeProofArtifact(outputPath, proof) { + const absolute = resolve(outputPath) + await mkdir(dirname(absolute), { recursive: true }) + await writeFile(absolute, `${JSON.stringify(proof, null, 2)}\n`, { mode: 0o600 }) +} + +export async function runStressProof({ + client: suppliedClient, + coordinates = proofCoordinates(), + workerFactory = spawnWorker, + outputPath, + writeArtifact = writeProofArtifact, +} = {}) { + const client = suppliedClient ?? new Sandbox(clientConfiguration()) + const activeWorkers = new Set() + const state = { phase: 'usage-before' } + let usageBefore + let usageAfter + let environmentId + let cleanupConfirmed = false + let outcome + let failure + let cleanupError + let artifactError + let finalProof + + try { + usageBefore = await client.usage() + state.phase = 'dispatch-create' + const dispatcher = workerFactory('dispatch', { + 'proof-id': coordinates.proofId, + 'session-id': coordinates.sessionId, + 'turn-id': coordinates.turnId, + marker: coordinates.marker, + }) + activeWorkers.add(dispatcher) + const created = await waitForRecord(dispatcher, 'created') + state.created = created + environmentId = created.environmentId + assert.equal(created.workspace.readMatched, true) + assert.equal(created.workspace.gitExitCode, 0) + assert.match(created.workspace.gitCommit, /^[0-9a-f]{40}$/u) + + state.phase = 'watch-create' + const watcher = workerFactory('watch', { + 'environment-id': environmentId, + 'session-id': coordinates.sessionId, + }) + activeWorkers.add(watcher) + await waitForRecord(watcher, 'listening') + state.phase = 'dispatch' + dispatcher.child.stdin.write('dispatch\n') + const admittedPromise = waitForRecord(dispatcher, 'admitted').then((record) => { + state.admitted = record + return record + }) + const cursorPromise = waitForRecord(watcher, 'cursor').then((record) => { + state.cursor = record + return record + }) + const [admitted, cursor] = await Promise.all([admittedPromise, cursorPromise]) + assert.equal(admitted.dispatched, true) + if (cursor.executionId !== undefined) + assert.equal( + cursor.executionId, + admitted.executionId, + 'cursor points to a different execution', + ) + + state.phase = 'kill-dispatcher' + const dispatcherDeath = await stopWorker(dispatcher) + state.dispatcherDeath = dispatcherDeath + state.phase = 'kill-watcher' + const watcherDeath = await stopWorker(watcher) + state.watcherDeath = watcherDeath + activeWorkers.delete(dispatcher) + activeWorkers.delete(watcher) + assert.equal(dispatcherDeath.signal, 'SIGKILL') + assert.equal(watcherDeath.signal, 'SIGKILL') + + state.phase = 'resume' + const resumedWorker = workerFactory('resume', { + 'environment-id': environmentId, + 'session-id': coordinates.sessionId, + 'execution-id': admitted.executionId, + 'turn-id': coordinates.turnId, + marker: coordinates.marker, + cursor: cursor.eventId, + }) + activeWorkers.add(resumedWorker) + const resumed = await waitForRecord(resumedWorker, 'resumed') + state.resumed = resumed + const resumedExit = await waitForWorkerExit(resumedWorker) + activeWorkers.delete(resumedWorker) + assert.equal(resumedExit.code, 0) + assertExactResumeEvidence(resumed, { + executionId: admitted.executionId, + cursorEventId: cursor.eventId, + }) + assert.equal(resumed.workspaceRetained, true) + + state.phase = 'cancellation-dispatch' + const recovered = await client.get(environmentId) + if (!recovered) throw new Error('Sandbox disappeared before cancellation proof') + const cancelSessionId = `${coordinates.sessionId}-cancel` + const cancelTurnId = `${coordinates.turnId}-cancel` + const cancelDispatch = await recovered.dispatchPrompt( + 'Run sleep 120 in the shell. After it exits, reply with exactly CANCEL_MISSED.', + { sessionId: cancelSessionId, turnId: cancelTurnId, timeoutMs: 180_000 }, + ) + state.cancelDispatch = cancelDispatch + if (typeof cancelDispatch.executionId !== 'string' || cancelDispatch.executionId.length === 0) + throw new Error('Cancellation dispatch returned no execution ID') + state.phase = 'cancel' + const cancelWorker = workerFactory('cancel', { + 'environment-id': environmentId, + 'session-id': cancelSessionId, + 'execution-id': cancelDispatch.executionId, + }) + activeWorkers.add(cancelWorker) + const cancelled = await waitForRecord(cancelWorker, 'cancelled') + state.cancelled = cancelled + const cancelExit = await waitForWorkerExit(cancelWorker) + activeWorkers.delete(cancelWorker) + assert.equal(cancelExit.code, 0) + assert.equal(cancelled.first.cancelled, true) + + state.phase = 'complete' + const sameExecution = resumed.duplicate.sameExecution === true + const duplicateReplay = duplicateTurnDetected(resumed.duplicate, admitted.executionId) + const cancellationReplay = cancellationReplayDetected(cancelled, cancelDispatch.executionId) + const gaps = [ + ...(!sameExecution ? ['turn-idempotency'] : []), + ...(sameExecution && (!duplicateReplay || resumed.duplicate.dispatched) + ? ['turn-idempotency-receipt'] + : []), + ...(!cancellationReplay ? ['cancellation-operation-replay'] : []), + ] + outcome = { + schemaVersion: 'braid.tangle-sandbox-stress.v1', + status: gaps.length === 0 ? 'passed' : 'passed-with-gaps', + gaps, + proofId: coordinates.proofId, + environmentId, + exactRun: { + sessionId: coordinates.sessionId, + executionId: admitted.executionId, + turnId: coordinates.turnId, + }, + checks: { + created: true, + workspaceReadWriteExecGit: true, + resourceUsageReported: created.workspace.resourceUsageReported, + processKilledAfterDispatch: dispatcherDeath.signal === 'SIGKILL', + processKilledAfterCursor: watcherDeath.signal === 'SIGKILL', + freshProcessReplay: true, + exactResultIdentity: true, + turnIdempotency: duplicateReplay, + turnIdempotencyReceipt: duplicateReplay && resumed.duplicate.dispatched === false, + workspaceRetained: true, + exactCancellation: cancelled.first.cancelled === true, + cancellationOperationReplay: cancellationReplay, + }, + timingsMs: { + create: created.createdMs, + workspace: created.workspaceMs, + dispatchAdmission: admitted.dispatchMs, + reconnectToResult: resumed.reconnectToResultMs, + }, + events: { + firstObservedId: cursor.eventId, + firstObservedType: cursor.eventType, + replayCount: resumed.replay.length, + replayUniqueCount: new Set(resumed.replay.map((event) => event.id)).size, + providerCursorInclusive: resumed.cursorWasInclusive, + }, + usage: resumed.result.usage, + costUsd: resumed.result.costUsd, + idempotency: { + completedTurn: resumed.completedTurn, + duplicate: resumed.duplicate, + messageStates: resumed.messageStates, + }, + cancellation: { + executionId: cancelDispatch.executionId, + first: cancelled.first, + second: cancelled.second, + sessionStatus: cancelled.sessionStatus, + }, + observation: created.workspace, + } + } catch (error) { + failure = error + state.failurePhase = state.phase + } finally { + for (const worker of activeWorkers) { + try { + await stopWorker(worker) + } catch (error) { + cleanupError = combineErrors(cleanupError, error) + } + } + state.phase = 'cleanup-sandbox' + try { + cleanupConfirmed = await cleanupProof(client, coordinates.proofId, environmentId) + } catch (error) { + cleanupError = combineErrors(cleanupError, error) + } + state.phase = 'usage-after' + try { + usageAfter = await client.usage() + } catch (error) { + cleanupError = combineErrors(cleanupError, error) + } + if (!cleanupError) { + try { + assert.equal(cleanupConfirmed, true) + } catch (error) { + cleanupError = error + } + } + const finalError = combineErrors(failure, cleanupError) + finalProof = finalError + ? failureOutcome({ + coordinates, + environmentId, + outcome, + state, + failure: finalError, + cleanupError, + cleanupConfirmed, + usageBefore, + usageAfter, + }) + : { + ...outcome, + cleanupConfirmed, + accountDelta: { + activeSandboxes: numericDifference(usageAfter, usageBefore, 'activeSandboxes'), + totalSandboxes: numericDifference(usageAfter, usageBefore, 'totalSandboxes'), + computeMinutes: numericDifference(usageAfter, usageBefore, 'computeMinutes'), + gpuSeconds: numericDifference(usageAfter, usageBefore, 'gpuSeconds'), + gpuCostUsd: numericDifference(usageAfter, usageBefore, 'gpuCostUsd'), + }, + } + if (outputPath) { + try { + await writeArtifact(outputPath, finalProof) + } catch (error) { + artifactError = error + } + } + } + + const finalError = combineErrors(combineErrors(failure, cleanupError), artifactError) + if (finalError) throw finalError + return finalProof +} + +function invokedDirectly() { + return ( + process.argv[1] !== undefined && resolve(process.argv[1]) === fileURLToPath(import.meta.url) + ) +} + +if (invokedDirectly()) { + const proof = await runStressProof({ outputPath: argument('output') }) + process.stdout.write(`${JSON.stringify(proof, null, 2)}\n`) +} diff --git a/scripts/live-required/tangle-sandbox-worker.mjs b/scripts/live-required/tangle-sandbox-worker.mjs new file mode 100644 index 0000000..1031aad --- /dev/null +++ b/scripts/live-required/tangle-sandbox-worker.mjs @@ -0,0 +1,342 @@ +import { createInterface } from 'node:readline' +import { resolve } from 'node:path' +import { fileURLToPath } from 'node:url' +import { Sandbox } from '@tangle-network/sandbox' + +const DEFAULT_SANDBOX_ENDPOINT = 'https://sandbox.tangle.tools' +const DEFAULT_MODEL_ENDPOINT = 'https://router.tangle.tools/v1' +const DEFAULT_MODEL = 'glm-5.2' +const DEFAULT_MODEL_PROVIDER = 'openai-compat' + +function argument(name, argv = process.argv) { + const index = argv.indexOf(`--${name}`) + if (index === -1 || index + 1 >= argv.length) return undefined + return argv[index + 1] +} + +function requiredArgument(name, argv = process.argv) { + const value = argument(name, argv) + if (typeof value !== 'string' || value.length === 0) throw new Error(`Missing --${name}`) + return value +} + +function requiredEnvironment(name, fallbacks = [], environment = process.env) { + for (const candidate of [name, ...fallbacks]) { + const value = environment[candidate]?.trim() + if (value) return value + } + throw new Error(`Missing ${name}`) +} + +function clientConfiguration(environment = process.env) { + return { + baseUrl: environment.BRAID_TANGLE_SANDBOX_ENDPOINT?.trim() || DEFAULT_SANDBOX_ENDPOINT, + apiKey: requiredEnvironment('BRAID_TANGLE_SANDBOX_API_KEY', ['TANGLE_API_KEY'], environment), + } +} + +function backendConfiguration(environment = process.env) { + const provider = environment.BRAID_TANGLE_SANDBOX_MODEL_PROVIDER?.trim() || DEFAULT_MODEL_PROVIDER + const model = environment.BRAID_TANGLE_SANDBOX_MODEL?.trim() || DEFAULT_MODEL + const baseUrl = environment.BRAID_TANGLE_SANDBOX_MODEL_ENDPOINT?.trim() || DEFAULT_MODEL_ENDPOINT + const apiKey = requiredEnvironment( + 'BRAID_TANGLE_SANDBOX_MODEL_API_KEY', + ['TANGLE_API_KEY'], + environment, + ) + return { + type: environment.BRAID_TANGLE_SANDBOX_RUNNER?.trim() || 'opencode', + profile: { + name: 'braid-cloud-proof', + harness: 'opencode', + model: { provider: 'tangle-router', default: `tangle-router/${model}` }, + }, + model: { provider, model, baseUrl, apiKey }, + } +} + +function writeRecord(record) { + process.stdout.write(`${JSON.stringify(record)}\n`) +} + +function eventExecutionId(event) { + const candidate = event?.executionId ?? event?.data?.executionId + return typeof candidate === 'string' && candidate.length > 0 ? candidate : undefined +} + +function waitForLine(expected, inputStream = process.stdin) { + const input = createInterface({ input: inputStream }) + return new Promise((resolve, reject) => { + const close = () => reject(new Error(`stdin closed before ${expected}`)) + input.once('close', close) + input.on('line', (line) => { + if (line !== expected) return + input.off('close', close) + input.close() + resolve() + }) + }) +} + +function waitUntilKilled() { + return new Promise(() => undefined) +} + +async function createAndDispatch({ + argv = process.argv, + environment = process.env, + client: suppliedClient, + write = writeRecord, + inputStream = process.stdin, +} = {}) { + const proofId = requiredArgument('proof-id', argv) + const sessionId = requiredArgument('session-id', argv) + const turnId = requiredArgument('turn-id', argv) + const marker = requiredArgument('marker', argv) + const client = suppliedClient ?? new Sandbox(clientConfiguration(environment)) + const backend = backendConfiguration(environment) + const started = performance.now() + const box = await client.create({ + name: proofId, + environment: 'universal', + idempotencyKey: proofId, + maxLifetimeSeconds: 600, + metadata: { owner: 'braid-live-proof', proofId }, + backend, + }) + const createdMs = performance.now() - started + + const workspaceStarted = performance.now() + const markerPath = '/tmp/braid-cloud-proof.txt' + await box.write(markerPath, `${marker}\n`) + const readBack = await box.read(markerPath) + const git = await box.exec( + 'set -eu; mkdir -p /tmp/braid-cloud-git; cd /tmp/braid-cloud-git; git init -q; git config user.email braid@local; git config user.name Braid; cp /tmp/braid-cloud-proof.txt proof.txt; git add proof.txt; git commit -qm proof; git rev-parse --verify HEAD', + ) + const resourceUsage = await box.resourceUsage().catch(() => null) + await box.createSession({ sessionId, title: 'Braid durability proof', backend }) + write({ + type: 'created', + pid: process.pid, + proofId, + environmentId: box.id, + sessionId, + turnId, + marker, + createdMs, + workspaceMs: performance.now() - workspaceStarted, + workspace: { + readMatched: readBack === `${marker}\n`, + gitExitCode: git.exitCode, + gitCommit: git.stdout.trim(), + resourceUsageReported: resourceUsage !== null, + runtimeEndpointReported: Boolean(box.connection?.runtimeUrl), + machineIdReported: Boolean(box.machineId), + regionReported: Boolean(box.region), + }, + }) + + await waitForLine('dispatch', inputStream) + const prompt = promptFor(marker) + const dispatchStarted = performance.now() + const dispatched = await box.dispatchPrompt(prompt, { + sessionId, + turnId, + timeoutMs: 180_000, + }) + if (typeof dispatched.executionId !== 'string' || dispatched.executionId.length === 0) + throw new Error('Sandbox dispatch returned no execution ID') + write({ + type: 'admitted', + pid: process.pid, + environmentId: box.id, + sessionId: dispatched.sessionId, + executionId: dispatched.executionId, + turnId, + marker, + prompt, + dispatchMs: performance.now() - dispatchStarted, + dispatched: dispatched.dispatched, + alreadyExisted: dispatched.alreadyExisted, + }) + await waitUntilKilled() +} + +async function watchFirstEvent({ + argv = process.argv, + environment = process.env, + client: suppliedClient, + write = writeRecord, +} = {}) { + const client = suppliedClient ?? new Sandbox(clientConfiguration(environment)) + const environmentId = requiredArgument('environment-id', argv) + const sessionId = requiredArgument('session-id', argv) + const box = await client.get(environmentId) + if (!box) throw new Error(`Sandbox ${environmentId} was not found`) + write({ type: 'listening', pid: process.pid, environmentId, sessionId }) + for await (const event of box.session(sessionId).events()) { + if (typeof event.id !== 'string' || event.id.length === 0) continue + const executionId = eventExecutionId(event) + write({ + type: 'cursor', + pid: process.pid, + environmentId, + sessionId, + eventId: event.id, + eventType: event.type, + ...(executionId ? { executionId } : {}), + }) + await waitUntilKilled() + } + throw new Error('Session event stream ended before a stable event ID arrived') +} + +export async function resumeRun({ + argv = process.argv, + environment = process.env, + client: suppliedClient, + write = writeRecord, +} = {}) { + const client = suppliedClient ?? new Sandbox(clientConfiguration(environment)) + const environmentId = requiredArgument('environment-id', argv) + const sessionId = requiredArgument('session-id', argv) + const executionId = requiredArgument('execution-id', argv) + const turnId = requiredArgument('turn-id', argv) + const marker = requiredArgument('marker', argv) + const cursor = requiredArgument('cursor', argv) + const box = await client.get(environmentId) + if (!box) throw new Error(`Sandbox ${environmentId} was not found`) + const reconnectStarted = performance.now() + const session = box.session(sessionId) + const replay = [] + for await (const event of session.events({ since: cursor, executionId })) { + if (typeof event.id === 'string' && event.id.length > 0) { + const eventExecution = eventExecutionId(event) + replay.push({ + id: event.id, + type: event.type, + ...(eventExecution ? { executionId: eventExecution } : {}), + }) + } + } + const result = await session.result({ executionId }) + const status = await session.status() + const completedTurn = await box.findCompletedTurn(turnId, { sessionId }) + const messages = await box.messages({ sessionId, limit: 100 }) + const duplicate = await box.dispatchPrompt(promptFor(marker), { + sessionId, + turnId, + timeoutMs: 180_000, + }) + let duplicateCancellation = null + if (duplicate.dispatched && duplicate.executionId && duplicate.executionId !== executionId) { + duplicateCancellation = await session.interrupt({ + executionId: duplicate.executionId, + }) + } + const readBack = await box.read('/tmp/braid-cloud-proof.txt') + const duplicateExecutionId = + typeof duplicate.executionId === 'string' && duplicate.executionId.length > 0 + ? duplicate.executionId + : null + write({ + type: 'resumed', + pid: process.pid, + environmentId, + sessionId, + executionId, + reconnectToResultMs: performance.now() - reconnectStarted, + replay, + cursorWasInclusive: replay[0]?.id === cursor, + result: { + status: result.status, + executionId: result.executionId, + markerMatched: typeof result.response === 'string' && result.response.trim() === marker, + usage: result.usage ?? null, + costUsd: result.costUsd ?? null, + }, + sessionStatus: status?.status ?? null, + completedTurn: { + found: completedTurn !== null, + executionId: + typeof completedTurn?.result?.executionId === 'string' + ? completedTurn.result.executionId + : null, + }, + messageStates: messages.map((message) => ({ + role: message.role, + status: message.metadata?.status ?? null, + completed: message.metadata?.completed ?? null, + interrupted: message.metadata?.interrupted ?? null, + turnId: message.metadata?.turnId ?? null, + })), + duplicate: { + dispatched: duplicate.dispatched, + executionId: duplicateExecutionId, + sameExecution: duplicateExecutionId === executionId, + alreadyExisted: duplicate.alreadyExisted === true, + cancellation: duplicateCancellation, + }, + workspaceRetained: readBack === `${marker}\n`, + }) +} + +export async function cancelRun({ + argv = process.argv, + environment = process.env, + client: suppliedClient, + write = writeRecord, +} = {}) { + const client = suppliedClient ?? new Sandbox(clientConfiguration(environment)) + const environmentId = requiredArgument('environment-id', argv) + const sessionId = requiredArgument('session-id', argv) + const executionId = requiredArgument('execution-id', argv) + const box = await client.get(environmentId) + if (!box) throw new Error(`Sandbox ${environmentId} was not found`) + const session = box.session(sessionId) + const first = await session.interrupt({ executionId }) + const second = await session.interrupt({ executionId }) + const status = await session.status() + if (typeof first?.cancelled !== 'boolean' || typeof second?.cancelled !== 'boolean') + throw new Error('Cancellation returned an invalid acknowledgement') + write({ + type: 'cancelled', + pid: process.pid, + environmentId, + sessionId, + executionId, + first, + second, + replayDetected: first.cancelled === true && second.cancelled === false, + sessionStatus: status?.status ?? null, + }) +} + +export function promptFor(marker) { + return `Read /tmp/braid-cloud-proof.txt with a tool. Run sleep 8 in the shell. Then reply with exactly ${marker}.` +} + +export async function runWorker(mode, options = {}) { + if (mode === 'dispatch') return createAndDispatch(options) + if (mode === 'watch') return watchFirstEvent(options) + if (mode === 'resume') return resumeRun(options) + if (mode === 'cancel') return cancelRun(options) + throw new Error(`Unknown worker mode ${String(mode)}`) +} + +function invokedDirectly() { + return ( + process.argv[1] !== undefined && resolve(process.argv[1]) === fileURLToPath(import.meta.url) + ) +} + +if (invokedDirectly()) { + runWorker(process.argv[2]).catch((error) => { + writeRecord({ + type: 'error', + pid: process.pid, + message: error instanceof Error ? error.message : String(error), + }) + process.exitCode = 1 + }) +} diff --git a/scripts/live-required/tangle.mjs b/scripts/live-required/tangle.mjs index f43aa79..ec20b73 100644 --- a/scripts/live-required/tangle.mjs +++ b/scripts/live-required/tangle.mjs @@ -1,7 +1,7 @@ import { connectionConfiguration } from './configuration.mjs' import { - PROOF_OPERATIONS, classifyExternalFailure, + PROOF_OPERATIONS, proofInvocation, proofReceipt, scalarMeasurement, @@ -14,8 +14,11 @@ import { runHeadlessCancellation, runHeadlessTurn, } from './headless.mjs' +import { runBraidSandboxSoak } from './tangle-sandbox-braid-soak.mjs' const TANGLE_ROWS = Object.freeze(['LIVE-06', 'LIVE-07', 'LIVE-08', 'LIVE-09', 'LIVE-10']) +const MINIMUM_SANDBOX_STRESS_RUNS = 3 +const MINIMUM_SANDBOX_STRESS_CONCURRENCY = 2 function tokenMarker(name) { return `LIVE_BRAID_${name}_OK` @@ -78,56 +81,91 @@ async function runInference({ repository, environment, binary, invocationId }) { } } -async function runSandbox({ repository, environment, binary, invocationId }) { +export async function runSandbox({ + repository, + environment, + binary, + invocationId, + stressRunner = runBraidSandboxSoak, +}) { const startedAt = new Date().toISOString() - const values = connectionConfiguration(environment, { - prefix: 'BRAID_TANGLE_SANDBOX', - kind: 'tangle-sandbox', - endpointNames: ['BRAID_TANGLE_SANDBOX_ENDPOINT', 'BRAID_TANGLE_ENDPOINT'], - modelNames: ['BRAID_TANGLE_SANDBOX_MODEL', 'BRAID_TANGLE_MODEL'], - runnerNames: ['BRAID_TANGLE_SANDBOX_RUNNER'], - providerNames: ['BRAID_TANGLE_SANDBOX_PROVIDER'], - }) - const config = await prepareProductionWorkspace({ - repository, - environment, - ...values, - }) - let turn - try { - turn = await runHeadlessTurn({ - binary, - config, - marker: tokenMarker('TANGLE_SANDBOX'), - prompt: `Reply with exactly ${tokenMarker('TANGLE_SANDBOX')} after creating and reading a temporary workspace file.`, - }) - const environmentId = turn.run.environmentId - if (typeof environmentId !== 'string' || environmentId.length === 0) { - throw new Error('sandbox turn completed without a materialized environment id') - } - return { - status: 'partial', - evidence: proofReceipt({ - invocationId, - operation: PROOF_OPERATIONS.tangleSandbox, - status: 'partial', - startedAt, - completedAt: new Date().toISOString(), - config: configEvidence(config), - runIds: [turn.run.id], - environmentId, - facts: { environmentId }, - checks: ['marker', 'environment-id'], - }), - unavailable: { - row: 'LIVE-07', - reason: - 'The direct Braid path does not independently prove replay, workspace read/write/exec/git, cancellation, retained environment, or cleanup. No parent-owned operation is available for the full matrix.', - }, - } - } finally { - if (turn?.session) await closeSession(turn.session).catch(() => undefined) - await config.cleanup() + const cohort = await stressRunner({ repository, environment, binary }) + if (cohort.status !== 'passed') { + const unresolved = cohort.failures?.join('; ') + throw new Error( + `LIVE-07 Braid Tangle Sandbox stress failed: ${unresolved ?? 'no failure details'}`, + ) + } + if ( + !Number.isSafeInteger(cohort.requestedRuns) || + cohort.requestedRuns < MINIMUM_SANDBOX_STRESS_RUNS || + cohort.attemptedRuns !== cohort.requestedRuns || + !Number.isSafeInteger(cohort.concurrency) || + cohort.concurrency < MINIMUM_SANDBOX_STRESS_CONCURRENCY || + !Array.isArray(cohort.attempts) || + cohort.attempts.length !== cohort.requestedRuns || + cohort.cleanup?.exactProofs !== cohort.requestedRuns || + cohort.cleanup?.exactResourcesRemaining !== 0 || + cohort.cleanup?.activeResourceDelta !== 0 + ) { + throw new Error( + 'LIVE-07 requires at least three complete cloud proofs, two-way concurrency, and exact zero-resource cleanup', + ) + } + const proof = cohort.attempts?.find((attempt) => attempt.index === 0)?.proof + if (proof?.status !== 'passed') { + throw new Error('LIVE-07 Braid Tangle Sandbox stress has no passing canary proof') + } + const firstRun = proof.runs?.first + const runIds = [ + ...new Set([ + firstRun?.id, + proof.runs?.resumed?.id, + proof.runs?.followUp?.id, + proof.runs?.cancelled?.id, + ]), + ].filter((runId) => typeof runId === 'string' && runId.length > 0) + const localEnvironmentId = firstRun?.environmentId ?? null + const activeResourceDelta = cohort.cleanup?.activeResourceDelta + const cloudControl = proof.progress?.firstControlRef ?? null + const facts = { + environmentId: localEnvironmentId, + resumedRunId: proof.runs?.resumed?.id ?? null, + followUpRunId: proof.runs?.followUp?.id ?? null, + cancelledRunId: proof.runs?.cancelled?.id ?? null, + resumeFromCursor: proof.replay?.resumeFromCursor ?? null, + finalCursor: proof.replay?.finalCursor ?? null, + cloudControl, + exactResource: cohort.cleanup?.exactResourcesRemaining === 0, + activeResourceDelta: typeof activeResourceDelta === 'number' ? activeResourceDelta : null, + } + return { + status: 'passed', + measurement: scalarMeasurement('LIVE-07'), + evidence: proofReceipt({ + invocationId, + operation: PROOF_OPERATIONS.tangleSandbox, + startedAt, + completedAt: new Date().toISOString(), + config: proof.config, + runIds, + environmentId: localEnvironmentId, + materializationDigest: firstRun?.materializationDigest ?? null, + facts, + observations: cohort, + environment, + checks: [ + 'marker', + 'environment-id', + 'workspace-read-write-exec-git', + 'sigkill-reconnect', + 'exclusive-replay', + 'follow-up-session', + 'cancel-retry-conflict', + 'exact-resource-cleanup', + ], + }), + observations: cohort, } } @@ -143,7 +181,13 @@ export async function runMatrixAdapter({ environment }) { } } -export async function runTangleFlows({ repository, environment }) { +export async function runTangleFlows({ + repository, + environment, + inferenceRunner = runInference, + sandboxRunner = runSandbox, + matrixRunner = runMatrixAdapter, +}) { const binary = await resolveBinary(repository, environment) const invocationId = proofInvocation('live-tangle') const flows = [] @@ -161,7 +205,7 @@ export async function runTangleFlows({ repository, environment }) { } let inference try { - inference = await runInference({ repository, environment, binary, invocationId }) + inference = await inferenceRunner({ repository, environment, binary, invocationId }) setFlow('LIVE-06', { row: 'LIVE-06', status: inference.status, evidence: inference.evidence }) measurements.push(inference.measurement) } catch (error) { @@ -169,22 +213,28 @@ export async function runTangleFlows({ repository, environment }) { addUnavailable('LIVE-06', classified.message) } try { - const sandbox = await runSandbox({ repository, environment, binary, invocationId }) - setFlow('LIVE-07', { row: 'LIVE-07', status: sandbox.status, evidence: sandbox.evidence }) + const sandbox = await sandboxRunner({ repository, environment, binary, invocationId }) + setFlow('LIVE-07', { + row: 'LIVE-07', + status: sandbox.status, + evidence: sandbox.evidence, + observations: sandbox.observations, + }) + measurements.push(sandbox.measurement) if (sandbox.unavailable) addUnavailable(sandbox.unavailable.row, sandbox.unavailable.reason) } catch (error) { const classified = classifyExternalFailure(error, 'Tangle sandbox', environment) addUnavailable('LIVE-07', classified.message) } try { - const matrix = await runMatrixAdapter({ repository, environment, binary }) - for (const row of TANGLE_ROWS.slice(1)) { + const matrix = await matrixRunner({ repository, environment, binary }) + for (const row of TANGLE_ROWS.slice(2)) { const reason = `${row} remains protected-unavailable: ${matrix.reason}` addUnavailable(row, reason) } } catch (error) { const classified = classifyExternalFailure(error, 'Tangle matrix', environment) - for (const row of TANGLE_ROWS.slice(1)) addUnavailable(row, classified.message) + for (const row of TANGLE_ROWS.slice(2)) addUnavailable(row, classified.message) } const complete = TANGLE_ROWS.every((row) => measurements.some((measurement) => measurement.name === row), diff --git a/scripts/package-proof-flows.mjs b/scripts/package-proof-flows.mjs index d7122dc..b217837 100644 --- a/scripts/package-proof-flows.mjs +++ b/scripts/package-proof-flows.mjs @@ -215,7 +215,7 @@ export async function runTerminal(binary, cwd, options) { }) const normalizedScreen = () => screen.replace(/\s+/gu, ' ').trim() - await waitFor(() => screen.includes('braid'), 'terminal header') + await waitFor(() => screen.includes('Braid starter'), 'terminal conversation shell') if (!options.inline) { session.write('\u0010') await waitFor( @@ -243,9 +243,7 @@ export async function runTerminal(binary, cwd, options) { session.resize(options.columns, options.rows) } await waitFor( - () => - normalizedScreen().includes('Fixture response through pi: hello from package proof') && - normalizedScreen().includes('completed'), + () => normalizedScreen().includes('Fixture response through pi: hello from package proof'), 'completed fixture response', ) const screenBeforeExit = screen @@ -255,7 +253,7 @@ export async function runTerminal(binary, cwd, options) { await sleep(30) session.write('hello from package proof') session.write('\r') - await waitFor(() => normalizedScreen().includes('streaming'), 'terminal retry start') + await waitFor(() => normalizedScreen().includes('working'), 'terminal retry start') // Deterministic execution intentionally does not advertise live steering. session.write('/steer deterministic package proof') session.write('\r') @@ -268,21 +266,17 @@ export async function runTerminal(binary, cwd, options) { await waitFor( () => normalizedScreen().includes('Fixture response through pi: hello from package proof') && - normalizedScreen().includes('completed') && - !normalizedScreen().includes('streaming'), + !normalizedScreen().includes('working'), 'terminal retry completion', ) session.write('cancel terminal proof') session.write('\r') - await waitFor(() => normalizedScreen().includes('streaming'), 'terminal cancellation start') + await waitFor(() => normalizedScreen().includes('working'), 'terminal cancellation start') session.write('/cancel') session.write('\r') await waitFor(() => normalizedScreen().includes('cancelled'), 'terminal cancellation') session.write('\u0003') - await waitFor( - () => screen.includes('press ctrl+c again to quit') || screen.includes('ctrl+c again to quit'), - 'armed terminal exit', - ) + await waitFor(() => screen.toLowerCase().includes('ctrl+c again to quit'), 'armed terminal exit') session.write('\u0003') let timeout const timedOut = new Promise((_, reject) => { @@ -325,7 +319,7 @@ export async function runSignalTerminal(binary, cwd) { session.onData((chunk) => { output += chunk }) - await waitFor(() => output.includes('braid'), 'signal terminal header') + await waitFor(() => output.includes('Braid starter'), 'signal terminal conversation shell') process.kill(session.pid, 'SIGINT') let timeout const timedOut = new Promise((_, reject) => { diff --git a/scripts/packed-rpc/first-run-tui.mjs b/scripts/packed-rpc/first-run-tui.mjs index 804d0e3..06f9956 100644 --- a/scripts/packed-rpc/first-run-tui.mjs +++ b/scripts/packed-rpc/first-run-tui.mjs @@ -86,7 +86,10 @@ export async function runPackedFirstRun(binary, repository) { }) let completed = false try { - await waitFor(() => output.includes('braid'), 'packed TUI header') + await waitFor( + () => output.includes('braid setup') || output.includes('Braid starter'), + 'packed TUI startup surface', + ) if (expectSetup) { await waitFor(() => output.includes('braid setup'), 'packed first-run setup') session.write('\r') @@ -125,10 +128,7 @@ export async function runPackedFirstRun(binary, repository) { const replyOffset = output.indexOf(expectedResponse, responseOffset) if (replyOffset < 0) return false const completedOutput = output.slice(replyOffset + expectedResponse.length) - return ( - completedOutput.includes('completed') || - completedOutput.includes('ready for a message') - ) + return completedOutput.includes('Ctrl+E') || completedOutput.includes('Ctrl+P') }, 'packed real response', 120_000, @@ -137,11 +137,7 @@ export async function runPackedFirstRun(binary, repository) { throw new Error(`packed response missing ${expectedResponse}\n${output}`, { cause: error }) } session.write('\u0003') - await waitFor( - () => - output.includes('press ctrl+c again to quit') || output.includes('ctrl+c again to quit'), - 'packed safe exit', - ) + await waitFor(() => output.toLowerCase().includes('ctrl+c again to quit'), 'packed safe exit') session.write('\u0003') const exit = await Promise.race([ exited, diff --git a/scripts/performance/process-probes.mjs b/scripts/performance/process-probes.mjs index e5ff72d..b8fb970 100644 --- a/scripts/performance/process-probes.mjs +++ b/scripts/performance/process-probes.mjs @@ -39,7 +39,7 @@ function visibleText(terminal) { } function hasVisibleFrame(terminal) { - return visibleText(terminal).some((line) => line.includes('braid')) + return visibleText(terminal).some((line) => line.includes('Braid starter')) } function hasStartupError(output) { @@ -265,8 +265,7 @@ export async function openPackedTui(options) { if (exited) return exitValue session.write('\u0003') await waitFor( - () => - output.includes('press ctrl+c again to quit') || output.includes('ctrl+c again to quit'), + () => output.toLowerCase().includes('ctrl+c again to quit'), 'packed TUI safe-exit prompt', 5_000, ) diff --git a/scripts/proof-package.mjs b/scripts/proof-package.mjs new file mode 100644 index 0000000..df67632 --- /dev/null +++ b/scripts/proof-package.mjs @@ -0,0 +1,50 @@ +import { execFile } from 'node:child_process' +import { lstat, mkdir, readFile, unlink } from 'node:fs/promises' +import { join, resolve } from 'node:path' +import { promisify } from 'node:util' + +import { pnpmInvocation } from './release/platform.mjs' + +const run = promisify(execFile) +const repository = new URL('../', import.meta.url).pathname.replace(/\/$/u, '') +const workPackage = process.argv[2] +const captureScript = { + w0: 'scripts/capture-w0.mjs', + w6: 'scripts/capture-visual.mjs', +}[workPackage] + +if (!captureScript) throw new Error('proof-package requires w0 or w6') + +const packageJson = JSON.parse(await readFile(join(repository, 'package.json'), 'utf8')) +const archiveName = `${packageJson.name.replace(/^@/u, '').replaceAll('/', '-')}-${packageJson.version}.tgz` +const artifactRoot = process.env.BRAID_RELEASE_ARTIFACT_ROOT + ? resolve(process.env.BRAID_RELEASE_ARTIFACT_ROOT) + : join(repository, 'artifacts', 'verification') +const outputRoot = join(artifactRoot, workPackage) +const proofPath = join(outputRoot, 'package-proof.json') +const archivePath = join(outputRoot, archiveName) + +await mkdir(outputRoot, { recursive: true }) +const priorArchive = await lstat(archivePath).catch(() => undefined) +if (priorArchive?.isSymbolicLink() || (priorArchive && !priorArchive.isFile())) + throw new Error(`refusing to replace non-file package archive: ${archivePath}`) +if (priorArchive) await unlink(archivePath) + +const pnpm = pnpmInvocation(['run', 'build']) +await run(pnpm.file, pnpm.args, { cwd: repository, stdio: 'inherit' }) +await run( + process.execPath, + [ + join(repository, 'scripts', 'verify-package.mjs'), + '--record', + proofPath, + '--tarball-output', + archivePath, + ], + { cwd: repository, stdio: 'inherit' }, +) +await run(process.execPath, [join(repository, captureScript)], { + cwd: repository, + env: { ...process.env, BRAID_RELEASE_TARBALL: archivePath }, + stdio: 'inherit', +}) diff --git a/scripts/test-pty.mjs b/scripts/test-pty.mjs index 98c61d0..19fc879 100644 --- a/scripts/test-pty.mjs +++ b/scripts/test-pty.mjs @@ -82,7 +82,7 @@ async function run(columns, rows, options = {}) { query = '' } }) - await waitFor(() => output.includes('braid'), `${columns}x${rows} header`) + await waitFor(() => output.includes('Braid starter'), `${columns}x${rows} conversation shell`) if (kitty) session.write('\u001b[112;5u') else if (keymap === undefined) session.write('\u0010') else session.write('\u0011') @@ -108,8 +108,7 @@ async function run(columns, rows, options = {}) { ) session.write('\u0003') await waitFor( - () => - output.includes('press ctrl+c again to quit') || output.includes('ctrl+c again to quit'), + () => output.toLowerCase().includes('ctrl+c again to quit'), `${columns}x${rows} safe exit`, ) session.write('\u0003') @@ -176,7 +175,10 @@ async function runAutocompleteRace() { }) }) try { - await waitFor(() => normalizeScreen(screen).includes('braid'), 'autocomplete race header') + await waitFor( + () => normalizeScreen(screen).includes('Braid starter'), + 'autocomplete race conversation shell', + ) session.write('/') await waitFor( () => normalizeScreen(screen).includes('Create an empty conversation'), @@ -199,7 +201,7 @@ async function runAutocompleteRace() { await sleep(50) session.write('\u0003') await waitFor( - () => normalizeScreen(screen).includes('ctrl+c again to quit'), + () => normalizeScreen(screen).toLowerCase().includes('ctrl+c again to quit'), 'autocomplete race safe exit', ) session.write('\u0003') diff --git a/src/adapters/analysis/agent-eval-version.ts b/src/adapters/analysis/agent-eval-version.ts index 792a4f3..71c44fa 100644 --- a/src/adapters/analysis/agent-eval-version.ts +++ b/src/adapters/analysis/agent-eval-version.ts @@ -1 +1,3 @@ -export const AGENT_EVAL_VERSION = '0.144.11' +import { installedPackageVersion } from '../../installed-package-version.js' + +export const AGENT_EVAL_VERSION = installedPackageVersion('@tangle-network/agent-eval') diff --git a/src/adapters/analysis/citations.ts b/src/adapters/analysis/citations.ts index f07ca55..91ba49d 100644 --- a/src/adapters/analysis/citations.ts +++ b/src/adapters/analysis/citations.ts @@ -32,18 +32,21 @@ function sourceEvent(evidence: FrozenAnalysisEvidence, id: string): FrozenAnalys return event } +function scalarStrings(value: unknown, seen = new Set()): readonly string[] { + if (typeof value === 'string') return [value] + if (value === null || typeof value !== 'object') return [] + if (seen.has(value)) return [] + seen.add(value) + const values = Array.isArray(value) ? value : Object.values(value) + return values.flatMap((item) => scalarStrings(item, seen)) +} + function eventText(event: FrozenAnalysisEvent): string { - if (event.event.kind === 'run.text.delta' || event.event.kind === 'run.reasoning.delta') { - return event.event.text - } - if (event.event.kind === 'run.warning' || event.event.kind === 'run.error') { - return event.event.message - } - try { - return JSON.stringify(event.event) - } catch { - return '' - } + return scalarStrings(event.event).join('\n') +} + +function eventContainsExcerpt(event: FrozenAnalysisEvent, excerpt: string): boolean { + return scalarStrings(event.event).some((value) => value.includes(excerpt)) } function messageText(evidence: FrozenAnalysisEvidence, messageId: string): string { @@ -80,7 +83,7 @@ function partText(evidence: FrozenAnalysisEvidence, partId: string): string { function assertExcerpt(event: FrozenAnalysisEvent, excerpt: string | undefined): void { if (excerpt === undefined) return - if (excerpt.length === 0 || !eventText(event).includes(excerpt)) { + if (excerpt.length === 0 || !eventContainsExcerpt(event, excerpt)) { throw new AnalysisCitationError( `Citation excerpt does not occur in frozen event ${String(event.id)}`, ) @@ -106,7 +109,7 @@ function sourceEventForSpan( throw new AnalysisCitationError(`Citation span ${span.spanId} has no frozen event`) return source } - const source = candidates.find((candidate) => eventText(candidate).includes(excerpt)) + const source = candidates.find((candidate) => eventContainsExcerpt(candidate, excerpt)) if (source === undefined) { throw new AnalysisCitationError( `Citation excerpt does not occur in frozen events for span ${span.spanId}`, diff --git a/src/adapters/analysis/question-analyst.ts b/src/adapters/analysis/question-analyst.ts index 1839efb..aff2ed8 100644 --- a/src/adapters/analysis/question-analyst.ts +++ b/src/adapters/analysis/question-analyst.ts @@ -28,8 +28,8 @@ async function prepareQuestionContext(store: TraceAnalysisStore): Promise> readonly observedCostUsd?: number readonly estimatedCostUsd?: number @@ -97,6 +110,8 @@ interface RuntimeCallSummary { readonly errorStatus?: number } +type RuntimeModelOperation = 'startRetainedRun' | 'streamAgentTurn' + function catalogPricing(model: string): CustomTokenPricing | undefined { const pricing = resolveModelPricing(model) if (pricing === null) return undefined @@ -172,6 +187,7 @@ function summarizeRuntimeEvents( const promptCache = promptCacheFacts(calls) const cachedTokens = sumPromptCache(promptCache, ['cachedTokens', 'cacheReadTokens']) const cacheWriteTokens = sumPromptCache(promptCache, ['cacheWriteTokens']) + const reasoningTokens = finiteTokenCount(final.metadata?.reasoningTokens) const observedCostUsd = completeCallAmount(calls, 'costUsd', (event) => event.usdKnown !== false) const estimatedCostUsd = completeCallAmount(calls, 'estimatedCostUsd') return { @@ -197,6 +213,7 @@ function summarizeRuntimeEvents( : 0, ...(cachedTokens === undefined ? {} : { cachedTokens }), ...(cacheWriteTokens === undefined ? {} : { cacheWriteTokens }), + ...(reasoningTokens === undefined ? {} : { reasoningTokens }), ...(Object.keys(promptCache).length === 0 ? {} : { promptCache }), ...(observedCostUsd === undefined ? {} : { observedCostUsd }), ...(estimatedCostUsd === undefined ? {} : { estimatedCostUsd }), @@ -208,6 +225,46 @@ function summarizeRuntimeEvents( } } +function summarizeRetainedResult( + result: import('@tangle-network/agent-interface/environment-provider').AgentTurnResult, + model: string, + startedAt: number, +): RuntimeCallSummary { + const endedAt = Date.now() + const inputTokens = finiteTokenCount(result.usage?.inputTokens) + const outputTokens = finiteTokenCount(result.usage?.outputTokens) + const usageCaptured = inputTokens !== undefined && outputTokens !== undefined + const errorStatus = result.error === undefined ? undefined : httpStatusFromReason(result.error) + const cachedTokens = finiteTokenCount(result.usage?.cacheReadInputTokens) + const cacheWriteTokens = finiteTokenCount(result.usage?.cacheCreationInputTokens) + const observedCostUsd = finiteNumber(result.usage?.cost) + const reasoningTokens = finiteTokenCount(result.usage?.reasoningTokens) + return { + events: [], + status: result.success ? 'completed' : 'failed', + reason: result.success + ? 'Retained model execution completed' + : (result.error ?? 'Retained model execution failed'), + content: result.text, + durationMs: Math.max(0, endedAt - startedAt), + endedAt, + inputTokens: inputTokens ?? 0, + outputTokens: outputTokens ?? 0, + ...(cachedTokens === undefined ? {} : { cachedTokens }), + ...(cacheWriteTokens === undefined ? {} : { cacheWriteTokens }), + ...(observedCostUsd === undefined ? {} : { observedCostUsd }), + ...(reasoningTokens === undefined ? {} : { reasoningTokens }), + usageCaptured, + reportedModel: model, + ...(!result.success ? { errorKind: 'backend' } : {}), + ...(errorStatus === undefined ? {} : { errorStatus }), + } +} + +function finiteTokenCount(value: unknown): number | undefined { + return typeof value === 'number' && Number.isSafeInteger(value) && value >= 0 ? value : undefined +} + function finiteNumber(value: unknown): number | undefined { return typeof value === 'number' && Number.isFinite(value) && value >= 0 ? value : undefined } @@ -273,6 +330,7 @@ function receiptFor( | 'observedCostUsd' | 'cachedTokens' | 'cacheWriteTokens' + | 'reasoningTokens' >, knownNoExecution = false, ): import('@tangle-network/agent-eval').CostReceiptInput { @@ -294,6 +352,7 @@ function receiptFor( outputTokens: usage.outputTokens, ...(usage.cachedTokens === undefined ? {} : { cachedTokens: usage.cachedTokens }), ...(usage.cacheWriteTokens === undefined ? {} : { cacheWriteTokens: usage.cacheWriteTokens }), + ...(usage.reasoningTokens === undefined ? {} : { reasoningTokens: usage.reasoningTokens }), } if (usage.observedCostUsd !== undefined) { return { ...receipt, actualCostUsd: usage.observedCostUsd } @@ -312,6 +371,7 @@ function responseFor( ): ChatResponse { const model = safeModelRoute(request.request.model) const receipt = receiptFor(model, pricing, summary) + const promptTokens = summary.inputTokens + (summary.cachedTokens ?? 0) const costUsd = summary.observedCostUsd ?? (summary.usageCaptured && pricing !== undefined ? costForTokenPricing(pricing, receipt) : null) @@ -329,10 +389,14 @@ function responseFor( return { content: summary.content, usage: { - promptTokens: summary.inputTokens, + promptTokens, completionTokens: summary.outputTokens, - totalTokens: summary.inputTokens + summary.outputTokens, + totalTokens: promptTokens + summary.outputTokens, captured: summary.usageCaptured, + ...(summary.cachedTokens === undefined ? {} : { cachedPromptTokens: summary.cachedTokens }), + ...(summary.reasoningTokens === undefined + ? {} + : { reasoningTokens: summary.reasoningTokens }), }, costUsd, model, @@ -357,6 +421,8 @@ function executionEvidence(input: { readonly partialEvents?: readonly RuntimeStreamEvent[] readonly startedAt: number readonly failure?: string + readonly operation: RuntimeModelOperation + readonly controlRef?: AgentExactRunControlRef }): Readonly> { const summary = input.summary const endedAt = summary?.endedAt ?? Date.now() @@ -366,7 +432,7 @@ function executionEvidence(input: { runtime: { package: '@tangle-network/agent-runtime', version: AGENT_RUNTIME_VERSION, - operation: 'streamAgentTurn', + operation: input.operation, }, ...(input.provider === undefined ? {} : { provider: input.provider }), callId: input.request.callId, @@ -392,6 +458,17 @@ function executionEvidence(input: { durationMs: summary?.durationMs ?? Math.max(0, endedAt - input.startedAt), billing: cost, dispatched: input.dispatched, + ...(input.controlRef === undefined + ? {} + : { + retained: { + runId: input.controlRef.runId, + environmentId: input.controlRef.environmentId, + sessionId: input.controlRef.sessionId, + executionId: input.controlRef.executionId, + requestDigest: input.controlRef.requestDigest, + }, + }), ...(summary === undefined ? {} : { @@ -409,6 +486,9 @@ function executionEvidence(input: { ...(summary.cacheWriteTokens === undefined ? {} : { cacheWriteTokens: summary.cacheWriteTokens }), + ...(summary.reasoningTokens === undefined + ? {} + : { reasoningTokens: summary.reasoningTokens }), ...(summary.promptCache === undefined ? {} : { promptCache: summary.promptCache }), ...(summary.reportedModel === undefined ? {} @@ -474,12 +554,12 @@ function analystCallProfile( ): AgentProfile { const source = snapshotAgentProfile(options.profile) const provider = source.model?.provider?.trim() - if (!provider) { + const bridge = options.connection.kind === 'cli-bridge' + if (!bridge && !provider) { throw new TypeError( 'Trace analysis requires AgentProfile.model.provider before model execution', ) } - const bridge = options.connection.kind === 'cli-bridge' let harness: AgentProfile['harness'] = 'cli-base' let model = safeModelRoute(options.model) let prompt: AgentProfile['prompt'] | undefined @@ -519,7 +599,7 @@ function analystCallProfile( : {}), model: { default: model, - provider, + ...(provider === undefined ? {} : { provider }), ...(reasoningEffort === undefined ? {} : { reasoningEffort }), metadata: { ...(bridge ? {} : { retry }), @@ -580,6 +660,9 @@ export function createRuntimeTraceModelOwner( let dispatched = false let summary: RuntimeCallSummary | undefined let executionProfileDigest = sourceProfileDigest + const operation: RuntimeModelOperation = + options.connection.kind === 'cli-bridge' ? 'startRetainedRun' : 'streamAgentTurn' + let controlRef: AgentExactRunControlRef | undefined try { const configuredModel = safePublicIdentifier(options.model) if (configuredModel === undefined) { @@ -593,41 +676,48 @@ export function createRuntimeTraceModelOwner( const messages = textMessages(request.request) const profile = analystCallProfile(options, request.request, retry) executionProfileDigest = canonicalAgentProfileDigestHex(profile) - const { createExecutor, streamAgentTurn } = await import( - '@tangle-network/agent-runtime/kernel' - ) - const executor = - options.connection.kind === 'cli-bridge' - ? createExecutor({ - backend: 'bridge', - bridgeUrl: options.baseUrl, - bridgeBearer: options.credential ?? LOCAL_ROUTER_BEARER, - }) - : createExecutor({ - backend: 'router', - routerBaseUrl: options.baseUrl, - routerKey: options.credential ?? LOCAL_ROUTER_BEARER, - ...(options.complete === undefined ? {} : { complete: options.complete }), - }) - const backend = Object.freeze({ - kind: 'executor' as const, - factory: executor, - profile, - agentRunName: configuredModel, - }) dispatched = true - for await (const event of streamAgentTurn( - backend, - { messages }, - { - signal: request.signal, + if (options.connection.kind === 'cli-bridge') { + const retained = await runRetainedCliBridgeModelCall({ + baseUrl: options.baseUrl, + bearerToken: options.credential ?? LOCAL_ROUTER_BEARER, + profile, + model: configuredModel, + messages, callId: request.callId, - correlationId: `braid-analysis-${request.callId}`, - }, - )) { - events.push(event) + signal: request.signal, + }) + controlRef = retained.controlRef + summary = summarizeRetainedResult(retained.result, configuredModel, startedAt) + } else { + const { createExecutor, streamAgentTurn } = await import( + '@tangle-network/agent-runtime/kernel' + ) + const executor = createExecutor({ + backend: 'router', + routerBaseUrl: options.baseUrl, + routerKey: options.credential ?? LOCAL_ROUTER_BEARER, + ...(options.complete === undefined ? {} : { complete: options.complete }), + }) + const backend = Object.freeze({ + kind: 'executor' as const, + factory: executor, + profile, + agentRunName: configuredModel, + }) + for await (const event of streamAgentTurn( + backend, + { messages }, + { + signal: request.signal, + callId: request.callId, + correlationId: `braid-analysis-${request.callId}`, + }, + )) { + events.push(event) + } + summary = summarizeRuntimeEvents(events, startedAt) } - summary = summarizeRuntimeEvents(events, startedAt) const receipt = receiptFor(options.model, pricing, summary) const evidence = executionEvidence({ request, @@ -641,6 +731,8 @@ export function createRuntimeTraceModelOwner( summary, dispatched, startedAt, + operation, + ...(controlRef === undefined ? {} : { controlRef }), }) if (summary.status !== 'completed') { return { @@ -657,6 +749,7 @@ export function createRuntimeTraceModelOwner( execution: evidence, } } catch (error) { + if (error instanceof RetainedModelCallError) controlRef = error.controlRef const message = publicError(error) const receipt = receiptFor( options.model, @@ -683,6 +776,8 @@ export function createRuntimeTraceModelOwner( dispatched, ...(events.length === 0 ? {} : { partialEvents: events }), startedAt, + operation, + ...(controlRef === undefined ? {} : { controlRef }), failure: message, }), } diff --git a/src/adapters/analysis/runtime-retained-model-call.ts b/src/adapters/analysis/runtime-retained-model-call.ts new file mode 100644 index 0000000..dc7f405 --- /dev/null +++ b/src/adapters/analysis/runtime-retained-model-call.ts @@ -0,0 +1,127 @@ +import type { AgentExactRunControlRef, AgentProfile } from '@tangle-network/agent-interface' +import type { AgentTurnResult } from '@tangle-network/agent-interface/environment-provider' +import { createCliBridgeProvider } from '@tangle-network/agent-provider-cli-bridge' +import { startRetainedRun } from '@tangle-network/agent-runtime/kernel' +import { canonicalDigest } from '../../domain/canonical.js' + +export interface RetainedModelCallInput { + readonly baseUrl: string + readonly bearerToken: string + readonly profile: Readonly + readonly model: string + readonly messages: ReadonlyArray> + readonly callId: string + readonly signal: AbortSignal +} + +export interface RetainedModelCallResult { + readonly result: AgentTurnResult + readonly controlRef: AgentExactRunControlRef +} + +export class RetainedModelCallError extends Error { + readonly controlRef: AgentExactRunControlRef + + constructor(controlRef: AgentExactRunControlRef, cause: unknown) { + super(cause instanceof Error ? cause.message : 'Retained CLI Bridge model call failed', { + cause, + }) + this.name = 'RetainedModelCallError' + this.controlRef = controlRef + } +} + +/** Execute one analyst request through the same retained CLI Bridge path as a Braid turn. */ +export async function runRetainedCliBridgeModelCall( + input: RetainedModelCallInput, +): Promise { + input.signal.throwIfAborted() + const digest = String( + canonicalDigest({ + callId: input.callId, + messages: input.messages, + model: input.model, + profile: input.profile, + }), + ) + const provider = createCliBridgeProvider({ + baseUrl: input.baseUrl, + bearerToken: input.bearerToken, + defaultModel: input.model, + fetch: globalThis.fetch, + }) + const handle = await startRetainedRun({ + provider, + environment: { + profile: input.profile, + ...(input.profile.harness === undefined ? {} : { backend: input.profile.harness }), + idempotencyKey: `analysis-environment-${digest}`, + }, + turn: { + prompt: JSON.stringify({ messages: input.messages }), + turnId: `analysis-turn-${digest}`, + signal: input.signal, + }, + identity: { + sessionId: `analysis-session-${digest}`, + executionId: `analysis-${digest}`, + }, + }) + try { + return { + result: await resultWithCancellation(handle, input.signal, digest), + controlRef: handle.controlRef, + } + } catch (error) { + throw new RetainedModelCallError(handle.controlRef, error) + } +} + +async function resultWithCancellation( + handle: Awaited>, + signal: AbortSignal, + digest: string, +): Promise { + if (signal.aborted) { + await handle.cancel({ operationId: `analysis-cancel-${digest}` }) + signal.throwIfAborted() + } + return new Promise((resolve, reject) => { + let settled = false + let settling = false + const beginSettlement = (): boolean => { + if (settled || settling) return false + settling = true + return true + } + const finish = (callback: () => void): void => { + if (settled) return + settled = true + signal.removeEventListener('abort', onAbort) + callback() + } + const rejectAfterCancellation = (reason: unknown, message: string): void => { + if (!beginSettlement()) return + void handle.cancel({ operationId: `analysis-cancel-${digest}` }).then( + () => finish(() => reject(reason)), + (cancellationError: unknown) => + finish(() => reject(new AggregateError([reason, cancellationError], message))), + ) + } + const onAbort = (): void => { + rejectAfterCancellation(signal.reason, 'Trace analysis cancellation was not confirmed') + } + signal.addEventListener('abort', onAbort, { once: true }) + void handle.result().then( + (result) => { + if (!beginSettlement()) return + finish(() => resolve(result)) + }, + (error: unknown) => + rejectAfterCancellation( + error, + 'Trace analysis failed and retained-run cancellation was not confirmed', + ), + ) + }) +} diff --git a/src/adapters/analysis/trace-analysis-adapter.ts b/src/adapters/analysis/trace-analysis-adapter.ts index fd80225..9b2cdea 100644 --- a/src/adapters/analysis/trace-analysis-adapter.ts +++ b/src/adapters/analysis/trace-analysis-adapter.ts @@ -38,6 +38,10 @@ import { import { registerBraidQuestionAnalyst } from './question-analyst.js' import { createRuntimeTraceModelOwner } from './runtime-model-owner.js' +const DEFAULT_ANALYSIS_MAX_OUTPUT_TOKENS = 16_384 +const DEFAULT_ANALYSIS_REASONING_MULTIPLIER = 4 +const MINIMUM_ANALYSIS_INPUT_HEADROOM_USD = 1 + export type TraceAnalysisDiagnosticKind = | 'missing-python' | 'missing-python-package' @@ -207,6 +211,24 @@ function engineConfigurationMessage(error: unknown): string { return 'The published agent-eval trace engine rejected the selected configuration.' } +function defaultModelCostCeiling( + pricing: CustomTokenPricing, + maxOutputTokens: number, + maxReasoningTokens: number, +): number { + const maximumOutputCost = + ((maxOutputTokens + maxReasoningTokens) * pricing.outputUsdPerMillion) / 1_000_000 + // The proxy reserves the complete output and reasoning limits before dispatch. + // Keep additional room for the actual control prompt without weakening token limits. + return ( + Math.ceil( + (maximumOutputCost + + Math.max(MINIMUM_ANALYSIS_INPUT_HEADROOM_USD, maximumOutputCost * 0.25)) * + 1_000_000, + ) / 1_000_000 + ) +} + /** * Configure the model-backed trace analysts for one exact profile/connection pair. * @@ -332,6 +354,14 @@ export async function createTraceAnalysisAdapter( options.recordExecution?.(observation) }, }) + const maxOutputTokens = options.maxOutputTokens ?? DEFAULT_ANALYSIS_MAX_OUTPUT_TOKENS + const maxReasoningTokens = + options.maxReasoningTokens ?? maxOutputTokens * DEFAULT_ANALYSIS_REASONING_MULTIPLIER + const maxCostUsd = + options.maxCostUsd ?? + (owner.pricing === undefined + ? undefined + : defaultModelCostCeiling(owner.pricing, maxOutputTokens, maxReasoningTokens)) const engine = createDspyRlmTraceEngine({ call: owner.call, callRef: owner.callRef, @@ -340,13 +370,9 @@ export async function createTraceAnalysisAdapter( // Coding models often return natural code and prose from RLM control turns. controlAdapter: 'tolerant', ...(owner.pricing === undefined ? {} : { pricing: { ...owner.pricing } }), - ...(options.maxCostUsd === undefined ? {} : { maxCostUsd: options.maxCostUsd }), - ...(options.maxOutputTokens === undefined - ? {} - : { maxOutputTokens: options.maxOutputTokens }), - ...(options.maxReasoningTokens === undefined - ? {} - : { maxReasoningTokens: options.maxReasoningTokens }), + ...(maxCostUsd === undefined ? {} : { maxCostUsd }), + maxOutputTokens, + maxReasoningTokens, ...(options.timeoutMs === undefined ? {} : { timeoutMs: options.timeoutMs }), runner: { command: python.runner.command, diff --git a/src/adapters/analysis/trace-event-projection.ts b/src/adapters/analysis/trace-event-projection.ts index 8b13d4f..6ab398c 100644 --- a/src/adapters/analysis/trace-event-projection.ts +++ b/src/adapters/analysis/trace-event-projection.ts @@ -8,7 +8,7 @@ import { LLM_REASONING_TOKENS, OPENINFERENCE_SPAN_KIND, OUTPUT_VALUE, -} from '@tangle-network/agent-eval' +} from '@tangle-network/agent-eval/trace-attributes' import { profileModelSettings } from '../../app/profile-model-settings.js' import type { FrozenAnalysisEvent } from '../../app/analysis-types.js' import type { TurnUsage } from '../../domain/entities.js' diff --git a/src/adapters/analysis/trace-store.ts b/src/adapters/analysis/trace-store.ts index 579db3c..d9f7dfd 100644 --- a/src/adapters/analysis/trace-store.ts +++ b/src/adapters/analysis/trace-store.ts @@ -5,7 +5,7 @@ import { otlpTextToTraceAnalysisStore, type ToolSpan, type TraceAnalysisStore, -} from '@tangle-network/agent-eval' +} from '@tangle-network/agent-eval/traces' import { verifyFrozenAnalysisSource } from '../../app/analysis-source.js' import type { FrozenAnalysisEvent, FrozenAnalysisEvidence } from '../../app/analysis-types.js' import { AnalysisSourceError } from '../../app/analysis-types.js' diff --git a/src/adapters/connections/cli-bridge-model-route.ts b/src/adapters/connections/cli-bridge-model-route.ts index 65cf2d3..33ad603 100644 --- a/src/adapters/connections/cli-bridge-model-route.ts +++ b/src/adapters/connections/cli-bridge-model-route.ts @@ -37,7 +37,7 @@ export function materializeBridgeModelRoute( export interface BridgeCatalogTarget { readonly route: string readonly runner: HarnessType - readonly provider: string + readonly provider?: string readonly model: string } @@ -51,8 +51,12 @@ export function bridgeCatalogTarget( const model = route.slice(runner.length + 1) if (model.length === 0) return undefined const providerSeparator = model.indexOf('/') - const provider = providerSeparator > 0 ? model.slice(0, providerSeparator) : runner - return { route, runner, provider, model } + return { + route, + runner, + model, + ...(providerSeparator > 0 ? { provider: model.slice(0, providerSeparator) } : {}), + } } /** Accepts a catalog entry only when its backend agrees with its encoded route. */ diff --git a/src/adapters/runtime/agent-runtime-version.ts b/src/adapters/runtime/agent-runtime-version.ts index 95dc2a1..7ca5b9e 100644 --- a/src/adapters/runtime/agent-runtime-version.ts +++ b/src/adapters/runtime/agent-runtime-version.ts @@ -1 +1,3 @@ -export const AGENT_RUNTIME_VERSION = '0.131.7' +import { installedPackageVersion } from '../../installed-package-version.js' + +export const AGENT_RUNTIME_VERSION = installedPackageVersion('@tangle-network/agent-runtime') diff --git a/src/adapters/runtime/cli-bridge-retained-execution.ts b/src/adapters/runtime/cli-bridge-retained-execution.ts index b2a7841..b0aef66 100644 --- a/src/adapters/runtime/cli-bridge-retained-execution.ts +++ b/src/adapters/runtime/cli-bridge-retained-execution.ts @@ -1,434 +1,34 @@ -import { - AgentExactRunControlRefSchema, - type AgentExactRunControlRef, -} from '@tangle-network/agent-interface' -import type { RetainedRunHandle } from '@tangle-network/agent-runtime/kernel' -import { canonicalDigest } from '../../domain/canonical.js' -import { publicMaterializationReceipt } from '../../domain/materialization-receipt.js' -import type { RuntimeEventEnvelope } from '../../domain/runtime-events.js' -import type { - CancelRunInput, - ControlAcknowledgement, - ExecuteTurnInput, - ExecutionAdmission, - ExecutionPort, - ProviderRunSnapshot, - RunCapabilities, -} from '../../ports/execution.js' -import { canonicalAgentProfileDigestHex } from '../agent-interface/profile-runtime.js' -import { - createCliBridgeRetainedPlan, - discoverCliBridgeControlRef, - reconnectCliBridgeRetainedRun, - startCliBridgeRetainedRun, - type CliBridgeRetainedPlan, -} from './cli-bridge-retained-run.js' -import { - finalRetainedEnvelope, - isTerminalRetainedStatus, - modelRequestsFromResult, - retainedCapabilities, - retainedExecutionKey, - retainedStatus, - retainedTurnUsage, -} from './cli-bridge-retained-projection.js' +import type { AgentExactRunControlRef } from '@tangle-network/agent-interface' +import type { ExecuteTurnInput } from '../../ports/execution.js' +import { createCliBridgeRetainedPlan } from './cli-bridge-retained-run.js' import type { PreparedCliBridgeConnection } from './production-cli-bridge-backend.js' +import { RetainedExecutionPort } from './retained-execution.js' export interface CliBridgeRetainedExecutionOptions { readonly resolve: (input: ExecuteTurnInput) => Promise readonly recover: (input: { readonly runId: string readonly providerSessionId?: string + readonly controlRef?: AgentExactRunControlRef }) => Promise } -interface PreparedRun { - readonly key: string - readonly plan: CliBridgeRetainedPlan - readonly capabilities: RunCapabilities - readonly materializationReceipt: Readonly> -} - -const MAX_RETAINED_CLIENTS = 128 - -/** Braid's durable CLI-Bridge path. CLI-Bridge owns the job; this port owns readers. */ -export class CliBridgeRetainedExecutionPort implements ExecutionPort { - readonly admissionMode = 'async' as const - readonly #options: CliBridgeRetainedExecutionOptions - readonly #prepared = new Map() - readonly #handles = new Map() - readonly #plans = new Map() - readonly #controlRefs = new Map() - readonly #readers = new Map() - readonly #detached = new Set() - readonly #lastCursors = new Map() - readonly #retainedOrder: string[] = [] - +/** Braid's durable CLI-Bridge path. CLI-Bridge owns the job; the shared port owns readers. */ +export class CliBridgeRetainedExecutionPort extends RetainedExecutionPort { constructor(options: CliBridgeRetainedExecutionOptions) { - this.#options = options - } - - async admit(input: ExecuteTurnInput): Promise { - const prepared = await this.#options.resolve(input) - const plan = await createCliBridgeRetainedPlan(prepared, input.runId) - const capabilities = retainedCapabilities(prepared) - const materializationReceipt = publicMaterializationReceipt({ - ...prepared.materializationReceipt, - backend: 'environment-provider', - environmentId: plan.environmentId, - providerRunId: plan.executionId, - retainedControl: 'exact-after-dispatch', - }) - const run = { - key: retainedExecutionKey(input), - plan, - capabilities, - materializationReceipt, - } - this.#prepared.set(input.runId, run) - this.#rememberPlan(input.runId, plan) - return { - capabilities, - provider: plan.provider.name, - environmentId: plan.environmentId, - providerSessionId: prepared.providerSessionId, - materializationReceipt, - profileDigest: canonicalAgentProfileDigestHex(input.profile), - capabilitiesDigest: canonicalDigest(capabilities), - materializationDigest: canonicalDigest(materializationReceipt), - } - } - - async *streamTurn(input: ExecuteTurnInput): AsyncGenerator { - const prepared = this.#prepared.get(input.runId) - if (!prepared || prepared.key !== retainedExecutionKey(input)) { - throw new Error('CLI Bridge retained run was not admitted with this exact request') - } - this.#prepared.delete(input.runId) - const handle = await startCliBridgeRetainedRun(prepared.plan, input) - this.#rememberHandle(input.runId, handle, prepared.plan) - this.#detached.delete(input.runId) - yield* this.#streamHandle({ - runId: input.runId, - handle, - plan: prepared.plan, - signal: input.signal, - includeObservation: true, - afterSequence: 0, - }) - } - - async *reconnect(input: { - readonly runId: string - readonly after?: string - readonly afterSequence?: number - readonly providerSessionId?: string - readonly controlRef?: AgentExactRunControlRef - readonly signal: AbortSignal - }): AsyncGenerator { - if (input.after !== undefined && input.afterSequence === undefined) { - throw new Error('CLI Bridge reconnect requires the saved event sequence') - } - const providerSessionId = this.#recoverySessionId( - input.runId, - input.providerSessionId, - input.controlRef, - ) - const plan = await this.#planFor(input.runId, providerSessionId) - const controlRef = await this.#controlRefFor(input.runId, plan, input.controlRef, input.signal) - if (!controlRef) return - const handle = await reconnectCliBridgeRetainedRun(plan, controlRef) - if (!handle) return - this.#rememberHandle(input.runId, handle, plan) - this.#detached.delete(input.runId) - yield* this.#streamHandle({ - runId: input.runId, - handle, - plan, - signal: input.signal, - includeObservation: (input.afterSequence ?? 0) === 0, - afterSequence: input.afterSequence ?? 0, - ...(input.after === undefined ? {} : { after: input.after }), - }) - } - - async detachRun(input: { - readonly runId: string - readonly operationId: string - readonly providerSessionId?: string - readonly controlRef?: AgentExactRunControlRef - }): Promise { - const reader = this.#readers.get(input.runId) - this.#detached.add(input.runId) - if (!reader) { - return { operationId: input.operationId, outcome: 'already-applied', detail: 'detached' } - } - reader.abort(new DOMException('Braid detached from the retained run', 'AbortError')) - return { operationId: input.operationId, outcome: 'accepted', detail: 'detached' } - } - - async cancelRun( - input: CancelRunInput & { readonly signal?: AbortSignal }, - ): Promise { - const handle = await this.#handleFor( - input.runId, - input.providerSessionId, - input.controlRef, - input.signal, - ) - if (!handle) { - return { - operationId: input.operationId, - outcome: 'unknown', - detail: 'CLI Bridge retained run was not found', - } - } - const result = await handle.cancel({ - operationId: input.operationId, - ...(input.reason === undefined ? {} : { reason: input.reason }), - ...(input.signal === undefined ? {} : { signal: input.signal }), - }) - if (result.status === 'conflict') { - return { operationId: input.operationId, outcome: 'rejected', detail: result.effect } - } - if (result.status === 'unknown') { - return { operationId: input.operationId, outcome: 'unknown', detail: result.effect } - } - this.#readers.get(input.runId)?.abort(new DOMException('Retained run cancelled', 'AbortError')) - this.#detached.delete(input.runId) - return { - operationId: input.operationId, - outcome: result.status === 'replayed' ? 'already-applied' : 'accepted', - detail: result.effect, - } - } - - async status(input: { - readonly runId: string - readonly providerSessionId?: string - readonly controlRef?: AgentExactRunControlRef - readonly signal?: AbortSignal - }): Promise { - const handle = await this.#handleFor( - input.runId, - input.providerSessionId, - input.controlRef, - input.signal, - ) - if (!handle) return null - const snapshot = await handle.status({ - ...(input.signal === undefined ? {} : { signal: input.signal }), - }) - const status = retainedStatus(snapshot.status, this.#detached.has(input.runId)) - const cursor = this.#lastCursors.get(input.runId) - const base: ProviderRunSnapshot = { - runId: input.runId, - status, - sessionId: handle.controlRef.sessionId, - ...(cursor === undefined ? {} : { cursor }), - } - if (!isTerminalRetainedStatus(status)) return base - if (status === 'cancelled' || status === 'expired') return base - try { - const result = await handle.result() - return { - ...base, - finalText: result.text, - usage: retainedTurnUsage( - result.usage, - this.#plans.get(input.runId)?.prepared.route, - modelRequestsFromResult(result), + super({ + resolve: async (input) => + createCliBridgeRetainedPlan(await options.resolve(input), input.runId), + recover: async ({ runId, providerSessionId, controlRef }) => + createCliBridgeRetainedPlan( + await options.recover({ + runId, + ...(providerSessionId === undefined ? {} : { providerSessionId }), + ...(controlRef === undefined ? {} : { controlRef }), + }), + runId, + controlRef, ), - ...(result.error === undefined ? {} : { error: result.error }), - } - } catch (error) { - return { - ...base, - error: error instanceof Error ? error.message : 'CLI Bridge retained result unavailable', - } - } - } - - async *#streamHandle(input: { - readonly runId: string - readonly handle: RetainedRunHandle - readonly plan: CliBridgeRetainedPlan - readonly signal: AbortSignal - readonly includeObservation: boolean - readonly afterSequence: number - readonly after?: string - }): AsyncGenerator { - const reader = new AbortController() - this.#readers.get(input.runId)?.abort(new DOMException('Reader replaced', 'AbortError')) - this.#readers.set(input.runId, reader) - const signal = AbortSignal.any([input.signal, reader.signal]) - let sequence = input.afterSequence - try { - if (input.includeObservation) { - sequence += 1 - const observedAt = new Date().toISOString() - const observation = await input.plan.prepared.observation.snapshot() - if (observation === undefined) { - throw new Error('CLI Bridge execution observation is unavailable') - } - yield { - runId: input.runId, - eventId: `${input.runId}:execution-bound`, - sequence, - receivedAt: observedAt, - event: { - type: 'braid.execution.observed', - observation, - controlRef: input.handle.controlRef, - timestamp: observedAt, - }, - } - } - signal.throwIfAborted() - const providerSequence = Math.max(0, input.afterSequence - 1) - for await (const envelope of input.handle.events({ - ...(input.after === undefined - ? {} - : { after: { cursor: input.after, sequence: providerSequence } }), - signal, - })) { - sequence = envelope.sequence + 1 - if (envelope.cursor !== undefined) this.#lastCursors.set(input.runId, envelope.cursor) - yield { ...envelope, sequence } - } - const result = await input.handle.result() - sequence += 1 - yield finalRetainedEnvelope(input.runId, sequence, input.plan.prepared.route, result) - } finally { - if (this.#readers.get(input.runId) === reader) this.#readers.delete(input.runId) - } - } - - async #handleFor( - runId: string, - providerSessionId: string | undefined, - supplied: AgentExactRunControlRef | undefined, - signal: AbortSignal | undefined, - ): Promise { - const active = this.#handles.get(runId) - if (active) { - this.#recoverySessionId(runId, providerSessionId, active.controlRef) - if (supplied !== undefined) { - const exact = this.#validatedControlRef(runId, supplied) - if (canonicalDigest(exact) !== canonicalDigest(active.controlRef)) { - throw new Error('retained control reference conflicts with the active run') - } - } - return active - } - const recoverySessionId = this.#recoverySessionId(runId, providerSessionId, supplied) - const plan = await this.#planFor(runId, recoverySessionId) - const controlRef = await this.#controlRefFor(runId, plan, supplied, signal) - if (!controlRef) return null - const handle = await reconnectCliBridgeRetainedRun(plan, controlRef) - if (handle) this.#rememberHandle(runId, handle, plan) - return handle - } - - async #controlRefFor( - runId: string, - plan: CliBridgeRetainedPlan, - supplied: AgentExactRunControlRef | undefined, - signal: AbortSignal | undefined, - ): Promise { - if (supplied !== undefined) { - const exact = this.#validatedControlRef(runId, supplied) - const cached = this.#controlRefs.get(runId) - if (cached !== undefined && canonicalDigest(cached) !== canonicalDigest(exact)) { - throw new Error('retained control reference conflicts with the saved run') - } - this.#rememberControlRef(runId, exact) - return exact - } - const cached = this.#controlRefs.get(runId) - if (cached) return cached - const discovered = await discoverCliBridgeControlRef(plan, runId, signal) - if (discovered) this.#rememberControlRef(runId, discovered) - return discovered - } - - async #planFor(runId: string, providerSessionId?: string): Promise { - const cached = this.#plans.get(runId) - if (cached) { - if ( - providerSessionId !== undefined && - cached.prepared.providerSessionId !== providerSessionId - ) { - throw new Error('retained provider session conflicts with the saved run') - } - return cached - } - const prepared = await this.#options.recover({ - runId, - ...(providerSessionId === undefined ? {} : { providerSessionId }), }) - const plan = await createCliBridgeRetainedPlan(prepared, runId) - this.#rememberPlan(runId, plan) - return plan - } - - #recoverySessionId( - runId: string, - providerSessionId: string | undefined, - controlRef: AgentExactRunControlRef | undefined, - ): string | undefined { - if (controlRef !== undefined) this.#validatedControlRef(runId, controlRef) - if ( - providerSessionId !== undefined && - controlRef !== undefined && - providerSessionId !== controlRef.sessionId - ) { - throw new Error('retained provider session conflicts with the exact run reference') - } - return controlRef?.sessionId ?? providerSessionId - } - - #rememberHandle(runId: string, handle: RetainedRunHandle, plan: CliBridgeRetainedPlan): void { - this.#handles.set(runId, handle) - this.#rememberControlRef(runId, handle.controlRef) - this.#rememberPlan(runId, plan) - } - - #rememberControlRef(runId: string, controlRef: AgentExactRunControlRef): void { - this.#controlRefs.set(runId, structuredClone(controlRef)) - this.#touch(runId) - } - - #rememberPlan(runId: string, plan: CliBridgeRetainedPlan): void { - this.#plans.set(runId, plan) - this.#touch(runId) - } - - #validatedControlRef( - runId: string, - controlRef: AgentExactRunControlRef, - ): AgentExactRunControlRef { - const exact = AgentExactRunControlRefSchema.parse(controlRef) - if (exact.runId !== runId) throw new Error('retained control reference names another run') - return exact - } - - #touch(runId: string): void { - const index = this.#retainedOrder.indexOf(runId) - if (index >= 0) this.#retainedOrder.splice(index, 1) - this.#retainedOrder.push(runId) - while (this.#retainedOrder.length > MAX_RETAINED_CLIENTS) { - const evictedIndex = this.#retainedOrder.findIndex( - (candidate) => !this.#readers.has(candidate), - ) - if (evictedIndex < 0) break - const [evicted] = this.#retainedOrder.splice(evictedIndex, 1) - if (evicted === undefined) break - this.#handles.delete(evicted) - this.#plans.delete(evicted) - this.#controlRefs.delete(evicted) - this.#lastCursors.delete(evicted) - this.#detached.delete(evicted) - } } } diff --git a/src/adapters/runtime/cli-bridge-retained-run.ts b/src/adapters/runtime/cli-bridge-retained-run.ts index 571a8c7..53f7b7b 100644 --- a/src/adapters/runtime/cli-bridge-retained-run.ts +++ b/src/adapters/runtime/cli-bridge-retained-run.ts @@ -1,20 +1,34 @@ import { - AgentExactRunControlRefSchema, type AgentExactRunControlRef, + AgentExactRunControlRefSchema, } from '@tangle-network/agent-interface' import type { AgentEnvironmentProvider } from '@tangle-network/agent-interface/environment-provider' import { + type RetainedRunHandle, reconnectRetainedRun, startRetainedRun, - type RetainedRunHandle, } from '@tangle-network/agent-runtime/kernel' +import { publicMaterializationReceipt } from '../../domain/materialization-receipt.js' +import type { RuntimeEventEnvelope } from '../../domain/runtime-events.js' import type { ExecuteTurnInput } from '../../ports/execution.js' import { safeExecutionId } from './production-backend-common.js' import type { PreparedCliBridgeConnection } from './production-cli-bridge-backend.js' +import type { + RetainedExecutionPlan, + RetainedResultProjection, +} from './retained-execution-contract.js' +import { + finalRetainedEnvelope, + isTerminalRetainedStatus, + modelRequestsFromResult, + retainedCapabilities, + retainedStatus, + retainedTurnUsage, +} from './retained-execution-projection.js' const MAX_STATUS_BYTES = 64 * 1024 -export interface CliBridgeRetainedPlan { +export interface CliBridgeRetainedPlan extends RetainedExecutionPlan { readonly prepared: PreparedCliBridgeConnection readonly provider: AgentEnvironmentProvider readonly environmentId: string @@ -24,19 +38,57 @@ export interface CliBridgeRetainedPlan { export async function createCliBridgeRetainedPlan( prepared: PreparedCliBridgeConnection, runId: string, + controlRef?: AgentExactRunControlRef, ): Promise { const { createCliBridgeProvider } = await import('@tangle-network/agent-provider-cli-bridge') - return Object.freeze({ + const provider = createCliBridgeProvider({ + baseUrl: prepared.bridgeUrl, + bearerToken: prepared.bearerToken, + defaultModel: prepared.route, + ...(prepared.fetch === undefined ? {} : { fetch: prepared.fetch }), + }) + const environmentId = controlRef?.environmentId ?? retainedEnvironmentId(runId) + const executionId = controlRef?.executionId ?? safeExecutionId(runId) + const providerName = provider.name + const capabilities = retainedCapabilities(prepared.capabilities) + const materializationReceipt = publicMaterializationReceipt({ + ...prepared.materializationReceipt, + backend: 'environment-provider', + environmentId, + providerRunId: controlRef?.runId ?? executionId, + retainedControl: 'exact-after-dispatch', + }) + const plan: CliBridgeRetainedPlan = { prepared, - provider: createCliBridgeProvider({ - baseUrl: prepared.bridgeUrl, - bearerToken: prepared.bearerToken, - defaultModel: prepared.route, - ...(prepared.fetch === undefined ? {} : { fetch: prepared.fetch }), + provider, + environmentId, + executionId, + providerName, + providerSessionId: controlRef?.sessionId ?? prepared.providerSessionId, + model: prepared.route, + capabilities, + materializationReceipt, + start: (input) => startCliBridgeRetainedRun(plan, input), + reconnect: (controlRef) => reconnectCliBridgeRetainedRun(plan, controlRef), + discover: (braidRunId, signal) => discoverCliBridgeControlRef(plan, braidRunId, signal), + observe: () => prepared.observation.snapshot(), + projectStatus: ({ status, detached }) => retainedStatus(status, detached), + isTerminalStatus: isTerminalRetainedStatus, + projectResult: (result): RetainedResultProjection => ({ + text: result.text, + usage: retainedTurnUsage(result.usage, prepared.route, modelRequestsFromResult(result)), + ...(result.error === undefined ? {} : { error: result.error }), }), - environmentId: retainedEnvironmentId(runId), - executionId: safeExecutionId(runId), - }) + projectFinal: ({ runId: braidRunId, sequence, result }): RuntimeEventEnvelope => + finalRetainedEnvelope( + braidRunId, + sequence, + prepared.route, + result, + 'Execute the retained CLI Bridge turn', + ), + } + return Object.freeze(plan) } export async function startCliBridgeRetainedRun( @@ -73,11 +125,12 @@ export async function reconnectCliBridgeRetainedRun( /** Recover the exact server-issued digest after a client crashed during dispatch. */ export async function discoverCliBridgeControlRef( plan: CliBridgeRetainedPlan, - runId: string, + _braidRunId: string, signal?: AbortSignal, ): Promise { + const providerRunId = plan.executionId const response = await (plan.prepared.fetch ?? globalThis.fetch)( - `${plan.prepared.bridgeUrl}/v1/runs/${encodeURIComponent(runId)}`, + `${plan.prepared.bridgeUrl}/v1/runs/${encodeURIComponent(providerRunId)}`, { method: 'GET', headers: { authorization: `Bearer ${plan.prepared.bearerToken}` }, @@ -99,14 +152,14 @@ export async function discoverCliBridgeControlRef( throw new Error('cli-bridge run discovery returned an invalid run snapshot') } const snapshot = value as Record - if (snapshot.id !== runId) { - throw new Error('cli-bridge run discovery returned another run') + if (snapshot.id !== providerRunId) { + throw new Error('cli-bridge run discovery returned another run identity') } return AgentExactRunControlRefSchema.parse({ - runId, + runId: snapshot.id, provider: plan.provider.name, environmentId: plan.environmentId, - sessionId: plan.prepared.providerSessionId, + sessionId: plan.providerSessionId, executionId: plan.executionId, requestDigest: snapshot.requestDigest, }) diff --git a/src/adapters/runtime/production-backend-common.ts b/src/adapters/runtime/production-backend-common.ts index 2647238..d74aabc 100644 --- a/src/adapters/runtime/production-backend-common.ts +++ b/src/adapters/runtime/production-backend-common.ts @@ -1,10 +1,10 @@ import type { AgentProfile, HarnessType } from '@tangle-network/agent-interface' -import { snapshotAgentProfile } from '../agent-interface/profile-runtime.js' import { ConnectionError } from '../../app/connection-errors.js' import type { ConnectionCatalog, ConnectionSelectionInput } from '../../app/connections.js' import type { ConnectionRecord } from '../../domain/entities.js' import type { ConnectionId } from '../../domain/ids.js' import type { ExecuteTurnInput } from '../../ports/execution.js' +import { snapshotAgentProfile } from '../agent-interface/profile-runtime.js' import type { ProductionConnectionOptions } from '../connections/production-connection-types.js' export interface ProductionExecutionSelection { @@ -27,6 +27,7 @@ export async function exactExecutionProfile( source: Readonly, selection: ProductionExecutionSelection, connectionId: ConnectionId, + options: { readonly requireProvider?: boolean } = {}, ): Promise { const profile = snapshotAgentProfile(source) const model = requiredProfileModel(profile, connectionId) @@ -47,7 +48,7 @@ export async function exactExecutionProfile( { connectionId }, ) } - if (!profile.model?.provider?.trim()) { + if (options.requireProvider !== false && !profile.model?.provider?.trim()) { throw new ConnectionError( 'CONNECTION_MODEL_REQUIRED', 'AgentProfile.model.provider must identify the model provider before execution', diff --git a/src/adapters/runtime/production-backend-resolver.ts b/src/adapters/runtime/production-backend-resolver.ts index ce99f39..47cffab 100644 --- a/src/adapters/runtime/production-backend-resolver.ts +++ b/src/adapters/runtime/production-backend-resolver.ts @@ -1,3 +1,4 @@ +import type { ExecuteTurnInput } from '../../ports/execution.js' import { connectionEndpoint } from '../connections/production-connection-endpoints.js' import type { PreparedExecution } from './prepared-execution.js' import type { @@ -5,13 +6,12 @@ import type { ProductionExecutionSelection, } from './production-backend-common.js' import { + type PreparedCliBridgeConnection, prepareCliBridgeConnection, resolveCliBridgeBackend, - type PreparedCliBridgeConnection, } from './production-cli-bridge-backend.js' import { resolveTangleInferenceBackend } from './production-tangle-inference-backend.js' import { resolveTangleSandboxBackend } from './production-tangle-sandbox-backend.js' -import type { ExecuteTurnInput } from '../../ports/execution.js' export type { ProductionBackendResolverOptions, diff --git a/src/adapters/runtime/production-cli-bridge-backend.ts b/src/adapters/runtime/production-cli-bridge-backend.ts index e3b00d1..0f20c92 100644 --- a/src/adapters/runtime/production-cli-bridge-backend.ts +++ b/src/adapters/runtime/production-cli-bridge-backend.ts @@ -3,10 +3,10 @@ import type { AgentProfile, HarnessType, } from '@tangle-network/agent-interface' -import { snapHarnessToModel } from '../agent-interface/harness-runtime.js' import { ConnectionError } from '../../app/connection-errors.js' import type { ConnectionId } from '../../domain/ids.js' import type { ExecuteTurnInput } from '../../ports/execution.js' +import { snapHarnessToModel } from '../agent-interface/harness-runtime.js' import { bridgeRunnerSupportsModel, materializeBridgeModelRoute, @@ -89,7 +89,9 @@ export async function prepareCliBridgeConnection( connectionId: ConnectionId, endpoint: string, ): Promise { - const profile = await exactExecutionProfile(input.profile, selection, connectionId) + const profile = await exactExecutionProfile(input.profile, selection, connectionId, { + requireProvider: false, + }) const model = requiredProfileModel(profile, connectionId) const runner = requiredProfileRunner(profile, connectionId) if (!bridgeRunnerSupportsModel(runner, model)) { diff --git a/src/adapters/runtime/retained-execution-contract.ts b/src/adapters/runtime/retained-execution-contract.ts new file mode 100644 index 0000000..f770aec --- /dev/null +++ b/src/adapters/runtime/retained-execution-contract.ts @@ -0,0 +1,58 @@ +import type { AgentExactRunControlRef } from '@tangle-network/agent-interface' +import type { AgentTurnResult } from '@tangle-network/agent-interface/environment-provider' +import type { RetainedRunHandle } from '@tangle-network/agent-runtime/kernel' +import type { TurnUsage } from '../../domain/entities.js' +import type { ExecutionEnvironmentObservation } from '../../domain/execution-observation.js' +import type { RuntimeEventEnvelope } from '../../domain/runtime-events.js' +import type { + ExecuteTurnInput, + ProviderRunSnapshot, + RunCapabilities, +} from '../../ports/execution.js' + +export interface RetainedResultProjection { + readonly text: string + readonly usage: TurnUsage + readonly error?: string +} + +/** Provider-specific facts and operations needed by the retained lifecycle. */ +export interface RetainedExecutionPlan { + readonly providerName: string + /** Unknown until a provider with server-issued ids admits the first run. */ + readonly environmentId?: string + readonly providerSessionId?: string + readonly model: string + readonly capabilities: RunCapabilities + readonly materializationReceipt: Readonly> + readonly start: (input: ExecuteTurnInput) => Promise + readonly reconnect: ( + controlRef: AgentExactRunControlRef, + signal?: AbortSignal, + ) => Promise + readonly discover: ( + braidRunId: string, + signal?: AbortSignal, + ) => Promise + readonly observe: () => Promise + readonly projectStatus: (input: { + readonly status: string | null + readonly detached: boolean + }) => ProviderRunSnapshot['status'] + readonly isTerminalStatus: (status: ProviderRunSnapshot['status']) => boolean + readonly projectResult: (result: AgentTurnResult) => RetainedResultProjection + readonly projectFinal: (input: { + readonly runId: string + readonly sequence: number + readonly result: AgentTurnResult + }) => RuntimeEventEnvelope +} + +export interface RetainedExecutionDriver { + readonly resolve: (input: ExecuteTurnInput) => Promise + readonly recover: (input: { + readonly runId: string + readonly providerSessionId?: string + readonly controlRef?: AgentExactRunControlRef + }) => Promise +} diff --git a/src/adapters/runtime/cli-bridge-retained-projection.ts b/src/adapters/runtime/retained-execution-projection.ts similarity index 68% rename from src/adapters/runtime/cli-bridge-retained-projection.ts rename to src/adapters/runtime/retained-execution-projection.ts index a2967b6..3628017 100644 --- a/src/adapters/runtime/cli-bridge-retained-projection.ts +++ b/src/adapters/runtime/retained-execution-projection.ts @@ -1,34 +1,26 @@ import type { TokenUsage } from '@tangle-network/agent-interface' -import type { AgentTurnResult } from '@tangle-network/agent-interface/environment-provider' -import { canonicalDigest } from '../../domain/canonical.js' +import type { + AgentEnvironmentCapabilities, + AgentTurnResult, +} from '@tangle-network/agent-interface/environment-provider' import type { TurnUsage } from '../../domain/entities.js' import type { RuntimeEventEnvelope } from '../../domain/runtime-events.js' import type { RunStatus } from '../../domain/state.js' -import type { ExecuteTurnInput, RunCapabilities } from '../../ports/execution.js' -import { canonicalAgentProfileDigestHex } from '../agent-interface/profile-runtime.js' -import type { PreparedCliBridgeConnection } from './production-cli-bridge-backend.js' +import type { RunCapabilities } from '../../ports/execution.js' -export function retainedExecutionKey(input: ExecuteTurnInput): string { - return canonicalDigest({ - runId: input.runId, - operationId: input.operationId, - text: input.text, - profile: canonicalAgentProfileDigestHex(input.profile), - connectionId: input.connectionId ?? null, - workspaceRoot: input.workspaceRoot ?? null, - sessionId: input.sessionId ?? null, - contextBoundary: input.contextBoundary ?? null, - }) -} - -export function retainedCapabilities(prepared: PreparedCliBridgeConnection): RunCapabilities { +export function retainedCapabilities(environment: AgentEnvironmentCapabilities): RunCapabilities { return Object.freeze({ - streaming: { live: true, replay: true, detach: true, turnIdempotency: true }, - sessions: { continue: prepared.capabilities.sessions.continue, messages: false }, + streaming: { + live: environment.streaming.live, + replay: environment.streaming.replay, + detach: environment.streaming.detach, + turnIdempotency: environment.streaming.turnIdempotency, + }, + sessions: { continue: environment.sessions.continue, messages: false }, controls: { cancel: true, steer: false, queue: true, status: true, recreate: true }, events: { stableIdentity: true, sequence: true, cursor: true }, - usage: true, - environment: prepared.capabilities, + usage: environment.usage, + environment, }) } @@ -81,6 +73,7 @@ export function finalRetainedEnvelope( sequence: number, model: string, result: AgentTurnResult, + intent: string, ): RuntimeEventEnvelope { const timestamp = new Date().toISOString() const usage = retainedTurnUsage(result.usage, model, modelRequestsFromResult(result)) @@ -91,9 +84,9 @@ export function finalRetainedEnvelope( receivedAt: timestamp, event: { type: 'final', - task: { id: runId, intent: 'Execute the retained CLI Bridge turn' }, + task: { id: runId, intent }, status: result.success ? 'completed' : 'failed', - reason: result.success ? 'completed' : (result.error ?? 'CLI Bridge run failed'), + reason: result.success ? 'completed' : (result.error ?? 'Retained run failed'), text: result.text, metadata: { model, @@ -109,9 +102,7 @@ export function finalRetainedEnvelope( }, ...(result.success || result.error === undefined ? {} - : { - error: { kind: 'backend', message: result.error }, - }), + : { error: { kind: 'backend', message: result.error } }), timestamp, }, } diff --git a/src/adapters/runtime/retained-execution-state.ts b/src/adapters/runtime/retained-execution-state.ts new file mode 100644 index 0000000..8165bea --- /dev/null +++ b/src/adapters/runtime/retained-execution-state.ts @@ -0,0 +1,232 @@ +import { + type AgentExactRunControlRef, + AgentExactRunControlRefSchema, +} from '@tangle-network/agent-interface' +import type { RetainedRunHandle } from '@tangle-network/agent-runtime/kernel' +import { canonicalDigest } from '../../domain/canonical.js' +import type { ExecuteTurnInput } from '../../ports/execution.js' +import { canonicalAgentProfileDigestHex } from '../agent-interface/profile-runtime.js' +import type { RetainedExecutionPlan } from './retained-execution-contract.js' + +const MAX_RETAINED_CLIENTS = 128 + +interface PreparedRetainedRun { + readonly key: string + readonly plan: RetainedExecutionPlan +} + +export function retainedExecutionKey(input: ExecuteTurnInput): string { + return canonicalDigest({ + runId: input.runId, + operationId: input.operationId, + text: input.text, + profile: canonicalAgentProfileDigestHex(input.profile), + connectionId: input.connectionId ?? null, + workspaceRoot: input.workspaceRoot ?? null, + sessionId: input.sessionId ?? null, + contextBoundary: input.contextBoundary ?? null, + }) +} + +/** Process-local handles and exact provider bindings for one Braid run. */ +export class RetainedExecutionState { + readonly #prepared = new Map() + readonly #startingHandles = new Map>() + readonly #handles = new Map() + readonly #plans = new Map() + readonly #controlRefs = new Map() + readonly #readers = new Map() + readonly #cancellationRequested = new Set() + readonly #detached = new Set() + readonly #lastCursors = new Map() + readonly #retainedOrder: string[] = [] + + rememberPrepared(runId: string, key: string, plan: RetainedExecutionPlan): void { + this.#assertAdmissionCapacity(runId) + this.#prepared.set(runId, { key, plan }) + this.rememberPlan(runId, plan) + } + + takePrepared(runId: string, key: string): RetainedExecutionPlan | undefined { + const prepared = this.#prepared.get(runId) + if (prepared === undefined || prepared.key !== key) return undefined + this.#prepared.delete(runId) + return prepared.plan + } + + hasPrepared(runId: string): boolean { + return this.#prepared.has(runId) + } + + discardPrepared(runId: string): void { + this.#prepared.delete(runId) + } + + plan(runId: string): RetainedExecutionPlan | undefined { + return this.#plans.get(runId) + } + + rememberPlan(runId: string, plan: RetainedExecutionPlan): void { + this.#plans.set(runId, plan) + this.#touch(runId) + } + + handle(runId: string): RetainedRunHandle | undefined { + return this.#handles.get(runId) + } + + rememberStartingHandle(runId: string, starting: Promise): void { + const existing = this.#startingHandles.get(runId) + if (existing !== undefined && existing !== starting) { + throw new Error('retained run already has an in-flight start') + } + this.#startingHandles.set(runId, starting) + this.#touch(runId) + } + + startingHandle(runId: string): Promise | undefined { + return this.#startingHandles.get(runId) + } + + clearStartingHandle(runId: string, starting: Promise): void { + if (this.#startingHandles.get(runId) === starting) this.#startingHandles.delete(runId) + } + + markCancellationRequested(runId: string): void { + this.#cancellationRequested.add(runId) + } + + isCancellationRequested(runId: string): boolean { + return this.#cancellationRequested.has(runId) + } + + rememberHandle(runId: string, plan: RetainedExecutionPlan, handle: RetainedRunHandle): void { + const exact = this.validateControlRef(runId, plan, handle.controlRef) + const saved = this.#controlRefs.get(runId) + if (saved !== undefined) this.assertSameControlRef(saved, exact) + this.#handles.set(runId, handle) + this.rememberControlRef(runId, plan, exact) + this.rememberPlan(runId, plan) + } + + controlRef(runId: string): AgentExactRunControlRef | undefined { + const controlRef = this.#controlRefs.get(runId) + return controlRef === undefined ? undefined : structuredClone(controlRef) + } + + rememberControlRef( + runId: string, + plan: RetainedExecutionPlan, + controlRef: AgentExactRunControlRef, + ): AgentExactRunControlRef { + const exact = this.validateControlRef(runId, plan, controlRef) + const saved = this.#controlRefs.get(runId) + if (saved !== undefined) this.assertSameControlRef(saved, exact) + this.#controlRefs.set(runId, structuredClone(exact)) + this.#touch(runId) + return exact + } + + validateControlRef( + _runId: string, + plan: RetainedExecutionPlan, + controlRef: AgentExactRunControlRef, + ): AgentExactRunControlRef { + // The map key is Braid's local ID. The provider owns controlRef.runId. + const exact = AgentExactRunControlRefSchema.parse(controlRef) + if (exact.provider !== plan.providerName) { + throw new Error('retained control reference names another provider') + } + if (plan.environmentId !== undefined && exact.environmentId !== plan.environmentId) { + throw new Error('retained control reference names another environment') + } + if (plan.providerSessionId !== undefined && exact.sessionId !== plan.providerSessionId) { + throw new Error('retained control reference names another provider session') + } + return exact + } + + assertProviderSession(plan: RetainedExecutionPlan, providerSessionId: string | undefined): void { + if ( + providerSessionId !== undefined && + plan.providerSessionId !== undefined && + providerSessionId !== plan.providerSessionId + ) { + throw new Error('retained provider session conflicts with the saved run') + } + } + + assertSameControlRef(expected: AgentExactRunControlRef, actual: AgentExactRunControlRef): void { + if (canonicalDigest(expected) !== canonicalDigest(actual)) { + throw new Error('retained control reference conflicts with the saved run') + } + } + + reader(runId: string): AbortController | undefined { + return this.#readers.get(runId) + } + + replaceReader(runId: string, reader: AbortController): AbortController | undefined { + const previous = this.#readers.get(runId) + this.#readers.set(runId, reader) + return previous + } + + clearReader(runId: string, reader: AbortController): void { + if (this.#readers.get(runId) === reader) this.#readers.delete(runId) + } + + markDetached(runId: string, detached: boolean): void { + if (detached) this.#detached.add(runId) + else this.#detached.delete(runId) + } + + isDetached(runId: string): boolean { + return this.#detached.has(runId) + } + + lastCursor(runId: string): string | undefined { + return this.#lastCursors.get(runId) + } + + rememberCursor(runId: string, cursor: string): void { + this.#lastCursors.set(runId, cursor) + } + + #touch(runId: string): void { + const index = this.#retainedOrder.indexOf(runId) + if (index >= 0) this.#retainedOrder.splice(index, 1) + this.#retainedOrder.push(runId) + while (this.#retainedOrder.length > MAX_RETAINED_CLIENTS) { + const evictedIndex = this.#retainedOrder.findIndex( + (candidate) => + !this.#prepared.has(candidate) && + !this.#readers.has(candidate) && + !this.#startingHandles.has(candidate), + ) + if (evictedIndex < 0) break + const [evicted] = this.#retainedOrder.splice(evictedIndex, 1) + if (evicted === undefined) break + this.#prepared.delete(evicted) + this.#startingHandles.delete(evicted) + this.#handles.delete(evicted) + this.#plans.delete(evicted) + this.#controlRefs.delete(evicted) + this.#cancellationRequested.delete(evicted) + this.#lastCursors.delete(evicted) + this.#detached.delete(evicted) + } + } + + #assertAdmissionCapacity(runId: string): void { + if (this.#retainedOrder.includes(runId)) return + if (this.#retainedOrder.length < MAX_RETAINED_CLIENTS) return + const evictable = this.#retainedOrder.some( + (candidate) => + !this.#prepared.has(candidate) && + !this.#readers.has(candidate) && + !this.#startingHandles.has(candidate), + ) + if (!evictable) throw new Error('Retained execution admission capacity reached') + } +} diff --git a/src/adapters/runtime/retained-execution-stream.ts b/src/adapters/runtime/retained-execution-stream.ts new file mode 100644 index 0000000..03b8685 --- /dev/null +++ b/src/adapters/runtime/retained-execution-stream.ts @@ -0,0 +1,60 @@ +import type { RetainedRunHandle } from '@tangle-network/agent-runtime/kernel' +import type { RuntimeEventEnvelope } from '../../domain/runtime-events.js' +import type { RetainedExecutionPlan } from './retained-execution-contract.js' +import type { RetainedExecutionState } from './retained-execution-state.js' + +export async function* streamRetainedExecution(input: { + readonly runId: string + readonly handle: RetainedRunHandle + readonly plan: RetainedExecutionPlan + readonly state: RetainedExecutionState + readonly signal: AbortSignal + readonly includeObservation: boolean + readonly afterSequence: number + readonly after?: string +}): AsyncGenerator { + const reader = new AbortController() + const previous = input.state.replaceReader(input.runId, reader) + previous?.abort(new DOMException('Reader replaced', 'AbortError')) + const signal = AbortSignal.any([input.signal, reader.signal]) + let sequence = input.afterSequence + try { + if (input.includeObservation) { + sequence += 1 + const observedAt = new Date().toISOString() + const observation = await input.plan.observe() + if (observation === undefined) { + throw new Error('Retained execution observation is unavailable') + } + yield { + runId: input.runId, + eventId: `${input.runId}:execution-bound`, + sequence, + receivedAt: observedAt, + event: { + type: 'braid.execution.observed', + observation, + controlRef: input.handle.controlRef, + timestamp: observedAt, + }, + } + } + signal.throwIfAborted() + const providerSequence = Math.max(0, input.afterSequence - 1) + for await (const envelope of input.handle.events({ + ...(input.after === undefined + ? {} + : { after: { cursor: input.after, sequence: providerSequence } }), + signal, + })) { + sequence = envelope.sequence + 1 + if (envelope.cursor !== undefined) input.state.rememberCursor(input.runId, envelope.cursor) + yield { ...envelope, runId: input.runId, sequence } + } + const result = await input.handle.result() + sequence += 1 + yield input.plan.projectFinal({ runId: input.runId, sequence, result }) + } finally { + input.state.clearReader(input.runId, reader) + } +} diff --git a/src/adapters/runtime/retained-execution.ts b/src/adapters/runtime/retained-execution.ts new file mode 100644 index 0000000..8ec1881 --- /dev/null +++ b/src/adapters/runtime/retained-execution.ts @@ -0,0 +1,373 @@ +import type { AgentExactRunControlRef } from '@tangle-network/agent-interface' +import type { RetainedRunHandle } from '@tangle-network/agent-runtime/kernel' +import { canonicalDigest } from '../../domain/canonical.js' +import type { RuntimeEventEnvelope } from '../../domain/runtime-events.js' +import type { + CancelRunInput, + ControlAcknowledgement, + ExecuteTurnInput, + ExecutionAdmission, + ExecutionPort, + ProviderRunSnapshot, +} from '../../ports/execution.js' +import { canonicalAgentProfileDigestHex } from '../agent-interface/profile-runtime.js' +import type { + RetainedExecutionDriver, + RetainedExecutionPlan, +} from './retained-execution-contract.js' +import { RetainedExecutionState, retainedExecutionKey } from './retained-execution-state.js' +import { streamRetainedExecution } from './retained-execution-stream.js' + +export type { + RetainedExecutionDriver, + RetainedExecutionPlan, + RetainedResultProjection, +} from './retained-execution-contract.js' + +interface ResolvedRetainedHandle { + readonly handle: RetainedRunHandle + readonly plan: RetainedExecutionPlan + readonly wasStarting: boolean +} + +/** Provider-neutral durable execution lifecycle used by Braid adapters. */ +export class RetainedExecutionPort implements ExecutionPort { + readonly admissionMode = 'async' as const + readonly #driver: RetainedExecutionDriver + readonly #state = new RetainedExecutionState() + + constructor(driver: RetainedExecutionDriver) { + this.#driver = driver + } + + async admit(input: ExecuteTurnInput): Promise { + const plan = await this.#driver.resolve(input) + this.#state.rememberPrepared(input.runId, retainedExecutionKey(input), plan) + return { + capabilities: plan.capabilities, + provider: plan.providerName, + ...(plan.environmentId === undefined ? {} : { environmentId: plan.environmentId }), + ...(plan.providerSessionId === undefined + ? {} + : { providerSessionId: plan.providerSessionId }), + materializationReceipt: plan.materializationReceipt, + profileDigest: canonicalAgentProfileDigestHex(input.profile), + capabilitiesDigest: canonicalDigest(plan.capabilities), + materializationDigest: canonicalDigest(plan.materializationReceipt), + } + } + + async *streamTurn(input: ExecuteTurnInput): AsyncGenerator { + if (this.#state.isCancellationRequested(input.runId)) return + const plan = this.#state.takePrepared(input.runId, retainedExecutionKey(input)) + if (plan === undefined) { + throw new Error('Retained run was not admitted with this exact request') + } + const starting = Promise.resolve().then(() => plan.start(input)) + this.#state.rememberStartingHandle(input.runId, starting) + let handle: RetainedRunHandle + try { + handle = await starting + this.#state.rememberHandle(input.runId, plan, handle) + } finally { + this.#state.clearStartingHandle(input.runId, starting) + } + this.#state.markDetached(input.runId, false) + yield* streamRetainedExecution({ + runId: input.runId, + handle, + plan, + state: this.#state, + signal: input.signal, + includeObservation: true, + afterSequence: 0, + }) + } + + async *reconnect(input: { + readonly runId: string + readonly after?: string + readonly afterSequence?: number + readonly providerSessionId?: string + readonly controlRef?: AgentExactRunControlRef + readonly signal: AbortSignal + }): AsyncGenerator { + if (input.after !== undefined && input.afterSequence === undefined) { + throw new Error('Retained reconnect requires the saved event sequence') + } + const providerSessionId = this.#recoverySessionId(input.providerSessionId, input.controlRef) + const plan = await this.#planFor(input.runId, providerSessionId, input.controlRef) + const controlRef = await this.#controlRefFor(input.runId, plan, input.controlRef, input.signal) + if (!controlRef) return + const handle = await plan.reconnect(controlRef, input.signal) + if (!handle) return + this.#state.rememberHandle(input.runId, plan, handle) + this.#state.markDetached(input.runId, false) + yield* streamRetainedExecution({ + runId: input.runId, + handle, + plan, + state: this.#state, + signal: input.signal, + includeObservation: (input.afterSequence ?? 0) === 0, + afterSequence: input.afterSequence ?? 0, + ...(input.after === undefined ? {} : { after: input.after }), + }) + } + + async detachRun(input: { + readonly runId: string + readonly operationId: string + readonly providerSessionId?: string + readonly controlRef?: AgentExactRunControlRef + readonly cursor?: string + readonly signal?: AbortSignal + }): Promise { + this.#validateKnownControl(input.runId, input.providerSessionId, input.controlRef) + const reader = this.#state.reader(input.runId) + this.#state.markDetached(input.runId, true) + if (!reader) { + return { operationId: input.operationId, outcome: 'already-applied', detail: 'detached' } + } + reader.abort(new DOMException('Braid detached from the retained run', 'AbortError')) + return { operationId: input.operationId, outcome: 'accepted', detail: 'detached' } + } + + async cancelRun( + input: CancelRunInput & { readonly signal?: AbortSignal }, + ): Promise { + this.#validateKnownControl(input.runId, input.providerSessionId, input.controlRef) + if (this.#state.isCancellationRequested(input.runId)) { + return { + operationId: input.operationId, + outcome: 'already-applied', + detail: 'cancelled-before-start', + } + } + if (this.#state.hasPrepared(input.runId)) { + this.#state.markCancellationRequested(input.runId) + this.#state.discardPrepared(input.runId) + return { + operationId: input.operationId, + outcome: 'accepted', + detail: 'cancelled-before-start', + } + } + const resolved = await this.#handleFor( + input.runId, + input.providerSessionId, + input.controlRef, + input.signal, + ) + if (!resolved) { + return { + operationId: input.operationId, + outcome: 'unknown', + detail: 'Retained run was not found', + } + } + const cancelSignal = resolved.wasStarting || input.signal?.aborted ? undefined : input.signal + const result = await resolved.handle.cancel({ + operationId: input.operationId, + ...(input.reason === undefined ? {} : { reason: input.reason }), + ...(cancelSignal === undefined ? {} : { signal: cancelSignal }), + }) + if (result.status === 'conflict') { + return { operationId: input.operationId, outcome: 'rejected', detail: result.effect } + } + if (result.status === 'unknown') { + return { operationId: input.operationId, outcome: 'unknown', detail: result.effect } + } + if (result.effect === 'cancel_requested') { + return { + operationId: input.operationId, + outcome: 'unknown', + detail: 'cancel_requested', + } + } + let effect = result.effect + if (effect === 'unknown' || effect === 'not_live') { + const reconciled = await this.#reconcileCancelled(resolved, input) + if (!reconciled) { + return { operationId: input.operationId, outcome: 'unknown', detail: result.effect } + } + effect = 'cancelled' + } + if (effect !== 'cancelled') { + return { operationId: input.operationId, outcome: 'unknown', detail: effect } + } + this.#state.reader(input.runId)?.abort(new DOMException('Retained run cancelled', 'AbortError')) + this.#state.markDetached(input.runId, false) + return { + operationId: input.operationId, + outcome: result.status === 'replayed' ? 'already-applied' : 'accepted', + detail: effect, + } + } + + async status(input: { + readonly runId: string + readonly providerSessionId?: string + readonly controlRef?: AgentExactRunControlRef + readonly signal?: AbortSignal + }): Promise { + const resolved = await this.#handleFor( + input.runId, + input.providerSessionId, + input.controlRef, + input.signal, + ) + if (!resolved) return null + const snapshot = await resolved.handle.status({ + ...(input.signal === undefined ? {} : { signal: input.signal }), + }) + const snapshotControlRef = this.#state.validateControlRef( + input.runId, + resolved.plan, + snapshot.controlRef, + ) + this.#state.assertSameControlRef(resolved.handle.controlRef, snapshotControlRef) + const status = resolved.plan.projectStatus({ + status: snapshot.status, + detached: this.#state.isDetached(input.runId), + }) + const cursor = this.#state.lastCursor(input.runId) + const base: ProviderRunSnapshot = { + runId: input.runId, + status, + sessionId: resolved.handle.controlRef.sessionId, + ...(cursor === undefined ? {} : { cursor }), + } + if (!resolved.plan.isTerminalStatus(status)) return base + if (status === 'cancelled' || status === 'expired') return base + try { + const projected = resolved.plan.projectResult(await resolved.handle.result()) + return { + ...base, + finalText: projected.text, + usage: projected.usage, + ...(projected.error === undefined ? {} : { error: projected.error }), + } + } catch (error) { + return { + ...base, + error: error instanceof Error ? error.message : 'Retained result unavailable', + } + } + } + + async #handleFor( + runId: string, + providerSessionId: string | undefined, + supplied: AgentExactRunControlRef | undefined, + signal: AbortSignal | undefined, + ): Promise { + const active = this.#state.handle(runId) + const activePlan = this.#state.plan(runId) + const starting = this.#state.startingHandle(runId) + if (active !== undefined || starting !== undefined) { + if (activePlan === undefined) throw new Error('Retained run state is incomplete') + this.#state.assertProviderSession(activePlan, providerSessionId) + let handle = active + if (handle === undefined) { + if (starting === undefined) throw new Error('Retained run state is incomplete') + handle = await starting + } + if (supplied !== undefined) { + const exact = this.#state.validateControlRef(runId, activePlan, supplied) + this.#state.assertSameControlRef(handle.controlRef, exact) + } + return { handle, plan: activePlan, wasStarting: starting !== undefined } + } + if (activePlan !== undefined) { + if (this.#state.isCancellationRequested(runId)) return null + throw new Error('Retained run state is incomplete') + } + const recoverySessionId = this.#recoverySessionId(providerSessionId, supplied) + const plan = await this.#planFor(runId, recoverySessionId, supplied) + const controlRef = await this.#controlRefFor(runId, plan, supplied, signal) + if (!controlRef) return null + const handle = await plan.reconnect(controlRef, signal) + if (handle) this.#state.rememberHandle(runId, plan, handle) + return handle === null ? null : { handle, plan, wasStarting: false } + } + + async #reconcileCancelled( + resolved: ResolvedRetainedHandle, + input: CancelRunInput & { readonly signal?: AbortSignal }, + ): Promise { + try { + const signal = resolved.wasStarting || input.signal?.aborted ? undefined : input.signal + const snapshot = await resolved.handle.status({ + ...(signal === undefined ? {} : { signal }), + }) + const exact = this.#state.validateControlRef(input.runId, resolved.plan, snapshot.controlRef) + this.#state.assertSameControlRef(resolved.handle.controlRef, exact) + return snapshot.status === 'cancelled' + } catch { + return false + } + } + + async #controlRefFor( + runId: string, + plan: RetainedExecutionPlan, + supplied: AgentExactRunControlRef | undefined, + signal: AbortSignal | undefined, + ): Promise { + if (supplied !== undefined) return this.#state.rememberControlRef(runId, plan, supplied) + const cached = this.#state.controlRef(runId) + if (cached !== undefined) return this.#state.rememberControlRef(runId, plan, cached) + const discovered = await plan.discover(runId, signal) + if (discovered === null) return null + return this.#state.rememberControlRef(runId, plan, discovered) + } + + async #planFor( + runId: string, + providerSessionId?: string, + controlRef?: AgentExactRunControlRef, + ): Promise { + const cached = this.#state.plan(runId) + if (cached !== undefined) { + this.#state.assertProviderSession(cached, providerSessionId) + return cached + } + const plan = await this.#driver.recover({ + runId, + ...(providerSessionId === undefined ? {} : { providerSessionId }), + ...(controlRef === undefined ? {} : { controlRef }), + }) + this.#state.assertProviderSession(plan, providerSessionId) + this.#state.rememberPlan(runId, plan) + return plan + } + + #recoverySessionId( + providerSessionId: string | undefined, + controlRef: AgentExactRunControlRef | undefined, + ): string | undefined { + if ( + providerSessionId !== undefined && + controlRef !== undefined && + providerSessionId !== controlRef.sessionId + ) { + throw new Error('retained provider session conflicts with the exact run reference') + } + return controlRef?.sessionId ?? providerSessionId + } + + #validateKnownControl( + runId: string, + providerSessionId: string | undefined, + controlRef: AgentExactRunControlRef | undefined, + ): void { + const plan = this.#state.plan(runId) + if (plan === undefined) return + this.#state.assertProviderSession(plan, providerSessionId) + if (controlRef === undefined) return + const exact = this.#state.validateControlRef(runId, plan, controlRef) + const saved = this.#state.controlRef(runId) + if (saved !== undefined) this.#state.assertSameControlRef(saved, exact) + } +} diff --git a/src/adapters/runtime/sandbox-observation.ts b/src/adapters/runtime/sandbox-observation.ts index 900bd6f..3fd2adc 100644 --- a/src/adapters/runtime/sandbox-observation.ts +++ b/src/adapters/runtime/sandbox-observation.ts @@ -1,5 +1,6 @@ import type { SandboxClientLike, SandboxInstanceLike } from '@tangle-network/agent-provider-tangle' import type { ExecutionEnvironmentObservation } from '../../domain/execution-observation.js' +import type { ExecutionObservationSource, SandboxLifecyclePolicy } from './prepared-execution.js' import { observeSandboxAccount } from './sandbox-account-observation.js' import { captureSandboxBox, @@ -15,7 +16,6 @@ import { type ObservableSandboxClient, type ObservableSandboxInstance, } from './sandbox-observation-types.js' -import type { ExecutionObservationSource, SandboxLifecyclePolicy } from './prepared-execution.js' export interface SandboxObservationBinding { readonly client: SandboxClientLike @@ -53,6 +53,22 @@ export function observeSandboxClient( observeSandboxAccount(client, state, unavailable, track, now) + const observeBox = (box: ObservableSandboxInstance): SandboxInstanceLike => { + captureSandboxBox(state, box, unavailable) + if (source.describePlacement === undefined) { + unavailable.add('verified-placement:not-exposed-by-client') + } else { + track( + Promise.resolve() + .then(() => source.describePlacement?.(box)) + .then((placement) => captureSandboxPlacement(state, placement, unavailable)), + ) + } + const proxy = wrapObservedSandboxBox(box, state, unavailable) + originals.set(proxy as object, box) + return proxy + } + const wrapped: SandboxClientLike = { async create(options, requestOptions) { state.lifecycle = 'creating' @@ -62,19 +78,7 @@ export function observeSandboxClient( state.storagePersistence = options?.ephemeral === true ? 'ephemeral-home' : 'persistent-home' try { const box = (await source.create(options, requestOptions)) as ObservableSandboxInstance - captureSandboxBox(state, box, unavailable) - if (source.describePlacement === undefined) { - unavailable.add('verified-placement:not-exposed-by-client') - } else { - track( - Promise.resolve() - .then(() => source.describePlacement?.(box)) - .then((placement) => captureSandboxPlacement(state, placement, unavailable)), - ) - } - const proxy = wrapObservedSandboxBox(box, state, unavailable) - originals.set(proxy as object, box) - return proxy + return observeBox(box) } catch (error) { state.lifecycle = 'failed' unavailable.add('provider-environment-id:not-materialized') @@ -84,7 +88,13 @@ export function observeSandboxClient( ...(source.get === undefined ? {} : { - get: (id, requestOptions) => source.get?.(id, requestOptions) ?? Promise.resolve(null), + async get(id, requestOptions) { + const box = (await source.get?.(id, requestOptions)) as + | ObservableSandboxInstance + | null + | undefined + return box === null || box === undefined ? null : observeBox(box) + }, }), ...(source.list === undefined ? {} diff --git a/src/adapters/tui/ui-entity-details.ts b/src/adapters/tui/ui-entity-details.ts index 3f422c6..bd22850 100644 --- a/src/adapters/tui/ui-entity-details.ts +++ b/src/adapters/tui/ui-entity-details.ts @@ -80,6 +80,8 @@ function analysisDetail(record: AnalysisRecord | undefined): EntityDetailView | status: record.status, lines: bounded(rendered.slice(1)), analysisFindingCount: analysis.findings.length, + analysisSupportedFindingCount: analysis.citationSupport.supportedFindings, + analysisCitationSupport: analysis.citationSupport.status, ...(analysis.execution === undefined ? {} : { analysisExecution: analysis.execution }), } } diff --git a/src/adapters/tui/ui-fixtures.ts b/src/adapters/tui/ui-fixtures.ts index ae6d66e..8052674 100644 --- a/src/adapters/tui/ui-fixtures.ts +++ b/src/adapters/tui/ui-fixtures.ts @@ -33,6 +33,7 @@ export const FIXTURE_INTERACTION: InteractionView = Object.freeze({ allowedOutcomes: Object.freeze(['accept', 'reject', 'cancel'] as const), responseScopes: Object.freeze(['once', 'session'] as const), queuePosition: 0, + queueTotal: 1, secret: false, }) diff --git a/src/adapters/tui/ui-intelligence-result-view.ts b/src/adapters/tui/ui-intelligence-result-view.ts index d30c91e..4bd6f20 100644 --- a/src/adapters/tui/ui-intelligence-result-view.ts +++ b/src/adapters/tui/ui-intelligence-result-view.ts @@ -53,6 +53,8 @@ export function withIntelligenceResult( status: analysis.status, lines: rendered.slice(1), analysisFindingCount: analysisView.findings.length, + analysisSupportedFindingCount: analysisView.citationSupport.supportedFindings, + analysisCitationSupport: analysisView.citationSupport.status, ...(analysisView.execution === undefined ? {} : { analysisExecution: analysisView.execution }), diff --git a/src/adapters/tui/ui-projection.ts b/src/adapters/tui/ui-projection.ts index 5ff2bd7..d214946 100644 --- a/src/adapters/tui/ui-projection.ts +++ b/src/adapters/tui/ui-projection.ts @@ -248,7 +248,7 @@ function completenessFor( } export function interactionViews(state: BraidState): InteractionView[] { - const views: InteractionView[] = [] + const views: Array> = [] for (const run of state.runs) { for (const item of run.interactions) { if (item.status !== 'pending') continue @@ -292,7 +292,7 @@ export function interactionViews(state: BraidState): InteractionView[] { }) } } - return views + return views.map((view) => ({ ...view, queueTotal: views.length })) } function answerSpecFor( diff --git a/src/app/production-composition.ts b/src/app/production-composition.ts index 7607038..decd5a1 100644 --- a/src/app/production-composition.ts +++ b/src/app/production-composition.ts @@ -8,8 +8,8 @@ import { import { CliBridgeRetainedExecutionPort } from '../adapters/runtime/cli-bridge-retained-execution.js' import { createProductionBackendResolver, - resolveProductionCliBridgeConnection, type ProductionBackendResolverOptions, + resolveProductionCliBridgeConnection, } from '../adapters/runtime/production-backend-resolver.js' import type { ConnectionRecord } from '../domain/entities.js' import type { ExecutionPort } from '../ports/execution.js' @@ -174,28 +174,28 @@ export function createProductionComposition( expectedKind: connection.kind, expectedUpdatedAt: connection.updatedAt, }, - ...(profile.model?.default === undefined ? {} : { model: profile.model.default }), - ...(profile.harness === undefined ? {} : { runner: profile.harness }), }), } const backendResolver = createProductionBackendResolver(resolverOptions) + const recoveryInput = (runId: string, providerSessionId?: string) => ({ + operationId: `recover-${runId}`, + runId, + text: '', + profile, + connectionId: connection.id, + ...(providerSessionId === undefined ? {} : { sessionId: providerSessionId }), + ...(config.workspaceRoot === undefined ? {} : { workspaceRoot: config.workspaceRoot }), + signal: new AbortController().signal, + }) const execution = connection.kind === 'cli-bridge' ? new CliBridgeRetainedExecutionPort({ resolve: (input) => resolveProductionCliBridgeConnection(resolverOptions, input), recover: ({ runId, providerSessionId }) => - resolveProductionCliBridgeConnection(resolverOptions, { - operationId: `recover-${runId}`, - runId, - text: '', - profile, - connectionId: connection.id, - ...(providerSessionId === undefined ? {} : { sessionId: providerSessionId }), - ...(config.workspaceRoot === undefined - ? {} - : { workspaceRoot: config.workspaceRoot }), - signal: new AbortController().signal, - }), + resolveProductionCliBridgeConnection( + resolverOptions, + recoveryInput(runId, providerSessionId), + ), }) : new AgentRuntimeExecutionPort(backendResolver) diff --git a/src/bin/braid-runtime.ts b/src/bin/braid-runtime.ts index 0796619..36be042 100644 --- a/src/bin/braid-runtime.ts +++ b/src/bin/braid-runtime.ts @@ -9,12 +9,12 @@ import { SystemClock } from '../ports/clock.js' import type { StartupPreview } from '../startup/preview-runtime.js' import { PRODUCT_DEMO_CONNECTION, PRODUCT_DEMO_PROFILE } from '../testing/product-demo-fixture.js' import type { CliOptions } from './args.js' -import { productionActiveProfile } from './production-active-profile.js' import { activateProductionConnection, openProductionApplication, } from './production-application.js' -import { loadProductionSetup, type ProductionStartupSetup } from './production-setup.js' +import { loadProductionProfileCatalog, loadProductionSetup } from './production-setup.js' +import type { ProductionStartupSetup } from './production-setup-types.js' import { loadProductionStartup, ProductionStartupError, @@ -24,9 +24,10 @@ import { createRuntimeStartupOptions } from './runtime-startup-options.js' import { defaultStatePath } from './state-path.js' export async function runBraid(options: CliOptions): Promise { const workspace = resolve(options.workspace) - const previewRuntime = usesInteractiveTerminal(options) - ? import('../startup/preview-runtime.js') - : undefined + const previewRuntime = + options.mode === 'tui' && !options.plain && process.stdin.isTTY && process.stdout.isTTY + ? import('../startup/preview-runtime.js') + : undefined const opened = await openApplication(options, workspace) const active = { current: { @@ -68,9 +69,6 @@ export async function runBraid(options: CliOptions): Promise { await active.current.close() } } -function usesInteractiveTerminal(options: CliOptions): boolean { - return options.mode === 'tui' && !options.plain && process.stdin.isTTY && process.stdout.isTTY -} async function openApplication( options: CliOptions, workspace: string, @@ -109,7 +107,9 @@ async function openApplication( const connectionId = startupOptions.connectionId ?? (restoredConnectionAvailable ? restoredConnectionId : production.connectionId) + let profiles: Awaited> try { + profiles = await loadProductionProfileCatalog(startupOptions, production, connectionId) await activateProductionConnection(configured.app, connectionId, production.connections) } catch (activationError) { await configured.close().catch(() => undefined) @@ -119,7 +119,7 @@ async function openApplication( ...configured, startupOptions, profileConnectionOptions: { - profiles: [productionActiveProfile(production.profile)], + profiles, connections: production.connections, ...(production.connectionOptions === undefined ? {} diff --git a/src/bin/production-profile-projection.ts b/src/bin/production-profile-projection.ts index 313bc01..cc7d57f 100644 --- a/src/bin/production-profile-projection.ts +++ b/src/bin/production-profile-projection.ts @@ -20,7 +20,7 @@ import type { ProductionStartupLoadOptions } from './production-startup.js' interface CompatibleBridgeModel { readonly route: string readonly runner: HarnessType - readonly provider: string + readonly provider?: string readonly model: string } @@ -65,7 +65,10 @@ function compatibleModel(model: BridgeModel): CompatibleBridgeModel | undefined const profile = validateProfileShape({ name: 'Braid model candidate', harness: candidate.runner, - model: { default: candidate.model, provider: candidate.provider }, + model: { + default: candidate.model, + ...(candidate.provider === undefined ? {} : { provider: candidate.provider }), + }, }) if (!profile.ok || profile.profile?.harness === undefined) return undefined return bridgeRunnerSupportsModel(profile.profile.harness, candidate.model) ? candidate : undefined @@ -88,8 +91,6 @@ function hasRunnableTarget(profile: Readonly): boolean { const model = profile.model?.default?.trim() return ( profile.harness !== undefined && - profile.model?.provider !== undefined && - profile.model.provider.trim().length > 0 && model !== undefined && model.length > 0 && bridgeRunnerSupportsModel( @@ -103,16 +104,14 @@ function targetKey(profile: Readonly): string | undefined { const runner = profile.harness const model = profile.model?.default?.trim() const provider = profile.model?.provider?.trim() - if ( - runner === undefined || - provider === undefined || - provider.length === 0 || - model === undefined || - model.length === 0 - ) { + if (runner === undefined || model === undefined || model.length === 0) { return undefined } - return materializeBridgeModelRoute(runner, model, profile.model?.provider) + return materializeBridgeModelRoute( + runner, + model, + provider === undefined || provider.length === 0 ? undefined : provider, + ) } function generatedProfile( @@ -126,7 +125,7 @@ function generatedProfile( harness: candidate.runner, model: { default: candidate.model, - provider: candidate.provider, + ...(candidate.provider === undefined ? {} : { provider: candidate.provider }), ...(options.effort === undefined ? {} : { reasoningEffort: options.effort }), }, } diff --git a/src/bin/production-setup-discovery.ts b/src/bin/production-setup-discovery.ts index 1ea40cc..3fda67e 100644 --- a/src/bin/production-setup-discovery.ts +++ b/src/bin/production-setup-discovery.ts @@ -5,6 +5,8 @@ import { } from '../adapters/connections/production-connection-endpoints.js' import { DEFAULT_TANGLE_SANDBOX_ENDPOINT } from '../adapters/connections/production-connection-types.js' import { discoverProfiles } from '../app/profiles.js' +import type { ProfileRecord } from '../app/profile-types.js' +import type { ProductionCompositionConfig } from '../app/production-composition.js' import type { ConnectionRecord } from '../domain/entities.js' import { createConnectionId } from '../domain/ids.js' import { @@ -14,6 +16,7 @@ import { import { discoverBridge } from './production-bridge-discovery.js' import { recoverPendingConnectionCredentialRemoval } from './production-connection-credential-cleanup.js' import { productionConfigPath, resolveProductionDatabaseKeyFile } from './production-key-path.js' +import { productionActiveProfile } from './production-active-profile.js' import { projectSetupProfiles, trustedProfileSources } from './production-profile-projection.js' import { recoverPendingProductionCredential } from './production-setup-credentials.js' import type { ProductionStartupSetup } from './production-setup-types.js' @@ -146,3 +149,42 @@ export async function loadProductionSetup( }, } } + +/** Rebuilds the profile picker after restart without changing the active profile. */ +export async function loadProductionProfileCatalog( + options: ProductionStartupLoadOptions, + production: ProductionCompositionConfig, + connectionId: string, +): Promise { + const connection = production.connections.find((record) => record.id === connectionId) + if (connection === undefined) { + throw new ProductionStartupError( + 'PRODUCTION_CONNECTION_REQUIRED', + 'The selected production connection does not exist', + ) + } + const discovered = await discoverProfiles({ + explicit: trustedProfileSources(options), + resolverContext: { workspaceRoot: options.workspace }, + }) + const active = productionActiveProfile(production.profile) + const seenDigests = new Set([active.digest]) + const profiles = [ + active, + ...discovered.profiles.filter((record) => { + if (seenDigests.has(record.digest)) return false + seenDigests.add(record.digest) + return true + }), + ] + const merged = { profiles, issues: discovered.issues } + if (connection.kind !== 'cli-bridge') { + return projectSetupProfiles(options, connection.endpoint ?? '', [], merged).profiles + } + const endpoint = canonicalBridgeEndpoint({ + ...options, + ...(connection.endpoint === undefined ? {} : { cliBridgeEndpoint: connection.endpoint }), + }) + const bridge = await discoverBridge(options, endpoint) + return projectSetupProfiles(options, endpoint, bridge.models, merged).profiles +} diff --git a/src/bin/production-setup.ts b/src/bin/production-setup.ts index d21488e..7778710 100644 --- a/src/bin/production-setup.ts +++ b/src/bin/production-setup.ts @@ -6,6 +6,7 @@ export { } from './production-setup-credentials.js' export { DEFAULT_CLI_BRIDGE_ENDPOINT, + loadProductionProfileCatalog, loadProductionSetup, } from './production-setup-discovery.js' export type { ProductionStartupPersistence } from './production-setup-persistence.js' diff --git a/src/domain/bounded-structured.ts b/src/domain/bounded-structured.ts index 058a370..df48acb 100644 --- a/src/domain/bounded-structured.ts +++ b/src/domain/bounded-structured.ts @@ -24,6 +24,8 @@ const SAFE_NUMERIC_FIELDS = new Set([ 'tokensoutput', 'maxtokens', 'maxcompletiontokens', + 'maxoutputtokens', + 'maxreasoningtokens', 'mintokens', 'tokencount', 'tokenestimate', diff --git a/src/domain/invariants-run.ts b/src/domain/invariants-run.ts index fb11473..838faf3 100644 --- a/src/domain/invariants-run.ts +++ b/src/domain/invariants-run.ts @@ -53,7 +53,6 @@ export function assertRunRecord(record: RunRecord): void { if (record.controlRef !== undefined) { const parsed = AgentExactRunControlRefSchema.safeParse(record.controlRef) if (!parsed.success) fail('run.controlRef is invalid') - if (record.controlRef.runId !== record.id) fail('run.controlRef.runId must match run.id') if ( record.providerSessionId !== undefined && record.controlRef.sessionId !== record.providerSessionId diff --git a/src/installed-package-version.ts b/src/installed-package-version.ts new file mode 100644 index 0000000..5b3939d --- /dev/null +++ b/src/installed-package-version.ts @@ -0,0 +1,39 @@ +import { readFileSync } from 'node:fs' +import { createRequire } from 'node:module' +import { dirname, join } from 'node:path' + +interface PackageDocument { + readonly name?: unknown + readonly version?: unknown +} + +/** Read package metadata beside the installed entry point without trusting a version constant. */ +export function installedPackageVersion(packageName: string): string { + const require = createRequire(import.meta.url) + try { + let directory = dirname(require.resolve(packageName)) + for (let depth = 0; depth < 4; depth += 1) { + try { + const document = JSON.parse( + readFileSync(join(directory, 'package.json'), 'utf8'), + ) as PackageDocument + if ( + document.name === packageName && + typeof document.version === 'string' && + document.version.length > 0 + ) { + return document.version + } + } catch (error) { + const code = error instanceof Error && 'code' in error ? error.code : undefined + if (code !== 'ENOENT') throw error + } + const parent = dirname(directory) + if (parent === directory) break + directory = parent + } + } catch { + // Evidence must say unavailable when installed metadata cannot be verified. + } + return 'unavailable' +} diff --git a/src/startup/preview-runtime.ts b/src/startup/preview-runtime.ts index d279217..038a6bc 100644 --- a/src/startup/preview-runtime.ts +++ b/src/startup/preview-runtime.ts @@ -5,9 +5,8 @@ import { TuiMainScreen } from '@earendil-works/pi-tui/dist/tui-main-screen.js' import { Text } from '@earendil-works/pi-tui/dist/components/text.js' import { AlternateScreenTerminal } from '../adapters/tui/alternate-screen-terminal.js' import { boundVisibleText } from '../views/shared/sanitize.js' -import { composerBorderLine } from '../views/tui/composer-view.js' import { installTerminalOutputPolicy } from '../views/tui/terminal-compatibility.js' -import { fitTerminalColumns, renderTerminalIdentity } from '../views/tui/terminal-identity.js' +import { renderTerminalContext } from '../views/tui/terminal-identity.js' import { createBraidTheme, type BraidTheme } from '../views/tui/theme.js' import { createTerminalSignalLatch, type TerminalSignalExitCode } from './terminal-signal-latch.js' @@ -65,7 +64,7 @@ export interface StartupPreviewHandoff { } class StartupFrame implements Component { - readonly #identity: Parameters[1] + readonly #identity: Parameters[1] readonly #transcript: Text readonly #theme: BraidTheme readonly #rows: () => number @@ -106,7 +105,7 @@ class StartupFrame implements Component { } this.#theme = theme this.#rows = rows - this.#status = status === 'ready' ? 'ready for a message' : status + this.#status = status const messages = state.messages .filter((message) => message.branchId === state.branchId && message.status !== 'redacted') .slice(-MAX_PREVIEW_MESSAGES) @@ -115,8 +114,7 @@ class StartupFrame implements Component { ) .join('\n\n') this.#transcript = new Text( - messages || - `Send a task to ${state.profile.name ?? 'this AgentProfile'}, or press Ctrl+P for commands.`, + messages || `Ask ${state.profile.name ?? 'this AgentProfile'} anything.`, 1, 1, ) @@ -148,26 +146,20 @@ class StartupFrame implements Component { render(width: number): string[] { const rows = Math.max(1, Math.floor(this.#rows())) - const top = renderTerminalIdentity(this.#theme, this.#identity, width) - const footer = fitTerminalColumns( - [this.#theme.success(this.#status)], - [this.#theme.text('Ctrl+P commands')], + const footer = renderTerminalContext( + this.#theme, + this.#identity, + [this.#theme.success(this.#status), this.#theme.text('Ctrl+P commands')], width, ) - const composer = [ - composerBorderLine(width, '› send', this.#theme), - `${this.#theme.accent('›')}${CURSOR_MARKER}`, - '', - '', - composerBorderLine(width, 'alt+enter newline · paste', this.#theme), - ] - const contentRows = Math.max(0, rows - top.length - composer.length - 1) + const composer = [`${this.#theme.accent('›')}${CURSOR_MARKER}`, '', ''] + const contentRows = Math.max(0, rows - composer.length - 1) const transcript = contentRows === 0 ? [] : this.#transcript.render(width).slice(-contentRows) const content = [ ...transcript, ...Array.from({ length: Math.max(0, contentRows - transcript.length) }, () => ''), ] - return [...top, ...content, ...composer, footer].slice(-rows) + return [...content, ...composer, footer].slice(-rows) } } diff --git a/src/testing/deterministic-backend.ts b/src/testing/deterministic-backend.ts index 58ee09b..1226681 100644 --- a/src/testing/deterministic-backend.ts +++ b/src/testing/deterministic-backend.ts @@ -6,7 +6,7 @@ import { isProductDemoProfile } from './product-demo-fixture.js' function responseFor(profile: Readonly, text: string): string { if (isProductDemoProfile(profile)) { - return 'Route confirmed. Your AgentProfile stays intact while agent-runtime sends this turn through Pi over Local CLI Bridge. Braid keeps the conversation, forks, approvals, and trace analysis in one place.' + return 'Pi will run the task through Local CLI Bridge with gpt-5.6-luna. The Release engineer AgentProfile supplies its instructions and permissions. Braid keeps the transcript, approvals, branches, and trace analysis.' } const runner = profile.harness ?? 'runtime default' return `Fixture response through ${runner}: ${text}` diff --git a/src/views/shared/analysis-model-call-presentation.ts b/src/views/shared/analysis-model-call-presentation.ts index f7a2a63..7e89d34 100644 --- a/src/views/shared/analysis-model-call-presentation.ts +++ b/src/views/shared/analysis-model-call-presentation.ts @@ -37,9 +37,9 @@ export function analysisModelCallView(record: AnalysisModelCallRecord): Analysis } export function analysisModelCallLine(call: AnalysisModelCallView): string { - const provider = call.provider ?? 'provider unknown' + const route = call.provider === undefined ? call.model : `${call.provider}/${call.model}` const latency = call.latencyMs === undefined ? 'latency unknown' : `latency ${call.latencyMs}ms` - return `#${call.sequence} ${provider}/${call.model} · ${tokens(call)} · ${cost(call)} · ${latency}` + return `#${call.sequence} ${route} · ${tokens(call)} · ${cost(call)} · ${latency}` } export function analysisModelCallSummary(calls: readonly AnalysisModelCallView[]): string { diff --git a/src/views/shared/analysis-presentation.ts b/src/views/shared/analysis-presentation.ts index b6f9651..0cb75e5 100644 --- a/src/views/shared/analysis-presentation.ts +++ b/src/views/shared/analysis-presentation.ts @@ -27,6 +27,9 @@ export function analysisViewForRecord(record: AnalysisRecord): AnalysisView { })), ) const costFooter = analysisCostFooter(record) + const citationCheck = (record.checks ?? record.provenance?.checks ?? []).find( + (check) => check.id === 'citation-support', + ) return { source: String(record.source.digest), ...(record.question === undefined ? {} : { question: sanitizeTerminalText(record.question) }), @@ -42,6 +45,12 @@ export function analysisViewForRecord(record: AnalysisRecord): AnalysisView { citationIds: finding.citations.map((citation) => String(citation.id)), })), citations, + citationSupport: { + status: citationCheck?.status ?? 'unavailable', + supportedFindings: record.findings.filter( + (finding) => finding.supported && finding.citations.length > 0, + ).length, + }, execution: analysisExecutionView(record), footer: [ { label: 'source complete', value: record.source.complete ? 'yes' : 'no' }, diff --git a/src/views/shared/models.ts b/src/views/shared/models.ts index dc60762..cabe29c 100644 --- a/src/views/shared/models.ts +++ b/src/views/shared/models.ts @@ -301,6 +301,7 @@ export interface InteractionView { readonly responseScopes: readonly import('../../domain/entities-interactions.js').AutomationRuleScope[] readonly remainingMs?: number readonly queuePosition: number + readonly queueTotal: number readonly secret: boolean readonly providerSession?: string } @@ -391,6 +392,8 @@ export interface EntityDetailView { readonly status: string readonly lines: readonly string[] readonly analysisFindingCount?: number + readonly analysisSupportedFindingCount?: number + readonly analysisCitationSupport?: 'passed' | 'failed' | 'unavailable' readonly analysisExecution?: AnalysisExecutionView } @@ -467,6 +470,10 @@ export interface AnalysisView { readonly eventId: string readonly text: string }[] + readonly citationSupport: { + readonly status: 'passed' | 'failed' | 'unavailable' + readonly supportedFindings: number + } readonly execution?: AnalysisExecutionView readonly footer: readonly { readonly label: string; readonly value: string }[] readonly error?: string diff --git a/src/views/shared/usage-projection.ts b/src/views/shared/usage-projection.ts index d0d4f42..c5c3383 100644 --- a/src/views/shared/usage-projection.ts +++ b/src/views/shared/usage-projection.ts @@ -1,29 +1,21 @@ import type { BraidState } from '../../domain/state.js' -import type { - SessionUsageView, - UsageMeasurementStatus, - UsageTotalsView, - UsageView, +import { + freezeView, + type SessionUsageView, + type UsageMeasurementStatus, + type UsageTotalsView, + type UsageView, } from './models.js' -export function usageForRun(run: BraidState['runs'][number], elapsedMs?: number): UsageView { - return { - input: run.inputTokens, - output: run.outputTokens, - ...(run.reasoningTokens === undefined ? {} : { reasoning: run.reasoningTokens }), - tokenStatus: tokenStatus(run.tokensKnown, run.inputTokens, run.outputTokens, run.llmCalls), - ...(run.costUsd === undefined ? {} : { costUsd: run.costUsd }), - ...(run.estimatedCostUsd === undefined ? {} : { estimatedCostUsd: run.estimatedCostUsd }), - costStatus: costStatus(run.usdKnown, run.costUsd, run.estimatedCostUsd), - ...(run.promptCache === undefined ? {} : { promptCache: run.promptCache }), - ...(run.llmCalls === undefined ? {} : { llmCalls: run.llmCalls }), - ...(run.llmLatencyMs === undefined ? {} : { llmLatencyMs: run.llmLatencyMs }), - ...(run.model === undefined ? {} : { model: run.model }), - ...(elapsedMs === undefined ? {} : { elapsedMs }), - } -} +const sessionUsageCache = new WeakMap() +/* + * Application state is immutable and receives a new identity for each revision. + * Retain its complete usage projection so idle renders do not rescan worker history. + */ export function sessionUsageFor(state: BraidState): SessionUsageView { + const cached = sessionUsageCache.get(state) + if (cached !== undefined) return cached const runs = state.runs.filter((run) => run.conversationId === state.conversationId) const analyses = state.analyses.filter( (analysis) => analysis.source.conversationId === state.conversationId, @@ -34,11 +26,30 @@ export function sessionUsageFor(state: BraidState): SessionUsageView { ) const supervisorIds = new Set(supervisors.map((supervisor) => String(supervisor.id))) const workers = state.workers.filter((worker) => supervisorIds.has(String(worker.supervisorId))) - return { + const usage = freezeView({ turns: totalsForRuns(runs), analyses: totalsForAnalyses(analyses), delegated: totalsForWorkers(workers), attribution: supervisors.length === 0 && analyses.length === 0 ? 'complete' : 'separate-totals', + } satisfies SessionUsageView) + sessionUsageCache.set(state, usage) + return usage +} + +export function usageForRun(run: BraidState['runs'][number], elapsedMs?: number): UsageView { + return { + input: run.inputTokens, + output: run.outputTokens, + ...(run.reasoningTokens === undefined ? {} : { reasoning: run.reasoningTokens }), + tokenStatus: tokenStatus(run.tokensKnown, run.inputTokens, run.outputTokens, run.llmCalls), + ...(run.costUsd === undefined ? {} : { costUsd: run.costUsd }), + ...(run.estimatedCostUsd === undefined ? {} : { estimatedCostUsd: run.estimatedCostUsd }), + costStatus: costStatus(run.usdKnown, run.costUsd, run.estimatedCostUsd), + ...(run.promptCache === undefined ? {} : { promptCache: run.promptCache }), + ...(run.llmCalls === undefined ? {} : { llmCalls: run.llmCalls }), + ...(run.llmLatencyMs === undefined ? {} : { llmLatencyMs: run.llmLatencyMs }), + ...(run.model === undefined ? {} : { model: run.model }), + ...(elapsedMs === undefined ? {} : { elapsedMs }), } } diff --git a/src/views/tui/activity-browser.ts b/src/views/tui/activity-browser.ts index 1a6ab43..fd5ade3 100644 --- a/src/views/tui/activity-browser.ts +++ b/src/views/tui/activity-browser.ts @@ -1,12 +1,6 @@ import { matchesKey } from '@earendil-works/pi-tui' import { effectiveElapsedMs, formatDuration } from '../shared/duration.js' -import type { - BraidViewModel, - EntityDetailView, - RunView, - UsageMeasurementStatus, - UsageTotalsView, -} from '../shared/models.js' +import type { BraidViewModel, EntityDetailView, RunView } from '../shared/models.js' import { sanitizeTerminalText } from '../shared/sanitize.js' import { projectActivityDocument, type ActivityDocumentItem } from './activity-document.js' import { executionTargetFor } from './execution-target.js' @@ -15,6 +9,7 @@ import { type EntityBrowserDocument, type EntityBrowserRow, } from './entity-browser.js' +import { metricsFor } from './terminal-usage.js' import type { BraidTheme } from './theme.js' export type ActivityBrowserScope = 'all' | 'runs' | 'analyses' | 'workers' @@ -82,9 +77,10 @@ export function activityDocument( .slice() .reverse() const target = executionTargetFor(view) + const usage = metricsFor(view) return { title: scope === 'all' ? 'activity' : `activity · ${scope}`, - context: `${target.profileName} · ${target.runner} · ${target.model} · ${sessionUsageLabel(view)}`, + context: [target.profileName, target.runner, target.model, ...usage].join(' · '), filterHint: `tab filter: ${scope}`, ...(pinned === undefined ? {} : { pinned }), ...(notice === undefined ? {} : { notice }), @@ -145,15 +141,12 @@ function runContext( const usage = item.usage const tokenPrefix = usage?.tokenStatus === 'complete' ? '' : '≥' const noObservedTokens = (usage?.input ?? 0) === 0 && (usage?.output ?? 0) === 0 - const tokenUsageUnknown = usage?.tokenStatus !== 'complete' && noObservedTokens + const hideTokens = usage?.tokenStatus !== 'complete' && noObservedTokens const cost = costLabel(usage) const cached = Object.values(usage?.promptCache ?? {}).reduce((total, value) => total + value, 0) const metrics = [ - ...(tokenUsageUnknown ? ['tokens unknown'] : []), - ...(usage?.input === undefined || tokenUsageUnknown ? [] : [`${tokenPrefix}${usage.input} in`]), - ...(usage?.output === undefined || tokenUsageUnknown - ? [] - : [`${tokenPrefix}${usage.output} out`]), + ...(usage?.input === undefined || hideTokens ? [] : [`${tokenPrefix}${usage.input} in`]), + ...(usage?.output === undefined || hideTokens ? [] : [`${tokenPrefix}${usage.output} out`]), ...(usage?.reasoning === undefined ? [] : [`${usage.reasoning} reasoning`]), ...(cached === 0 ? [] : [`${cached} cached`]), ...(cost === undefined ? [] : [cost]), @@ -182,144 +175,50 @@ function runContext( ? [] : [`max output tokens: ${target.maxOutputTokens}`]), ...(metrics.length === 0 ? [] : [`usage: ${metrics.join(' · ')}`]), - ...(usage === undefined - ? [] - : [`model calls: ${usage.llmCalls === undefined ? 'unknown' : usage.llmCalls}`]), - ...(usage === undefined - ? [] - : [ - `model latency: ${ - usage.llmLatencyMs === undefined ? 'unknown' : `${Math.round(usage.llmLatencyMs)}ms` - }`, - ]), - ...(usage?.tokenStatus === undefined ? [] : [`token measurement: ${usage.tokenStatus}`]), - ...(usage?.costStatus === undefined ? [] : [`cost measurement: ${usage.costStatus}`]), + ...measurementValue('model calls', usage?.llmCalls, usage !== undefined), + ...measurementValue( + 'model latency', + usage?.llmLatencyMs, + usage !== undefined, + (value) => `${Math.round(value)}ms`, + ), + ...measurementStatus('token measurement', usage?.tokenStatus), + ...measurementStatus('cost measurement', usage?.costStatus), `history: ${sanitizeTerminalText(run.completeness)}`, ...(run.error === undefined ? [] : [`! ${sanitizeTerminalText(run.error)}`]), ] } +function measurementValue( + label: string, + value: number | undefined, + measurementExists: boolean, + format: (value: number) => string = (measured) => String(measured), +): readonly string[] { + if (value !== undefined) return [`${label}: ${format(value)}`] + return measurementExists ? [`${label}: not reported`] : [] +} + +function measurementStatus(label: string, status: string | undefined): readonly string[] { + if (status === undefined) return [] + return [`${label}: ${status === 'unknown' ? 'not reported' : status}`] +} + function costLabel(usage: RunView['usage']): string | undefined { if (usage === undefined) return undefined if (usage.costStatus === 'reported' && usage.costUsd !== undefined) { return `$${usage.costUsd.toFixed(4)}` } if (usage.costStatus === 'observed-floor' && usage.costUsd !== undefined) { - return usage.costUsd > 0 ? `≥$${usage.costUsd.toFixed(4)}` : 'cost unknown' + return usage.costUsd > 0 ? `≥$${usage.costUsd.toFixed(4)}` : undefined } - if (usage.estimatedCostUsd !== undefined && usage.estimatedCostUsd > 0) - return `~$${usage.estimatedCostUsd.toFixed(4)}` - return usage.costStatus === 'unknown' ? 'cost unknown' : undefined -} - -function sessionUsageLabel(view: BraidViewModel): string { - const turns = view.sessionUsage.turns - if (hasMeasurementTelemetry(view.sessionUsage)) { - return [ - usageGroup('turns', turns, true), - usageGroup('analysis', view.sessionUsage.analyses, false), - usageGroup('workers', view.sessionUsage.delegated, false), - ] - .filter((value): value is string => value !== undefined) - .join(' · ') - } - const prefix = turns.tokenStatus === 'complete' ? '' : '≥' - const tokens = - turns.tokenStatus !== 'complete' && (turns.input ?? 0) === 0 && (turns.output ?? 0) === 0 - ? 'tokens unknown' - : `${prefix}${turns.input ?? 0}/${prefix}${turns.output ?? 0} tok` - const directCost = costLabel(turns) - const delegated = view.sessionUsage.delegated - const delegatedCost = costLabel(delegated) - const analyses = view.sessionUsage.analyses - const analysisCost = costLabel(analyses) - return [ - tokens, - ...(directCost === undefined ? [] : [directCost]), - ...((turns.llmCalls ?? 0) === 0 ? [] : [`${turns.llmCalls} calls`]), - ...((turns.llmLatencyMs ?? 0) === 0 ? [] : [`${Math.round(turns.llmLatencyMs ?? 0)}ms model`]), - ...(delegated.sourceCount === 0 ? [] : [`workers ${delegatedCost ?? 'cost unknown'}`]), - ...(analyses.sourceCount === 0 ? [] : [`analysis ${analysisCost ?? 'cost unknown'}`]), - ].join(' · ') -} - -function hasMeasurementTelemetry(view: BraidViewModel['sessionUsage']): boolean { - return [view.turns, view.analyses, view.delegated].some(hasMeasurementFields) -} - -function hasMeasurementFields(usage: UsageTotalsView): boolean { - return ( - usage.callStatus !== undefined || - usage.latencyStatus !== undefined || - usage.unknownCallSources !== undefined || - usage.unknownLatencySources !== undefined + if ( + usage.costStatus === 'estimated' && + usage.estimatedCostUsd !== undefined && + usage.estimatedCostUsd >= 0 ) -} - -function usageGroup( - label: string, - usage: UsageTotalsView, - includeEmpty: boolean, -): string | undefined { - if (!includeEmpty && usage.sourceCount === 0) return undefined - const metrics = tokenMetrics(usage) - const cost = costLabel(usage) - if (cost !== undefined) metrics.push(cost) - const calls = measurementMetric( - 'calls', - usage.llmCalls, - usage.callStatus, - usage.unknownCallSources, - usage.sourceCount, - (value) => String(Math.round(value)), - ) - const latency = measurementMetric( - 'latency', - usage.llmLatencyMs, - usage.latencyStatus, - usage.unknownLatencySources, - usage.sourceCount, - (value) => `${Math.round(value)}ms`, - ) - if (calls !== undefined) metrics.push(calls) - if (latency !== undefined) metrics.push(latency) - return metrics.length === 0 ? undefined : `${label} ${metrics.join(' · ')}` -} - -function tokenMetrics(usage: UsageTotalsView): string[] { - const tokenPrefix = usage.tokenStatus === 'complete' ? '' : '≥' - const noObservedTokens = (usage.input ?? 0) === 0 && (usage.output ?? 0) === 0 - const tokenUsageUnknown = usage.tokenStatus !== 'complete' && noObservedTokens - if (tokenUsageUnknown) return ['usage unknown'] - return [ - ...(usage.input === undefined ? [] : [`in ${tokenPrefix}${compactNumber(usage.input)}`]), - ...(usage.output === undefined ? [] : [`out ${tokenPrefix}${compactNumber(usage.output)}`]), - ] -} - -function measurementMetric( - label: string, - value: number | undefined, - status: UsageMeasurementStatus | undefined, - unknownSources: number | undefined, - sourceCount: number, - format: (value: number) => string, -): string | undefined { - if (status === undefined && value === undefined && unknownSources === undefined) return undefined - if (sourceCount === 0 && value === undefined && (unknownSources ?? 0) === 0) return undefined - const missing = Math.max(0, unknownSources ?? (value === undefined ? 1 : 0)) - if (value === undefined || status === 'unknown') { - return `${label} unknown${missing > 0 ? ` (${missing} missing)` : ''}` - } - const prefix = status === 'partial' ? '≥' : '' - const suffix = status === 'partial' && missing > 0 ? ` (+${missing} missing)` : '' - return `${label} ${prefix}${format(value)}${suffix}` -} - -function compactNumber(value: number): string { - if (value < 1_000) return String(Math.round(value)) - if (value < 1_000_000) return `${(value / 1_000).toFixed(value < 10_000 ? 1 : 0)}k` - return `${(value / 1_000_000).toFixed(value < 10_000_000 ? 1 : 0)}m` + return `~$${usage.estimatedCostUsd.toFixed(4)}` + return undefined } function activityContext( diff --git a/src/views/tui/activity.ts b/src/views/tui/activity.ts index 8af291c..f0cee4b 100644 --- a/src/views/tui/activity.ts +++ b/src/views/tui/activity.ts @@ -13,15 +13,15 @@ export class ActivityView extends Container { } setView(view: BraidViewModel): void { - const document = projectActivityDocument(view) + const items = projectActivityDocument(view).items.filter(isLiveWork) this.clear() - this.addChild(new Text(this.#theme.brand('activity'), 1, 0)) - if (document.items.length === 0) { + this.addChild(new Text(this.#theme.brand('live work'), 1, 0)) + if (items.length === 0) { this.addChild(new Spacer(1)) - this.addChild(new Text(this.#theme.muted('No recorded activity.'), 1, 0)) + this.addChild(new Text(this.#theme.muted('No live work.'), 1, 0)) } else { this.addChild(new Spacer(1)) - for (const item of document.items) this.addChild(this.#item(item)) + for (const item of items) this.addChild(this.#item(item)) } this.invalidate() } @@ -30,23 +30,25 @@ export class ActivityView extends Container { const status = sanitizeTerminalText(item.status) const detail = item.summary !== item.title ? ` · ${sanitizeTerminalText(item.summary)}` : '' const title = sanitizeTerminalText(item.title) - const color = - item.status === 'failed' || item.status === 'storage-failure' - ? this.#theme.danger - : item.status === 'running' - ? this.#theme.warning - : item.status === 'complete' || item.status === 'completed' - ? this.#theme.success - : this.#theme.muted - const symbol = - item.status === 'failed' || item.status === 'storage-failure' - ? 'x' - : item.status === 'running' - ? '>' - : item.status === 'complete' || item.status === 'completed' - ? 'ok' - : '·' + const active = item.status === 'running' || item.status === 'streaming' + const color = active ? this.#theme.warning : this.#theme.muted + const symbol = active ? '>' : '·' const elapsed = item.durationMs === undefined ? '' : ` ${Math.round(item.durationMs)}ms` return new TruncatedText(`${color(`${symbol} ${status}`)} ${title}${elapsed}${detail}`, 1, 0) } } + +const LIVE_WORK_STATUSES = new Set([ + 'loading', + 'starting', + 'streaming', + 'running', + 'waiting', + 'detached', + 'reconnecting', + 'cancelling', +]) + +function isLiveWork(item: ActivityDocumentItem): boolean { + return LIVE_WORK_STATUSES.has(item.status) +} diff --git a/src/views/tui/composer-view.ts b/src/views/tui/composer-view.ts index 0e8313d..9c63cde 100644 --- a/src/views/tui/composer-view.ts +++ b/src/views/tui/composer-view.ts @@ -2,6 +2,7 @@ import { Container, CURSOR_MARKER, type Editor, + stripTerminalSequences, truncateToWidth, visibleWidth, } from '@earendil-works/pi-tui' @@ -11,6 +12,8 @@ import type { BraidTheme } from './theme.js' const MIN_USABLE_ROWS = 3 const COMPOSER_FRACTION = 0.4 +export type ComposerMode = 'queue' | 'steer' + type ComposerSourceView = Pick< BraidViewModel, 'activeRunId' | 'capabilities' | 'queue' | 'queueCount' @@ -30,7 +33,10 @@ export interface ComposerViewOptions { } /** Projects the existing controller view into composer-only display state. */ -export function composerProjectionFor(view: ComposerSourceView): ComposerProjection { +export function composerProjectionFor( + view: ComposerSourceView, + preferredMode: ComposerMode = 'queue', +): ComposerProjection { const active = view.activeRunId !== undefined if (!active) { const send = view.capabilities['run.send']?.available === true @@ -43,18 +49,23 @@ export function composerProjectionFor(view: ComposerSourceView): ComposerProject const queue = view.capabilities['run.queue']?.available === true const steer = view.capabilities['run.steer']?.available === true + const action = + preferredMode === 'steer' && steer ? 'steer' : queue ? 'queue' : steer ? 'steer' : 'unavailable' const queuePosition = queue ? nextQueuePosition(view) : undefined const queueLabel = queuePosition === undefined ? 'queue' : `queue next #${String(queuePosition)}` - const actionLabel = queue - ? steer - ? `${queueLabel} · steer /steer` - : queueLabel - : steer - ? 'steer /steer' - : 'queue unavailable' + const actionLabel = + action === 'steer' + ? queue + ? 'steer now · Alt+S queue' + : 'steer now' + : action === 'queue' + ? steer + ? `${queueLabel} · Alt+S steer` + : queueLabel + : 'input unavailable' return { - action: queue ? 'queue' : steer ? 'steer' : 'unavailable', + action, actionLabel, ...(queuePosition === undefined ? {} : { queuePosition }), hint: 'alt+enter newline · paste', @@ -80,6 +91,7 @@ export class ComposerView extends Container { actionLabel: 'send', hint: 'alt+enter newline · paste', } + #mode: ComposerMode = 'queue' constructor(options: ComposerViewOptions) { super() @@ -93,34 +105,36 @@ export class ComposerView extends Container { return this.#editor } + setMode(mode: ComposerMode): void { + this.#mode = mode + } + setView(view: ComposerSourceView): void { - this.#projection = composerProjectionFor(view) + this.#projection = composerProjectionFor(view, this.#mode) this.invalidate() } override render(width: number): string[] { - const lines = boundedEditorLines(this.#editor.render(width), this.#rows()) - if (lines.length === 0) return lines - const last = lines.length - 1 - lines[0] = composerBorderLine(width, `› ${this.#projection.actionLabel}`, this.#theme) - if (last > 1) lines[1] = promptBodyLine(lines[1], this.#theme) - if (last > 0) lines[last] = composerBorderLine(width, this.#projection.hint, this.#theme) - return lines + const lines = this.#editor.render(width) + if (lines.length === 0) return [] + const bottomBorder = editorBottomBorder(lines, width) + const body = boundedEditorBody(lines.slice(1, bottomBorder), this.#rows(), width) + if (body.length > 0) { + body[0] = promptBodyLine(body[0], this.#theme, promptActionLabel(this.#projection)) + } + const autocomplete = lines.slice(bottomBorder + 1) + if (autocomplete.length === 0) return body + return [...body, composerBorderLine(width, this.#projection.hint, this.#theme), ...autocomplete] } } -function boundedEditorLines(lines: readonly string[], terminalRows: number): string[] { - if (lines.length === 0) return [] - const budget = composerRowBudget(terminalRows) - const top = lines[0] ?? '' - const bottom = lines.at(-1) ?? '' - const body = lines.slice(1, -1) - const bodyBudget = Math.max(1, budget - 2) +function boundedEditorBody(body: readonly string[], terminalRows: number, width: number): string[] { + const bodyBudget = Math.max(MIN_USABLE_ROWS, composerRowBudget(terminalRows) - 2) const minimumBodyRows = Math.min(MIN_USABLE_ROWS, bodyBudget) const visibleBodyRows = Math.min(bodyBudget, Math.max(minimumBodyRows, body.length)) - const bodyWidth = visibleWidth(body[0] ?? '') + const bodyWidth = Math.max(1, visibleWidth(body[0] ?? ''), width) - if (body.length === visibleBodyRows) return [...lines] + if (body.length === visibleBodyRows) return [...body] const cursorIndex = body.findIndex((line) => line.includes(CURSOR_MARKER)) const lastStart = Math.max(0, body.length - visibleBodyRows) @@ -133,7 +147,18 @@ function boundedEditorLines(lines: readonly string[], terminalRows: number): str ' '.repeat(bodyWidth), ), ] - return [top, ...paddedBody, bottom] + return paddedBody +} + +function editorBottomBorder(lines: readonly string[], width: number): number { + const index = lines.findIndex((line, lineIndex) => lineIndex > 0 && isEditorBorder(line, width)) + return index < 0 ? lines.length - 1 : index +} + +function isEditorBorder(line: string, width: number): boolean { + const plain = stripTerminalSequences(line) + if (visibleWidth(plain) !== Math.max(1, width)) return false + return /^─+$/u.test(plain) || /^─── [↓↑] \d+ more /u.test(plain) } export function composerRowBudget(terminalRows: number): number { @@ -151,7 +176,21 @@ export function composerBorderLine(width: number, label: string, theme: BraidThe return `${theme.editor.borderColor('─'.repeat(left))}${theme.accent(padded)}${theme.editor.borderColor('─'.repeat(right))}` } -function promptBodyLine(line: string | undefined, theme: BraidTheme): string { +function promptActionLabel(projection: ComposerProjection): string | undefined { + if (projection.action === 'send') return undefined + if (projection.action === 'queue') { + return projection.queuePosition === undefined ? 'queue' : `queue #${projection.queuePosition}` + } + if (projection.action === 'steer') return 'steer' + return projection.actionLabel +} + +function promptBodyLine(line: string | undefined, theme: BraidTheme, action?: string): string { if (line === undefined || !line.startsWith(' ')) return line ?? '' - return `${theme.accent('›')}${line.slice(1)}` + const prefix = action === undefined ? theme.accent('›') : theme.warning(`› ${action}`) + return truncateToWidth( + `${prefix}${action === undefined ? '' : ' '}${line.slice(1)}`, + visibleWidth(line), + '', + ) } diff --git a/src/views/tui/fork-preview.ts b/src/views/tui/fork-preview.ts index 6f88350..5301fd7 100644 --- a/src/views/tui/fork-preview.ts +++ b/src/views/tui/fork-preview.ts @@ -23,6 +23,9 @@ export class ForkPreviewPanel extends Container implements Focusable { #focused = false #submitted = false #canConfirm = false + #scrollOffset = 0 + #lastBodyRows = 1 + #lastBodyLength = 0 constructor(theme: BraidTheme, options: ForkPreviewPanelOptions = {}) { super() @@ -44,6 +47,7 @@ export class ForkPreviewPanel extends Container implements Focusable { this.clear() this.#submitted = false this.#canConfirm = false + this.#scrollOffset = 0 this.#error.setText('') const preview = view.forkPreview if (!preview) { @@ -78,18 +82,14 @@ export class ForkPreviewPanel extends Container implements Focusable { : this.#theme.warning('boundary: not reported by the fork plan'), ), ) - const fields = preview.fields.filter( - (field) => field !== boundaryField && !isExecutionField(field.label), - ) - for (const field of fields.slice(0, 3)) { + const fields = preview.fields.filter((field) => field !== boundaryField) + for (const field of fields) { this.addChild( this.#line( `${sanitizeTerminalText(field.label)}: ${sanitizeTerminalText(field.source)} → ${sanitizeTerminalText(field.destination)}`, ), ) } - if (fields.length > 3) - this.addChild(this.#line(this.#theme.muted(`+${fields.length - 3} fields not shown`))) this.addChild(this.#error) if (!this.#canConfirm) { this.addChild( @@ -122,6 +122,7 @@ export class ForkPreviewPanel extends Container implements Focusable { this.#onCancel?.() return } + if (this.#handleNavigation(data)) return if (this.#submitted || !this.#canConfirm || this.#onConfirm === undefined) return if (matchesKey(data, 'enter') || matchesKey(data, 'y')) { this.#submitted = true @@ -130,17 +131,51 @@ export class ForkPreviewPanel extends Container implements Focusable { } override render(width: number): string[] { + const rows = Math.max(4, Math.floor(this.#rows())) + const bodyRows = Math.max(1, rows - 4) + const body = super.render(width) + this.#lastBodyRows = bodyRows + this.#lastBodyLength = body.length + const maximum = Math.max(0, body.length - bodyRows) + this.#scrollOffset = Math.max(0, Math.min(this.#scrollOffset, maximum)) + const visible = body.slice(this.#scrollOffset, this.#scrollOffset + bodyRows) + const position = + body.length > bodyRows + ? `↑/↓ inspect ${this.#scrollOffset + 1}-${this.#scrollOffset + visible.length}/${body.length}` + : undefined + const footer = + position !== undefined && width < 60 + ? this.#canConfirm + ? '↑/↓ inspect · enter/y create · ←/esc' + : '↑/↓ inspect · ←/esc cancel' + : [this.#footer, position].filter(Boolean).join(' · ') return focusedSurfaceLines({ theme: this.#theme, title: this.#title, ...(this.#context === undefined ? {} : { context: this.#context }), - body: super.render(width), - footer: this.#footer, + body: visible, + footer, width, - rows: this.#rows(), + rows, }) } + #handleNavigation(data: string): boolean { + const maximum = Math.max(0, this.#lastBodyLength - this.#lastBodyRows) + const page = Math.max(1, this.#lastBodyRows - 1) + const previous = this.#scrollOffset + if (matchesKey(data, 'up')) this.#scrollOffset -= 1 + else if (matchesKey(data, 'down')) this.#scrollOffset += 1 + else if (matchesKey(data, 'pageUp')) this.#scrollOffset -= page + else if (matchesKey(data, 'pageDown')) this.#scrollOffset += page + else if (matchesKey(data, 'home')) this.#scrollOffset = 0 + else if (matchesKey(data, 'end')) this.#scrollOffset = maximum + else return false + this.#scrollOffset = Math.max(0, Math.min(this.#scrollOffset, maximum)) + if (this.#scrollOffset !== previous) this.invalidate() + return true + } + #line(value: string): TruncatedText { return new TruncatedText(value, 1, 0) } @@ -149,7 +184,3 @@ export class ForkPreviewPanel extends Container implements Focusable { function isBoundaryField(label: string): boolean { return /boundary|context|through|message|turn/iu.test(sanitizeTerminalText(label)) } - -function isExecutionField(label: string): boolean { - return label === 'operation id' || label === 'plan digest' -} diff --git a/src/views/tui/interaction-presentation.ts b/src/views/tui/interaction-presentation.ts index 5b7cb3b..28ce726 100644 --- a/src/views/tui/interaction-presentation.ts +++ b/src/views/tui/interaction-presentation.ts @@ -97,8 +97,10 @@ export function isSecretInteraction(interaction: InteractionView): boolean { export function interactionHeading(interaction: InteractionView): string { const kind = sanitizeTerminalText(interaction.kind).toLocaleLowerCase() || 'interaction' - if (kind === 'plan') return 'plan review' - return kind + const heading = kind === 'plan' ? 'plan review' : kind + return interaction.queueTotal > 1 + ? `${heading} · request ${interaction.queuePosition + 1}/${interaction.queueTotal}` + : heading } export function runContext(interaction: InteractionView): string { @@ -110,12 +112,7 @@ export function runContext(interaction: InteractionView): string { .map((value) => (value === undefined ? '' : sanitizeTerminalText(value))) .filter((value) => value.length > 0) .join(' @ ') - return [ - requester, - `run ${shortIdentifier(interaction.runId)}`, - ...(interaction.queuePosition > 0 ? [`request ${interaction.queuePosition + 1}`] : []), - timeout, - ] + return [requester, `run ${shortIdentifier(interaction.runId)}`, timeout] .filter(Boolean) .join(' · ') } diff --git a/src/views/tui/keyboard.ts b/src/views/tui/keyboard.ts index ba9f547..cd53cf8 100644 --- a/src/views/tui/keyboard.ts +++ b/src/views/tui/keyboard.ts @@ -7,6 +7,7 @@ export type GlobalKeyAction = | 'graph' | 'switcher' | 'activity' + | 'toggleSteer' | 'toggleDetails' | 'help' | 'previousBranch' @@ -39,6 +40,8 @@ const ACTION_ALIASES: Readonly> = Object.freeze( graph: 'graph', switcher: 'switcher', activity: 'activity', + toggleSteer: 'toggleSteer', + 'toggle-steer': 'toggleSteer', toggleDetails: 'toggleDetails', 'toggle-details': 'toggleDetails', help: 'help', @@ -58,6 +61,7 @@ const DEFAULT_KEYMAP: BraidKeymap = Object.freeze({ graph: Object.freeze(['ctrl+g'] as KeyId[]), switcher: Object.freeze(['ctrl+k'] as KeyId[]), activity: Object.freeze(['f2'] as KeyId[]), + toggleSteer: Object.freeze(['alt+s'] as KeyId[]), toggleDetails: Object.freeze(['ctrl+e'] as KeyId[]), help: Object.freeze(['?'] as KeyId[]), previousBranch: Object.freeze(['alt+up'] as KeyId[]), diff --git a/src/views/tui/terminal-app.ts b/src/views/tui/terminal-app.ts index d87cac0..dee767e 100644 --- a/src/views/tui/terminal-app.ts +++ b/src/views/tui/terminal-app.ts @@ -170,6 +170,7 @@ export class BraidTerminalApp { dispatch: (intent, restoreText) => this.#dispatch(intent, restoreText), isStopped: () => this.#stopped, stop: () => this.stop(), + composerMode: () => this.#input.composerMode, }) this.#shell.editor.setAutocompleteProvider(autocomplete) options.tui.addChild(this.#shell) diff --git a/src/views/tui/terminal-chrome.ts b/src/views/tui/terminal-chrome.ts index d8d175d..7042155 100644 --- a/src/views/tui/terminal-chrome.ts +++ b/src/views/tui/terminal-chrome.ts @@ -2,22 +2,18 @@ import type { Component } from '@earendil-works/pi-tui' import type { BraidViewModel } from '../shared/models.js' import { sanitizeNotification } from '../shared/sanitize.js' import { executionTargetFor, type ExecutionTargetView } from './execution-target.js' -import { - cleanTerminalField, - fitTerminalAtomic, - fitTerminalColumns, - renderTerminalIdentity, - terminalValuePart, -} from './terminal-identity.js' +import { fitTerminalAtomic, renderTerminalContext, terminalValuePart } from './terminal-identity.js' import { modeForColumns } from './layout.js' import type { BraidTheme } from './theme.js' import { metricsFor } from './terminal-usage.js' +import type { ComposerMode } from './composer-view.js' export interface TerminalChromeState { readonly view: BraidViewModel readonly quitArmed: boolean readonly activityVisible: boolean readonly navigationHint: string + readonly composerMode: ComposerMode } /** Fixed-height terminal chrome; the transcript never owns these lines. */ @@ -39,29 +35,8 @@ export class TerminalChrome implements Component { return [...this.renderTop(width), ...this.renderBottom(width)] } - renderTop(width: number): string[] { - const state = this.#state - if (!state) return [] - const { view } = state - const target = executionTargetFor(view) - return renderTerminalIdentity( - this.#theme, - { - workspace: view.workspace, - conversationTitle: view.conversationTitle, - branch: view.branch, - profileName: target.profileName, - runner: target.runner, - model: target.model, - connection: target.connection, - ...(target.effort === undefined ? {} : { effort: target.effort }), - ...(target.maxOutputTokens === undefined - ? {} - : { maxOutputTokens: target.maxOutputTokens }), - execution: executionLabel(target), - }, - width, - ) + renderTop(_width: number): string[] { + return [] } renderBottom(width: number): string[] { @@ -70,35 +45,71 @@ export class TerminalChrome implements Component { const safeWidth = Math.max(1, Math.floor(width)) const { view } = state const mode = modeForColumns(safeWidth) - const execution = terminalValuePart(this.#theme, executionLabel(executionTargetFor(view))) - const status = fitTerminalAtomic( - statusText(this.#theme, view, conciseStatus(view, state.quitArmed)), - safeWidth, + const target = executionTargetFor(view) + const transientNotice = state.quitArmed ? undefined : transientNoticeFor(view) + if (transientNotice !== undefined) { + return [fitTerminalAtomic(statusText(this.#theme, view, transientNotice), safeWidth)] + } + const status = statusText(this.#theme, view, conciseStatus(view, state.quitArmed, mode)) + const hint = terminalValuePart( + this.#theme, + navigationHint(view, state.navigationHint, state.composerMode), ) - const hint = terminalValuePart(this.#theme, navigationHint(view, state.navigationHint)) const metrics = mode === 'wide' ? metricsFor(view).map((metric) => terminalValuePart(this.#theme, metric)) : [] - return [fitTerminalColumns([status, execution], [hint, ...metrics], safeWidth)] + const prioritizesAction = state.quitArmed || view.activeRunId !== undefined || status.length > 0 + const right = state.quitArmed + ? [status] + : view.activeRunId !== undefined + ? [status, hint] + : status.length > 0 + ? [status, hint] + : metrics.length > 0 + ? metrics + : [status, hint] + return [ + renderTerminalContext( + this.#theme, + { + workspace: view.workspace, + conversationTitle: view.conversationTitle, + branch: view.branch, + profileName: target.profileName, + runner: target.runner, + model: target.model, + connection: target.connection, + ...(target.effort === undefined ? {} : { effort: target.effort }), + ...(target.maxOutputTokens === undefined + ? {} + : { maxOutputTokens: target.maxOutputTokens }), + execution: executionLabel(target), + }, + right, + safeWidth, + prioritizesAction ? 'right' : 'left', + ), + ] } } function executionLabel(target: ExecutionTargetView): string { const environment = target.environment if (environment === undefined) return '' - const location = environment.location === 'local' ? 'local' : environment.location - const executionKind = - environment.kind === 'sandbox' - ? 'sandbox' - : environment.provider === 'cli-bridge' - ? 'CLI' - : environment.provider - return `exec ${location ?? 'unknown'} ${executionKind} · ${environment.lifecycle}` + if (environment.location === 'local' && environment.provider === 'cli-bridge') return '' + const location = environment.location === 'unknown' ? undefined : environment.location + const executionKind = environment.kind === 'sandbox' ? 'sandbox' : environment.provider + const lifecycle = + environment.lifecycle === 'active' || environment.lifecycle === 'ready' + ? undefined + : environment.lifecycle + return [location, executionKind, lifecycle].filter(Boolean).join(' ') } function statusText(theme: BraidTheme, view: BraidViewModel, value: string): string { const safe = sanitizeNotification(value) + if (value === 'outcome unknown') return theme.warning(safe) if (view.status === 'failed' || view.status === 'storage-failure') return theme.danger(safe) if (view.status === 'running' || view.status === 'waiting' || view.status === 'cancelling') return theme.warning(safe) @@ -106,38 +117,70 @@ function statusText(theme: BraidTheme, view: BraidViewModel, value: string): str return theme.success(safe) } -function conciseStatus(view: BraidViewModel, quitArmed: boolean): string { - if (quitArmed) return 'ctrl+c again to quit' +function conciseStatus( + view: BraidViewModel, + quitArmed: boolean, + mode: ReturnType, +): string { + if (quitArmed) return 'Ctrl+C again to quit' + if (terminalOutcomeIsUnknown(view)) return 'outcome unknown' + const status = view.status + if (status === 'starting') + return mode === 'narrow' ? 'starting · Ctrl+C stop' : 'starting · Ctrl+C cancel' + if (status === 'streaming' || status === 'running') + return mode === 'narrow' ? 'working · Ctrl+C stop' : 'working · Ctrl+C cancel' + if (status === 'waiting') return 'waiting for input' + if (status === 'detached') return 'running remotely' + if (status === 'reconnecting') return 'reconnecting' + if (status === 'cancelling') return 'stopping' + if (status === 'completed' || status === 'ready' || status === 'empty') return '' + if (status === 'cancelled') return 'cancelled' + if (status === 'failed') return 'failed' + if (status === 'expired') return 'expired' + if (status === 'unknown') return 'status unavailable' + if (status === 'storage-failure') return 'storage error' const notification = sanitizeNotification(view.notice ?? view.statusText) - if ( - view.activeRunId !== undefined && - (view.status === 'starting' || - view.status === 'streaming' || - view.status === 'running' || - view.status === 'waiting') - ) { - return `${notification || view.status} · Ctrl+C cancel` - } - if (view.status === 'failed' || view.status === 'unknown') { - const run = view.runs.at(-1) - if (run !== undefined) { - const outcome = run.completeness === 'unknown' ? 'outcome unknown' : 'outcome failed' - return `${outcome} · operation ${shortIdentifier(run.operationId ?? run.id)}` - } - } - if (notification.length > 0) return notification - return cleanTerminalField(view.status) || 'unknown' + return notification === 'ready for a message' ? '' : notification } -function navigationHint(view: BraidViewModel, fallback: string): string { - if (view.activeRunId !== undefined) return 'Enter queues input' +function navigationHint( + view: BraidViewModel, + fallback: string, + composerMode: ComposerMode, +): string { + if (view.activeRunId !== undefined) { + const queue = view.capabilities['run.queue']?.available === true + const steer = view.capabilities['run.steer']?.available === true + if (queue && steer) + return composerMode === 'steer' ? 'Enter steers · Alt+S queue' : 'Enter queues · Alt+S steer' + if (queue) return 'Enter queues' + if (steer) return 'Enter steers' + return 'input unavailable' + } + if (terminalOutcomeIsUnknown(view)) return '/activity inspect' if (view.status === 'failed' || view.status === 'unknown') { - return '/export preserve · /new continue' + return '/activity details · /new' } return fallback } -function shortIdentifier(value: string): string { - const safe = cleanTerminalField(value) - return safe.length <= 18 ? safe : `${safe.slice(0, 10)}…${safe.slice(-6)}` +function terminalOutcomeIsUnknown(view: BraidViewModel): boolean { + if (view.activeRunId !== undefined) return false + const completeness = view.runs.at(-1)?.completeness + return ( + completeness === 'incomplete' || + completeness === 'missing-history' || + completeness === 'unknown' + ) +} + +function transientNoticeFor(view: BraidViewModel): string | undefined { + if (view.status !== 'empty' && view.status !== 'ready' && view.status !== 'completed') { + return undefined + } + const notice = sanitizeNotification(view.notice ?? view.statusText) + if (!notice || notice === 'ready' || notice === 'completed' || notice === 'ready for a message') { + return undefined + } + return notice } diff --git a/src/views/tui/terminal-command-controller.ts b/src/views/tui/terminal-command-controller.ts index 4999a10..394135b 100644 --- a/src/views/tui/terminal-command-controller.ts +++ b/src/views/tui/terminal-command-controller.ts @@ -15,6 +15,7 @@ import type { BraidViewModel } from '../shared/models.js' import { executionTargetFor } from './execution-target.js' import type { TerminalDraftController } from './terminal-drafts.js' import type { TerminalOverlayController } from './terminal-overlays.js' +import type { ComposerMode } from './composer-view.js' export interface TerminalCommandControllerOptions { readonly controller: BraidUiController @@ -25,6 +26,7 @@ export interface TerminalCommandControllerOptions { readonly dispatch: (intent: BraidIntent, restoreText?: string) => Promise readonly isStopped: () => boolean readonly stop: () => void + readonly composerMode: () => ComposerMode } /** Owns prompt parsing and command routing; it never owns terminal layout. */ @@ -37,6 +39,7 @@ export class TerminalCommandController { readonly #dispatch: TerminalCommandControllerOptions['dispatch'] readonly #isStopped: () => boolean readonly #stop: () => void + readonly #composerMode: () => ComposerMode constructor(options: TerminalCommandControllerOptions) { this.#controller = options.controller @@ -47,6 +50,7 @@ export class TerminalCommandController { this.#dispatch = options.dispatch this.#isStopped = options.isStopped this.#stop = options.stop + this.#composerMode = options.composerMode } submit(rawText: string): void { @@ -81,15 +85,17 @@ export class TerminalCommandController { const queueCapability = view.capabilities['run.queue'] const steerCapability = view.capabilities['run.steer'] const queueAvailable = active && queueCapability?.available === true + const steerAvailable = active && steerCapability?.available === true + const steerSelected = steerAvailable && (!queueAvailable || this.#composerMode() === 'steer') const capability = active - ? queueAvailable - ? queueCapability - : steerCapability + ? steerSelected + ? steerCapability + : queueCapability : view.capabilities['run.send'] if (!capability?.available) { this.#editor.setText(rawText) this.#overlays.openUnavailable( - active && !queueAvailable + active && this.#composerMode() === 'steer' ? 'Steering unavailable' : active ? 'Queue unavailable' @@ -104,9 +110,9 @@ export class TerminalCommandController { const operationId = this.#nextOperationId() const intent: BraidIntent = !active ? { type: 'send', operationId, text: parsed.text } - : queueAvailable - ? { type: 'queue', operationId, text: parsed.text } - : steerIntent(operationId, parsed.text) + : steerSelected + ? steerIntent(operationId, parsed.text) + : { type: 'queue', operationId, text: parsed.text } void this.#dispatch(intent, rawText) } diff --git a/src/views/tui/terminal-identity.ts b/src/views/tui/terminal-identity.ts index c398864..2a3acd5 100644 --- a/src/views/tui/terminal-identity.ts +++ b/src/views/tui/terminal-identity.ts @@ -1,6 +1,6 @@ import { truncateToWidth, visibleWidth } from '@earendil-works/pi-tui' import { sanitizeTerminalText } from '../shared/sanitize.js' -import { type LayoutMode, modeForColumns } from './layout.js' +import { modeForColumns } from './layout.js' import type { BraidTheme } from './theme.js' export interface TerminalIdentityView { @@ -21,62 +21,47 @@ export function renderTerminalIdentity( identity: TerminalIdentityView, width: number, ): string[] { + return [renderTerminalContext(theme, identity, [], width)] +} + +/** Renders the selected AgentProfile and execution route beside the composer. */ +export function renderTerminalContext( + theme: BraidTheme, + identity: TerminalIdentityView, + right: readonly string[], + width: number, + priority: 'left' | 'right' = 'left', +): string { const safeWidth = Math.max(1, Math.floor(width)) const mode = modeForColumns(safeWidth) - const header = identityHeader(theme, identity, safeWidth, mode) - const profile = terminalValuePart(theme, `AgentProfile ${identity.profileName}`) - const route = terminalValuePart( + const model = + mode === 'wide' ? cleanTerminalField(identity.model) : compactModelName(identity.model) + const profile = terminalValuePart( theme, - mode === 'wide' - ? `runner ${identity.runner} · model ${identity.model}` - : `${identity.runner} / ${compactModelName(identity.model)}`, + mode === 'narrow' ? identity.profileName : `profile ${identity.profileName}`, ) - const effort = identity.effort ? terminalValuePart(theme, `thinking ${identity.effort}`) : '' - const outputLimit = - identity.maxOutputTokens === undefined - ? '' - : terminalValuePart(theme, `output ≤${compactTerminalNumber(identity.maxOutputTokens)}`) + const route = terminalValuePart(theme, `${identity.runner} / ${model}`) const connection = terminalValuePart( theme, mode === 'narrow' ? compactConnectionName(identity.connection) : identity.connection, ) + const effortValue = cleanTerminalField(identity.effort) + const effort = + mode === 'wide' && effortValue && effortValue !== 'none' && effortValue !== 'off' + ? terminalValuePart(theme, effortValue) + : '' const execution = terminalValuePart(theme, identity.execution ?? '') - - if (mode === 'narrow') { - return [ - header, - fitTerminalAtomic(joinPrefix([route, connection, effort, outputLimit], safeWidth), safeWidth), - ] - } - - return [ - header, - fitTerminalColumns([profile, route], [connection, effort, outputLimit, execution], safeWidth), - ] -} - -function identityHeader( - theme: BraidTheme, - identity: TerminalIdentityView, - width: number, - mode: LayoutMode, -): string { - if (mode === 'narrow') { - return fitTerminalAtomic( - `${theme.brand('braid')} ${terminalValuePart(theme, `AgentProfile ${identity.profileName}`)}`, - width, - ) - } - const workspace = fieldPart(theme, 'cwd', workspaceBasename(identity.workspace)) - const session = fieldPart( - theme, - 'session', - cleanTerminalField(identity.conversationTitle) || 'new conversation', + const branchValue = cleanTerminalField(identity.branch) + const branch = + mode === 'wide' && branchValue && branchValue !== 'main' && branchValue !== 'branch-1' + ? terminalValuePart(theme, branchValue) + : '' + return fitTerminalColumns( + [profile, route, connection, effort, execution, branch], + right, + safeWidth, + priority, ) - const branch = fieldPart(theme, 'branch', identity.branch) - const parts = [theme.brand('braid'), workspace, session] - if (mode === 'wide' && branch) parts.push(branch) - return joinHeader(parts, width) } function compactModelName(value: string): string { @@ -99,53 +84,11 @@ export function cleanTerminalField(value: string | null | undefined): string { .trim() } -function workspaceBasename(workspace: string | null | undefined): string { - const safe = cleanTerminalField(workspace) - if (!safe) return 'workspace' - const trimmed = safe.replace(/[\\/]+$/gu, '') - if (!trimmed) return 'workspace' - const pieces = trimmed.split(/[\\/]/u).filter((piece) => piece.length > 0) - if (pieces.length <= 1) return safe - return pieces.at(-1) ?? 'workspace' -} - export function terminalValuePart(theme: BraidTheme, value: string): string { const safe = cleanTerminalField(value) return safe ? theme.text(safe) : '' } -function fieldPart(theme: BraidTheme, label: string, value: string | null | undefined): string { - const safeLabel = cleanTerminalField(label) - const safeValue = cleanTerminalField(value) - if (!safeLabel || !safeValue) return '' - return `${theme.muted(safeLabel)} ${theme.text(safeValue)}` -} - -function joinHeader(parts: readonly string[], width: number): string { - const present = parts.filter((part) => part.length > 0) - if (present.length === 0) return '' - const base = joinPrefix(present.slice(0, 2), width, ' ') - if (present.length <= 2 || !base) return base - let result = base - for (const part of present.slice(2)) { - const candidate = `${result} · ${part}` - if (visibleWidth(candidate) > width) break - result = candidate - } - return result -} - -function joinPrefix(parts: readonly string[], width: number, separator = ' · '): string { - const present = parts.filter((part) => part.length > 0) - let result = '' - for (const part of present) { - const candidate = result ? `${result}${separator}${part}` : part - if (visibleWidth(candidate) > width) break - result = candidate - } - return result -} - export function fitTerminalAtomic(value: string, width: number): string { return truncateToWidth(value, width, '…') } @@ -173,6 +116,7 @@ export function fitTerminalColumns( left: readonly string[], right: readonly string[], width: number, + priority: 'left' | 'right' = 'left', ): string { const leftPrefixes = prefixes(left, width, ' · ') const rightPrefixes = prefixes(right, width, ' · ') @@ -189,7 +133,9 @@ export function fitTerminalColumns( leftValue && rightValue ? `${leftValue}${' '.repeat(gap)}${rightValue}` : leftValue || rightValue - const score = (leftValue && rightValue ? 1_000 : 0) + leftIndex * 10 + rightIndex + const score = + (leftValue && rightValue ? 1_000 : 0) + + (priority === 'right' ? rightIndex * 10 + leftIndex : leftIndex * 10 + rightIndex) if (score > bestScore && visibleWidth(candidate) <= width) { best = candidate bestScore = score diff --git a/src/views/tui/terminal-input-controller.ts b/src/views/tui/terminal-input-controller.ts index 4d365b0..0860aaa 100644 --- a/src/views/tui/terminal-input-controller.ts +++ b/src/views/tui/terminal-input-controller.ts @@ -7,6 +7,7 @@ import type { ModalCoordinator } from './modal-coordinator.js' import type { TerminalDraftController } from './terminal-drafts.js' import type { TerminalOverlayController } from './terminal-overlays.js' import type { BraidShell } from './terminal-shell.js' +import type { ComposerMode } from './composer-view.js' export interface TerminalInputControllerOptions { readonly tui: TUI @@ -26,12 +27,10 @@ export interface TerminalInputControllerOptions { export type ActivityVisibility = 'auto' | 'hidden' | 'visible' export function activityVisibleFor( - view: Pick, + _view: Pick, visibility: ActivityVisibility, ): boolean { - if (visibility === 'visible') return true - if (visibility === 'hidden') return false - return view.activeRunId !== undefined + return visibility === 'visible' } /** Routes non-text terminal keys and owns the short-lived quit/activity state. */ @@ -51,6 +50,7 @@ export class TerminalInputController { #quitTimer: ReturnType | undefined #quitArmed = false #activityVisibility: ActivityVisibility = 'auto' + #composerMode: ComposerMode = 'queue' constructor(options: TerminalInputControllerOptions) { this.#tui = options.tui @@ -75,12 +75,21 @@ export class TerminalInputController { return activityVisibleFor(this.#controller.view(), this.#activityVisibility) } + get composerMode(): ComposerMode { + return this.#composerMode + } + close(): void { if (this.#quitTimer) clearTimeout(this.#quitTimer) this.#quitTimer = undefined } handle(data: string): { consume?: boolean } | undefined { + if (matchesKeyAction(data, this.#keymap, 'toggleSteer')) { + if (this.#tui.hasOverlay() || this.#interactionOpen()) return undefined + this.#toggleComposerMode() + return { consume: true } + } if (isTextInputSequence(data)) return undefined if (matchesKeyAction(data, this.#keymap, 'closeOverlay') && this.#tui.hasOverlay()) { if (this.#interactionOpen()) return undefined @@ -167,6 +176,24 @@ export class TerminalInputController { return undefined } + #toggleComposerMode(): void { + const view = this.#controller.view() + const queue = + view.activeRunId !== undefined && view.capabilities['run.queue']?.available === true + const steer = + view.activeRunId !== undefined && view.capabilities['run.steer']?.available === true + if (!steer) { + this.#overlays.openUnavailable( + 'Steering unavailable', + 'The active run does not advertise live steering.', + ) + return + } + this.#composerMode = queue && this.#composerMode === 'steer' ? 'queue' : 'steer' + this.#shell.setComposerMode(this.#composerMode) + this.#stateChanged() + } + #requestShutdown(): void { void this.#drafts.flush().then(() => this.#dispatch({ type: 'shutdown', operationId: this.#nextOperationId() }).then((result) => { diff --git a/src/views/tui/terminal-shell.ts b/src/views/tui/terminal-shell.ts index 476f1de..58be041 100644 --- a/src/views/tui/terminal-shell.ts +++ b/src/views/tui/terminal-shell.ts @@ -9,7 +9,7 @@ import { import type { BraidViewModel } from '../shared/models.js' import { sanitizeTerminalText } from '../shared/sanitize.js' import { ActivityView } from './activity.js' -import { ComposerView } from './composer-view.js' +import { type ComposerMode, ComposerView } from './composer-view.js' import { layoutFor } from './layout.js' import { TerminalChrome } from './terminal-chrome.js' import type { BraidTheme } from './theme.js' @@ -26,6 +26,7 @@ export class BraidShell extends Container { #showActivity = false #modalVisible = false #quitArmed = false + #composerMode: ComposerMode = 'queue' constructor( tui: TUI, @@ -53,6 +54,13 @@ export class BraidShell extends Container { return this.#composer.editor } + setComposerMode(mode: ComposerMode): void { + this.#composerMode = mode + this.#composer.setMode(mode) + if (this.#view) this.#composer.setView(this.#view) + this.#refreshChrome() + } + setActivityVisible(visible: boolean): void { this.#showActivity = visible this.invalidate() @@ -74,6 +82,7 @@ export class BraidShell extends Container { quitArmed, activityVisible: this.#showActivity, navigationHint: this.#transcript.navigationHint(), + composerMode: this.#composerMode, }) this.#composer.editor.disableSubmit = false this.#composer.editor.borderColor = @@ -151,6 +160,7 @@ export class BraidShell extends Container { quitArmed: this.#quitArmed, activityVisible: this.#showActivity, navigationHint: this.#transcript.navigationHint(), + composerMode: this.#composerMode, }) this.invalidate() } diff --git a/src/views/tui/terminal-usage.ts b/src/views/tui/terminal-usage.ts index d109899..83d36c7 100644 --- a/src/views/tui/terminal-usage.ts +++ b/src/views/tui/terminal-usage.ts @@ -1,177 +1,78 @@ import type { BraidViewModel, UsageMeasurementStatus, UsageTotalsView } from '../shared/models.js' import { compactTerminalNumber } from './terminal-identity.js' +/** Returns only telemetry that the provider measured or priced. */ export function metricsFor(view: BraidViewModel): string[] { - const totals = view.sessionUsage.turns - if (hasMeasurementTelemetry(view.sessionUsage)) { - return [ - usageGroup('turns', totals), - usageGroup('analysis', view.sessionUsage.analyses), - usageGroup('workers', view.sessionUsage.delegated), - ].filter((value): value is string => value !== undefined) - } - if (totals.sourceCount === 0) return [] - - return legacyMetricsFor(view) -} - -function legacyMetricsFor(view: BraidViewModel): string[] { - const totals = view.sessionUsage.turns - const input = totals.input - const output = totals.output - const cost = totals.costUsd - const tokenPrefix = totals.tokenStatus === 'complete' ? '' : '≥' - const metrics: string[] = [] - const noObservedTokens = (input ?? 0) === 0 && (output ?? 0) === 0 - const tokenUsageUnknown = totals.tokenStatus !== 'complete' && noObservedTokens - if (tokenUsageUnknown) { - metrics.push('usage unknown') - } - if (input !== undefined && !tokenUsageUnknown) - metrics.push(`in ${tokenPrefix}${compactTerminalNumber(input)}`) - if (output !== undefined && !tokenUsageUnknown) - metrics.push(`out ${tokenPrefix}${compactTerminalNumber(output)}`) - if (totals.costStatus === 'reported' && cost !== undefined && Number.isFinite(cost)) { - metrics.push(`$${cost.toFixed(4)}`) - } else if ( - totals.costStatus === 'estimated' && - totals.estimatedCostUsd !== undefined && - Number.isFinite(totals.estimatedCostUsd) && - totals.estimatedCostUsd >= 0 - ) { - metrics.push(`~$${totals.estimatedCostUsd.toFixed(4)}`) - } else if (cost !== undefined && cost > 0 && Number.isFinite(cost)) { - metrics.push(`≥$${cost.toFixed(4)}`) - } else if ( - totals.estimatedCostUsd !== undefined && - Number.isFinite(totals.estimatedCostUsd) && - totals.estimatedCostUsd >= 0 - ) { - metrics.push(`~$${totals.estimatedCostUsd.toFixed(4)}`) - } else if (!tokenUsageUnknown) { - metrics.push('cost unknown') - } - if ((totals.llmCalls ?? 0) > 0) metrics.push(`${totals.llmCalls} calls`) - if ((totals.llmLatencyMs ?? 0) > 0) - metrics.push(`${Math.round(totals.llmLatencyMs ?? 0)}ms model`) - const analyses = view.sessionUsage.analyses - if (analyses.sourceCount > 0) { - const exact = analyses.costStatus === 'reported' - metrics.push( - analyses.costUsd === undefined || (!exact && analyses.costUsd === 0) - ? 'analysis $unknown' - : `analysis ${exact ? '' : '≥'}$${analyses.costUsd.toFixed(4)}`, - ) - } - const delegated = view.sessionUsage.delegated - if (delegated.sourceCount > 0) { - metrics.push( - delegated.costUsd === undefined || delegated.costUsd === 0 - ? 'workers $unknown' - : `workers ≥$${delegated.costUsd.toFixed(4)}`, - ) - } - return metrics -} - -function hasMeasurementTelemetry(view: BraidViewModel['sessionUsage']): boolean { - return [view.turns, view.analyses, view.delegated].some(hasMeasurementFields) -} - -function hasMeasurementFields(usage: UsageTotalsView): boolean { - return ( - usage.callStatus !== undefined || - usage.latencyStatus !== undefined || - usage.unknownCallSources !== undefined || - usage.unknownLatencySources !== undefined - ) + return [ + ...usageMetrics(view.sessionUsage.turns), + usageGroup('analysis', view.sessionUsage.analyses), + usageGroup('workers', view.sessionUsage.delegated), + ].filter((value): value is string => value !== undefined) } function usageGroup(label: string, usage: UsageTotalsView): string | undefined { if (usage.sourceCount === 0) return undefined - const metrics = [...tokenMetrics(usage), costMetric(usage)].filter( - (value): value is string => value !== undefined, - ) - const calls = measurementMetric( - 'calls', - usage.llmCalls, - usage.callStatus, - usage.unknownCallSources, - (value) => String(Math.round(value)), - ) - const latency = measurementMetric( - 'latency', - usage.llmLatencyMs, - usage.latencyStatus, - usage.unknownLatencySources, - (value) => `${Math.round(value)}ms`, - ) - if (calls !== undefined) metrics.push(calls) - if (latency !== undefined) metrics.push(latency) - return metrics.length === 0 ? undefined : `${label} ${metrics.join(' · ')}` + const metrics = usageMetrics(usage) + if (metrics.length === 0) return undefined + return `${label} ${metrics.join(' · ')}` +} + +function usageMetrics(usage: UsageTotalsView): string[] { + if (usage.sourceCount === 0) return [] + return [ + ...tokenMetrics(usage), + costMetric(usage), + measurementMetric('calls', usage.llmCalls, usage.callStatus, (value) => + String(Math.round(value)), + ), + measurementMetric( + 'model', + usage.llmLatencyMs, + usage.latencyStatus, + (value) => `${Math.round(value)}ms`, + ), + ].filter((value): value is string => value !== undefined) } function tokenMetrics(usage: UsageTotalsView): string[] { - const tokenPrefix = usage.tokenStatus === 'complete' ? '' : '≥' - const noObservedTokens = (usage.input ?? 0) === 0 && (usage.output ?? 0) === 0 - const tokenUsageUnknown = usage.tokenStatus !== 'complete' && noObservedTokens - if (tokenUsageUnknown) return ['usage unknown'] + const input = finiteNonNegative(usage.input) + const output = finiteNonNegative(usage.output) + const noObservedTokens = (input ?? 0) === 0 && (output ?? 0) === 0 + if (usage.tokenStatus !== 'complete' && noObservedTokens) return [] + const prefix = usage.tokenStatus === 'complete' ? '' : '≥' return [ - ...(usage.input === undefined - ? [] - : [`in ${tokenPrefix}${compactTerminalNumber(usage.input)}`]), - ...(usage.output === undefined - ? [] - : [`out ${tokenPrefix}${compactTerminalNumber(usage.output)}`]), + ...(input === undefined ? [] : [`in ${prefix}${compactTerminalNumber(input)}`]), + ...(output === undefined ? [] : [`out ${prefix}${compactTerminalNumber(output)}`]), ] } function costMetric(usage: UsageTotalsView): string | undefined { - if ( - usage.costStatus === undefined && - usage.costUsd === undefined && - usage.estimatedCostUsd === undefined - ) { - return undefined + const reported = finiteNonNegative(usage.costUsd) + if (usage.costStatus === 'reported' && reported !== undefined) { + return `$${reported.toFixed(4)}` } - const cost = usage.costUsd - if (usage.costStatus === 'reported' && cost !== undefined && Number.isFinite(cost)) { - return `$${cost.toFixed(4)}` + const estimated = finiteNonNegative(usage.estimatedCostUsd) + if (usage.costStatus === 'estimated' && estimated !== undefined) { + return `~$${estimated.toFixed(4)}` } - if ( - usage.costStatus === 'estimated' && - usage.estimatedCostUsd !== undefined && - Number.isFinite(usage.estimatedCostUsd) && - usage.estimatedCostUsd >= 0 - ) { - return `~$${usage.estimatedCostUsd.toFixed(4)}` + if (usage.costStatus === 'observed-floor' && reported !== undefined && reported > 0) { + return `≥$${reported.toFixed(4)}` } - if (cost !== undefined && cost > 0 && Number.isFinite(cost)) { - return `≥$${cost.toFixed(4)}` - } - if ( - usage.estimatedCostUsd !== undefined && - Number.isFinite(usage.estimatedCostUsd) && - usage.estimatedCostUsd >= 0 - ) { - return `~$${usage.estimatedCostUsd.toFixed(4)}` - } - return usage.costStatus === undefined ? undefined : 'cost unknown' + return undefined } function measurementMetric( label: string, value: number | undefined, status: UsageMeasurementStatus | undefined, - unknownSources: number | undefined, format: (value: number) => string, ): string | undefined { - if (status === undefined && value === undefined && unknownSources === undefined) return undefined - const missing = Math.max(0, unknownSources ?? (value === undefined ? 1 : 0)) - if (value === undefined || status === 'unknown') { - return `${label} unknown${missing > 0 ? ` (${missing} missing)` : ''}` - } + const measured = finiteNonNegative(value) + if (measured === undefined || status === 'unknown') return undefined const prefix = status === 'partial' ? '≥' : '' - const suffix = status === 'partial' && missing > 0 ? ` (+${missing} missing)` : '' - return `${label} ${prefix}${format(value)}${suffix}` + return `${label} ${prefix}${format(measured)}` +} + +function finiteNonNegative(value: number | undefined): number | undefined { + return value !== undefined && Number.isFinite(value) && value >= 0 ? value : undefined } diff --git a/src/views/tui/transcript.ts b/src/views/tui/transcript.ts index fe6d753..c19e9e4 100644 --- a/src/views/tui/transcript.ts +++ b/src/views/tui/transcript.ts @@ -1,4 +1,4 @@ -import { Box, type Component, Container, matchesKey, Spacer, Text } from '@earendil-works/pi-tui' +import { type Component, Container, matchesKey, Spacer, Text } from '@earendil-works/pi-tui' import type { BraidViewModel, MessageView, TranscriptPartView } from '../shared/models.js' import { sanitizeDiff, sanitizeMarkdown, sanitizeTerminalText } from '../shared/sanitize.js' import { SafeMarkdown } from './safe-markdown.js' @@ -47,9 +47,7 @@ export class TranscriptView extends Container { this.addChild(new Spacer(1)) this.addChild( new Text( - this.#theme.muted( - `Send a task to ${sanitizeTerminalText(view.profileName)}, or press Ctrl+P for commands.`, - ), + this.#theme.muted(`Ask ${sanitizeTerminalText(view.profileName)} anything.`), 1, 0, ), @@ -194,19 +192,13 @@ export class TranscriptView extends Container { if (this.#theme.highContrast) container.addChild(new Text(this.#theme.accent(`${message.role}:`), 1, 0)) if (message.role === 'user') { - const box = new Box(1, 0, this.#theme.userBackground) - box.addChild( - new SafeMarkdown( - sanitizeMarkdown(message.text), - 0, + container.addChild( + new Text( + `${this.#theme.highContrast ? '' : this.#theme.accent('› ')}${sanitizeTerminalText(message.text)}`, + 1, 0, - this.#theme.markdown, - undefined, - undefined, - { allowHyperlinks: this.#theme.terminalMetadata }, ), ) - container.addChild(box) return container } diff --git a/test/activity-document.test.ts b/test/activity-document.test.ts index 6b8c83a..09f296a 100644 --- a/test/activity-document.test.ts +++ b/test/activity-document.test.ts @@ -75,13 +75,34 @@ test('one activity document preserves event status, tree identity, duration, usa const browserRows = activityDocument(view).rows assert.equal(browserRows.find((row) => row.id === 'tool-result')?.status, 'failed') + const recordedRun = view.runs[0] + assert.ok(recordedRun) + const missingUsage = activityDocument({ + ...view, + runs: [ + { + ...recordedRun, + usage: { + tokenStatus: 'unknown', + costStatus: 'unknown', + }, + }, + ], + }) + const missingDetail = missingUsage.rows.find((row) => row.id === 'run-1')?.detailLines.join('\n') + assert.match(missingDetail ?? '', /model calls: not reported/u) + assert.match(missingDetail ?? '', /model latency: not reported/u) + assert.match(missingDetail ?? '', /token measurement: not reported/u) + assert.match(missingDetail ?? '', /cost measurement: not reported/u) + const rail = new ActivityView(theme) rail.setView(view) - assert.match(rail.render(80).join('\n'), /failed shell.*permission denied/u) + assert.match(rail.render(80).join('\n'), /running shell.*started/u) + assert.doesNotMatch(rail.render(80).join('\n'), /failed shell|permission denied/u) }) -test('the wide rail uses a divider and activity visibility defaults to active runs', async () => { - assert.equal(activityVisibleFor({ activeRunId: 'run-1' }, 'auto'), true) +test('the wide live-work rail uses a divider and remains explicitly opt-in', async () => { + assert.equal(activityVisibleFor({ activeRunId: 'run-1' }, 'auto'), false) assert.equal(activityVisibleFor({}, 'auto'), false) assert.equal(activityVisibleFor({}, 'visible'), true) assert.equal(activityVisibleFor({ activeRunId: 'run-1' }, 'hidden'), false) diff --git a/test/analysis-durable.test.ts b/test/analysis-durable.test.ts index 77c4645..6144652 100644 --- a/test/analysis-durable.test.ts +++ b/test/analysis-durable.test.ts @@ -16,6 +16,8 @@ import { type AnalystRegistryPort, } from '../src/adapters/analysis/eval-analyst.js' import { ProductionAnalysisAnalyst } from '../src/adapters/analysis/production-analysis-analyst.js' +import { mapAnalystFinding } from '../src/adapters/analysis/citations.js' +import { buildAnalysisTraceStore } from '../src/adapters/analysis/trace-store.js' import { AnalysisComparisonService } from '../src/app/analysis-comparison.js' import type { AnalysisAnalyst } from '../src/app/analysis-execution-session.js' import { snapshotAnalysisExecutionTarget } from '../src/app/analysis-execution-target.js' @@ -405,6 +407,47 @@ test('analysis source does not cross the state revision while persisted events l assert.notEqual(prepared.evidence.events.at(-1)?.event.kind, 'run.finished') }) +test('trace citations validate exact scalar text without JSON escaping', () => { + const exact = 'Separator class [^\\p{L}\\p{N}] is wider than "punctuation and spaces".' + const history = runHistory('run-analysis-scalar', 'turn-analysis-scalar').map((envelope) => + envelope.event.kind === 'run.finished' + ? { ...envelope, event: { ...envelope.event, finalText: exact } } + : envelope, + ) + const host = new MemoryHost(history) + const evidence = freezeAnalysisSource({ + state: host.state, + events: host.eventHistory(), + runId: 'run-analysis-scalar', + }) + const trace = buildAnalysisTraceStore(evidence) + const terminalEvent = evidence.events.find((event) => event.event.kind === 'run.finished') + assert.ok(terminalEvent) + const span = trace.spans.find((candidate) => + candidate.eventIds.includes(String(terminalEvent.id)), + ) + assert.ok(span) + const mapped = mapAnalystFinding(evidence, trace, { + schema_version: '1.0.0', + finding_id: 'finding-exact-scalar', + analyst_id: 'efficiency-behavioral', + produced_at: NOW, + severity: 'info', + area: 'tool-use', + claim: exact, + confidence: 0.95, + evidence_refs: [ + { + kind: 'span', + uri: `trace://${encodeURIComponent(trace.traceId)}/span/${encodeURIComponent(span.spanId)}`, + excerpt: exact, + }, + ], + }) + assert.equal(mapped.supported, true) + assert.equal(mapped.citations[0]?.quote, exact) +}) + test('stable operation identity replays exactly and rejects digest conflicts', async () => { const calls = { count: 0 } const host = singleHost() diff --git a/test/analysis-model-call-roundtrip.test.ts b/test/analysis-model-call-roundtrip.test.ts index bb19dee..567e39a 100644 --- a/test/analysis-model-call-roundtrip.test.ts +++ b/test/analysis-model-call-roundtrip.test.ts @@ -178,7 +178,7 @@ test('analysis model calls survive event replay and appear as concise detail lin assert.equal( modelCallField?.value, '#1 openai/gpt-5.6-luna · tokens 120 in / 45 out · cost $0.0123 · latency 88ms\n' + - '#2 provider unknown/glm-5.2 · tokens unknown · cost unknown · latency unknown', + '#2 glm-5.2 · tokens unknown · cost unknown · latency unknown', ) assert.deepEqual(details.data.usage, { input: 165, diff --git a/test/analysis-supervision.test.ts b/test/analysis-supervision.test.ts index f9dac63..470c8ae 100644 --- a/test/analysis-supervision.test.ts +++ b/test/analysis-supervision.test.ts @@ -26,13 +26,13 @@ import { AnalysisComparisonService } from '../src/app/analysis-comparison.js' import { comparisonSnapshot } from '../src/app/analysis-comparison-evidence.js' import { compareFrozenRuns } from '../src/app/analysis-comparison-facts.js' import { resultFromComparisonRecord } from '../src/app/analysis-comparison-record.js' -import { AnalysisPromotionService } from '../src/app/analysis-promotion.js' -import { AnalysisService } from '../src/app/analysis-service.js' import { analysisIdentity } from '../src/app/analysis-operation.js' +import { AnalysisPromotionService } from '../src/app/analysis-promotion.js' import { completedAnalysisRecord, initialAnalysisRecord, } from '../src/app/analysis-result-mapper.js' +import { AnalysisService } from '../src/app/analysis-service.js' import { freezeAnalysisSource, verifyFrozenAnalysisSource } from '../src/app/analysis-source.js' import { type AnalysisApplicationHost, @@ -316,7 +316,8 @@ test('trace analysis keeps run identity, outcome, usage, and merged tools in one const trace = buildAnalysisTraceStore(evidence) const prepared = await BRAID_QUESTION_ANALYST_DEFINITION.prepareContext(trace.store) assert.match(prepared, /Exact trace id: "run-analysis-trace-projection"/u) - assert.match(prepared, /SUBMIT\(answer, json\.dumps\(findings\)\)/u) + assert.match(prepared, /SUBMIT\(answer=answer, findings_json=json\.dumps\(findings\)\)/u) + assert.doesNotMatch(prepared, /SUBMIT\(answer, json\.dumps\(findings\)\)/u) assert.match(prepared, /Omit subject from every finding/u) const view = await trace.store.viewTrace({ trace_id: trace.traceId }) assert.equal('spans' in view, true) diff --git a/test/automation-interaction-commands.test.ts b/test/automation-interaction-commands.test.ts index 8e3de62..2b19f4a 100644 --- a/test/automation-interaction-commands.test.ts +++ b/test/automation-interaction-commands.test.ts @@ -259,6 +259,7 @@ test('automation commands work through the command controller and Escape remains allowedOutcomes: ['accept', 'cancel'], responseScopes: ['once'], queuePosition: 0, + queueTotal: 1, secret: false, } const interactionShell = new InteractionShell( diff --git a/test/automation-rule-panel.test.ts b/test/automation-rule-panel.test.ts index 81119c0..3502f5d 100644 --- a/test/automation-rule-panel.test.ts +++ b/test/automation-rule-panel.test.ts @@ -20,6 +20,7 @@ const interaction: InteractionView = { allowedOutcomes: ['accept', 'once', 'session', 'persistent', 'cancel'], responseScopes: ['once', 'session', 'persistent'], queuePosition: 0, + queueTotal: 1, secret: false, } diff --git a/test/cli-bridge-retained-restart.test.ts b/test/cli-bridge-retained-restart.test.ts index 5426809..ac99f4d 100644 --- a/test/cli-bridge-retained-restart.test.ts +++ b/test/cli-bridge-retained-restart.test.ts @@ -3,8 +3,13 @@ import { mkdtemp, rm } from 'node:fs/promises' import { tmpdir } from 'node:os' import { join } from 'node:path' import test from 'node:test' -import { defineAgentProfile } from '@tangle-network/agent-interface' +import { type AgentExactRunControlRef, defineAgentProfile } from '@tangle-network/agent-interface' +import type { RetainedRunHandle } from '@tangle-network/agent-runtime/kernel' import { MemoryCredentialStore } from '../src/adapters/credentials/memory.js' +import { + type RetainedExecutionPlan, + RetainedExecutionPort, +} from '../src/adapters/runtime/retained-execution.js' import { createApplicationUiController } from '../src/adapters/tui/application-ui-controller.js' import { createDurableBraidApplication, @@ -17,6 +22,7 @@ import { import type { ConnectionRecord } from '../src/domain/entities.js' import { createConnectionId } from '../src/domain/ids.js' import { isRuntimeEventEnvelope } from '../src/domain/runtime-events.js' +import { DEFAULT_RUN_CAPABILITIES } from '../src/ports/execution.js' import { RandomIds } from '../src/ports/ids.js' import { startRuntimeBridgeServer } from './support/runtime-bridge-server.js' @@ -52,6 +58,96 @@ async function waitFor(predicate: () => boolean, timeoutMs = 2_000): Promise handle, + reconnect: async (requested) => { + assert.deepEqual(requested, controlRef) + return handle + }, + discover: async () => controlRef, + observe: async () => undefined, + projectStatus: () => 'streaming', + isTerminalStatus: () => false, + projectResult: (result) => ({ + text: result.text, + usage: { input: 0, output: 0 }, + }), + projectFinal: () => { + throw new Error('final projection is not part of this recovery test') + }, + } +} + +test('retained recovery passes the exact provider reference into plan construction', async () => { + const localRunId = 'braid/local-recovery-key' + const controlRef: AgentExactRunControlRef = { + runId: 'cloud-provider-random-run', + provider: 'cloud-provider', + environmentId: 'cloud-server-issued-environment', + sessionId: 'cloud-session-1', + executionId: 'cloud-execution-1', + requestDigest: `sha256:${'b'.repeat(64)}`, + } + let recoveredRunId: string | undefined + let recoveredEnvironmentId: string | undefined + const execution = new RetainedExecutionPort({ + resolve: async () => { + throw new Error('fresh admission is not part of this recovery test') + }, + recover: async ({ runId, controlRef: exact }) => { + recoveredRunId = runId + recoveredEnvironmentId = exact?.environmentId + if (exact === undefined) throw new Error('recovery lost the exact provider reference') + return recoveryPlan(exact) + }, + }) + + const snapshot = await execution.status({ + runId: localRunId, + providerSessionId: controlRef.sessionId, + controlRef, + }) + assert.equal(recoveredRunId, localRunId) + assert.equal(recoveredEnvironmentId, controlRef.environmentId) + assert.equal(snapshot?.runId, localRunId) + assert.equal(snapshot?.sessionId, controlRef.sessionId) +}) + test('durable Braid detaches, restarts, and resumes one retained CLI Bridge job exactly once', async () => { const root = await mkdtemp(join(tmpdir(), 'braid-retained-restart-')) const bridge = await startRuntimeBridgeServer({ @@ -161,7 +257,7 @@ test('restart discovery preserves a continued provider session before the contro const root = await mkdtemp(join(tmpdir(), 'braid-retained-crash-window-')) const bridge = await startRuntimeBridgeServer({ holdStreams: true }) const configuration = production(bridge.endpoint) - const runId = 'run-retained-crash-window' + const runId = 'run/retained-crash-window' const providerSessionId = 'session-existing-conversation' const abort = new AbortController() try { @@ -216,6 +312,46 @@ test('restart discovery preserves a continued provider session before the contro } }) +test('restart discovery rejects a status response for another CLI Bridge run', async () => { + const root = await mkdtemp(join(tmpdir(), 'braid-retained-foreign-run-')) + const bridge = await startRuntimeBridgeServer({ + holdStreams: true, + statusRunId: 'foreign-provider-run', + }) + const configuration = production(bridge.endpoint) + const runId = 'run/retained-foreign-identity' + const abort = new AbortController() + try { + const first = createProductionComposition({ ...configuration, workspaceRoot: root }) + if (first.execution.admit === undefined) throw new Error('Retained admission is unavailable') + const input = { + operationId: 'operation-retained-foreign-identity', + runId, + text: 'Keep this exact run isolated.', + profile: configuration.profile, + connectionId: configuration.connectionId, + workspaceRoot: root, + signal: abort.signal, + } + await first.execution.admit(input) + const stream = first.execution.streamTurn(input)[Symbol.asyncIterator]() + await stream.next() + await stream.return?.() + + const restarted = createProductionComposition({ ...configuration, workspaceRoot: root }) + if (restarted.execution.status === undefined) { + throw new Error('Retained recovery controls are unavailable') + } + await assert.rejects(restarted.execution.status({ runId }), /another run identity/u) + assert.equal(bridge.requests.length, 1) + } finally { + bridge.complete() + abort.abort() + await bridge.close() + await rm(root, { recursive: true, force: true }) + } +}) + test('detach during execution-observation persistence stops the retained reader', async () => { const root = await mkdtemp(join(tmpdir(), 'braid-retained-detach-race-')) const bridge = await startRuntimeBridgeServer({ holdStreams: true }) @@ -266,3 +402,114 @@ test('detach during execution-observation persistence stops the retained reader' await rm(root, { recursive: true, force: true }) } }) + +test('retained control keeps provider-owned identity separate from the Braid run id', async () => { + const root = await mkdtemp(join(tmpdir(), 'braid-retained-provider-identity-')) + const bridge = await startRuntimeBridgeServer({ holdStreams: true }) + const configuration = production(bridge.endpoint) + const runId = 'run/provider-owned-identity' + const providerSessionId = 'session-provider-owned-identity' + const abort = new AbortController() + try { + const composition = createProductionComposition({ ...configuration, workspaceRoot: root }) + if (composition.execution.admit === undefined) { + throw new Error('Retained admission is unavailable') + } + const input = { + operationId: 'operation-retained-provider-identity', + runId, + text: 'Keep the provider identity exact.', + profile: configuration.profile, + connectionId: configuration.connectionId, + workspaceRoot: root, + sessionId: providerSessionId, + signal: abort.signal, + } + await composition.execution.admit(input) + const stream = composition.execution.streamTurn(input)[Symbol.asyncIterator]() + const observation = await stream.next() + if ( + observation.done || + observation.value === undefined || + !isRuntimeEventEnvelope(observation.value) || + observation.value.event.type !== 'braid.execution.observed' || + observation.value.event.controlRef === undefined + ) { + throw new Error('Retained execution did not emit an exact control reference') + } + const controlRef = observation.value.event.controlRef + assert.notEqual(controlRef.runId, runId) + assert.equal(controlRef.provider, 'cli-bridge') + assert.equal(controlRef.sessionId, providerSessionId) + + bridge.complete(controlRef.runId) + const providerEvent = await stream.next() + assert.equal(providerEvent.done, false) + if (providerEvent.value === undefined || !isRuntimeEventEnvelope(providerEvent.value)) { + throw new Error('Retained execution did not emit a provider event envelope') + } + assert.equal(providerEvent.value.runId, runId) + + if (composition.execution.status === undefined) + throw new Error('Retained status is unavailable') + await assert.rejects( + composition.execution.status({ + runId, + controlRef: { ...controlRef, executionId: `${controlRef.executionId}-changed` }, + }), + /conflicts with the saved run/u, + ) + await stream.return?.() + } finally { + abort.abort() + await bridge.close() + await rm(root, { recursive: true, force: true }) + } +}) + +test('cancel_requested remains an unconfirmed Braid outcome', async () => { + const root = await mkdtemp(join(tmpdir(), 'braid-retained-cancel-requested-')) + const bridge = await startRuntimeBridgeServer({ + holdStreams: true, + cancellation: { effect: 'cancel_requested' }, + }) + const configuration = production(bridge.endpoint) + const abort = new AbortController() + try { + const composition = createProductionComposition({ ...configuration, workspaceRoot: root }) + if ( + composition.execution.admit === undefined || + composition.execution.cancelRun === undefined + ) { + throw new Error('Retained admission or cancellation is unavailable') + } + const input = { + operationId: 'operation-retained-cancel-requested', + runId: 'run-retained-cancel-requested', + text: 'Remain live until cancellation is confirmed.', + profile: configuration.profile, + connectionId: configuration.connectionId, + workspaceRoot: root, + signal: abort.signal, + } + await composition.execution.admit(input) + const stream = composition.execution.streamTurn(input)[Symbol.asyncIterator]() + const observation = await stream.next() + assert.equal(observation.done, false) + + const cancellation = await composition.execution.cancelRun({ + operationId: 'operation-retained-cancel-requested-control', + runId: input.runId, + }) + assert.equal('outcome' in cancellation ? cancellation.outcome : cancellation.status, 'unknown') + assert.equal('outcome' in cancellation ? cancellation.detail : undefined, 'cancel_requested') + if (composition.execution.status === undefined) + throw new Error('Retained status is unavailable') + assert.equal((await composition.execution.status({ runId: input.runId }))?.status, 'streaming') + await stream.return?.() + } finally { + abort.abort() + await bridge.close() + await rm(root, { recursive: true, force: true }) + } +}) diff --git a/test/cli-startup.test.ts b/test/cli-startup.test.ts index d9386a1..61ed28e 100644 --- a/test/cli-startup.test.ts +++ b/test/cli-startup.test.ts @@ -201,10 +201,10 @@ test('startup preview renders real route context and replays early input once', }) await terminal.waitForRender() const output = (await terminal.flushAndGetViewport()).join('\n') - assert.match(output, /braid\s+cwd\s+braid/u) - assert.match(output, /AgentProfile Luna coding.*claude-code \/ luna-max.*CLI Bridge/u) + assert.equal(output.includes('cwd'), false) + assert.match(output, /profile Luna coding.*claude-code \/ luna-max.*CLI Bridge/u) assert.match(output, /Ready safely/u) - assert.match(output, /completed.*Ctrl\+P commands/u) + assert.match(output, /completed/u) assert.equal(output.includes('\u001b[31m'), false) assert.equal(writes.join('').includes('\u001b]'), false) diff --git a/test/domain-invariants.test.ts b/test/domain-invariants.test.ts index 311e9be..58250f0 100644 --- a/test/domain-invariants.test.ts +++ b/test/domain-invariants.test.ts @@ -1,21 +1,33 @@ import assert from 'node:assert/strict' import test from 'node:test' +import type { AgentExactRunControlRef } from '@tangle-network/agent-interface' import { STARTER_PROFILE } from '../src/app/composition.js' -import { assertConnectionRecord, assertInteractionRecord } from '../src/domain/invariants.js' +import type { ConnectionRecord, InteractionRecord, RunRecord } from '../src/domain/entities.js' import type { BraidEvent, JournalEventEnvelope } from '../src/domain/events.js' -import { reduceEvent } from '../src/domain/reducer.js' -import { initialState } from '../src/domain/state.js' import { + createBranchId, createConnectionId, + createConversationId, + createEnvironmentId, createEventId, createGraphEdgeId, createGraphNodeId, createInteractionId, createOperationId, + createProviderSessionId, createRunId, + createTurnId, createWorkspaceId, } from '../src/domain/ids.js' -import type { ConnectionRecord, InteractionRecord } from '../src/domain/entities.js' +import { + assertConnectionRecord, + assertInteractionRecord, + assertRunRecord, +} from '../src/domain/invariants.js' +import { createAdmissionReceipt } from '../src/domain/receipts.js' +import { reduceEvent } from '../src/domain/reducer.js' +import { initialState } from '../src/domain/state.js' +import { DEFAULT_RUN_CAPABILITIES } from '../src/ports/execution.js' const at = '2026-08-02T00:00:00.000Z' @@ -224,3 +236,81 @@ test('unknown interaction kinds remain renderable through the canonical answer s /secret-designated|secret interaction/u, ) }) + +function providerOwnedRun(): RunRecord { + const runId = createRunId('run-local-identity') + const conversationId = createConversationId('conversation-run-identity') + const branchId = createBranchId('branch-run-identity') + const turnId = createTurnId('turn-run-identity') + const operationId = createOperationId('operation-run-identity') + const providerSessionId = createProviderSessionId('provider-session-run-identity') + const receipt = createAdmissionReceipt({ + runId, + turnId, + operationId, + conversationId, + branchId, + admittedAt: at, + profile: STARTER_PROFILE, + text: 'provider-owned run identity', + capabilities: DEFAULT_RUN_CAPABILITIES, + provider: 'tangle-sandbox', + providerSessionId, + }) + const requestDigest = `sha256:${'a'.repeat(64)}` as `sha256:${string}` + const controlRef: AgentExactRunControlRef = { + runId: 'tangle-execution-provider-owned', + provider: 'tangle-sandbox', + environmentId: 'tangle-environment-provider-owned', + sessionId: providerSessionId, + executionId: 'tangle-execution-provider-owned', + requestDigest, + } + return { + id: runId, + conversationId, + branchId, + turnId, + operationId, + status: 'streaming', + receipt, + capabilities: receipt.capabilities, + providerSessionId, + environmentId: createEnvironmentId('environment-run-identity'), + controlRef, + inputTokens: 0, + outputTokens: 0, + tokensKnown: false, + usdKnown: false, + complete: false, + startedAt: at, + updatedAt: at, + lastProviderSequence: 0, + eventCount: 0, + interactions: [], + activity: [], + eventDetails: [], + } +} + +test('run identity keeps local run IDs separate from provider-owned control references', () => { + const run = providerOwnedRun() + + assert.notEqual(run.controlRef?.runId, run.id) + assert.doesNotThrow(() => assertRunRecord(run)) +}) + +test('run identity still binds a control reference to the persisted provider session', () => { + const run = providerOwnedRun() + const controlRef = run.controlRef + assert.ok(controlRef) + + assert.throws( + () => + assertRunRecord({ + ...run, + controlRef: { ...controlRef, sessionId: 'tangle-session-not-persisted' }, + }), + /run\.controlRef\.sessionId must match run\.providerSessionId/u, + ) +}) diff --git a/test/installed-package-version.test.ts b/test/installed-package-version.test.ts new file mode 100644 index 0000000..1298901 --- /dev/null +++ b/test/installed-package-version.test.ts @@ -0,0 +1,22 @@ +import assert from 'node:assert/strict' +import { readFileSync } from 'node:fs' +import test from 'node:test' +import { createRequire } from 'node:module' +import { dirname, join } from 'node:path' +import { AGENT_EVAL_VERSION } from '../src/adapters/analysis/agent-eval-version.js' +import { AGENT_RUNTIME_VERSION } from '../src/adapters/runtime/agent-runtime-version.js' + +const require = createRequire(import.meta.url) + +function installedVersion(packageName: string): string { + const entry = require.resolve(packageName) + const document = JSON.parse(readFileSync(join(dirname(entry), '..', 'package.json'), 'utf8')) as { + readonly version: string + } + return document.version +} + +test('runtime evidence reads the installed package versions', () => { + assert.equal(AGENT_RUNTIME_VERSION, installedVersion('@tangle-network/agent-runtime')) + assert.equal(AGENT_EVAL_VERSION, installedVersion('@tangle-network/agent-eval')) +}) diff --git a/test/intelligence-dispatch.test.ts b/test/intelligence-dispatch.test.ts index c073bc6..42e2901 100644 --- a/test/intelligence-dispatch.test.ts +++ b/test/intelligence-dispatch.test.ts @@ -434,7 +434,7 @@ test('the terminal opens saved ask and comparison results instead of reducing th terminal.sendInput('\u001b') await terminal.waitForRender() - assert.match(terminal.getViewport().join('\n'), /AgentProfile Braid starter/u) + assert.match(terminal.getViewport().join('\n'), /Analysis complete:/u) terminal.sendInput(`/compare ${baselineRunId} ${candidateRunId}`) terminal.sendInput('\r') await waitUntil(() => app.state().analyses.length === 2) @@ -454,10 +454,10 @@ test('the terminal opens saved ask and comparison results instead of reducing th assert.match(terminal.getViewport().join('\n'), /\/ask · frozen question/u) terminal.sendInput('\u001b[D') await terminal.waitForRender() - assert.match(terminal.getViewport().join('\n'), /AgentProfile Braid starter/u) + assert.match(terminal.getViewport().join('\n'), /(?:Analysis|Comparison) complete:/u) terminal.sendInput('\u001b') await terminal.waitForRender() - assert.match(terminal.getViewport().join('\n'), /AgentProfile Braid starter/u) + assert.match(terminal.getViewport().join('\n'), /(?:Analysis|Comparison) complete:/u) view.stop() await done @@ -534,6 +534,7 @@ test('saved analysis results present status-aware next actions', () => { recipe: 'ask', findings: [], citations: [], + citationSupport: { status: 'passed', supportedFindings: 0 }, footer: [], } as const diff --git a/test/keyboard.test.ts b/test/keyboard.test.ts index 47818e6..dc10c0f 100644 --- a/test/keyboard.test.ts +++ b/test/keyboard.test.ts @@ -17,6 +17,7 @@ import { VirtualTerminal } from './support/virtual-terminal.js' test('keyboard bindings accept legacy and Kitty encodings with fallback', () => { setKittyProtocolActive(false) assert.equal(matchesKey('\u0010', 'ctrl+p'), true) + assert.equal(matchesKey('\u001bs', 'alt+s'), true) assert.equal(matchesKey('\u001b[200~paste\u001b[201~', 'escape'), false) setKittyProtocolActive(true) assert.equal(matchesKey('\u001b[112;5u', 'ctrl+p'), true) diff --git a/test/observability.test.ts b/test/observability.test.ts index 8018992..4eaabb0 100644 --- a/test/observability.test.ts +++ b/test/observability.test.ts @@ -187,6 +187,38 @@ test('sandbox cleanup continues when resource sampling throws', async () => { ) }) +test('sandbox reconnect observes the server-issued environment returned by get', async () => { + const source = { + async create() { + throw new Error('reconnect must not create another sandbox') + }, + async get(id: string) { + return { + id, + name: 'reconnected-box', + status: 'running', + createdAt: new Date('2026-08-09T11:58:00.000Z'), + async *streamPrompt() {}, + } + }, + } as unknown as SandboxClientLike + const observed = observeSandboxClient( + source, + { mode: 'retained', cleanup: 'explicit', continuity: 'session' }, + () => at, + ) + + const box = await observed.client.get?.('sandbox-reconnected') + const snapshot = await observed.observation.snapshot() + + assert.equal(box?.id, 'sandbox-reconnected') + assert.equal(snapshot?.providerEnvironmentId, 'sandbox-reconnected') + assert.equal(snapshot?.name, 'reconnected-box') + assert.equal(snapshot?.lifecycle, 'ready') + assert.equal(snapshot?.lifecycleMode, 'retained') + assert.equal(snapshot?.cleanup, 'explicit') +}) + test('sandbox observation never guesses an unrecognized provider lifecycle', async () => { const source = { async create() { diff --git a/test/plain-accessibility.test.ts b/test/plain-accessibility.test.ts index 3e44222..cb34e85 100644 --- a/test/plain-accessibility.test.ts +++ b/test/plain-accessibility.test.ts @@ -194,6 +194,7 @@ test('plain accessibility output includes non-secret interaction details and cho responseScopes: ['once', 'session'], allowedOutcomes: ['accept', 'reject', 'cancel'], queuePosition: 0, + queueTotal: 1, secret: false, } const output = plainAccessibilityText(minimalView({ interactions: [interaction] })) diff --git a/test/production-composition.test.ts b/test/production-composition.test.ts index 7fbc9fd..4b6ef76 100644 --- a/test/production-composition.test.ts +++ b/test/production-composition.test.ts @@ -46,6 +46,7 @@ import { createProductionCredentialContext } from '../src/bin/production-credent import { DEFAULT_CLI_BRIDGE_ENDPOINT, describeProductionSelection, + loadProductionProfileCatalog, loadProductionSetup, persistableProductionProfile, prepareProductionSelection, @@ -228,7 +229,15 @@ test('production composition persists acknowledged CLI Bridge cancellation', asy reason: 'operator requested cancellation', }) const state = await cancellation.completion - assert.equal(state.runs[0]?.status, 'aborted') + assert.equal( + state.runs[0]?.status, + 'aborted', + JSON.stringify({ + run: state.runs[0], + events: app.events(), + cancellations: bridge.cancellations, + }), + ) assert.equal(state.runs[0]?.terminalReason, 'CONTROL_DETAIL') assert.equal(state.lastError, null) assert.equal(bridge.cancellations.length, 1) @@ -549,6 +558,36 @@ test('schema-v1 CLI Bridge profiles load as portable models and dispatch one run } }) +test('runner-only CLI Bridge models execute without an invented provider', async () => { + const root = await mkdtemp(join(tmpdir(), 'braid-production-runner-model-')) + const record = connection('cli-bridge', 'runner-model', 'http://127.0.0.1:4010') + const runnerProfile = defineAgentProfile({ + name: 'Claude runner alias', + harness: 'claude-code', + model: { default: 'opus', reasoningEffort: 'high' }, + }) + const prepared = await resolveProductionBackend( + { + connections: new ConnectionRegistry([record]), + workspaceCwd: root, + select: () => ({ connection: { connectionId: record.id } }), + }, + { + operationId: 'operation-runner-model', + runId: 'run-runner-model', + text: 'verify runner alias', + profile: runnerProfile, + signal: new AbortController().signal, + }, + { connection: { connectionId: record.id } }, + ) + + assert.equal(prepared.kind, 'prepared-execution') + assert.equal(prepared.backend.profile.model?.default, 'opus') + assert.equal(prepared.backend.profile.model?.provider, undefined) + assert.equal(prepared.materializationReceipt.route, 'claude-code/opus') +}) + test('startup resolves relative database keys beside external config and rejects workspace paths', async () => { const workspace = await mkdtemp(join(tmpdir(), 'braid-production-key-workspace-')) const configDirectory = await mkdtemp(join(tmpdir(), 'braid-production-key-config-')) @@ -1555,11 +1594,8 @@ test('first-run catalog preserves trusted profiles and adds each advertised mode true, ) assert.equal( - setup.profiles.filter( - (record) => - record.profile.model?.default === 'zai-coding-plan/glm-5.2' && - record.profile.model.provider === 'zai-coding-plan', - ).length, + setup.profiles.filter((record) => record.profile.model?.default === 'zai-coding-plan/glm-5.2') + .length, 1, ) assert.equal( @@ -1580,6 +1616,70 @@ test('first-run catalog preserves trusted profiles and adds each advertised mode assert.equal(initial?.profile.model?.default, 'zai-coding-plan/glm-5.2') }) +test('configured restart keeps alternate profiles and routes each run from its AgentProfile', async () => { + const root = await mkdtemp(join(tmpdir(), 'braid-production-restart-profiles-')) + await mkdir(join(root, '.braid'), { recursive: true }) + const product = defineAgentProfile({ + name: 'Product engineer', + harness: 'claude-code', + model: { default: 'opus' }, + }) + const analyst = defineAgentProfile({ + name: 'Trace analyst', + harness: 'claude-code', + model: { default: 'sonnet' }, + }) + await writeFile(join(root, '.braid', 'profile.json'), `${canonicalCandidateJson(analyst)}\n`, { + mode: 0o600, + }) + const selectedConnection = connection('cli-bridge', 'restart-profiles', 'http://127.0.0.1:3345') + const fetch: typeof globalThis.fetch = async (input) => { + const path = new URL(String(input)).pathname + if (path === '/health') { + return new Response( + JSON.stringify({ status: 'ok', backends: [{ name: 'claude-code', state: 'ready' }] }), + { status: 200 }, + ) + } + return new Response( + JSON.stringify({ + data: [ + { id: 'claude-code/opus', backend: 'claude-code' }, + { id: 'claude-code/sonnet', backend: 'claude-code' }, + ], + }), + { status: 200 }, + ) + } + const catalog = await loadProductionProfileCatalog( + { workspace: root, fetch }, + { profile: product, connections: [selectedConnection], connectionId: selectedConnection.id }, + selectedConnection.id, + ) + assert.deepEqual(catalog.map((record) => record.profile.name).sort(), [ + 'Product engineer', + 'Trace analyst', + ]) + + const composition = createProductionComposition({ + profile: product, + connections: [selectedConnection], + connectionId: selectedConnection.id, + workspaceRoot: root, + connectionOptions: { fetch }, + }) + const admission = await composition.execution.admit?.({ + operationId: 'operation-restart-profile-route', + runId: 'run-restart-profile-route', + text: 'Review the retained run.', + profile: analyst, + connectionId: selectedConnection.id, + workspaceRoot: root, + signal: new AbortController().signal, + }) + assert.equal(admission?.materializationReceipt?.route, 'claude-code/sonnet') +}) + test('first-run catalog keeps the Codex route out of the portable profile', async () => { const root = await mkdtemp(join(tmpdir(), 'braid-production-codex-route-')) const fetch: typeof globalThis.fetch = async (input) => { @@ -1609,6 +1709,7 @@ test('first-run catalog keeps the Codex route out of the portable profile', asyn const initial = setup.profiles.find((record) => record.id === setup.initialProfileId) assert.equal(initial?.profile.harness, 'codex') assert.equal(initial?.profile.model?.default, 'default') + assert.equal(initial?.profile.model?.provider, undefined) assert.equal( setup.profiles.some((record) => record.profile.model?.default === 'openai-codex/gpt-5.6-luna'), false, diff --git a/test/profiles.test.ts b/test/profiles.test.ts index ba18868..9f5db3b 100644 --- a/test/profiles.test.ts +++ b/test/profiles.test.ts @@ -217,7 +217,7 @@ test('the installed canonical profile schema round-trips every field and extensi assert.equal(canonicalCandidateJson(imported.profile), json) assert.deepEqual(imported.profile.extensions, fullProfile.extensions) assert.deepEqual(imported.profile.resources, fullProfile.resources) - assert.equal(AGENT_INTERFACE_PACKAGE_VERSION, '0.46.1') + assert.equal(AGENT_INTERFACE_PACKAGE_VERSION, '0.47.0') }) test('unknown canonical fields fail closed while namespaced extensions remain opaque', () => { diff --git a/test/retained-execution-lifecycle.test.ts b/test/retained-execution-lifecycle.test.ts new file mode 100644 index 0000000..f1805ca --- /dev/null +++ b/test/retained-execution-lifecycle.test.ts @@ -0,0 +1,324 @@ +import assert from 'node:assert/strict' +import test from 'node:test' +import { defineAgentProfile, type AgentExactRunControlRef } from '@tangle-network/agent-interface' +import type { RetainedRunHandle } from '@tangle-network/agent-runtime/kernel' +import { + type RetainedExecutionPlan, + RetainedExecutionPort, +} from '../src/adapters/runtime/retained-execution.js' +import { DEFAULT_RUN_CAPABILITIES, type ExecuteTurnInput } from '../src/ports/execution.js' + +const now = '2026-08-12T12:00:00.000Z' +const profile = defineAgentProfile({ + name: 'Retained lifecycle test', + harness: 'pi', + model: { provider: 'openai', default: 'openai/gpt-5' }, +}) + +type CancelOptions = Parameters[0] +type CancelResult = Awaited> + +function controlRef(suffix: string): AgentExactRunControlRef { + return { + provider: 'retained-test', + environmentId: `environment-${suffix}`, + sessionId: `session-${suffix}`, + executionId: `execution-${suffix}`, + runId: `provider-run-${suffix}`, + requestDigest: `sha256:${'a'.repeat(64)}`, + } +} + +function input(suffix: string): ExecuteTurnInput { + return { + operationId: `operation-${suffix}`, + runId: `run-${suffix}`, + text: 'Continue the retained task.', + profile, + signal: new AbortController().signal, + } +} + +function handle( + exact: AgentExactRunControlRef, + options: { + readonly cancel?: (input: CancelOptions) => Promise + readonly status?: RetainedRunHandle['status'] + } = {}, +): RetainedRunHandle { + return { + controlRef: exact, + status: + options.status ?? + (async () => ({ + runId: exact.runId, + controlRef: exact, + status: 'running' as const, + effect: 'unknown' as const, + observedAt: now, + })), + async *events() {}, + async result() { + return { text: '', success: true } + }, + async respondToInteraction() { + throw new Error('interaction is not part of this test') + }, + async contextBoundary() { + return null + }, + async continueNative() { + throw new Error('native continuation is not part of this test') + }, + cancel: + options.cancel ?? + (async (request) => ({ + operationId: request.operationId, + requestDigest: exact.requestDigest, + status: 'accepted' as const, + effect: 'cancelled' as const, + snapshot: { + runId: exact.runId, + controlRef: exact, + status: 'cancelled' as const, + effect: 'cancelled' as const, + observedAt: now, + }, + })), + } +} + +function plan( + exact: AgentExactRunControlRef, + start: () => Promise, + startedHandle = handle(exact), +): RetainedExecutionPlan { + return { + providerName: exact.provider, + environmentId: exact.environmentId, + providerSessionId: exact.sessionId, + model: 'openai/gpt-5', + capabilities: DEFAULT_RUN_CAPABILITIES, + materializationReceipt: {}, + start, + reconnect: async () => startedHandle, + discover: async () => null, + observe: async () => undefined, + projectStatus: () => 'streaming', + isTerminalStatus: () => false, + projectResult: (result) => ({ text: result.text, usage: { input: 0, output: 0 } }), + projectFinal: () => { + throw new Error('final projection is not part of this test') + }, + } +} + +function executionFor( + resolve: (input: ExecuteTurnInput) => Promise, +): RetainedExecutionPort { + return new RetainedExecutionPort({ + resolve, + recover: async () => { + throw new Error('recovery is not part of this lifecycle test') + }, + }) +} + +test('pre-start cancellation prevents the retained plan from starting', async () => { + const exact = controlRef('pre-start') + let starts = 0 + const retainedPlan = plan(exact, async () => { + starts += 1 + return handle(exact) + }) + const execution = executionFor(async () => retainedPlan) + const runInput = input('pre-start') + + await execution.admit(runInput) + const acknowledgement = await execution.cancelRun({ + operationId: 'operation-pre-start-cancel', + runId: runInput.runId, + }) + + assert.deepEqual(acknowledgement, { + operationId: 'operation-pre-start-cancel', + outcome: 'accepted', + detail: 'cancelled-before-start', + }) + const stream = execution.streamTurn(runInput)[Symbol.asyncIterator]() + assert.deepEqual(await stream.next(), { done: true, value: undefined }) + assert.equal(starts, 0) + + const replayed = await execution.cancelRun({ + operationId: 'operation-pre-start-cancel-replay', + runId: runInput.runId, + }) + assert.equal(replayed.outcome, 'already-applied') +}) + +test('in-flight start cancellation omits an expired foreground signal', async () => { + const exact = controlRef('in-flight') + let resolveStart: (value: RetainedRunHandle) => void = () => undefined + let receivedSignal: AbortSignal | undefined + const started = new Promise((resolve) => { + resolveStart = resolve + }) + const startedHandle = handle(exact, { + cancel: async (request) => { + receivedSignal = request.signal + return { + operationId: request.operationId, + requestDigest: exact.requestDigest, + status: 'accepted', + effect: 'cancelled', + snapshot: { + runId: exact.runId, + controlRef: exact, + status: 'cancelled', + effect: 'cancelled', + observedAt: now, + }, + } + }, + }) + const execution = executionFor(async () => plan(exact, async () => started, startedHandle)) + const runInput = input('in-flight') + await execution.admit(runInput) + const stream = execution.streamTurn(runInput)[Symbol.asyncIterator]() + const streamCompletion = stream.next().catch(() => undefined) + await new Promise((resolve) => setImmediate(resolve)) + + const control = new AbortController() + const cancellation = execution.cancelRun({ + operationId: 'operation-in-flight-cancel', + runId: runInput.runId, + signal: control.signal, + }) + control.abort(new Error('foreground deadline elapsed')) + resolveStart(startedHandle) + + const acknowledgement = await cancellation + await streamCompletion + assert.equal(acknowledgement.outcome, 'accepted') + assert.equal(receivedSignal, undefined) +}) + +test('ambiguous cancellation effects reconcile or remain unknown', async () => { + for (const effect of ['unknown', 'not_live'] as const) { + const exact = controlRef(`ambiguous-${effect}`) + let statusCalls = 0 + const retainedHandle = handle(exact, { + status: async () => { + statusCalls += 1 + return { + runId: exact.runId, + controlRef: exact, + status: 'running', + effect: 'unknown', + observedAt: now, + } + }, + cancel: async (request) => ({ + operationId: request.operationId, + requestDigest: exact.requestDigest, + status: 'accepted', + effect, + snapshot: { + runId: exact.runId, + controlRef: exact, + status: 'running', + effect, + observedAt: now, + }, + }), + }) + const execution = executionFor(async () => + plan(exact, async () => retainedHandle, retainedHandle), + ) + const runInput = input(`ambiguous-${effect}`) + await execution.admit(runInput) + const stream = execution.streamTurn(runInput)[Symbol.asyncIterator]() + await stream.next().catch(() => undefined) + + const acknowledgement = await execution.cancelRun({ + operationId: `operation-ambiguous-${effect}`, + runId: runInput.runId, + }) + assert.deepEqual(acknowledgement, { + operationId: `operation-ambiguous-${effect}`, + outcome: 'unknown', + detail: effect, + }) + assert.equal(statusCalls, 1) + } +}) + +test('a reconciled cancelled snapshot confirms an ambiguous cancellation', async () => { + const exact = controlRef('reconciled') + let statusCalls = 0 + const retainedHandle = handle(exact, { + status: async () => { + statusCalls += 1 + return { + runId: exact.runId, + controlRef: exact, + status: 'cancelled', + effect: 'cancelled', + observedAt: now, + } + }, + cancel: async (request) => ({ + operationId: request.operationId, + requestDigest: exact.requestDigest, + status: 'accepted', + effect: 'unknown', + snapshot: { + runId: exact.runId, + controlRef: exact, + status: 'running', + effect: 'unknown', + observedAt: now, + }, + }), + }) + const execution = executionFor(async () => + plan(exact, async () => retainedHandle, retainedHandle), + ) + const runInput = input('reconciled') + await execution.admit(runInput) + const stream = execution.streamTurn(runInput)[Symbol.asyncIterator]() + await stream.next().catch(() => undefined) + + const acknowledgement = await execution.cancelRun({ + operationId: 'operation-reconciled-cancel', + runId: runInput.runId, + }) + assert.deepEqual(acknowledgement, { + operationId: 'operation-reconciled-cancel', + outcome: 'accepted', + detail: 'cancelled', + }) + assert.equal(statusCalls, 1) +}) + +test('admission rejects at capacity instead of evicting a prepared plan', async () => { + let starts = 0 + const execution = executionFor(async (runInput) => { + const exact = controlRef(runInput.runId) + return plan(exact, async () => { + starts += 1 + return handle(exact) + }) + }) + const inputs = Array.from({ length: 129 }, (_, index) => input(`capacity-${index}`)) + + for (const runInput of inputs.slice(0, 128)) await execution.admit(runInput) + await assert.rejects( + execution.admit(inputs[128] as ExecuteTurnInput), + /Retained execution admission capacity reached/u, + ) + + const firstStream = execution.streamTurn(inputs[0] as ExecuteTurnInput)[Symbol.asyncIterator]() + await firstStream.next().catch(() => undefined) + assert.equal(starts, 1) +}) diff --git a/test/scripts.test.ts b/test/scripts.test.ts index 359ea11..457990e 100644 --- a/test/scripts.test.ts +++ b/test/scripts.test.ts @@ -39,6 +39,8 @@ const upstreamSupport = await import('../scripts/release/upstream-evidence.mjs') const { evaluateUpstreamRequirementChecks, UPSTREAM_REQUIREMENT_OWNERS } = upstreamSupport // @ts-expect-error The release scripts are intentionally JavaScript entry points. const { renderVerificationReport } = await import('../scripts/release/verification-report.mjs') +// @ts-expect-error The visual definitions are an executable JavaScript release helper. +const { createStateDefinitions } = await import('../scripts/capture-visual-definitions.mjs') const packageJson = JSON.parse( await readFile(new URL('../../package.json', import.meta.url), 'utf8'), @@ -67,6 +69,43 @@ test('W5 exposes stable checks for every requested release surface', () => { for (const script of required) assert.equal(typeof packageJson.scripts[script], 'string', script) }) +test('active streaming capture waits for a terminal run state before exiting', async () => { + let screen = 'working' + let cancelled = false + let closed = false + const definition = createStateDefinitions((value: string) => value).find( + (candidate: { readonly name: string }) => candidate.name === 'active-streaming', + ) + assert.ok(definition) + + await definition.run({ + input(data: string) { + if (data === '\r' && cancelled) screen = 'stopping' + if (data === '/cancel') cancelled = true + }, + screen: () => screen, + async waitFor(predicate: () => boolean, label: string) { + if (label === 'active work') { + assert.equal(predicate(), true) + return + } + assert.equal(label, 'cancellation') + assert.equal(predicate(), false, 'stopping is not a terminal run state') + screen = 'cancelled' + assert.equal(predicate(), true) + }, + async captureState() { + return { point: { screen: 'working' }, record: { view: { status: 'running' } } } + }, + async closeNormally() { + assert.equal(screen, 'cancelled') + closed = true + }, + }) + + assert.equal(closed, true) +}) + test('the scoped test runner rejects an unregistered scope instead of silently running the wrong suite', async () => { const source = await readFile(new URL('../../scripts/run-tests.mjs', import.meta.url), 'utf8') assert.match(source, /No compiled tests registered for scope/u) diff --git a/test/security.test.ts b/test/security.test.ts index 07184bf..230dfa9 100644 --- a/test/security.test.ts +++ b/test/security.test.ts @@ -906,6 +906,16 @@ test('secret-designated interaction values are rejected before journal persisten assert.doesNotThrow(() => assertPersistablePayload({ inputTokens: 12, outputTokens: 7, reasoningTokens: 3 }), ) + assert.doesNotThrow(() => + assertPersistablePayload({ maxOutputTokens: 16_384, maxReasoningTokens: 32_768 }), + ) + assert.throws( + () => assertPersistablePayload({ maxOutputTokens: 'never persist this' }), + (error: unknown) => + error instanceof Error && + error.name === 'StorageError' && + error.message.includes('Secret-bearing'), + ) assert.doesNotThrow(() => assertPersistablePayload({ tokensKnown: false })) assert.throws( () => assertPersistablePayload({ tokensKnown: 'false' }), diff --git a/test/support/runtime-bridge-server.ts b/test/support/runtime-bridge-server.ts index ffda294..56d098d 100644 --- a/test/support/runtime-bridge-server.ts +++ b/test/support/runtime-bridge-server.ts @@ -23,10 +23,20 @@ export interface RuntimeBridgeServerOptions { readonly expectedBearer?: string readonly responseText?: string | ((body: Readonly>) => string) readonly estimatedCostUsd?: number + readonly usage?: { + readonly promptTokens: number + readonly completionTokens: number + readonly cacheReadInputTokens?: number + readonly cacheCreationInputTokens?: number + readonly reasoningTokens?: number + } readonly holdStreams?: boolean + readonly statusFailureStatus?: number + readonly statusRunId?: string readonly cancellation?: { readonly mode?: 'acknowledged' | 'rejected' readonly delayMs?: number + readonly effect?: 'cancel_requested' | 'cancelled' } } @@ -130,13 +140,21 @@ function runtimeResponseFrames( body: Readonly>, responseText: string, estimatedCostUsd?: number, + usage?: RuntimeBridgeServerOptions['usage'], ): readonly RuntimeResponseFrame[] { const terminal = { choices: [{ delta: {}, finish_reason: 'stop' }], usage: { model_requests: 1, - prompt_tokens: 2, - completion_tokens: 3, + prompt_tokens: usage?.promptTokens ?? 2, + completion_tokens: usage?.completionTokens ?? 3, + ...(usage?.cacheReadInputTokens === undefined + ? {} + : { cache_read_input_tokens: usage.cacheReadInputTokens }), + ...(usage?.cacheCreationInputTokens === undefined + ? {} + : { cache_creation_input_tokens: usage.cacheCreationInputTokens }), + ...(usage?.reasoningTokens === undefined ? {} : { reasoning_tokens: usage.reasoningTokens }), cost_known: false, ...(estimatedCostUsd === undefined ? {} @@ -161,14 +179,24 @@ function runtimeResponseResult( body: Readonly>, text: string, estimatedCostUsd?: number, + usage?: RuntimeBridgeServerOptions['usage'], ): Readonly> { + const promptTokens = usage?.promptTokens ?? 2 + const completionTokens = usage?.completionTokens ?? 3 return { choices: [{ message: { role: 'assistant', content: text }, finish_reason: 'stop' }], usage: { model_requests: 1, - prompt_tokens: 2, - completion_tokens: 3, - total_tokens: 5, + prompt_tokens: promptTokens, + completion_tokens: completionTokens, + total_tokens: promptTokens + completionTokens, + ...(usage?.cacheReadInputTokens === undefined + ? {} + : { cache_read_input_tokens: usage.cacheReadInputTokens }), + ...(usage?.cacheCreationInputTokens === undefined + ? {} + : { cache_creation_input_tokens: usage.cacheCreationInputTokens }), + ...(usage?.reasoningTokens === undefined ? {} : { reasoning_tokens: usage.reasoningTokens }), ...(estimatedCostUsd === undefined ? {} : { cost: estimatedCostUsd }), }, profile_materialization: profileMaterialization(body), @@ -315,10 +343,15 @@ export async function startRuntimeBridgeServer( response.end(JSON.stringify({ error: { type: 'run_not_found' } })) return } + if (options.statusFailureStatus !== undefined) { + response.writeHead(options.statusFailureStatus, { 'content-type': 'application/json' }) + response.end(JSON.stringify({ error: { type: 'status_unavailable' } })) + return + } response.writeHead(200, { 'content-type': 'application/json' }) response.end( JSON.stringify({ - id: run.id, + id: options.statusRunId ?? run.id, requestDigest: run.digest, status: run.status, terminal: run.terminal, @@ -365,7 +398,9 @@ export async function startRuntimeBridgeServer( } return } - if (run !== undefined) { + const cancellationEffect = + run === undefined ? 'not_live' : (options.cancellation?.effect ?? 'cancelled') + if (run !== undefined && cancellationEffect === 'cancelled') { run.status = 'cancelled' run.terminal = true finishReaders(run) @@ -388,7 +423,7 @@ export async function startRuntimeBridgeServer( requestDigest: exactCancellation.requestDigest, run: exactCancellation.run, status: 'accepted', - effect: run === undefined ? 'not_live' : 'cancelled', + effect: cancellationEffect, }), ) } else { @@ -396,8 +431,13 @@ export async function startRuntimeBridgeServer( JSON.stringify({ cancelled: run !== undefined, cancel_requested: true, - terminal: true, - run: { id: runId, requestDigest, status: 'cancelled', terminal: true }, + terminal: cancellationEffect !== 'cancel_requested', + run: { + id: runId, + requestDigest, + status: cancellationEffect === 'cancel_requested' ? 'running' : 'cancelled', + terminal: cancellationEffect !== 'cancel_requested', + }, }), ) } @@ -435,10 +475,12 @@ export async function startRuntimeBridgeServer( 'x-run-id': runId, 'x-run-request-digest': digest, }) - response.end(JSON.stringify(runtimeResponseResult(body, text, options.estimatedCostUsd))) + response.end( + JSON.stringify(runtimeResponseResult(body, text, options.estimatedCostUsd, options.usage)), + ) return } - const frames = runtimeResponseFrames(body, text, options.estimatedCostUsd) + const frames = runtimeResponseFrames(body, text, options.estimatedCostUsd, options.usage) const run = existing ?? ({ diff --git a/test/tangle-live07-wiring.test.mjs b/test/tangle-live07-wiring.test.mjs new file mode 100644 index 0000000..c06b64e --- /dev/null +++ b/test/tangle-live07-wiring.test.mjs @@ -0,0 +1,166 @@ +import assert from 'node:assert/strict' +import { resolve } from 'node:path' +import test from 'node:test' +import { runSandbox, runTangleFlows } from '../scripts/live-required/tangle.mjs' +import { cleanupRetainedResourceByRunId } from '../scripts/live-required/tangle-sandbox-braid-stress.mjs' + +const repository = resolve(new URL('../', import.meta.url).pathname) + +function passedStressProof() { + const first = { id: 'run-first', environmentId: 'environment-local' } + return { + status: 'passed', + config: { + endpoint: 'https://sandbox.tangle.tools', + connectionId: 'connection-live-07', + connectionKind: 'tangle-sandbox', + credentialConfigured: true, + model: 'glm-5.2', + runner: 'pi', + }, + runs: { + first, + resumed: { id: first.id, environmentId: first.environmentId }, + followUp: { id: 'run-follow-up', environmentId: first.environmentId }, + cancelled: { id: 'run-cancelled', environmentId: first.environmentId }, + }, + replay: { + resumeFromCursor: 'cursor-before-kill', + finalCursor: 'cursor-final', + }, + cleanup: { exactResource: true, activeResourceDelta: 0 }, + progress: { + firstControlRef: { + provider: 'tangle-sandbox', + environmentId: 'environment-cloud', + sessionId: 'session-cloud', + executionId: 'execution-cloud', + runId: 'run-cloud', + requestDigest: `sha256:${'a'.repeat(64)}`, + }, + }, + } +} + +function passedStressCohort() { + const attempts = Array.from({ length: 3 }, (_, index) => ({ + index, + proof: { + ...passedStressProof(), + proofId: `proof-${index}`, + progress: { + firstControlRef: { + ...passedStressProof().progress.firstControlRef, + environmentId: `environment-cloud-${index}`, + }, + }, + }, + })) + return { + status: 'passed', + failures: [], + requestedRuns: 3, + attemptedRuns: 3, + concurrency: 2, + cleanup: { exactProofs: 3, exactResourcesRemaining: 0, activeResourceDelta: 0 }, + attempts, + } +} + +test('built-in LIVE-07 wiring emits evidence and one measurement with deduped run IDs', async () => { + const stressRunner = async () => passedStressCohort() + const sandbox = await runSandbox({ + repository, + environment: {}, + binary: 'unused-injected-binary', + invocationId: 'live-required-test-invocation', + stressRunner, + }) + + assert.equal(sandbox.status, 'passed') + assert.equal(sandbox.measurement.name, 'LIVE-07') + assert.equal(sandbox.evidence.status, 'passed') + assert.deepEqual(sandbox.evidence.run.ids, ['run-first', 'run-follow-up', 'run-cancelled']) + + const flows = await runTangleFlows({ + repository, + environment: {}, + inferenceRunner: async () => ({ + status: 'passed', + measurement: { kind: 'scalar', name: 'LIVE-06', unit: 'verified-flow', value: 1 }, + evidence: null, + }), + sandboxRunner: (input) => runSandbox({ ...input, stressRunner }), + matrixRunner: async () => ({ status: 'unavailable', reason: 'not in LIVE-07 scope' }), + }) + + assert.deepEqual( + flows.measurements.map((measurement) => measurement.name), + ['LIVE-06', 'LIVE-07'], + ) + assert.equal(flows.flows.find((flow) => flow.row === 'LIVE-07')?.evidence?.status, 'passed') + assert.deepEqual( + flows.unavailable.map((entry) => entry.row), + ['LIVE-08', 'LIVE-09', 'LIVE-10'], + ) +}) + +test('LIVE-07 rejects a passing canary presented as a stress cohort', async () => { + const cohort = passedStressCohort() + await assert.rejects( + runSandbox({ + repository, + environment: {}, + binary: 'unused-injected-binary', + invocationId: 'live-required-test-underpowered-cohort', + stressRunner: async () => ({ + ...cohort, + requestedRuns: 1, + attemptedRuns: 1, + concurrency: 1, + cleanup: { exactProofs: 1, exactResourcesRemaining: 0, activeResourceDelta: 0 }, + attempts: cohort.attempts.slice(0, 1), + }), + }), + /at least three complete cloud proofs/u, + ) +}) + +test('fail-safe cleanup derives one exact Braid resource from the first local run ID', async () => { + const firstRunId = 'local/run-1' + const box = { + id: 'environment-fallback', + name: 'braid-session-braid-local-run-1', + metadata: { + owner: 'braid', + lifecycle: 'retained', + providerSessionId: 'session-braid-local-run-1', + }, + deleted: false, + async delete() { + this.deleted = true + }, + } + const other = { + id: 'environment-other', + name: box.name, + metadata: { + owner: 'other', + lifecycle: 'retained', + providerSessionId: box.metadata.providerSessionId, + }, + } + const client = { + async list() { + return [other, box] + }, + async get(id) { + return id === box.id && !box.deleted ? box : null + }, + } + + const cleanup = await cleanupRetainedResourceByRunId(client, firstRunId) + assert.equal(cleanup.confirmed, true) + assert.equal(cleanup.id, box.id) + assert.equal(box.deleted, true) +}) diff --git a/test/tangle-sandbox-braid-soak-script.test.mjs b/test/tangle-sandbox-braid-soak-script.test.mjs new file mode 100644 index 0000000..a9ec8d1 --- /dev/null +++ b/test/tangle-sandbox-braid-soak-script.test.mjs @@ -0,0 +1,188 @@ +import assert from 'node:assert/strict' +import test from 'node:test' +import { setTimeout as delay } from 'node:timers/promises' +import { runBraidSandboxSoak } from '../scripts/live-required/tangle-sandbox-braid-soak.mjs' + +function passedProof(index, overrides = {}) { + return { + status: 'passed', + proofId: `proof-${index}`, + processes: { binarySha256: 'b'.repeat(64) }, + progress: { + firstControlRef: { + provider: 'tangle-sandbox', + environmentId: `sandbox-${index}`, + sessionId: `session-${index}`, + executionId: `execution-${index}`, + runId: `provider-run-${index}`, + requestDigest: `sha256:${'a'.repeat(64)}`, + }, + }, + cleanup: { + exactResource: true, + activeResourceDelta: 0, + identity: { confirmed: true, remainingIds: [] }, + }, + accountIdentityConsistency: { + stable: true, + customerId: 'customer-1', + billingOwnerId: 'billing-owner-1', + }, + usage: [ + { + phase: 'before', + status: 'observed', + value: { activeSandboxes: 0, totalSandboxes: index, computeMinutes: index }, + }, + { + phase: 'after', + status: 'observed', + value: { activeSandboxes: 0, totalSandboxes: index + 1, computeMinutes: index + 1 }, + }, + ], + spend: { + rows: [ + { + label: 'resumed-first-turn', + tokens: { status: 'observed', input: 10 + index, output: 2 + index }, + cost: { status: 'observed', usd: 0.001 + index / 1_000 }, + }, + { + label: 'follow-up-turn', + tokens: { status: 'observed', input: 5, output: 1 }, + cost: { status: 'unavailable' }, + }, + { + label: 'cancelled-turn', + tokens: { status: 'unavailable' }, + cost: { status: 'unavailable' }, + }, + ], + }, + timing: { + totalMs: 100 + index, + workspace: { elapsedMs: 20 + index }, + reconnect: { elapsedMs: 30 + index }, + }, + ...overrides, + } +} + +test('sandbox soak stops after a failed canary before spending on more runs', async () => { + let calls = 0 + const result = await runBraidSandboxSoak({ + runs: 5, + concurrency: 3, + stressRunner: async ({ attemptIndex, requireZeroActiveResourceDelta }) => { + calls += 1 + assert.equal(attemptIndex, 0) + assert.equal(requireZeroActiveResourceDelta, true) + return passedProof(0, { status: 'failed' }) + }, + }) + + assert.equal(calls, 1) + assert.equal(result.status, 'failed') + assert.equal(result.attemptedRuns, 1) + assert.equal(result.stoppedAfterCanary, true) + assert.match(result.failures[0], /attempted 1 of 5/u) +}) + +test('sandbox soak preserves protected-provider unavailability', async () => { + const unavailable = new Error('protected credential is unavailable') + unavailable.unavailable = true + await assert.rejects( + runBraidSandboxSoak({ + runs: 3, + stressRunner: async () => { + throw unavailable + }, + }), + (error) => error === unavailable, + ) +}) + +test('sandbox soak runs one canary then a bounded parallel cohort with full summaries', async () => { + let active = 0 + let maximumActive = 0 + const strictness = [] + const result = await runBraidSandboxSoak({ + runs: 5, + concurrency: 2, + stressRunner: async ({ attemptIndex, requireZeroActiveResourceDelta }) => { + strictness[attemptIndex] = requireZeroActiveResourceDelta + active += 1 + maximumActive = Math.max(maximumActive, active) + if (attemptIndex > 0) await delay(5) + active -= 1 + return passedProof(attemptIndex) + }, + }) + + assert.equal(result.status, 'passed') + assert.equal(result.attemptedRuns, 5) + assert.equal(maximumActive, 2) + assert.deepEqual(strictness, [true, false, false, false, false]) + assert.deepEqual(result.latency.totalMs, { + n: 5, + min: 100, + median: 102, + p90: 104, + max: 104, + }) + assert.equal(result.latency.phases.workspace.n, 5) + assert.equal(result.cleanup.exactProofs, 5) + assert.equal(result.cleanup.exactResourcesRemaining, 0) + assert.deepEqual(result.sessionSpend.tokens, { + observedRuns: 10, + unavailableRuns: 5, + missingRuns: 0, + input: 85, + output: 25, + }) + assert.equal(result.sessionSpend.cost.observedRuns, 5) + assert.equal(result.sessionSpend.cost.unavailableRuns, 10) + assert.ok(Math.abs(result.sessionSpend.cost.usd - 0.015) < Number.EPSILON) +}) + +test('sandbox soak rejects reused cloud identity despite individually passing proofs', async () => { + const result = await runBraidSandboxSoak({ + runs: 2, + concurrency: 1, + stressRunner: async ({ attemptIndex }) => + passedProof(attemptIndex, { + progress: { + firstControlRef: { + provider: 'tangle-sandbox', + environmentId: 'sandbox-reused', + sessionId: `session-${attemptIndex}`, + executionId: `execution-${attemptIndex}`, + runId: `provider-run-${attemptIndex}`, + requestDigest: `sha256:${'a'.repeat(64)}`, + }, + }, + }), + }) + + assert.equal(result.status, 'failed') + assert.ok(result.failures.includes('cloud environment identity was reused across runs')) +}) + +test('sandbox soak stops scheduling after a post-canary failure', async () => { + const started = [] + const result = await runBraidSandboxSoak({ + runs: 8, + concurrency: 2, + stressRunner: async ({ attemptIndex }) => { + started.push(attemptIndex) + if (attemptIndex === 1) return passedProof(attemptIndex, { status: 'failed' }) + if (attemptIndex > 1) await delay(10) + return passedProof(attemptIndex) + }, + }) + + assert.equal(result.status, 'failed') + assert.ok(started.length <= 3) + assert.ok(started.includes(0)) + assert.ok(started.includes(1)) +}) diff --git a/test/tangle-sandbox-braid-stress-script.test.mjs b/test/tangle-sandbox-braid-stress-script.test.mjs new file mode 100644 index 0000000..05565e9 --- /dev/null +++ b/test/tangle-sandbox-braid-stress-script.test.mjs @@ -0,0 +1,356 @@ +import assert from 'node:assert/strict' +import test from 'node:test' + +import { + cloudFailureEventTimeline, + spendDisclosure, +} from '../scripts/live-required/tangle-sandbox-braid-stress.mjs' + +import { + assertEnvironmentIdentity, + assertExclusiveResume, + assertNonTerminalRun, + assertNonVacuousVisibleEvents, + assertProviderResumeProgress, + assertSameCloudSession, + assertSameControlRef, + assertUniqueVisibleEvents, + environmentForRun, + latestCursorFromResponses, + MissingIntegrationError, + observationFromResponses, + resourceDelta, + runObservations, + visibleEventKeys, + waitForControlIdentity, + waitForVisibleEvents, +} from '../scripts/live-required/tangle-sandbox-braid-stress-support.mjs' + +const controlRef = { + provider: 'tangle-sandbox', + environmentId: 'sandbox-1', + sessionId: 'session-1', + executionId: 'execution-1', + runId: 'provider-run-1', + requestDigest: `sha256:${'a'.repeat(64)}`, +} + +function event(kind, payload) { + return { type: 'event', event: { kind, payload } } +} + +test('extracts exact control identity and an explicit provider cursor', () => { + const responses = [ + event('run.environment.observed', { + runId: 'local-run-1', + controlRef, + provider: { eventId: 'environment-event-1', providerSequence: 1 }, + }), + event('run.text.delta', { + runId: 'local-run-1', + provider: { + cursor: 'cursor-7', + eventId: 'provider-event-7', + providerSequence: 2, + }, + }), + ] + + assert.deepEqual(observationFromResponses(responses, 'local-run-1'), { + controlRef, + cursor: 'cursor-7', + event: responses[0], + }) + assert.equal(latestCursorFromResponses(responses, 'local-run-1'), 'cursor-7') +}) + +test('rejects visible events without stable provider identity and catches duplicates', () => { + assert.throws( + () => assertUniqueVisibleEvents([event('run.text.delta', { runId: 'run-1' })], 'run-1', 'test'), + MissingIntegrationError, + ) + + const responses = [ + event('run.text.delta', { + runId: 'run-1', + provider: { eventId: 'event-1', providerSequence: 1, cursor: 'cursor-1' }, + }), + event('run.text.delta', { + runId: 'run-1', + provider: { eventId: 'event-1', providerSequence: 2, cursor: 'cursor-2' }, + }), + ] + assert.throws(() => assertUniqueVisibleEvents(responses, 'run-1', 'test'), /duplicate visible/iu) +}) + +test('never attributes an unscoped provider event to a run', () => { + const responses = [ + event('run.environment.observed', { value: { runId: 'run-1' } }), + event('run.environment.observed', { value: { runId: 'run-2' } }), + event('run.text.delta', { source: { eventId: 'ambiguous-event' } }), + ] + assert.deepEqual(visibleEventKeys(responses, 'run-1'), []) + assert.deepEqual( + visibleEventKeys( + [ + event('run.environment.observed', { runId: 'run-1' }), + event('run.text.delta', { + provider: { eventId: 'ambiguous-event', providerSequence: 1, cursor: 'cursor-1' }, + }), + ], + 'run-1', + ), + [], + ) +}) + +test('requires an exclusive resume set and preserves local environment identity', () => { + assert.deepEqual(assertExclusiveResume(['ack-1'], ['fresh-2']), []) + assert.throws(() => assertExclusiveResume(['ack-1'], ['ack-1']), /acknowledged/iu) + + const environment = { + id: 'environment-local-1', + providerEnvironmentId: 'sandbox-1', + } + const state = { environments: [environment] } + const run = { environmentId: environment.id } + assert.equal(environmentForRun(state, run), environment) + assert.equal( + assertEnvironmentIdentity(run, state, { environmentId: 'sandbox-1' }, 'test'), + environment, + ) +}) + +test('distinguishes same-run replay identity from a new cloud turn', () => { + assert.doesNotThrow(() => assertSameControlRef(controlRef, { ...controlRef }, 'reconnect')) + assert.doesNotThrow(() => + assertSameCloudSession( + controlRef, + { + ...controlRef, + executionId: 'execution-2', + runId: 'provider-run-2', + requestDigest: `sha256:${'b'.repeat(64)}`, + }, + 'follow-up', + ), + ) +}) + +test('preserves unknown resource values instead of treating them as zero', () => { + assert.deepEqual( + resourceDelta( + { activeSandboxes: 2, computeMinutes: 4 }, + { activeSandboxes: 2, computeMinutes: undefined }, + ), + { + activeSandboxes: 0, + totalSandboxes: null, + computeMinutes: null, + gpuSeconds: null, + gpuCostUsd: null, + unknownFields: ['totalSandboxes', 'computeMinutes', 'gpuSeconds', 'gpuCostUsd'], + }, + ) +}) + +test('preserves observed zero, unavailable null, and missing telemetry projections', () => { + const environment = { + id: 'environment-local-1', + providerEnvironmentId: 'sandbox-1', + lifecycle: 'ready', + placement: { provider: 'tangle', region: 'us-west-2' }, + accountUsage: { activeSandboxes: 0 }, + unavailableTelemetry: ['gpu'], + } + const observations = runObservations( + { + id: 'run-1', + environmentId: environment.id, + inputTokens: 0, + outputTokens: 0, + costUsd: null, + }, + { environments: [environment] }, + ) + + assert.deepEqual(observations.run.inputTokens, { status: 'observed', value: 0 }) + assert.deepEqual(observations.run.costUsd, { status: 'unavailable', value: null }) + assert.deepEqual(observations.run.latencyMs, { status: 'missing' }) + assert.deepEqual(observations.environment.accountUsage, { + status: 'observed', + value: { activeSandboxes: 0 }, + }) + assert.deepEqual(observations.environment.gpu, { status: 'unavailable', value: null }) + assert.deepEqual(observations.environment.machineId, { status: 'missing' }) +}) + +test('summarizes every unique cloud run without converting unknown spend to zero', () => { + const spend = spendDisclosure({ + resumed: { + id: 'run-first', + status: 'completed', + inputTokens: 0, + outputTokens: 7, + tokensKnown: true, + costUsd: 0, + usdKnown: true, + costStatus: 'reported', + startedAt: '2026-08-12T00:00:00.000Z', + terminalAt: '2026-08-12T00:00:01.000Z', + }, + followUp: { + id: 'run-follow-up', + status: 'completed', + tokensKnown: false, + usdKnown: false, + }, + cancelled: { id: 'run-cancelled', status: 'cancelled' }, + }) + + assert.deepEqual(spend.totals.tokens, { + observedRuns: 1, + unavailableRuns: 1, + missingRuns: 1, + input: 0, + output: 7, + }) + assert.deepEqual(spend.totals.cost, { + observedRuns: 1, + unavailableRuns: 1, + missingRuns: 1, + usd: 0, + }) + assert.deepEqual(spend.rows[0].duration, { status: 'observed', milliseconds: 1000 }) + assert.equal(spend.rows[1].tokens.status, 'unavailable') + assert.equal(spend.rows[2].tokens.status, 'missing') +}) + +test('failure diagnostics retain the run boundary without exposing credentials', () => { + const timeline = cloudFailureEventTimeline( + [ + { + type: 'event', + sequence: 7, + event: { + kind: 'run.unknown', + runId: 'run-1', + detail: 'HTTP 404 authorization: Bearer should-not-leak', + }, + }, + event('run.unknown', { runId: 'run-2', detail: 'unrelated' }), + ], + 'run-1', + ) + + assert.deepEqual(timeline, [ + { + sequence: 7, + kind: 'run.unknown', + runId: 'run-1', + detail: 'HTTP 404 authorization=[redacted]', + }, + ]) + assert.equal(JSON.stringify(timeline).includes('should-not-leak'), false) + assert.equal(JSON.stringify(timeline).includes('unrelated'), false) +}) + +test('rejects vacuous replay evidence and every terminal pre-kill state', () => { + assert.doesNotThrow(() => assertNonTerminalRun({ status: 'running' }, 'first run')) + for (const status of [ + 'completed', + 'failed', + 'aborted', + 'cancelled', + 'expired', + 'blocked', + 'unknown', + ]) { + assert.throws( + () => assertNonTerminalRun({ status }, 'first run'), + /terminal before the forced restart/iu, + ) + } + assert.throws( + () => assertNonVacuousVisibleEvents({ count: 0 }, 'pre-kill replay'), + /no stable visible provider events/iu, + ) + assert.doesNotThrow(() => assertNonVacuousVisibleEvents({ count: 1 }, 'pre-kill replay')) +}) + +test('requires fresh replay to advance beyond the persisted provider cursor', () => { + const acknowledged = [ + event('run.text.delta', { + runId: 'run-1', + provider: { eventId: 'event-1', providerSequence: 4, cursor: 'cursor-4' }, + }), + ] + const resumed = [ + event('run.text.delta', { + runId: 'run-1', + provider: { eventId: 'event-2', providerSequence: 5, cursor: 'cursor-5' }, + }), + ] + assert.deepEqual(assertProviderResumeProgress(acknowledged, resumed, 'run-1', 'cursor-4'), { + acknowledgedSequence: 4, + firstFreshSequence: 5, + }) + assert.throws( + () => assertProviderResumeProgress(acknowledged, [], 'run-1', 'cursor-4'), + /no stable visible provider events/iu, + ) + assert.throws( + () => assertProviderResumeProgress(acknowledged, resumed, 'run-1', 'wrong-cursor'), + /did not identify an acknowledged provider event/iu, + ) +}) + +test('waits for the first stable visible event before allowing a restart snapshot', async () => { + const session = { responses: [] } + setTimeout(() => { + session.responses.push( + event('run.text.delta', { + runId: 'run-1', + provider: { + eventId: 'provider-event-1', + providerSequence: 1, + cursor: 'cursor-1', + }, + }), + ) + }, 15) + + const visible = await waitForVisibleEvents(session, 'run-1', 200, 'first process') + assert.deepEqual(visible, { + count: 1, + keys: ['provider-event-1'], + events: [ + { + kind: 'run.text.delta', + eventId: 'provider-event-1', + providerSequence: 1, + cursor: 'cursor-1', + }, + ], + }) +}) + +test('stops pre-kill waits as soon as the run becomes terminal', async () => { + const session = { + responses: [ + { + type: 'state', + state: { runs: [{ id: 'run-1', status: 'unknown' }] }, + }, + ], + } + + await assert.rejects( + waitForControlIdentity(session, 'run-1', 10_000), + /became unknown before exposing exact provider identity/iu, + ) + await assert.rejects( + waitForVisibleEvents(session, 'run-1', 10_000, 'first process'), + /became unknown before emitting a stable visible provider event/iu, + ) +}) diff --git a/test/terminal-responsive.test.ts b/test/terminal-responsive.test.ts index 4ace310..f32d8df 100644 --- a/test/terminal-responsive.test.ts +++ b/test/terminal-responsive.test.ts @@ -74,36 +74,37 @@ test('chrome uses complete responsive groups at every reference width', () => { quitArmed: false, activityVisible: false, navigationHint: 'Ctrl+P commands', + composerMode: 'queue', }) const narrow = plainLines(chrome, 40) - assert.equal(narrow.length, 3) - assert.match(narrow[0] ?? '', /braid\s+AgentProfile Release engineer/u) - assert.match(narrow[1] ?? '', /pi \/ glm-5\.2.*Local CLI Bridge/u) - assert.match(narrow[2] ?? '', /completed.*Ctrl\+P commands/u) + assert.equal(narrow.length, 1) + assert.match(narrow[0] ?? '', /Release engineer.*Ctrl\+P commands/u) assert.doesNotMatch(narrow.join('\n'), /\/home\/drew|\.worktrees|…/u) const standardTop = chrome.renderTop(80).join('\n') const standardBottom = chrome.renderBottom(80).join('\n') const standard = [standardTop, standardBottom].join('\n') - assert.match(standard, /braid\s+cwd\s+braid-integration/u) - assert.match(standard, /New conversation/u) - assert.match(standardTop, /AgentProfile Release engineer/u) - assert.match(standardTop, /pi \/ glm-5\.2/u) + assert.equal(standardTop, '') + assert.match(standardBottom, /profile Release engineer/u) + assert.match(standardBottom, /pi \/ glm-5\.2/u) assert.match(standard, /Local CLI Bridge/u) - assert.match(standardBottom, /completed.*Ctrl\+P commands/u) - assert.doesNotMatch(standard, /branch-1|in 1\.2k|out 567|\$0\.0312/u) + assert.match(standardBottom, /Ctrl\+P commands/u) + assert.doesNotMatch(standard, /braid|cwd|New conversation|branch-1|in 1\.2k|out 567|\$0\.0312/u) assert.doesNotMatch(standard, /…/u) - const wideTop = chrome.renderTop(120).join('\n') const wide = plainLines(chrome, 120).join('\n') - assert.match(wide, /branch\s+branch-1/u) - assert.match(wideTop, /model tangle-router\/glm-5\.2/u) - assert.match(wideTop, /output ≤8\.2k/u) + assert.match(wide, /profile Release engineer/u) + assert.match(wide, /pi \/ tangle-router\/glm-5\.2/u) + assert.match(wide, /Local CLI Bridge/u) + assert.match(wide, /high/u) assert.match(wide, /in 1\.2k/u) assert.match(wide, /out 567/u) assert.match(wide, /\$0\.0312/u) - assert.doesNotMatch(wide, /\/home\/drew|\.worktrees|…/u) + assert.doesNotMatch( + wide, + /braid|cwd|New conversation|branch-1|output ≤|\/home\/drew|\.worktrees|…/u, + ) for (const width of [1, 2, 10, 40, 80, 120, 200]) { for (const line of plainLines(chrome, width)) assert.ok(visibleWidth(line) <= width) @@ -122,14 +123,20 @@ test('chrome exposes active-run controls and failure recovery without hiding out statusText: 'streaming', activeRunId: 'run-metrics', runs: [{ ...run, status: 'running', completeness: 'streaming' }], + capabilities: { + ...base.capabilities, + 'run.queue': { available: true, source: 'provider' }, + 'run.steer': { available: true, source: 'provider' }, + }, }, quitArmed: false, activityVisible: false, navigationHint: 'Ctrl+P commands', + composerMode: 'queue', }) const active = chrome.render(80).join('\n') assert.match(active, /Ctrl\+C cancel/u) - assert.match(active, /Enter queues input/u) + assert.match(active, /Enter queues · Alt\+S steer/u) chrome.setState({ view: { @@ -148,11 +155,29 @@ test('chrome exposes active-run controls and failure recovery without hiding out quitArmed: false, activityVisible: false, navigationHint: 'Ctrl+P commands', + composerMode: 'queue', }) const failed = chrome.render(80).join('\n') - assert.match(failed, /outcome failed · operation operation-/u) - assert.match(failed, /\/export preserve · \/new continue/u) + assert.match(failed, /failed/u) + assert.match(failed, /\/activity details · \/new/u) assert.doesNotMatch(failed, /operation-that-is-long-enough-to-shorten/u) + + chrome.setState({ + view: { + ...base, + status: 'failed', + statusText: 'RUNTIME_FINAL_ERROR', + runs: [{ ...run, status: 'failed', completeness: 'missing-history' }], + }, + quitArmed: false, + activityVisible: false, + navigationHint: 'Ctrl+P commands', + composerMode: 'queue', + }) + const uncertain = chrome.render(80).join('\n') + assert.match(uncertain, /outcome unknown/u) + assert.match(uncertain, /\/activity inspect/u) + assert.doesNotMatch(uncertain, /failed|\/new/u) }) test('layout breakpoints preserve transcript room and short-terminal overlays', () => { @@ -191,16 +216,11 @@ test('published Pi virtual terminals keep the composer and valid cells at all si assert.equal(viewport.length, rows, `${columns}x${rows} row count`) for (const line of viewport) assert.ok(visibleWidth(line) <= columns, `${columns}x${rows} line exceeds width`) - assert.match(viewport.join('\n'), /braid/u) - assert.match(viewport.join('\n'), /completed/u) - assert.ok( - viewport.some((line) => line.includes('─')), - `${columns}x${rows} composer border`, - ) - const composerTop = viewport.findIndex((line) => line.includes('› send')) - const composerBottom = viewport.findIndex((line) => line.includes('alt+enter newline')) - assert.ok(composerTop >= 0, `${columns}x${rows} composer action`) - assert.equal(composerBottom - composerTop, 4, `${columns}x${rows} idle composer rows`) + assert.doesNotMatch(viewport.join('\n'), /braid|completed|─/u) + const prompt = viewport.findIndex((line) => line.includes('›')) + assert.ok(prompt >= 0, `${columns}x${rows} composer prompt`) + assert.equal(rows - prompt, 4, `${columns}x${rows} composer and context rows`) + assert.match(viewport.at(-1) ?? '', /Release engineer/u) assert.doesNotMatch(viewport.join('\n'), /\/home\/drew|\.worktrees/u) tui.stop() @@ -239,7 +259,11 @@ test('composer derives truthful actions and keeps three usable rows at 40x12', a assert.equal(projection.action, 'queue') assert.equal(projection.queuePosition, 2) assert.match(projection.actionLabel, /queue next #2/u) - assert.match(projection.actionLabel, /steer \/steer/u) + assert.match(projection.actionLabel, /Alt\+S steer/u) + + const steering = composerProjectionFor(active, 'steer') + assert.equal(steering.action, 'steer') + assert.match(steering.actionLabel, /Alt\+S queue/u) assert.equal(composerRowBudget(12), 5) const unavailable = composerProjectionFor({ @@ -251,7 +275,7 @@ test('composer derives truthful actions and keeps three usable rows at 40x12', a }, }) assert.equal(unavailable.action, 'unavailable') - assert.equal(unavailable.actionLabel, 'queue unavailable') + assert.equal(unavailable.actionLabel, 'input unavailable') const terminal = new VirtualTerminal(40, 12) const tui = new TuiMainScreen(terminal) @@ -268,13 +292,11 @@ test('composer derives truthful actions and keeps three usable rows at 40x12', a await terminal.waitForRender() const viewport = terminal.getViewport() - const top = viewport.findIndex((line) => line.includes('queue next #2')) - const bottom = viewport.findIndex((line) => line.includes('alt+enter newline')) - assert.ok(top >= 0) - assert.ok(bottom > top) - assert.equal(bottom - top, 4, 'composer must retain three usable rows') - assert.match(viewport.join('\n'), /steer \/steer/u) - assert.ok(viewport.slice(top + 1, bottom).some((line) => line.includes('›'))) + const prompt = viewport.findIndex((line) => line.includes('›')) + assert.ok(prompt >= 0) + assert.equal(viewport.length - prompt, 4, 'composer must retain three usable rows and context') + assert.match(viewport.join('\n'), /working · Ctrl\+C stop/u) + assert.match(viewport[prompt] ?? '', /› queue #2/u) assert.equal(viewport.length, 12) for (const line of viewport) assert.ok(visibleWidth(line) <= 40) @@ -305,11 +327,10 @@ test('composer bounds long drafts without changing Pi editor input behavior', () shell.editor.setText(Array.from({ length: 60 }, (_, index) => `line-${index}`).join('\n')) const lines = shell.render(40) - const top = lines.findIndex((line) => line.includes('› send')) - const bottom = lines.findIndex((line) => line.includes('alt+enter newline')) - assert.ok(top >= 0) - assert.ok(bottom > top) - assert.equal(bottom - top, 4) + const prompt = lines.findIndex((line) => line.includes('›')) + assert.ok(prompt >= 0) + assert.equal(lines.length - prompt, 4) + assert.doesNotMatch(lines.join('\n'), /─|alt\+enter newline/u) assert.equal(lines.length, 12) for (const line of lines) assert.ok(visibleWidth(line) <= 40) }) diff --git a/test/terminal-usage-status.test.ts b/test/terminal-usage-status.test.ts index 20bde9d..1b04080 100644 --- a/test/terminal-usage-status.test.ts +++ b/test/terminal-usage-status.test.ts @@ -17,15 +17,18 @@ test('terminal usage keeps direct, analysis, and worker measurements separate an const metrics = metricsFor(view) assert.deepEqual(metrics, [ - 'turns in ≥10 · out ≥20 · ≥$0.0100 · calls ≥2 (+1 missing) · latency ≥120ms (+1 missing)', - 'analysis usage unknown · cost unknown · calls unknown (1 missing) · latency unknown (1 missing)', - 'workers in 0 · out 0 · $0.0000 · calls 0 · latency 0ms', + 'in ≥10', + 'out ≥20', + '≥$0.0100', + 'calls ≥2', + 'model ≥120ms', + 'workers in 0 · out 0 · $0.0000 · calls 0 · model 0ms', ]) const context = activityDocument(view).context ?? '' - assert.match(context, /turns .*calls ≥2 \(\+1 missing\).*latency ≥120ms \(\+1 missing\)/u) - assert.match(context, /analysis .*calls unknown \(1 missing\).*latency unknown \(1 missing\)/u) - assert.match(context, /workers .*calls 0.*latency 0ms/u) + assert.match(context, /in ≥10.*calls ≥2.*model ≥120ms/u) + assert.doesNotMatch(context, /analysis|unknown|missing/u) + assert.match(context, /workers .*calls 0.*model 0ms/u) }) test('terminal chrome keeps telemetry out of narrow layouts', () => { @@ -35,10 +38,11 @@ test('terminal chrome keeps telemetry out of narrow layouts', () => { quitArmed: false, activityVisible: false, navigationHint: 'Ctrl+P commands', + composerMode: 'queue', }) const narrow = chrome.render(40) - assert.equal(narrow.length, 3) + assert.equal(narrow.length, 1) assert.doesNotMatch(narrow.join('\n'), /calls|analysis|workers|missing/u) for (const line of narrow) assert.ok(visibleWidth(line) <= 40) }) @@ -59,8 +63,8 @@ test('terminal usage presents a complete estimate instead of an observed-cost fl }, } - assert.match(metricsFor(estimated)[0] ?? '', /~\$0\.1500/u) - assert.doesNotMatch(metricsFor(estimated)[0] ?? '', /≥\$0\.1000/u) + assert.match(metricsFor(estimated).join(' · '), /~\$0\.1500/u) + assert.doesNotMatch(metricsFor(estimated).join(' · '), /≥\$0\.1000/u) const freeEstimate = { ...estimated, @@ -73,7 +77,7 @@ test('terminal usage presents a complete estimate instead of an observed-cost fl }, }, } - assert.match(metricsFor(freeEstimate)[0] ?? '', /~\$0\.0000/u) + assert.match(metricsFor(freeEstimate).join(' · '), /~\$0\.0000/u) }) test('execution identity comes from one active run receipt instead of current profile fields', () => { @@ -151,13 +155,13 @@ test('execution identity comes from one active run receipt instead of current pr quitArmed: false, activityVisible: true, navigationHint: 'Ctrl+P commands', + composerMode: 'queue', }) const rendered = chrome.render(120).join('\n') - assert.match(rendered, /AgentProfile Exact run profile/u) - assert.match(rendered, /runner pi/u) - assert.match(rendered, /tangle-router\/glm-5\.2/u) + assert.match(rendered, /profile Exact run profile/u) + assert.match(rendered, /pi \/ tangle-router\/glm-5\.2/u) assert.match(rendered, /Local CLI Bridge/u) - assert.match(rendered, /exec local CLI · active/u) + assert.doesNotMatch(rendered, /exec local CLI|active/u) assert.doesNotMatch(rendered, /Next profile|openai\/gpt-next|Next connection/u) const activity = activityDocument(view) diff --git a/test/trace-analysis-configuration.test.ts b/test/trace-analysis-configuration.test.ts index 9a31e41..c0d3c67 100644 --- a/test/trace-analysis-configuration.test.ts +++ b/test/trace-analysis-configuration.test.ts @@ -258,6 +258,9 @@ test('configures the published DSPy RLM engine and model-backed analyst registry assert.equal(result.engine.executionConfig.model, 'pi/tangle-router/glm-5.2') assert.equal(result.engine.executionConfig.control_adapter, 'tolerant') assert.deepEqual(result.engine.executionConfig.pricing, PRICING) + assert.equal(result.engine.executionConfig.max_output_tokens, 16_384) + assert.equal(result.engine.executionConfig.max_reasoning_tokens, 65_536) + assert.equal(result.engine.executionConfig.max_cost_usd, 1.16384) assert.equal('base_url' in result.engine.executionConfig, false) assert.equal('api_key_provided' in result.engine.executionConfig, false) assert.deepEqual(result.modelExecutions(), []) @@ -279,6 +282,23 @@ test('configures the published DSPy RLM engine and model-backed analyst registry assert.deepEqual(askIds, [BRAID_QUESTION_ANALYST_ID]) }) +test('analysis cost capacity admits the configured output and reasoning limits', async () => { + const selected = connection('cli-bridge', 'cost-capacity', 'http://127.0.0.1:4010') + const result = await createTraceAnalysisAdapter( + baseOptions(selected, { + profile: { harness: 'claude-code', model: { default: 'opus' } }, + pricing: { inputUsdPerMillion: 15, outputUsdPerMillion: 75 }, + maxOutputTokens: 32_768, + }), + ) + + assert.equal(result.status, 'engine-configured') + if (result.status !== 'engine-configured') return + assert.equal(result.engine.executionConfig.max_output_tokens, 32_768) + assert.equal(result.engine.executionConfig.max_reasoning_tokens, 131_072) + assert.equal(result.engine.executionConfig.max_cost_usd, 15.36) +}) + test('defines a bounded cited-answer analyst for /ask', () => { assert.equal(BRAID_QUESTION_ANALYST_DEFINITION.id, BRAID_QUESTION_ANALYST_ID) assert.equal(BRAID_QUESTION_ANALYST_DEFINITION.toolGroup, 'singleTrace') @@ -586,6 +606,13 @@ test('runtime-owned CLI Bridge analysis uses the harness executor with portable expectedBearer: 'credential-never-recorded', responseText: '{"answer":"bridge ok"}', estimatedCostUsd: 0, + usage: { + promptTokens: 2, + completionTokens: 3, + cacheReadInputTokens: 5, + cacheCreationInputTokens: 7, + reasoningTokens: 1, + }, }) try { const selected = connection('cli-bridge', 'runtime-bridge-owner', bridge.endpoint) @@ -610,15 +637,27 @@ test('runtime-owned CLI Bridge analysis uses the harness executor with portable assert.equal(result.succeeded, true) if (!result.succeeded) return assert.equal(result.response.content, '{"answer":"bridge ok"}') + assert.deepEqual(result.response.usage, { + promptTokens: 7, + completionTokens: 3, + totalTokens: 10, + captured: true, + cachedPromptTokens: 5, + reasoningTokens: 1, + }) assert.deepEqual(result.receipt.customTokenPricing, PRICING) + assert.equal(result.receipt.inputTokens, 2) + assert.equal(result.receipt.cachedTokens, 5) + assert.equal(result.receipt.cacheWriteTokens, 7) + assert.equal(result.receipt.reasoningTokens, 1) assert.deepEqual((result.execution as { readonly billing?: unknown }).billing, { status: 'estimated', - usd: 0.000008, + usd: 0.00002, }) assert.equal(bridge.requests.length, 1) const body = bridge.requests[0]?.body assert.equal(body?.model, 'pi/tangle-router/glm-5.2') - assert.equal(body?.max_tokens, 64) + assert.equal(body?.max_tokens, undefined) const executionProfile = body?.agent_profile as AgentProfile assert.equal(executionProfile.harness, 'pi') assert.equal(executionProfile.model?.default, 'tangle-router/glm-5.2') @@ -627,17 +666,20 @@ test('runtime-owned CLI Bridge analysis uses the harness executor with portable assert.equal(executionProfile.model?.metadata?.maxTokens, 64) assert.equal(executionProfile.model?.metadata?.retry, undefined) assert.match(executionProfile.prompt?.systemPrompt ?? '', /text-generation endpoint/u) + assert.match(executionProfile.prompt?.systemPrompt ?? '', /messages array/u) + assert.match(executionProfile.prompt?.systemPrompt ?? '', /machine format/u) + assert.match(executionProfile.prompt?.systemPrompt ?? '', /exact identifier/u) assert.match(executionProfile.prompt?.systemPrompt ?? '', /Do not inspect files/u) assert.deepEqual(executionProfile.tools, { + bash: false, edit: false, read: false, - shell: false, write: false, }) assert.deepEqual(executionProfile.permissions, { + bash: 'deny', edit: 'deny', read: 'deny', - shell: 'deny', write: 'deny', }) assert.deepEqual(executionProfile.extensions, { pi: { load: [] } }) @@ -647,6 +689,11 @@ test('runtime-owned CLI Bridge analysis uses the harness executor with portable assert.deepEqual(JSON.parse(messages[0]?.content ?? '{}'), { messages: optimizerRequest().request.messages, }) + assert.equal( + (result.execution as { readonly runtime?: { readonly operation?: string } }).runtime + ?.operation, + 'startRetainedRun', + ) assert.doesNotMatch(JSON.stringify(result.execution), /private analyst instruction/u) assert.doesNotMatch(JSON.stringify(result.execution), /private trace question/u) } finally { @@ -654,6 +701,100 @@ test('runtime-owned CLI Bridge analysis uses the harness executor with portable } }) +test('runtime-owned CLI Bridge analysis cancels a detached run when result retrieval fails', async () => { + const bridge = await startRuntimeBridgeServer({ + expectedBearer: 'credential-never-recorded', + statusFailureStatus: 503, + }) + try { + const selected = connection('cli-bridge', 'runtime-bridge-failure', bridge.endpoint) + const owner = createRuntimeTraceModelOwner({ + profile: { + name: 'Product engineer', + harness: 'pi', + model: { default: 'tangle-router/glm-5.2', provider: 'tangle-router' }, + }, + connection: selected, + baseUrl: bridge.endpoint, + credential: 'credential-never-recorded', + model: 'pi/tangle-router/glm-5.2', + pricing: PRICING, + }) + + const result = await owner.call(optimizerRequest()) + assert.equal(result.succeeded, false) + if (result.succeeded) return + assert.equal(bridge.requests.length, 1) + assert.equal(bridge.cancellations.length, 1) + assert.equal( + typeof (result.execution as { readonly retained?: { readonly runId?: unknown } }).retained + ?.runId, + 'string', + ) + assert.equal((result.execution as { readonly dispatched?: unknown }).dispatched, true) + assert.equal(result.receipt.usageUnknown, true) + assert.equal(result.receipt.costUnknown, true) + } finally { + await bridge.close() + } +}) + +test('runtime-owned Claude analysis accepts a runner alias with exact tool isolation', async () => { + const bridge = await startRuntimeBridgeServer({ + expectedBearer: 'credential-never-recorded', + responseText: '{"answer":"bridge ok"}', + estimatedCostUsd: 0, + }) + try { + const selected = connection('cli-bridge', 'runtime-claude-owner', bridge.endpoint) + const owner = createRuntimeTraceModelOwner({ + profile: { + name: 'Product engineer', + harness: 'claude-code', + model: { default: 'opus', reasoningEffort: 'high' }, + }, + connection: selected, + baseUrl: bridge.endpoint, + credential: 'credential-never-recorded', + model: 'claude-code/opus', + pricing: PRICING, + }) + + const result = await owner.call( + optimizerRequest({ model: 'claude-code/opus', thinking: 'disabled' }), + ) + assert.equal(result.succeeded, true) + if (!result.succeeded) return + assert.equal(result.response.content, '{"answer":"bridge ok"}') + assert.equal(bridge.requests.length, 1) + const body = bridge.requests[0]?.body + assert.equal(body?.model, 'claude-code/opus') + const executionProfile = body?.agent_profile as AgentProfile + assert.equal(executionProfile.harness, 'claude-code') + assert.equal(executionProfile.model?.default, 'opus') + assert.equal(executionProfile.model?.provider, undefined) + assert.deepEqual(executionProfile.tools, { + bash: false, + edit: false, + read: false, + write: false, + }) + assert.deepEqual(executionProfile.permissions, { + bash: 'deny', + edit: 'deny', + read: 'deny', + write: 'deny', + }) + assert.equal( + (result.execution as { readonly runtime?: { readonly operation?: string } }).runtime + ?.operation, + 'startRetainedRun', + ) + } finally { + await bridge.close() + } +}) + test('runtime-owned trace model preserves observed cost when token usage is unknown', async () => { const owner = runtimeOwner(async () => ({ model: 'pi/tangle-router/glm-5.2', diff --git a/test/tui-core-workflows.test.ts b/test/tui-core-workflows.test.ts index ca3733a..2495a74 100644 --- a/test/tui-core-workflows.test.ts +++ b/test/tui-core-workflows.test.ts @@ -170,6 +170,7 @@ function analysisView(recipe: string): BraidViewModel { text: 'retry started after the provider timeout', }, ], + citationSupport: { status: 'passed', supportedFindings: 1 }, footer: [{ label: 'cost', value: '$0.02 · 120ms' }], }, } @@ -190,6 +191,7 @@ const permission: InteractionView = { allowedOutcomes: ['accept', 'reject', 'cancel'], responseScopes: ['once', 'session'], queuePosition: 0, + queueTotal: 1, secret: false, } @@ -208,7 +210,13 @@ test('core workflow overlays keep mode, consequence, and controls visible at 40x assert.match(forkScreen.join('\n'), /source: conversation:source/u) assert.match(forkScreen.join('\n'), /destination: conversation:copy/u) assert.match(forkScreen.join('\n'), /boundary: message:42/u) - assert.match(forkScreen.join('\n'), /enter\/y create fork · ←\/esc cancel/u) + assert.match(forkScreen.join('\n'), /operation id:/u) + assert.match(forkScreen.join('\n'), /plan digest:/u) + if (columns === 80) { + assert.match(forkScreen.join('\n'), /operation id: operation-fork-preview/u) + assert.match(forkScreen.join('\n'), /plan digest: digest:fork-preview/u) + } + assert.match(forkScreen.join('\n'), /enter\/y create(?: fork)? .*←\/esc/u) const graphScreen = await renderOverlay(graph, columns, rows) assertFits(graphScreen, columns) @@ -240,7 +248,11 @@ test('long workflow state preserves the closing key instead of pushing it below }, }) const forkScreen = await renderOverlay(fork, 40, 12) - assert.match(forkScreen.join('\n'), /enter\/y create fork · ←\/esc cancel/u) + assert.match(forkScreen.join('\n'), /enter\/y create .*←\/esc/u) + fork.handleInput('\u001b[F') + const forkTail = fork.render(40).join('\n') + assert.match(forkTail, /metadata 8: source-8/u) + assert.match(forkTail, /↑\/↓ inspect/u) const graph = new GraphView(theme) graph.setView({ @@ -637,10 +649,13 @@ test('comparison view leads with both outcomes and pages through every captured test('approval actions are navigable and secret responses never render values', async () => { const responses: unknown[] = [] - const shell = new InteractionShell(permission, theme, (response) => responses.push(response)) + const shell = new InteractionShell({ ...permission, queueTotal: 3 }, theme, (response) => + responses.push(response), + ) const screen = await renderOverlay(shell, 40, 12) assertFits(screen, 40) assert.match(screen.join('\n'), /permission/u) + assert.match(screen.join('\n'), /request 1\/3/u) assert.match(screen.join('\n'), /1\. Approve/u) assert.match(screen.join('\n'), /2\. Reject/u) assert.match(screen.join('\n'), /3\. Cancel/u) diff --git a/test/tui.test.ts b/test/tui.test.ts index 47ee72c..839b337 100644 --- a/test/tui.test.ts +++ b/test/tui.test.ts @@ -34,7 +34,7 @@ async function waitUntil(predicate: () => boolean, timeoutMs = 1_000): Promise readonly terminal: VirtualTerminal readonly view: BraidTerminalApp @@ -50,7 +50,7 @@ async function startSteeringTerminal(): Promise<{ const execution: ExecutionPort = { capabilities: () => ({ ...DEFAULT_RUN_CAPABILITIES, - controls: { ...DEFAULT_RUN_CAPABILITIES.controls, queue: false, steer: true }, + controls: { ...DEFAULT_RUN_CAPABILITIES.controls, queue, steer: true }, }), async *streamTurn(input): AsyncIterable { await new Promise((resolve) => { @@ -245,7 +245,7 @@ test('the real Braid root renders and sends at all four reference sizes', async await terminal.waitForRender() const screen = terminal.getScrollBuffer().join('\n') - assert.match(screen, /braid/u) + assert.match(screen, /Braid starter/u) assert.match(screen, /hello\s+Braid/u) assert.match(screen, /Fixture response through pi/u) assert.equal( @@ -263,7 +263,7 @@ test('composer fallback steers exactly once without queueing or sending', async const harness = await startSteeringTerminal() try { await harness.terminal.waitForRender() - assert.match(harness.terminal.getViewport().join('\n'), /steer \/steer/u) + assert.match(harness.terminal.getViewport().join('\n'), /Enter steers/u) harness.terminal.sendInput('correct course') harness.terminal.sendInput('\r') await waitUntil(() => harness.calls.steer === 1) @@ -297,6 +297,27 @@ test('/steer text uses the typed steer path exactly once without queueing or sen } }) +test('Alt+S changes an active composer from queue to steer', async () => { + const harness = await startSteeringTerminal(true) + try { + await harness.terminal.waitForRender() + assert.match(harness.terminal.getViewport().join('\n'), /Enter queues · Alt\+S steer/u) + harness.terminal.sendInput('\u001bs') + await harness.terminal.waitForRender() + assert.match(harness.terminal.getViewport().join('\n'), /Enter steers · Alt\+S queue/u) + + harness.terminal.sendInput('change direction') + harness.terminal.sendInput('\r') + await waitUntil(() => harness.calls.steer === 1) + assert.deepEqual(harness.calls.steerTexts, ['change direction']) + assert.equal(harness.calls.queue, 0) + } finally { + harness.view.stop() + await harness.done + await harness.app.close() + } +}) + test('transcript navigation repaints the real terminal frame', async () => { const terminal = new VirtualTerminal(80, 24) const tui = new TuiMainScreen(terminal) @@ -409,7 +430,7 @@ test('the searchable command overlay restores editor focus after close', async ( assert.doesNotMatch(overlay, /alt\+enter newline/u) terminal.sendInput('\u001b') await terminal.waitForRender() - assert.match(terminal.getViewport().join('\n'), /alt\+enter newline/u) + assert.match(terminal.getViewport().join('\n'), /profile Braid starter.*Ctrl\+P commands/u) terminal.sendInput('focus restored') assert.equal(view.editor.getText(), 'focus restored') @@ -619,7 +640,7 @@ test('Ctrl+C clears, cancels, then requires a second idle press to quit', async terminal.sendInput('\u0003') await terminal.waitForRender() assert.equal(stopped, false) - assert.match(terminal.getViewport().join('\n'), /ctrl\+c again to quit/u) + assert.match(terminal.getViewport().join('\n'), /Ctrl\+C again to quit/u) terminal.sendInput('\u0003') await done assert.equal(stopped, true) diff --git a/test/usage-projection.test.ts b/test/usage-projection.test.ts index 3fb39d0..de4c9c8 100644 --- a/test/usage-projection.test.ts +++ b/test/usage-projection.test.ts @@ -377,6 +377,20 @@ test('empty totals are explicitly unknown without inventing sources', () => { assert.equal(usage.unknownLatencySources, 0) }) +test('unchanged application state reuses one immutable session usage projection', () => { + const state = stateFor({ + runs: [runFor({ llmCalls: 1 })], + supervisors: [{ id: 'supervisor-usage', rootRunId: 'run-usage' }], + workers: [{ supervisorId: 'supervisor-usage', inputTokens: 4, outputTokens: 2 }], + }) + const usage = sessionUsageFor(state) + + assert.equal(sessionUsageFor(state), usage) + assert.equal(Object.isFrozen(usage), true) + assert.equal(Object.isFrozen(usage.delegated), true) + assert.notEqual(sessionUsageFor({ ...state }), usage) +}) + function stateFor(input: { readonly runs?: readonly Record[] readonly analyses?: readonly Record[] diff --git a/test/w6-ui.test.ts b/test/w6-ui.test.ts index 6554ecf..a3ce3b5 100644 --- a/test/w6-ui.test.ts +++ b/test/w6-ui.test.ts @@ -279,7 +279,7 @@ test('named graph, analysis, activity, profile editor, connection setup, and sto const fork = new ForkPreviewPanel(theme) fork.setView(view) assert.match(graph.render(80).join('\n'), /conversation graph/u) - assert.match(activity.render(80).join('\n'), /activity/u) + assert.match(activity.render(80).join('\n'), /live work/u) assert.match(profile.render(80).join('\n'), /profile/u) assert.match(connection.render(80).join('\n'), /connection/u) assert.match(help.render(80).join('\n'), /analyze/u) @@ -304,19 +304,20 @@ test('stable chrome keeps identity and status outside transcript history', () => quitArmed: false, activityVisible: false, navigationHint: 'Ctrl+P commands', + composerMode: 'queue', }) const lines = chrome.render(40) const firstLine = lines[0] ?? '' - assert.match(firstLine, /braid\s+AgentProfile reviewer/u) - assert.match(lines[1] ?? '', /pi \/ deterministic/u) - assert.match(lines[2] ?? '', /completed.*Ctrl\+P commands/u) + assert.equal(lines.length, 1) + assert.match(firstLine, /reviewer.*Ctrl\+P commands/u) assert.doesNotMatch(firstLine, /fixture|deterministic/u) assert.ok(visibleWidth(firstLine) <= 40) const standard = chrome.render(80) - assert.match(standard[1] ?? '', /reviewer.*pi \/ deterministic/u) - assert.match(standard[1] ?? '', /deterministic fixture/u) - assert.match(standard[2] ?? '', /completed.*Ctrl\+P commands/u) + assert.equal(standard.length, 1) + assert.match(standard[0] ?? '', /reviewer.*pi \/ deterministic/u) + assert.match(standard[0] ?? '', /deterministic fixture/u) + assert.match(standard[0] ?? '', /Ctrl\+P commands/u) for (const line of standard) assert.ok(visibleWidth(line) <= 80) }) @@ -358,7 +359,7 @@ test('terminal chrome aggregates known conversation metrics without filling gaps }, }, }), - ['out 0', 'cost unknown'], + ['out 0'], ) assert.deepEqual( metricsFor({ @@ -375,7 +376,7 @@ test('terminal chrome aggregates known conversation metrics without filling gaps }, }, }), - ['usage unknown'], + [], ) }) @@ -624,6 +625,7 @@ const interaction: InteractionView = { allowedOutcomes: ['once', 'reject', 'cancel'], responseScopes: ['once'], queuePosition: 0, + queueTotal: 1, secret: true, }