{"_id":"@alainux/orb","_rev":"5-4ba0a5de2ba7c92973e896311905d576","name":"@alainux/orb","dist-tags":{"latest":"0.6.3"},"versions":{"0.5.3":{"name":"@alainux/orb","version":"0.5.3","keywords":["pi-package","pi-extension","coding-agent","voice","realtime","gemini-live","openai-realtime"],"license":"MIT","_id":"@alainux/orb@0.5.3","maintainers":[{"name":"alainux","email":"alain.jacomet.forte@gmail.com"}],"homepage":"https://alainux.github.io/orb/","bugs":{"url":"https://github.com/alainux/orb/issues"},"pi":{"extensions":["./extensions/voice.ts"]},"bin":{"orb":"bin/orb.mjs"},"dist":{"shasum":"3597a3243ada55c6d77742a6230ee5b9f7712a2a","tarball":"https://registry.npmjs.org/@alainux/orb/-/orb-0.5.3.tgz","fileCount":112,"integrity":"sha512-AIOiAL58uL/axkvYZiA9o/F0Hjc/PX3Ukpbhdj7bMikAi6WXx4ZW9a5sG2BszRRQwM+z1ybXuD0HGN/huvb5ag==","signatures":[{"sig":"MEYCIQD326KBx6OvnVnMYzZ6WwpqU/nNefSJFnGXomSdnOKr5AIhAMDfAtO+XmkSHzgIqR9XwbXP5jSLwhnxMwo9za9Q9qB5","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":5654631},"type":"module","engines":{"node":">=22.19.0"},"gitHead":"d6d953ef588f989b2dfe55b7d9096bf5b3845f21","scripts":{"test":"npm run build:test && node --test .test-dist/tests/*.test.js","build":"npm run build:ts","check":"npm run typecheck && npm test && npm run test:audio-helper && npm run build && npm run smoke && node scripts/verify-package.mjs","smoke":"node scripts/smoke-load.mjs","doctor":"node scripts/doctor.mjs","prepack":"npm run check","build:ts":"node scripts/clean.mjs dist && tsc -p tsconfig.build.json","build:all":"npm run build:ts && npm run build:audio","typecheck":"tsc --noEmit","build:test":"node scripts/clean.mjs .test-dist && tsc -p tsconfig.test.json","pack:check":"npm pack --dry-run --ignore-scripts","build:audio":"node scripts/build-audio-helper.mjs","postinstall":"node scripts/build-audio-helper.mjs --if-needed --soft","test:audio-helper":"cd audio-helper && go test ./internal/..."},"_npmUser":{"name":"alainux","email":"alain.jacomet.forte@gmail.com"},"repository":{"url":"git+https://github.com/alainux/orb.git","type":"git"},"_npmVersion":"11.16.0","description":"Conversational realtime voice control for the Pi coding harness, with hardware-timed Go audio and autonomous Pi task orchestration.","directories":{},"_nodeVersion":"24.18.0","dependencies":{"ws":"^8.18.3","@google/genai":"^2.13.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"@types/ws":"^8.18.1","typescript":"^5.8.3","@types/node":"^24.0.0"},"peerDependencies":{"@earendil-works/pi-tui":"*","@earendil-works/pi-coding-agent":"*"},"_npmOperationalInternal":{"tmp":"tmp/orb_0.5.3_1786106858496_0.5493733955078519","host":"s3://npm-registry-packages-npm-production"}},"0.6.0":{"name":"@alainux/orb","version":"0.6.0","keywords":["pi-package","pi-extension","coding-agent","voice","realtime","gemini-live","openai-realtime"],"license":"MIT","_id":"@alainux/orb@0.6.0","maintainers":[{"name":"alainux","email":"alain.jacomet.forte@gmail.com"}],"homepage":"https://alainux.github.io/orb/","bugs":{"url":"https://github.com/alainux/orb/issues"},"pi":{"extensions":["./extensions/voice.ts"]},"bin":{"orb":"bin/orb.mjs"},"dist":{"shasum":"19d4f5a96205eec99efdf7303df10e136240ff6f","tarball":"https://registry.npmjs.org/@alainux/orb/-/orb-0.6.0.tgz","fileCount":141,"integrity":"sha512-FsLmyr2WNNlhl6/afrOxwHCFDgJFs0m8LnD55no9b9GS+VtTrMmvg0yqqgR3rTVKBSfOl+QPrAOEJ3em+sO3Kg==","signatures":[{"sig":"MEYCIQCl+igssE1ah+ByFrw0nEwOJrjhb0Udp8pQGrvGx0UaTwIhAOvBy+qrp5Hb58zYCLwz1QenxzVxyHcp+qb+MnkThtFo","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":6096099},"type":"module","engines":{"node":">=22.19.0"},"gitHead":"333c0a1101fe7b30161fa895691d70ae06dc8122","scripts":{"test":"npm run build:test && node --test .test-dist/tests/*.test.js","build":"npm run build:ts","check":"npm run typecheck && npm test && npm run test:audio-helper && npm run build && npm run smoke && node scripts/verify-package.mjs","smoke":"node scripts/smoke-load.mjs","doctor":"node scripts/doctor.mjs","prepack":"npm run check","build:ts":"node scripts/clean.mjs dist && tsc -p tsconfig.build.json","build:all":"npm run build:ts && npm run build:audio","typecheck":"tsc --noEmit","build:test":"node scripts/clean.mjs .test-dist && tsc -p tsconfig.test.json","pack:check":"npm pack --dry-run --ignore-scripts","build:audio":"node scripts/build-audio-helper.mjs","postinstall":"node scripts/build-audio-helper.mjs --if-needed --soft","test:audio-helper":"cd audio-helper && go test ./internal/..."},"_npmUser":{"name":"alainux","email":"alain.jacomet.forte@gmail.com"},"repository":{"url":"git+https://github.com/alainux/orb.git","type":"git"},"_npmVersion":"11.16.0","description":"Conversational realtime voice control for the Pi coding harness, with hardware-timed Go audio and autonomous Pi task orchestration.","directories":{},"_nodeVersion":"24.18.0","dependencies":{"ws":"^8.18.3","@google/genai":"^2.13.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"@types/ws":"^8.18.1","typescript":"^5.8.3","@types/node":"^24.0.0"},"peerDependencies":{"@earendil-works/pi-tui":"*","@earendil-works/pi-coding-agent":"*"},"_npmOperationalInternal":{"tmp":"tmp/orb_0.6.0_1786186310589_0.016829755082306663","host":"s3://npm-registry-packages-npm-production"}},"0.6.1":{"name":"@alainux/orb","version":"0.6.1","keywords":["pi-package","pi-extension","coding-agent","voice","realtime","gemini-live","openai-realtime"],"license":"MIT","_id":"@alainux/orb@0.6.1","maintainers":[{"name":"alainux","email":"alain.jacomet.forte@gmail.com"}],"homepage":"https://alainux.github.io/orb/","bugs":{"url":"https://github.com/alainux/orb/issues"},"pi":{"extensions":["./extensions/voice.ts"]},"bin":{"orb":"bin/orb.mjs"},"dist":{"shasum":"1f07497ce6ba4839375423735e73edaeeadde1c2","tarball":"https://registry.npmjs.org/@alainux/orb/-/orb-0.6.1.tgz","fileCount":151,"integrity":"sha512-beqzg/zPrK5UvGukLOSHUa1HxmNRqu3LL7Y/rL/YTgwhxRdj6dyMUCxZxZ/3jHaTnlwqSdj31H0jE/obYweSTg==","signatures":[{"sig":"MEQCIEtLzvaVDdPJzbslpfiLQE5vgNniH5jEzGFM6WmxHdfNAiBOwNTvXpTSbfPTW9Ob4aX4PHTUCmiAViS4xM/A1tgGvg==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":7487980},"type":"module","engines":{"node":">=22.19.0"},"gitHead":"f42824687f369dd50b1183b42f0c3f7bc31abeac","scripts":{"lint":"eslint src tests extensions scripts","test":"npm run build:test && node --test .test-dist/tests/*.test.js","build":"npm run build:ts","check":"npm run typecheck && npm run lint && npm test && npm run test:audio-helper && npm run build && npm run smoke && node scripts/verify-package.mjs","smoke":"node scripts/smoke-load.mjs","doctor":"node scripts/doctor.mjs","prepack":"npm run check","build:ts":"node scripts/clean.mjs dist && tsc -p tsconfig.build.json","build:all":"npm run build:ts && npm run build:audio","typecheck":"tsc --noEmit","build:test":"node scripts/clean.mjs .test-dist && tsc -p tsconfig.test.json","pack:check":"npm pack --dry-run --ignore-scripts","build:audio":"node scripts/build-audio-helper.mjs","postinstall":"node scripts/build-audio-helper.mjs --if-needed --soft","test:audio-helper":"cd audio-helper && go test ./internal/..."},"_npmUser":{"name":"alainux","email":"alain.jacomet.forte@gmail.com"},"repository":{"url":"git+https://github.com/alainux/orb.git","type":"git"},"_npmVersion":"11.16.0","description":"Conversational realtime voice control for the Pi coding harness, with hardware-timed Go audio and autonomous Pi task orchestration.","directories":{},"_nodeVersion":"24.18.0","dependencies":{"ws":"^8.18.3","@google/genai":"^2.13.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"^10.8.1","@types/ws":"^8.18.1","typescript":"^5.8.3","@types/node":"^24.0.0","typescript-eslint":"^8.66.0"},"peerDependencies":{"@earendil-works/pi-tui":"*","@earendil-works/pi-coding-agent":"*"},"_npmOperationalInternal":{"tmp":"tmp/orb_0.6.1_1786209553310_0.6696557759058572","host":"s3://npm-registry-packages-npm-production"}},"0.6.2":{"name":"@alainux/orb","version":"0.6.2","keywords":["pi-package","pi-extension","coding-agent","voice","realtime","gemini-live","openai-realtime"],"license":"MIT","_id":"@alainux/orb@0.6.2","maintainers":[{"name":"alainux","email":"alain.jacomet.forte@gmail.com"}],"homepage":"https://alainux.github.io/orb/","bugs":{"url":"https://github.com/alainux/orb/issues"},"pi":{"extensions":["./extensions/voice.ts"]},"bin":{"orb":"bin/orb.mjs"},"dist":{"shasum":"fcb359459ead544b0219b20370434d3dda16dca5","tarball":"https://registry.npmjs.org/@alainux/orb/-/orb-0.6.2.tgz","fileCount":157,"integrity":"sha512-VKlD+7+oOmNvKFK7HMR6Qs+Vjt8i7Vdl2vNniW4AZVvPJhl+qtm8T4Xa77a0XCe/ztNs754rYD6417v/nPYdow==","signatures":[{"sig":"MEUCIQCkLl+yUfNtZ3D3RVHhVGJrSCz73XEsyy19X/CQr6aliAIgBaJWOiOHV+pZO5WyzNotMe2GolIubg8X9r8DoEH1PJs=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":8169544},"type":"module","engines":{"node":">=22.19.0"},"gitHead":"e1d00fb7ddbc3a3f615974602f7e91242b36dac6","scripts":{"lint":"eslint src tests extensions scripts","test":"npm run build:test && node --test .test-dist/tests/*.test.js","build":"npm run build:ts","check":"npm run typecheck && npm run lint && npm test && npm run test:audio-helper && npm run build && npm run smoke && node scripts/verify-package.mjs","smoke":"node scripts/smoke-load.mjs","doctor":"node scripts/doctor.mjs","prepack":"npm run check","build:ts":"node scripts/clean.mjs dist && tsc -p tsconfig.build.json","coverage":"npm run build:test && node --test --experimental-test-coverage --test-force-exit .test-dist/tests/*.test.js","build:all":"npm run build:ts && npm run build:audio","typecheck":"tsc --noEmit","build:test":"node scripts/clean.mjs .test-dist && tsc -p tsconfig.test.json","pack:check":"npm pack --dry-run --ignore-scripts","build:audio":"node scripts/build-audio-helper.mjs","coverage:ci":"npm run build:test && node --test --experimental-test-coverage --test-coverage-lines=80 --test-coverage-branches=80 --test-coverage-functions=75 --test-coverage-exclude=.test-dist/src/providers/** --test-coverage-exclude=.test-dist/src/audio/** --test-coverage-exclude=.test-dist/src/controller.js --test-force-exit .test-dist/tests/*.test.js","postinstall":"node scripts/build-audio-helper.mjs --if-needed --soft","test:audio-helper":"cd audio-helper && go test ./internal/..."},"_npmUser":{"name":"alainux","email":"alain.jacomet.forte@gmail.com"},"repository":{"url":"git+https://github.com/alainux/orb.git","type":"git"},"_npmVersion":"11.16.0","description":"Conversational realtime voice control for the Pi coding harness, with hardware-timed Go audio and autonomous Pi task orchestration.","directories":{},"_nodeVersion":"24.18.0","dependencies":{"ws":"^8.18.3","@google/genai":"^2.13.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"^10.8.1","@types/ws":"^8.18.1","typescript":"^5.8.3","@types/node":"^24.0.0","typescript-eslint":"^8.66.0"},"peerDependencies":{"@earendil-works/pi-tui":"*","@earendil-works/pi-coding-agent":"*"},"_npmOperationalInternal":{"tmp":"tmp/orb_0.6.2_1786261898496_0.8917678875069526","host":"s3://npm-registry-packages-npm-production"}},"0.6.3":{"name":"@alainux/orb","version":"0.6.3","description":"Conversational realtime voice control for the Pi coding harness, with hardware-timed Go audio and autonomous Pi task orchestration.","license":"MIT","type":"module","repository":{"type":"git","url":"git+https://github.com/alainux/orb.git"},"homepage":"https://alainux.github.io/orb/","bugs":{"url":"https://github.com/alainux/orb/issues"},"publishConfig":{"access":"public"},"keywords":["pi-package","pi-extension","coding-agent","voice","realtime","gemini-live","openai-realtime"],"bin":{"orb":"bin/orb.mjs"},"pi":{"extensions":["./extensions/voice.ts"]},"engines":{"node":">=22.19.0"},"dependencies":{"@google/genai":"^2.13.0","ws":"^8.18.3"},"peerDependencies":{"@earendil-works/pi-coding-agent":"*","@earendil-works/pi-tui":"*"},"devDependencies":{"@types/node":"^24.0.0","@types/ws":"^8.18.1","eslint":"^10.8.1","typescript":"^5.8.3","typescript-eslint":"^8.66.0"},"scripts":{"typecheck":"tsc --noEmit","build:test":"node scripts/clean.mjs .test-dist && tsc -p tsconfig.test.json","test":"npm run build:test && node --test .test-dist/tests/*.test.js","coverage":"npm run build:test && node --test --experimental-test-coverage --test-force-exit .test-dist/tests/*.test.js","coverage:ci":"npm run build:test && node --test --experimental-test-coverage --test-coverage-lines=80 --test-coverage-branches=80 --test-coverage-functions=75 --test-coverage-exclude=.test-dist/src/providers/** --test-coverage-exclude=.test-dist/src/audio/** --test-coverage-exclude=.test-dist/src/controller.js --test-force-exit .test-dist/tests/*.test.js","check":"npm run typecheck && npm run lint && npm test && npm run test:audio-helper && npm run build && npm run smoke && node scripts/verify-package.mjs","pack:check":"npm pack --dry-run --ignore-scripts","prepack":"npm run check","build":"npm run build:ts","smoke":"node scripts/smoke-load.mjs","doctor":"node scripts/doctor.mjs","build:audio":"node scripts/build-audio-helper.mjs","test:audio-helper":"cd audio-helper && go test ./internal/...","postinstall":"node scripts/build-audio-helper.mjs --if-needed --soft","build:ts":"node scripts/clean.mjs dist && tsc -p tsconfig.build.json","build:all":"npm run build:ts && npm run build:audio","lint":"eslint src tests extensions scripts"},"gitHead":"0fff55e8d01c19d94e6b450ff03af89b0c94f01a","_id":"@alainux/orb@0.6.3","_nodeVersion":"24.18.0","_npmVersion":"11.16.0","dist":{"integrity":"sha512-yzqH/AFfPZryq0xm6+C5R2IWwu5Tt+PNI+sIDni6UUcf2T/RSO1eqmSrpNM9i4cL4+0rG4pPjlBUhVM+6jcBUQ==","shasum":"658d9fd8ce810b9059e52c6bdafb7eae785c19c2","tarball":"https://registry.npmjs.org/@alainux/orb/-/orb-0.6.3.tgz","fileCount":157,"unpackedSize":8174903,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQCyMtpsbDBWO51XnXy1i0tn9+1agx1Z/BtlBzM/RoU1VAIhAKTMt9d1UlYH+mFtd6n6m1MXo8tEermzl1w11FvxbVCf"}]},"_npmUser":{"name":"alainux","email":"alain.jacomet.forte@gmail.com"},"directories":{},"maintainers":[{"name":"alainux","email":"alain.jacomet.forte@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/orb_0.6.3_1786264489921_0.13578914719200408"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-07T12:47:38.336Z","modified":"2026-08-09T08:34:50.321Z","0.5.3":"2026-08-07T12:47:38.743Z","0.6.0":"2026-08-08T10:51:50.857Z","0.6.1":"2026-08-08T17:19:13.571Z","0.6.2":"2026-08-09T07:51:38.743Z","0.6.3":"2026-08-09T08:34:50.164Z"},"bugs":{"url":"https://github.com/alainux/orb/issues"},"license":"MIT","homepage":"https://alainux.github.io/orb/","keywords":["pi-package","pi-extension","coding-agent","voice","realtime","gemini-live","openai-realtime"],"repository":{"type":"git","url":"git+https://github.com/alainux/orb.git"},"description":"Conversational realtime voice control for the Pi coding harness, with hardware-timed Go audio and autonomous Pi task orchestration.","maintainers":[{"name":"alainux","email":"alain.jacomet.forte@gmail.com"}],"readme":"> [!WARNING]\n> **Early-stage software.** Orb is pre-1.0 and under active development. It is unstable and changes a lot between releases — expect breaking changes, rough edges, and issues (including audio glitches and configuration churn). Please file bugs you hit; this project gets better with real-world use, but treat it as experimental, not production-ready.\n\n<div align=\"center\">\n\n<img src=\"docs/assets/orb-logo.svg\" width=\"88\" alt=\"Orb logo\">\n\n# Orb\n\n**Conversational realtime voice for the Pi coding harness.**\n\n[![CI](https://github.com/alainux/orb/actions/workflows/ci.yml/badge.svg)](https://github.com/alainux/orb/actions/workflows/ci.yml)\n[![npm](https://img.shields.io/npm/v/%40alainux%2Forb?label=npm)](https://www.npmjs.com/package/@alainux/orb)\n![license](https://img.shields.io/badge/license-MIT-6b7280)\n![Pi package](https://img.shields.io/badge/Pi-package-8b5cf6)\n![realtime voice](https://img.shields.io/badge/voice-full--duplex-3b82f6)\n\n<img src=\"site/assets/orb.gif\" alt=\"Orb for Pi — animated\" width=\"100%\">\n\n</div>\n\nOrb adds a full-duplex voice layer to [Pi](https://pi.dev). Talk about the project at a high level; Orb turns your intent into useful engineering work, drives Pi while it works, interrupts or redirects it when needed, and comes back with the outcome rather than narrating every command.\n\nYou can keep using Pi normally at the same time. Your keyboard, Pi's editor, Pi's own output, and direct `!` commands remain visible and independent.\n\n## Contents\n\n- [What it feels like](#what-it-feels-like)\n- [Interface](#interface)\n- [Install](#install)\n- [Usage](#usage)\n  - [Providers](#providers)\n  - [Commands](#commands)\n  - [Keyboard shortcuts](#keyboard-shortcuts)\n  - [Preferences](#preferences)\n- [Pi control](#pi-control)\n  - [Permissions](#permissions)\n- [Scratchpad](#scratchpad)\n- [Audio reliability](#audio-reliability)\n- [Configuration](#configuration)\n- [Long-running sessions](#long-running-sessions)\n- [Development](#development)\n- [Project layout](#project-layout)\n- [Documentation](#documentation)\n- [License](#license)\n\n## What it feels like\n\n> **You:** Can you explore the project?\n>\n> **Orb:** Sure — one sec.\n>\n> *Orb delegates a complete repository exploration, including the relevant build/tests, and waits while Pi works.*\n>\n> **Orb:** It’s a TypeScript Pi package with a native audio sidecar. The build is healthy; the release path is the main area I’d tighten.\n\nChange direction at any point:\n\n> **You:** Wait, never mind. Focus on the failing tests instead.\n>\n> *Orb cancels the current Pi turn and delegates the new task.*\n>\n> **Orb:** Got it.\n\nNo routine “shall I continue?” prompts.\n\n## Interface\n\nOrb inherits Pi's active theme and renders a compact panel above Pi:\n\n- **Left:** a living, positive-space sphere of dots animated from real seeded noise — Perlin fBm + domain warping (ported from the site's labs) — carrying a drifting two-energy-region color field across the theme's primary↔secondary anchors. Talking (composing) reads as a crisp two-tone sphere with a white pressure bloom, working (searching) calms it with a broad cognition sweep, idle (smoke) is a quiet presence that keeps flowing while muted renders it gray. Sharp audio onsets birth center-to-edge pressure pulses that swell the body and bloom a sparse particle halo.\n- **Right:** a chronological script of `YOU`, `ORB`, and Orb's own tool/control actions.\n- **Below:** the normal Pi screen and prompt editor remain untouched.\n\nWhen the scratchpad is open, the right side becomes the working document with a small recent-turn strip below it.\n\n## Install\n\n### Pi package\n\n```bash\npi install https://github.com/alainux/orb\npi\n```\n\nThen start a session with:\n\n```text\n/voice\n```\n\n### Convenience launcher\n\nmacOS / Linux:\n\n```bash\ncurl -fsSL https://raw.githubusercontent.com/alainux/orb/main/scripts/install.sh | sh\norb\n```\n\nWindows PowerShell:\n\n```powershell\niwr https://raw.githubusercontent.com/alainux/orb/main/scripts/install.ps1 -UseBasicParsing | iex\norb\n```\n\nThe launcher simply starts Pi with Orb auto-enabled. Plain `pi` + `/voice` remains fully supported.\n\n### npm\n\n```bash\nnpm install -g @alainux/orb\norb\n```\n\nPublished releases ship platform audio binaries. Go is kept as the audio-helper implementation language and developer fallback; normal users should not need to build it.\n\n## Usage\n\n### Providers\n\nGemini Live:\n\n```bash\nexport ORB_PROVIDER=gemini\nexport GEMINI_API_KEY=\"your-key\"\n```\n\nOpenAI Realtime:\n\n```bash\nexport ORB_PROVIDER=openai\nexport OPENAI_API_KEY=\"your-key\"\n```\n\n### Commands\n\n| Command | Description |\n| --- | --- |\n| `/voice` | Start voice mode with the configured provider |\n| `/voice start gemini` | Start with Gemini Live |\n| `/voice start openai` | Start with OpenAI Realtime |\n| `/voice stop` | Stop voice mode (`off` works too) |\n| `/voice status` | Show the current session status |\n| `/voice log` | Show the conversation and tool log |\n| `/voice settings` | Open the interactive settings panel (`prefs` works too) |\n| `/voice help` | List the available commands |\n| `/voice provider gemini\\|openai` | Set the provider for the next session (persisted) |\n| `/voice mute` | Toggle microphone mute |\n| `/voice mute on` / `/voice mute off` | Mute or unmute the microphone |\n| `/voice voice` | Cycle to the next voice live (persisted) |\n| `/voice voice <name>` | Set a specific voice by name (persisted) |\n| `/voice voice list` | List the available voices |\n| `/voice thinking` | Cycle the reasoning display (`minimized` / `full` / `hidden`) |\n| `/voice scratchpad` | Open the scratchpad widget |\n| `/voice scratchpad open\\|close` | Open or close the scratchpad |\n| `/voice scratchpad view` | Open the document in the scrollable overlay |\n| `/voice scratchpad edit` | Edit the document in Pi's editor |\n| `/voice scratchpad load <path>` | Load a file into the scratchpad |\n| `/voice scratchpad save [path]` | Save the scratchpad to a file |\n| `/voice scratchpad dispatch` | Send the document (or a selection) to Pi |\n\n### Keyboard shortcuts\n\n| Shortcut | Action |\n| --- | --- |\n| `Ctrl+Alt+V` | Toggle voice mode |\n| `Ctrl+Alt+M` | Mute or unmute the microphone |\n| `Ctrl+Alt+T` | Cycle the reasoning display |\n\n### Preferences\n\n- **Durable preferences** — provider, model, voice, auto-start, reasoning budget, context compression, session resumption, braille, and audio tuning live in the config file and are read at startup.\n- **Reasoning display** — `ui.thinkingDisplay` (`full` / `minimized` / `hidden`) is honored as the source of how the model's thought is surfaced. You can flip it for the current session with `/voice thinking` or `Ctrl+Alt+T` — that edits the option in memory only, never a file and never a session entry, and a fresh launch starts from the config default again.\n- **Settings panel** — `/voice settings` opens a Pi settings panel: the **Reveal reasoning** toggle (session-only), the **Provider / Voice / Auto-start voice** preferences (editable and persisted to the user config), and the remaining durable values as read-only reference (edit those in the config file).\n\n## Pi control\n\nOrb can direct the active Pi harness through Pi's extension APIs instead of pretending slash commands are text. The voice companion deliberately only exercises the orchestration surface of the harness — it never configures Pi.\n\n**What it can do:**\n\n- Cancel the active generation/tool run.\n- Delegate every real coding task to Pi — the companion never edits the project itself (it holds no `read`/`bash`/`write`/`edit`/`grep`/`find`/`ls` tools); it translates your requirements and directs the background agent.\n- Wait for visible Pi activity or completion and inspect results.\n\n**What it never does:**\n\n- Switch Pi's model, change its thinking level, enable or disable its tools, run a shell, or change the voice agent's own voice. Those are set by the config file, not changed at runtime or by voice.\n\nThis makes sequences such as **cancel → delegate something else** possible entirely by voice.\n\nDirect user `!` commands and their visible output are observed as part of Orb's internal Pi context when Pi exposes them. Pi's `!!` form stays deliberately excluded from model context. Orb does not duplicate Pi's own log in its panel because you can already see it on screen.\n\n### Permissions\n\nThese capabilities are independently configurable, scoped to orchestration only (cancel) and the scratchpad. There are deliberately no runtime configuration knobs — the model, thinking level, tools, shell, and the voice model's own voice are set by the config file, not changed by voice:\n\n```json\n{\n  \"permissions\": {\n    \"scratchpadRead\": true,\n    \"scratchpadWrite\": true,\n    \"scratchpadOutsideProject\": false,\n    \"cancelPi\": true\n  }\n}\n```\n\n- **The companion is a purely communicative layer.** It can talk to the human, read the visible Pi log (`read_pi_log` — recent conversation and tool results) to understand factual project state, delegate everything that needs the project's files changed (`run_pi_task`), observe Pi (`observe_pi`), and manage its ephemeral scratchpad.\n- **It holds no project files.** There is no `read`/`bash`/`write`/`edit`/`grep`/`find`/`ls` and no `read_herdr_pane`; it cannot inspect or touch the tree itself and is intentionally limited to a read-only view of what is already visible.\n- **Cancellation is the only control surface.** When you say \"cancel / stop / drop that\", it calls `cancel_pi_task`, which aborts the running Pi task via `ctx.abort()`. It is gated by the `cancelPi` permission, never changes model/thinking/tools/shell or configuration, and is a safe no-op when Pi is already idle.\n- **Prompt overrides.** The system prompt can be overridden with `ORB_SYSTEM_PROMPT` / `PI_VOICE_SYSTEM_PROMPT` or a `voice.systemPromptFile`; the per-tool permission gates (`scratchpadRead`, `scratchpadWrite`, `scratchpadOutsideProject`, `cancelPi`) still apply.\n\n## Scratchpad\n\nThe scratchpad is an ephemeral working document for cases where a single spoken command is not enough: long prompts, TODOs, review notes, requirements, migration plans, and so on.\n\nExamples:\n\n```text\n\"Open the scratchpad and load TODO.md.\"\n\"Add an item about retry behavior.\"\n\"Dispatch the first three items to Pi.\"\n\"Save this as docs/release-plan.md.\"\n```\n\n- **Operations** — open / read / replace / append / load / save / dispatch / close. Dispatch can send the whole document or a selected subset. File reads and writes are project-scoped by default.\n- **Viewing** — `/voice scratchpad view` opens the document in a focusable, scrollable overlay that renders it as Markdown using Pi's active theme. It follows the live tail while the agent appends (so new lines arrive at the bottom), and you can scroll with `↑/↓`, `PgUp/PgDn`, `Ctrl+U/D`, `Home/End`; `r` re-reads the latest content and `Esc`/`q` closes it. Without a command, the inline widget panel shows a live window of the same document during a session.\n\n## Audio reliability\n\nThe audio device is owned by a small Go/miniaudio sidecar; Node never paces speaker samples. The sidecar also owns an adaptive hardware-side jitter buffer.\n\n```text\nrealtime provider ⇄ TypeScript transport ⇄ Go jitter buffer ⇄ hardware callback\n```\n\nIf Pi briefly stalls provider delivery while rendering or running tools, playback pauses, rebuilds a small lead, and resumes at the hardware clock rather than getting stuck emitting tiny fragments. The buffer never skips or time-compresses PCM. A recovery counter is shown in the Orb footer and diagnostics.\n\nTwo safeguards make that recovery *automatic* rather than incidental:\n\n- **Faster re-prime on a choppy spiral.** If a second underrun arrives while the previous rebuild has not yet delivered a healthy lead, the buffer escalates the adaptive lead by a larger step so playback re-buffers in fewer, shorter interruptions (a long tail of single-glitch gaps never forms).\n- **Latency doesn't accumulate.** After delivery has been continuously healthy for a sustained streak (or a response ends naturally), the adaptive lead relaxes back toward its base, so a choppy episode never leaves permanently elevated latency behind for the next turn.\n\nOrb also **auto-detects choppiness onset** from the sidecar's underrun-recovery counter (a lone recovery is a normal transient stall; a cluster inside a short window is real choppiness), surfaces it live (`CHOPPY` in the Orb footer + `audio choppy · adjusting` status), and — when the microphone dropped frames during the same episode — automatically resyncs the capture path so the next human turn starts from clean audio rather than a garbled half-sentence.\n\nBarge-in clears the old response immediately. Repeated interruption storms are detected and the input path is resynchronized to break speaker→microphone feedback loops.\n\n**Configuration.** Audio tuning is configurable:\n\n```json\n{\n  \"audio\": {\n    \"bufferMs\": 140,\n    \"maxBufferMs\": 380,\n    \"recoveryStepMs\": 40,\n    \"interruptionStormCount\": 3,\n    \"interruptionStormWindowMs\": 1800,\n    \"interruptionRecoveryMuteMs\": 320,\n    \"choppinessWindowRecoveries\": 3,\n    \"choppinessWindowMs\": 1500,\n    \"choppinessRecoverSilenceMs\": 1500,\n    \"inputResyncDrops\": 3,\n    \"inputResyncWindowMs\": 1500,\n    \"inputResyncCooldownMs\": 4000\n  }\n}\n```\n\n**Diagnostics.** Run `npm run doctor` for the active helper and provider diagnostics.\n\n## Configuration\n\nOrb merges configuration in order (later values win):\n\n1. `~/.config/orb/config.json` (`%APPDATA%\\\\orb\\\\config.json` on Windows)\n2. `<project>/.orb/config.json`\n3. `ORB_CONFIG=/some/config.json`\n4. environment overrides\n\nThe voice system prompt is a simple two-layer model: a **single authoritative default** at [`prompts/default.md`](prompts/default.md) (identity, invariants, persona, tool guidance, and delegation behavior), plus an **optional user override**. An override — `voice.promptFile`, `ORB_PROMPT_FILE`, or an inline `voice.systemPrompt` — replaces the default wholesale.\n\nOrb also starts voice automatically when a Pi session begins (on by default). Set `autoStartVoice` to `false` to opt out, or `ORB_AUTO_START=false`.\n\n**Example:**\n\n```json\n{\n  \"provider\": \"gemini\",\n  \"autoStartVoice\": true,\n  \"voice\": {\n    \"temperature\": 0.83,\n    \"promptFile\": \".orb/voice-prompt.md\"\n  },\n  \"ui\": {\n    \"panelHeight\": 12,\n    \"activityLines\": 8,\n    \"orbDensity\": 1.30,\n    \"orbReactivity\": 0.7,\n    \"orbBraille\": true\n  },\n  \"scratchpad\": {\n    \"panelHeight\": 18,\n    \"maxBytes\": 524288\n  }\n}\n```\n\nSee [Configuration](docs/CONFIGURATION.md) for the full reference.\n\n## Long-running sessions\n\nGemini Live periodically rotates connections. Orb enables context-window compression and Developer-API session resumption, stores the current resumption handle, closes expiring sockets promptly on `GoAway`, and reconnects. If it cannot safely resume, voice closes with a friendly message and can be reopened with `/voice`; Pi itself stays alive.\n\n## Development\n\n```bash\ngit clone https://github.com/alainux/orb.git\ncd orb\nnpm install --ignore-scripts\nnpm run check\nnpm run build:audio\npi -e ./extensions/voice.ts\n```\n\n`npm run build:audio` uses `go build -mod=mod`, so dependencies from `audio-helper/go.mod` are resolved automatically. There is no manual `go get` step.\n\n**Useful targets:**\n\n| Target | Purpose |\n| --- | --- |\n| `npm run typecheck` | Type-check the source |\n| `npm test` | Run the unit test suite |\n| `npm run coverage` | Run tests with a V8 coverage report |\n| `npm run coverage:ci` | Same, with enforced thresholds + headless-excluded modules |\n| `npm run test:audio-helper` | Run the Go audio-helper tests |\n| `npm run build` | Build `dist/` |\n| `npm run smoke` | Smoke-load the built extension |\n| `npm run pack:check` | Dry-run the package tarball |\n\n**Test coverage.** Both coverage targets compile to `.test-dist/` and run the same suite with Node's built-in experimental coverage (`--experimental-test-coverage`):\n\n- `npm run coverage` prints line / branch / function coverage for every loaded module.\n- `npm run coverage:ci` applies the same run but *enforces* thresholds (≥80% line, ≥80% branch, ≥75% function) — CI fails when a threshold is missed — and excludes the `providers/`, `audio/`, and `controller.js` modules, whose uncovered paths are inherently live-network / native-hardware (not meaningfully coverable headlessly).\n\nThe runner reports line, branch, and function percentages only; it does not emit a separate “statement” figure (line coverage is the closest analogue). For type-source mapped coverage with a statement column, point `c8` at `.test-dist/tests/*.test.js`.\n\n## Project layout\n\n```text\nextensions/        Pi package entry point\nsrc/providers/     Gemini / OpenAI realtime adapters\nsrc/audio/         Node ↔ Go audio transport\naudio-helper/      hardware-clocked audio + adaptive playout buffer\nsrc/controller.ts  voice/Pi orchestration\nsrc/types.ts       OrbPermissions — the voice agent's permission gates\nsrc/pi-log.ts      visible Pi observation used internally\nsrc/scratchpad.ts  ephemeral collaborative document\nsrc/orb.ts         positive-space noise-field orb (listening/speaking/thinking)\nsrc/widget.ts      Pi-themed UI\nprompts/           configurable voice-agent prompt\nconfig/            example configuration\nsite/              static project website\n```\n\n## Documentation\n\n- [Architecture](docs/ARCHITECTURE.md)\n- [Configuration](docs/CONFIGURATION.md)\n- [Releasing](docs/RELEASING.md)\n- [Contributing](CONTRIBUTING.md)\n\n## License\n\nMIT. See [LICENSE](LICENSE).\n","readmeFilename":"README.md"}