{"_id":"qwen-live-harness","_rev":"2-ba0a3978b8387b01769c04511cc77956","name":"qwen-live-harness","dist-tags":{"latest":"0.4.2"},"versions":{"0.4.1":{"name":"qwen-live-harness","version":"0.4.1","license":"Apache-2.0","_id":"qwen-live-harness@0.4.1","maintainers":[{"name":"natsueme","email":"zeusdream7@gmail.com"}],"homepage":"https://github.com/QwenLM/Qwen-Live-Harness#readme","bugs":{"url":"https://github.com/QwenLM/Qwen-Live-Harness/issues"},"bin":{"qwen-live-harness":"dist/index.js"},"dist":{"shasum":"8122eb26d30673a316d808923e8cfe45bd94933b","tarball":"https://registry.npmjs.org/qwen-live-harness/-/qwen-live-harness-0.4.1.tgz","fileCount":138,"integrity":"sha512-LQgYvj3z/Zyouz/EAWkHc+OKR5AEp3FSwe30a0iYvqvh4UGMP9cSnP1HZN/idPhb6GuWJ5hZvigD+p719zteTQ==","signatures":[{"sig":"MEUCIBwKjg/6bR6d93rdOaet8ddRHIUJP0twRKtk5eNwW8hJAiEAxoV7IKdFNd04D7gTgEwP8A0b5yPmIJk1skcIgSxMfqk=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":1413343},"main":"dist/index.js","type":"module","_from":"file:/Users/mochi/Downloads/qwen-live-harness-0.4.1.3mjx2io3/qwen-live-harness-0.4.1.tgz","types":"dist/index.d.ts","engines":{"node":">=22.13"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./i18n":{"types":"./dist/i18n/messages.d.ts","import":"./dist/i18n/messages.js"},"./startup":{"types":"./dist/startup.d.ts","import":"./dist/startup.js"},"./subagents":{"types":"./dist/subagents/types.d.ts","import":"./dist/subagents/types.js"},"./package.json":"./package.json"},"scripts":{"test":"vitest run","build":"node build.mjs","clean":"rm -rf dist tsconfig.tsbuildinfo tsconfig.build.tsbuildinfo","watch":"tsc --watch","test:ci":"vitest run","build:ts":"tsc --build tsconfig.build.json","typecheck":"tsc --noEmit"},"_npmUser":{"name":"natsueme","email":"zeusdream7@gmail.com"},"_resolved":"/Users/mochi/Downloads/qwen-live-harness-0.4.1.3mjx2io3/qwen-live-harness-0.4.1.tgz","_integrity":"sha512-LQgYvj3z/Zyouz/EAWkHc+OKR5AEp3FSwe30a0iYvqvh4UGMP9cSnP1HZN/idPhb6GuWJ5hZvigD+p719zteTQ==","repository":{"url":"git+https://github.com/QwenLM/Qwen-Live-Harness.git","type":"git","directory":"packages/qwen-live-harness"},"_npmVersion":"10.9.8","description":"Standalone realtime voice harness for Qwen Code, Qoder and other ACP agents.","directories":{},"_nodeVersion":"22.23.1","dependencies":{"ws":"^8.18.0","prompts":"^2.4.2","ansi-regex":"^6.2.2","@node-rs/jieba":"2.0.2","proper-lockfile":"^4.1.2","@agentclientprotocol/sdk":"^0.14.1"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^3.1.1","esbuild":"^0.25.0","@types/ws":"^8.5.0","typescript":"^5.3.3","@types/node":"^22.13.10","@qwen-code/sdk":"0.1.12","@types/prompts":"^2.4.9","@types/proper-lockfile":"^4.1.4"},"_npmOperationalInternal":{"tmp":"tmp/qwen-live-harness_0.4.1_1789386933043_0.40280389640854875","host":"s3://npm-registry-packages-npm-production"}},"0.4.2":{"_id":"qwen-live-harness@0.4.2","bin":{"qwen-live-harness":"dist/index.js"},"bugs":{"url":"https://github.com/QwenLM/Qwen-Live-Harness/issues"},"dist":{"shasum":"6bdb425a7a4a897a418ce7fb32dfe751a93c4d53","tarball":"https://registry.npmjs.org/qwen-live-harness/-/qwen-live-harness-0.4.2.tgz","fileCount":140,"integrity":"sha512-3qMx5gqqvYOZj1e9RXt6NJftVW7E3ckPHGaCkXCdhNWqJghdY+eF1MYqBH3Grjxn1sL8cQOBjzSWHCucqHocsg==","signatures":[{"sig":"MEUCICrnddqfodgKuOEzz524tOhssC27K55nVhq0t3GhAKALAiEAwtw8rMZp3abloKEScDo3ngOP0/DCF/ghlGmYBnZrOpU=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQDiepad7K9FDV8pzDNjEBoMnpH7rSNlUGaLiUBhzbE3TgIhAOgWcXq8ozIPRAEPqa3dBVYHIct+1uOWyD6bJWyxGrHQ"}],"unpackedSize":1432499},"main":"dist/index.js","name":"qwen-live-harness","type":"module","types":"dist/index.d.ts","engines":{"node":">=22.13"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./i18n":{"types":"./dist/i18n/messages.d.ts","import":"./dist/i18n/messages.js"},"./startup":{"types":"./dist/startup.d.ts","import":"./dist/startup.js"},"./lifecycle":{"types":"./dist/lifecycle.d.ts","import":"./dist/lifecycle.js"},"./subagents":{"types":"./dist/subagents/types.d.ts","import":"./dist/subagents/types.js"},"./package.json":"./package.json"},"gitHead":"b3550d55614f4326183608820a5bf8bc57285978","license":"Apache-2.0","scripts":{"test":"vitest run","build":"node build.mjs","clean":"rm -rf dist tsconfig.tsbuildinfo tsconfig.build.tsbuildinfo","watch":"tsc --watch","test:ci":"vitest run","build:ts":"tsc --build tsconfig.build.json","typecheck":"tsc --noEmit"},"version":"0.4.2","_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:f2894e97-ebc7-40d1-b04c-4ce65b805023"}},"homepage":"https://github.com/QwenLM/Qwen-Live-Harness#readme","repository":{"url":"git+https://github.com/QwenLM/Qwen-Live-Harness.git","type":"git","directory":"packages/qwen-live-harness"},"_npmVersion":"11.19.1","description":"Standalone realtime voice harness for Qwen Code, Qoder and other ACP agents.","directories":{},"maintainers":[{"name":"natsueme","email":"zeusdream7@gmail.com"}],"_nodeVersion":"22.20.0","dependencies":{"ws":"^8.18.0","prompts":"^2.4.2","ansi-regex":"^6.2.2","@node-rs/jieba":"2.0.2","proper-lockfile":"^4.1.2","@agentclientprotocol/sdk":"^0.14.1"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^3.1.1","esbuild":"^0.25.0","@types/ws":"^8.5.0","typescript":"^5.3.3","@types/node":"^22.13.10","@qwen-code/sdk":"0.1.12","@types/prompts":"^2.4.9","@types/proper-lockfile":"^4.1.4"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/qwen-live-harness_0.4.2_1789448177520_0.9532320450503353"}}},"time":{"created":"2026-09-14T11:55:32.808Z","modified":"2026-09-15T04:56:17.788Z","0.4.1":"2026-09-14T11:55:33.175Z","0.4.2":"2026-09-15T04:56:17.633Z"},"bugs":{"url":"https://github.com/QwenLM/Qwen-Live-Harness/issues"},"license":"Apache-2.0","homepage":"https://github.com/QwenLM/Qwen-Live-Harness#readme","repository":{"url":"git+https://github.com/QwenLM/Qwen-Live-Harness.git","type":"git","directory":"packages/qwen-live-harness"},"description":"Standalone realtime voice harness for Qwen Code, Qoder and other ACP agents.","maintainers":[{"name":"natsueme","email":"zeusdream7@gmail.com"}],"readme":"# qwen-live-harness\n\nQwen Live Harness is a standalone realtime voice daemon that orchestrates\ncoding sessions through voice.\n\n`qwen-live-harness` connects three parties:\n\n- **Qwen Live Harness Host** (the macOS overlay app) over the Qwen Live Harness Host WebSocket\n  protocol v9 — writes `~/.qwen-live-harness/run/daemon.json` for discovery, so an\n  already-installed Host connects automatically.\n- **A DashScope realtime voice model** (`qwen-omni` realtime) that owns the\n  conversation: VAD, direct answers, and a tool surface for dispatching work\n  to coding sessions.\n- **Coding sessions** through a `BackendAdaptor`. Two adaptors are available:\n  one drives `qwen serve` over REST/SSE, and one spawns any ACP-compatible\n  agent (`qwen --acp`, `qodercli --acp`, `gemini --acp`, etc.) as a child\n  process over JSON-RPC stdio. Multiple backends can coexist with per-session\n  routing.\n\nThe live session itself is fully owned by this daemon (JSONL logs under\n`~/.qwen-live-harness/sessions/`); backend sessions are ordinary coding sessions\nthat keep running after a call ends.\n\nMemory is enabled by default. Local libraries under `~/.qwen-live-harness/memories/`\nretain dialogue, selected user facts and optional visual observations across\ncalls. The orb's Settings → Memory section controls the feature, selected library, names and\nconsolidation model. See [Memory configuration and behavior](#memory).\n\n## Quick Start\n\n### 1. Install\n\nQwen Live Harness requires Node.js 22.13 or newer for built-in SQLite/FTS5 support.\n\n```bash\n# From the standalone repository\ngit clone git@github.com:QwenLM/Qwen-Live-Harness.git\ncd Qwen-Live-Harness\nnpm ci && npm run build\n```\n\n### 2. Run the setup wizard\n\n```bash\nnpm run init\n```\n\nThe wizard will:\n\n- First let you choose **简体中文 / English** with the **Left / Right** arrow\n  keys and Enter. All following questions and fixed setup messages use that\n  language; a fresh setup initially selects Simplified Chinese.\n- Scan your PATH for installed coding agents (qodercli, qwen, gemini,\n  claude, codex) and list what it found\n- Let you pick a default backend and add additional ones\n- Ask for your DashScope realtime API key\n- Let you select the DashScope Realtime API name (the default is\n  `qwen3.5-omni-plus-realtime`)\n- Ask whether to enable Memory (default: yes), then ask for the DashScope\n  consolidation model when enabled (default: `qwen3.7-plus`)\n- Set a default working directory for coding sessions\n- On macOS: check if the Qwen Live Harness Host app is installed and offer to install it\n- Register the current Node/CLI runtime so the installed Host can start it\n\nWhen done, it writes `~/.qwen-live-harness/config.json` and desktop startup\nregistration, without starting the Host, daemon or a call. From a source\ncheckout, continue with `npm start`. For a globally installed package, the\nequivalent commands are `qwen-live-harness init` and `qwen-live-harness`; both the\nnpm package and executable are named `qwen-live-harness`, with no npm scope.\nBefore the matching signed Host is published, skip the wizard's optional Host\ndownload and run the source-built Host described below.\n\nThe new identities do not read the previous installation's configuration, Memory,\ndiscovery, Host preferences or environment-variable names. Run the wizard again\nand update shell overrides to `QWEN_LIVE_HARNESS_*`. Existing apps and user data\nare left untouched; there is no automatic migration or old-name fallback. See\n[migration details](../../docs/migration.md).\n\n### 3. Start the application\n\n```bash\nnpm start\n# Or, with the global CLI: qwen-live-harness\n```\n\nThe CLI starts or reuses a daemon and opens the installed macOS Host. After\nsetup, opening **Qwen Live Harness Host** from Applications or Launchpad works\nas well: it connects to an existing daemon or launches the registered one.\nRepeated CLI or desktop launches reuse the running application. Existing users\nwith a valid config can run the CLI once to refresh desktop startup registration\nwithout overwriting their configuration.\n\nThe Host does not contain Node or the daemon package. A Host downloaded on its\nown asks you to install the CLI and complete `qwen-live-harness init` first.\nIf Node or the CLI moves, run the CLI from its new installation to refresh the\nregistration. The desktop launcher uses the recorded absolute Node/entry paths\nand PATH, so it does not depend on Finder loading your shell startup files.\nSee [Desktop startup](#desktop-startup) for diagnostics and custom directories.\n\nFor source Host development, use the internal daemon-only mode in one terminal\nand build/run the Host in a second terminal from the repository root:\n\n```bash\n# Terminal 1\nnpm start -- --daemon-only --debug\n\n# Terminal 2\nnpm ci --prefix packages/qwen-live-harness-host\nnpm run build:host\nnpm --prefix packages/qwen-live-harness-host start\n```\n\nQuit the previous daemon and Host first. On macOS, the Qwen Live Harness Host\nreads the new discovery file and connects\nautomatically. Once the Host connection, selected-source permissions and\nself-checks are ready, a newly launched Host starts one call automatically.\nAn existing call or an explicit start/stop/new/quit consumes that startup\nintention; reconnects, renderer reloads and failed starts never loop into a\nnew call. Press `Command+E` to start or end a call manually (an explicitly\nconfigured shortcut still takes precedence).\n\n### Desktop startup\n\nInitialization atomically saves configuration before writing a private runtime\nregistration at `~/.qwen-live-harness/run/runtime.json`. Ordinary CLI startup\nrefreshes it as well. Registration stores the Node executable, CLI entry point,\nversion, working directory, data/discovery directories and PATH. It does not\ncopy API keys or arbitrary environment variables. Use config for settings that\nmust also work when launching from the desktop.\n\n`QWEN_LIVE_HARNESS_DATA_DIR` selects the config/data directory.\n`QWEN_LIVE_HARNESS_DISCOVERY_DIR` selects the discovery base independently;\nruntime registration and discovery are under its `run/` directory. For a\nseparately launched Host using a custom discovery base, set its\n`QWEN_LIVE_HARNESS_DISCOVERY_FILE` to the full `run/daemon.json` path. The default\ndiscovery base remains `~/.qwen-live-harness` even when the data directory changes.\n\nHost-initiated startup writes bounded diagnostics under the discovery base's\n`run/logs/` directory, keeping the latest five `daemon-startup-*.log` files with\nat most 1 MiB per file. Missing configuration, invalid runtime paths, version\nincompatibility and failed startups are surfaced to the user. An initial launch\ncan start a missing daemon once; a subsequent disconnect does not cause an\nautomatic restart loop. `End call` ends the interaction while leaving the app\navailable. `Quit Host` requests coordinated shutdown of the daemon and Host.\n\nWhen the CLI owns the foreground daemon, `Ctrl+C` cancels any in-progress Host\nverification and closes that daemon together with its matching Host. This also\nworks while Host is still opening or reconnecting. A short CLI invocation that\nonly reuses an existing daemon does not end that daemon when the invocation exits.\nNormal startup reports the current stage; `--debug` also includes stage timings.\n\nThe internal `--daemon-only` CLI option runs just the daemon for debugging and\ntests. Host uses this mode when bootstrapping, preventing recursive Host launches.\n\nThe setup panel and orb first appear at the bottom-right. Drag the setup\nheader or orb to move them; Host remembers the shared position across restarts\nand clamps it to a visible display if monitors change. Hover the orb to reveal\nmicrophone, voice output, Start/End call, Settings and Quit controls. They fade\none second after the pointer leaves, unless settings or keyboard focus need\nthem. Ending a call leaves a gray orb in place; it does not quit the app.\n\nSettings presents Audio Source, Video Source and Capture Mode as peer groups,\nfollowed by Memory. The selected mode shows its own explanation. It preserves focus,\ndrafts and camera preview while state changes, and closes on Escape or outside\ninteraction. Setup asks only for the selected source's permissions, so Camera\ndoes not require screen recording or accessibility permission. The orb uses\ncompact visible bounds when dragged to an edge; opening Settings temporarily\nfits the whole panel on screen, without replacing the saved resting position.\nCamera selection shows a nearby preview by default. Its floating eye button\nonly hides/shows the preview; it does not stop camera input or frame delivery.\nSelecting Screen or quitting retains the existing camera shutdown behavior.\n\n**Open config.json ↗** at the top of Settings opens the connected standalone\ndaemon's actual configuration in the OS-associated JSON editor or IDE. This\nrespects the daemon's `QWEN_LIVE_HARNESS_DATA_DIR`, even when Host starts separately.\nSave the file and restart Qwen Live Harness to apply manual edits. Older daemons and\nbuilt-in `qwen serve` do not advertise this action. Missing, non-regular (including\nsymlink) files or editor failures show an error; the action never creates or\noverwrites configuration.\n\n**Language** is followed by Theme at the end of Settings. It switches the fixed Live interface\nbetween English and Simplified Chinese and saves the selection in the top-level\n`language` config field (`\"en\"` or `\"zh-CN\"`). Existing configs without the field\nremain English. Language changes apply during a call without restarting media;\nmodel prompts, responses, transcripts and user/device/library names are not\ntranslated. A connected standalone daemon owns the saved preference. With a\nlegacy Host connection, the selection is saved only in Host's local preference.\n\nDrag the Settings title bar to move the panel; it shares the orb's remembered\nposition. Opening Settings first fits the whole panel into the display work\narea and waits for native positioning before showing it. Microphone animation\nnow amplifies small input peaks visually, with a bounded envelope and smooth\nrelease; it does not change the recorded audio gain.\n\nAll fixed Live display translations are maintained as paired `en` / `zh-CN`\nentries in [`src/i18n/messages.ts`](src/i18n/messages.ts). Edit both values there,\npreserve matching `{placeholders}`, then rebuild Live and Host. Host compiles\nthis same public, browser-safe module into its bundle; no second dictionary or\nruntime language-pack installation is needed. Technical diagnostics and raw\nexternal error details retain their original language.\n\n`Quit Host` gracefully shuts down the connected standalone Live daemon and\nHost, including owned ACP processes, Memory work and discovery. It does not\nterminate an independently running `qwen serve`. A legacy WebShell connection\nonly ends its Live call and closes Host. If shutdown is not confirmed, the\norb remains with an error and Quit can retry the same authenticated instance.\nIt never redirects a retry to a different discovered daemon.\nSame-instance reconnects retain the authenticated shutdown target, independently\nof the WebSocket. Quit always attempts the authenticated shutdown request. It\ncompletes only with a matching receipt, or after a refused connection plus an OS\nprobe proves the original daemon PID no longer exists; a PID probe alone, HTTP\nerrors, resets and timeouts do not prove shutdown. Failed Quit keeps media stopped.\nOn cleanup failure the daemon retains only its authenticated shutdown control\nendpoint and discovery, rejecting new work. A retry closes only the resources\nthat previously failed; successful cleanup steps are not repeated. Cleanup logs\nidentify the failed resource and bounded, credential-redacted causes. Signal-driven\nprocess exit instead releases only its own discovery record even if cleanup fails;\nit never removes a replacement daemon's record.\n\nOn other platforms: the Qwen Live Harness Host app is macOS-only (it needs native\nmicrophone, global shortcut, and screen capture). Linux/Windows users cannot\nuse voice features until a Host is available on their platform.\n\n## Subagents\n\nHover or keyboard-focus the orb to reveal a side summary explicitly labelled\n**Subagents** / **子智能体**. Click it for a compact list, then select a task for\ndetails within that same frameless panel, with **Back** to the list. The view covers\nProactive monitors/reminders and tasks delegated by Live to a coding harness.\nIt shows the original request, actual status, latest activity, public\nintermediate text, available plan/tool updates and final result. It does not\ninvent a completion percentage or expose thought chunks/raw tool payloads.\n\nThe compact summary shows dot/running and check/completed counts. The running\ndot gently pulses only while connected with active tasks, and stays static with\nsystem reduced motion. A waiting marker appears only when input is needed;\ntooltips and accessible labels retain exact counts, including when large counts\nare displayed as `999+`.\n\n`Running` counts active tasks, including queued and waiting tasks. `Completed`\ncounts successful outcomes plus cancelled Proactive monitors, whose details\nstill say Cancelled. Cancelled timers/harness jobs, failed and interrupted\ntasks remain distinct. `Needs you` only counts tasks waiting for user input or\napproval, not failures or interrupted tasks. Monitor evaluations and repeat notifications do not\ncreate extra tasks, and an instruction joined to a running job does not count\ntwice. Monitor notification delivery is shown separately from task completion.\n\nEnding the voice call stops Proactive sampling and retains its terminal\nrecords. Harness tasks keep running and updating this view without reopening\naudio or the realtime model. Permission requests received while no voice call\nis active remain pending and can be answered from the task details. The panel\nshows only real backend requests and the scope of each offered Allow / Deny\nchoice; it never bypasses a sandbox or invents an approval for an ordinary\nfilesystem error. Requests without a confirmed task identity appear separately.\nTheir pending count also activates the summary's attention marker. Overlong\nrequests require review in the backend before approval; Live still offers Deny\nwhen the backend supports it.\nFor supported Codex ACP sessions, Live selects the advertised **Ask for approval**\nmode before sending work. Unsupported or failed mode selection is logged rather\nthan silently claiming manual approval is available. Existing backend sessions\nand global permission settings are not changed.\n\nUse **Stop** on a task row or in its details to stop that exact Harness task or\nmonitor. A pending cancellation says **Stopping…** until confirmed; an unknown\ntask identity or unsupported backend cannot fall back to stopping a different\ntask in the same session. Stop requests and confirmed outcomes are sent to Omni\nas silent text context, queued while it is busy and retained across End call for\nthe next call in the same daemon run. **Close** only dismisses the panel.\n\nLive imposes no active Harness/monitor count limit. Independent Harness work\nuses separate sessions; adding instructions to an existing session retains its\nsteering/queue semantics. Backend quotas, per-session queue bounds and available\nmachine/API resources still apply.\n\nThe orb is never resized or moved to fit task windows. The side summary has a\nroughly one-second hover grace period. Expanded lists and details stay open until\nClose or Escape, including through blur, Settings, orb dragging and disconnection.\nOnly the collapsed summary hides during dragging; a subsequent hover reanchors\nit inside the new display work area. Drag either expanded header to move the\npanel; Back preserves its location, clamping the new size to the display.\nTask updates do not move windows or task rows, and output follows the tail only\nwhen you were already at the bottom. Closing the panel does not stop its task.\n\nThe final Host Settings option, **Theme**, follows **Language** and offers\nSystem (default), Light mode and Dark mode. This Host-local preference applies\nto all its surfaces without restarting media or changing daemon settings.\n\nHistory belongs to the current daemon run, not a cross-restart task archive.\nAll active tasks retain bounded details, alongside the latest 32 ended tasks.\nPrevious / Next pages contain at most 32 tasks and 240 KiB per snapshot; selected\ndetails are fetched separately. Omitted records/truncated output are labelled,\nand totals still include omitted tasks. Original backend text may contain\nsensitive work content, so the view is\nlocal and only the authenticated Host receives it. New task updates are\ncapability-negotiated; older Hosts/daemons keep their existing behavior.\n\n## Configuration\n\nConfiguration comes from `~/.qwen-live-harness/config.json` (generated by `init`),\nwith environment variables (`DASHSCOPE_API_KEY`, `QWEN_LIVE_HARNESS_*`) as overrides.\n\n```jsonc\n{\n  \"language\": \"en\",\n  \"realtimeApiKey\": \"sk-...\",\n  \"realtimeModel\": \"qwen3.5-omni-plus-realtime\",\n  \"memory\": {\n    \"enabled\": true,\n    \"dir\": \"\",\n    \"defaultId\": \"default\",\n    \"updater\": { \"model\": \"qwen3.7-plus\" },\n    \"observer\": { \"enabled\": false },\n  },\n  \"visualInput\": {\n    \"source\": \"screen\",\n    \"screenDisplayId\": \"primary\",\n    \"mode\": \"on-demand\",\n    \"fps\": 1,\n    \"cameraResolution\": { \"width\": 1280, \"height\": 720 },\n    \"cameraSnapshotResolution\": \"native\",\n    \"liveResolution\": { \"width\": 1280, \"height\": 720 },\n    \"snapshotResolution\": \"native\",\n  },\n  \"proactive\": {\n    \"enabled\": true,\n    \"monitor\": {\n      \"sessionRecycleEvals\": 60,\n    },\n    \"scheduler\": {\n      \"evalIntervalSec\": 2,\n      \"maxFailuresPerTask\": 3,\n      \"repeat\": {\n        \"cooldownSec\": 3,\n        \"maxWaitTtsSec\": 30,\n        \"clearBufferOnResume\": true,\n      },\n    },\n    \"vision\": {\n      \"fps\": 1,\n      \"windowSizeSec\": 10,\n      \"minEvalDurationSec\": 0,\n    },\n    \"audio\": {\n      \"windowSizeSec\": 60,\n      \"minEvalDurationSec\": 0,\n    },\n  },\n  \"defaultCwd\": \"~/work/my-project\",\n  \"backends\": [\n    {\n      \"name\": \"qodercli\",\n      \"kind\": \"acp\",\n      \"command\": \"/usr/local/bin/qodercli\",\n      \"args\": [\"--acp\"],\n      \"default\": true,\n    },\n    {\n      \"name\": \"qwen\",\n      \"kind\": \"acp\",\n      \"command\": \"/usr/bin/qwen\",\n      \"args\": [\"--acp\"],\n    },\n  ],\n}\n```\n\nSee `src/config.ts` for the full list of options and validation rules.\nVisual input has two independent settings. `source` is `screen` or `camera`;\n`mode` is `on-demand` or `live-feed`. The defaults are Screen + On Demand,\n1 FPS, a 1280×720 camera stream, 1280×720 Live Feed frames, and\nnative-resolution On Demand Screen and Camera assets. `cameraResolution`\ncontrols the camera preview/Live Feed stream; `cameraSnapshotResolution`\nindependently controls Camera Appshot assets (`native` or a width/height pair).\n`snapshotResolution` controls Screen snapshots. For example,\n`\"cameraSnapshotResolution\": { \"width\": 1920, \"height\": 1080 }` requests an\nasset fitted within that size without changing the 720p preview.\n`qwen-live-harness init` writes these defaults without asking extra questions, so they\ncan be edited directly afterward.\n\nProactive is enabled by default. It adds condition monitors, live narration,\nand device-time reminders. Each perception task uses an independent,\ntext-only DashScope Realtime connection while reusing the foreground\nRealtime endpoint, API key, and model. Monitor sessions send no voice setting\nand output text only. Enabling Proactive exposes the tools; observation starts\nonly after a perception task is created. Triggered announcements wait for\nforeground speech and Host playback to finish, then play one at a time in\nFIFO order. Repeated monitors and narration continue observing while earlier\nevents wait or play, so multiple events from the same task can queue. Event\nmonitors retain their cooldown and false-edge rule for distinct occurrences;\nnarration retains novelty-sensitive updates. Each playback acknowledgement\nretires only its own event. Cancelling or updating a task removes all of its\nold queued events. The delivery ACK\ntimeout starts when foreground Realtime accepts the announcement and emits\n`response.created`; this prevents a missing Host playback receipt from\nblocking the FIFO forever. There is no Live-level monitor admission cap;\nlegacy `maxConcurrentTasks` settings are accepted but no longer enforced or\nwritten by init. Vision and audio retain their own\n`windowSizeSec` and `minEvalDurationSec`, including in a combined monitor and\nafter a Monitor connection is recycled. Task-list replies include remaining\ntimer duration, reminder content, monitor condition/focus, repeat state, and\nthe number of pending notifications. A user request may chain Proactive\ntools, such as listing tasks and then cancelling one, without a new utterance.\n\nPositive visual warm-up can use elapsed observation time for successful captures\nslower than the requested FPS. A capture gap longer than three nominal frame\nintervals (with a one-second tolerance floor) starts a fresh observation period;\nold frames cannot warm a new isolated frame. The default zero warm-up still\naccepts a single fresh frame.\n\nThe environment overrides are `QWEN_LIVE_HARNESS_VISUAL_SOURCE`,\n`QWEN_LIVE_HARNESS_VISUAL_MODE`, `QWEN_LIVE_HARNESS_VISUAL_FPS`,\n`QWEN_LIVE_HARNESS_CAMERA_RESOLUTION` (for example `1280x720`),\n`QWEN_LIVE_HARNESS_CAMERA_SNAPSHOT_RESOLUTION` (`native` or `WIDTHxHEIGHT`),\n`QWEN_LIVE_HARNESS_VISUAL_LIVE_RESOLUTION` (for example `1280x720`), and\n`QWEN_LIVE_HARNESS_VISUAL_SNAPSHOT_RESOLUTION` (`native` or `WIDTHxHEIGHT`). FPS must\nbe between 0.1 and 10. Source and Mode can also be changed for the current call\nfrom the Host orb; an orb change does not rewrite the configuration file.\nSet `QWEN_LIVE_HARNESS_PROACTIVE_ENABLED=false` (or `0`) to disable Proactive entirely;\n`true` and `1` enable it. The remaining Proactive parameters are configured in\n`config.json`.\n\nUnrecognized keys inside `visualInput`, `proactive` and `memory` are rejected;\na misspelled camera setting does not silently select the default Screen source.\n\nFor runtime diagnostics, start the daemon with:\n\n```bash\nqwen-live-harness --debug\n```\n\nDebug output goes to foreground stderr and reports Host connection state, call\nlifecycle, visual capture/frame acceptance, Proactive evidence gates/evaluations,\nnotification queue and playback transitions, and harness lifecycle events even\nafter a call ends. `realtime.protocol` records selected provider event types,\nIDs, cancellation metadata and committed-input counts for timing diagnosis.\nIt does not print API keys, image payloads, raw audio, prompts or transcript contents.\nRun the Electron Host separately with `--live-harness-debug`, not `--debug` (Electron\nreserves that flag). The Host switch does not enable daemon diagnostics.\n\nFor Monitor delivery, match the `frameHash` (first 16 SHA256 hex characters of\nJPEG bytes) across Host capture, daemon capture/frame receipt, and\n`proactive.monitor_image_sent`. Only successful socket writes increment the\nper-commit image/audio counters in `proactive.monitor_commit`; audio totals\ninclude protocol silence. `proactive.monitor_committed` confirms the provider\nacknowledgement, and `proactive.monitor_action` classifies `wait`, `reply`,\n`function_call` or `invalid` without printing the response text. Native display\nand orb-position events are recorded by the Host switch, so capture loss can\nbe distinguished from geometry changes.\n\n**Visual Monitor recordings:** daemon debug mode (also enabled by\n`QWEN_LIVE_HARNESS_LOG_LEVEL=debug`) additionally saves actual Monitor requests under\n`<OS temporary directory>/qwen-live-harness-monitor-debug/`. Normal runs and audio-only\nMonitors do not record media. Each visual Monitor gets a directory, including\ncombined audio/visual Monitors; WebSocket recycling stays in the same directory.\n`proactive.monitor_debug_started` prints its absolute path. Each inference logs\n`proactive.monitor_request_saved` with both Monitor and request directories:\n\n```text\nmonitor-<creation-time>-<id>/\n  monitor.json\n  requests/000001/\n    request.json\n    image-0001.jpg\n    input.wav\n    response.json\n```\n\nJPEGs are the exact frames successfully sent to the model. The mono 16 kHz\nPCM16 WAV contains the sent audio, including protocol silence. JSON retains\ninstructions, event order, audio offsets, frame hashes and the reference to the\npreceding request in that transport; the response file records returned text,\nparsed action or failure. Check preceding requests for the resident conversation\nhistory. Queued/dropped frames are not presented as sent frames.\n\nThese are **sensitive recordings of real screen/camera content, task prompts and,\nfor audio/visual Monitors, microphone audio**. Connection credentials are omitted;\nvisible or spoken secrets inside media are not redacted. Directories/files are\nowner-only. Debug startup and new Monitor creation keep only the ten most\nrecently created Monitor directories; this is not a ten-request or disk-size\nlimit. An evicted Monitor keeps running but stops recording and logs skipped\nrequests. Disk/permission failures or exceeding the 32 MiB pending-write budget\ndisable that recorder and log an incomplete recording without stopping the call.\nLong-running debug Monitors can consume significant disk space; disable debug\nafter diagnosis and do not share recordings without reviewing their contents.\n\nFor `qwen3.5-omni-plus-realtime` and `qwen3.5-omni-flash-realtime`, the initial\nsession explicitly requests mono PCM input at 16 kHz and output at 24 kHz via\n`audio.input.format` / `audio.output.format`, as documented in the\n[DashScope session API](https://help.aliyun.com/zh/model-studio/client-events#26a8302028sjm).\nOlder/custom models retain the legacy PCM fields and 24 kHz playback contract.\nHost playback uses a default-device-rate AudioContext, not a forced 24 kHz\nhardware clock. With current output-end-marker negotiation, each response is\ncontinuously band-limited/resampled into device-rate buffers and scheduled at\ninteger sample boundaries, avoiding independent chunk-conversion spikes and\nunnecessary gaps. The end marker flushes the short filter tail. Older peers\nwithout end markers retain the legacy Web Audio conversion\nand drain behavior. Bluetooth headset microphone activation can\nstill switch the device into hands-free mode; use a separate/built-in microphone\nwhile keeping headphones as system output when listening to music or video.\nInput mute releases capture devices without ending the call; Host's status bar\nshows microphone/output mute states beneath the main call status.\n\n### Supported backends\n\n| Backend     | Kind        | ACP entry                                   | Notes                               |\n| ----------- | ----------- | ------------------------------------------- | ----------------------------------- |\n| Qoder CLI   | `acp`       | `qodercli --acp`                            | Hidden flag; uses Qoder's own login |\n| Qwen Code   | `acp`       | `qwen --acp`                                | Native ACP mode                     |\n| Gemini CLI  | `acp`       | `gemini --experimental-acp`                 | Official ACP support                |\n| Claude Code | `acp`       | `npx @agentclientprotocol/claude-agent-acp` | Adapter-based                       |\n| Codex       | `acp`       | `npx @agentclientprotocol/codex-acp`        | Adapter-based                       |\n| qwen serve  | `qwen-code` | REST/SSE to `qwen serve` daemon             | Legacy; no ACP needed               |\n\nMultiple backends can coexist — the voice model sees all sessions across\nall backends in `session_list` and can route `handoff` to a specific one by\nname.\n\n## Memory\n\nIf the default Memory HTTP endpoint cannot be derived from `realtimeEndpoint`,\nLive logs a warning and keeps daemon setup and local memory available. Model-backed\nMemory features without their own valid endpoint stay unavailable; explicit\nMemory endpoint settings remain independent. Correct the realtime endpoint and\nrestart to restore the shared default.\n\nMemory records final dialogue text, lets the foreground model edit working memory with `omnibio`, consolidates selected facts after the call, and retrieves earlier dialogue or visual observations with `omniretrieve`.\n\n### Setup and controls\n\n`qwen-live-harness init` asks whether to enable Memory (yes by default), then asks for the consolidation model (`qwen3.7-plus` by default). Remaining defaults are written without more questions. Existing configurations receive defaults at load time, so reinitialization is optional.\n\nOpen **Settings → Memory** on the orb to enable/disable Memory, enable optional **Visual memory**, choose a library, use **New** or **Rename**, and edit the **Consolidation model**. End the call before selecting/creating a library or changing its model. Renaming and switches remain available during calls. New creates and selects a library; OFF preserves the selection and stored data. Accepted changes are merged into the Live config file.\n\n### Storage and lifecycle\n\nBy default each library lives in `~/.qwen-live-harness/memories/<id>/` with private `meta.json` and `dialogue.db` files. Its stable id is independent of its display name. `memory.dir` can override the root; relative paths resolve under the Live data directory (`QWEN_LIVE_HARNESS_DATA_DIR`, normally `~/.qwen-live-harness`). Node.js 22.13 or newer is required for SQLite/FTS5. Node versions that label SQLite experimental may print their built-in warning.\n\nFinal user transcripts and ordinary assistant replies become searchable segments, including interrupted replies. Synthetic Proactive notices, backend speech, repair turns and tool wrappers are excluded from user dialogue. Memory does not persist raw microphone audio or image frames.\n\nThe model selects reusable facts through `omnibio`. Edits to this ordered working-memory list are persisted immediately. At detachment/call end, the consolidation model classifies the final list into a long-term profile and time-sensitive recent items. It does not mine the entire raw transcript. Profile and recent items are frozen per attachment; working memory and retrieved context can change during the call.\n\nConsolidation is serialized per library and deduplicated by session and working-memory version. Shutdown waits `shutdownWaitSec`, then abandons remaining requests. WM snapshots remain on disk, but abandoned jobs are not automatically replayed on restart. Runtime diagnostics report the outcome.\n\n### Retrieval and visual memory\n\nDialogue retrieval combines Jieba search tokens, SQLite BM25 and optional embedding similarity. A failed or slow embedding request falls back to keyword search. Time ranges reweight candidates rather than excluding every older match; the unsegmented conversation tail can also be searched.\n\nVisual memory defaults off and follows the selected Screen/Camera source. It observes the first available frame, then at the configured interval. Live Feed reuses current frames; On Demand privately captures a bounded frame without requiring a Proactive task or invoking the foreground Appshot tool. Only a cleaned description is persisted. Disabling visual capture preserves access to historical visual observations. Source/attachment changes invalidate late results.\n\nRetrieved content is published through four memory data sections in the model instructions. Tool receipts report only source/counts. The follow-up response receives the newest instructions while persistent session configuration updates wait until the model is idle. Memory text grants no tool authority. Oversized edits are rejected before changing the model's numbered working-memory list.\n\n### DashScope connection\n\nEmbeddings use the existing key and regional endpoint's `/compatible-mode/v1/embeddings`. Consolidation and visual observation use `/compatible-mode/v1/chat/completions`, ordinary text/image requests with no Realtime voice setting. The default consolidation model is `qwen3.7-plus`; `observer.model` inherits it unless explicitly set. Embeddings default to `text-embedding-v4`.\n\n`updater.baseUrl` and `observer.baseUrl` optionally select another compatible endpoint; their `apiKeyEnv` names an environment variable, not a secret stored in config. Empty overrides reuse the existing DashScope connection. No private gateway is hardcoded.\n\nOfficial references checked 2026-09-05:\n\n- [Chat Completions](https://help.aliyun.com/zh/model-studio/compatibility-of-openai-with-dashscope)\n- [Embeddings](https://help.aliyun.com/zh/model-studio/text-embedding-synchronous-api)\n- [Models](https://help.aliyun.com/zh/model-studio/models)\n\n### Configuration defaults\n\nPlace this object under `memory` in `~/.qwen-live-harness/config.json`. The optional Observer model is omitted so it follows the consolidation model.\n\n```json\n{\n  \"enabled\": true,\n  \"dir\": \"\",\n  \"defaultId\": \"default\",\n  \"retrieve\": {\n    \"topK\": 3,\n    \"maxChars\": 5000,\n    \"retrievedMaxChars\": 6000,\n    \"useVector\": true,\n    \"model\": \"text-embedding-v4\",\n    \"timeoutMs\": 400,\n    \"backfillTimeoutMs\": 10000,\n    \"cacheSize\": 1000,\n    \"minSim\": 0.4,\n    \"vecLimit\": 50,\n    \"ftsLimit\": 50,\n    \"ftsAndTryThreshold\": 20,\n    \"andBoost\": 1.2,\n    \"timeRangeBoost\": 2,\n    \"timeEdgeDays\": 2,\n    \"rrfK\": 60,\n    \"envMinGapSec\": 600\n  },\n  \"preload\": {\n    \"ltmMaxPerField\": 6,\n    \"ltmMaxChars\": 800,\n    \"stmUpcomingGraceDays\": 2,\n    \"stmMaxAgeDays\": 90,\n    \"recencyLambda\": 0.05,\n    \"upcomingWeight\": 1.5,\n    \"ongoingWeight\": 1,\n    \"urgentBoost\": 1.5,\n    \"urgentDays\": 3,\n    \"stmMaxItems\": 20,\n    \"stmMaxChars\": 1200\n  },\n  \"updater\": {\n    \"enabled\": true,\n    \"model\": \"qwen3.7-plus\",\n    \"baseUrl\": \"\",\n    \"apiKeyEnv\": \"\",\n    \"timeoutMs\": 120000,\n    \"temperature\": 0,\n    \"maxTokens\": 2048,\n    \"maxWmEntries\": 64,\n    \"shutdownWaitSec\": 2\n  },\n  \"observer\": {\n    \"enabled\": false,\n    \"baseUrl\": \"\",\n    \"apiKeyEnv\": \"\",\n    \"intervalSec\": 60,\n    \"timeoutMs\": 60000,\n    \"temperature\": 0,\n    \"maxTokens\": 400,\n    \"maxContentChars\": 400,\n    \"maxFrameAgeSec\": 15\n  },\n  \"wm\": {\n    \"maxEntries\": 128,\n    \"maxEntryChars\": 200\n  },\n  \"segment\": {\n    \"maxTurns\": 4,\n    \"minTurnsBeforeGapCut\": 2,\n    \"maxChars\": 1000,\n    \"silenceGapSec\": 60\n  }\n}\n```\n\n`retrieve.maxChars` bounds unrendered bodies and must not exceed `retrievedMaxChars`, the rendered section budget. Background embedding timeout must be at least the live-query timeout. Visual retrieval spreads observations by `envMinGapSec`; it returns fewer results rather than padding them with near-duplicate frames.\n\nRun `qwen-live-harness --debug` for state, counts, timing and failure diagnostics. These omit memory content and credentials. Detailed preload and consolidation audit records stay in the private library database.\n\n## Host Bootstrap\n\nThe Qwen Live Harness Host installer is built in. On macOS, `qwen-live-harness init` checks if\nthe Host is installed and offers to download and install it (sha256 +\ncodesign + team identifier verification). The daemon also exposes HTTP\nendpoints behind its Bearer token:\n\n```bash\nTOKEN=$(jq -r .token ~/.qwen-live-harness/run/daemon.json)\nPORT=$(jq -r .url ~/.qwen-live-harness/run/daemon.json | sed 's/.*://')\ncurl -H \"authorization: Bearer $TOKEN\" \"http://127.0.0.1:$PORT/live/setup\"\n```\n\n## Protocol v9: Visual Input and Playback Identity\n\nThe daemon speaks Qwen Live Harness Host protocol v9. Output audio carries an epoch and\n`outputId`; the Host echoes both in `host.playback_started` and\n`host.playback_completed`. This prevents receipts from cleared audio from\nsettling a newer playback stream in the same call. A Host that advertises\n`outputAudioEndMarkerV1` receives an explicit end marker for each output id and\nreports completion only after that marker and every scheduled frame have\ndrained. Consecutive response outputs may drain independently, but Qwen Live Harness\nreopens the Proactive FIFO only after all of them complete. Hosts without this\ncapability keep the legacy playback-drain behavior.\n\nStandalone v9 daemons advertise an optional `visualInput` object during the\nwelcome handshake. `host.visual_settings` changes Source/Mode for the current\ncall, `host.visual_frame` carries bounded Live Feed JPEGs, and\n`host.capture_visual` / `host.visual_capture_result` implement correlated On\nDemand capture. The built-in `qwen serve` integration uses the same pair with\nsource `screen` while omitting the standalone Source/Mode controls.\n\nIn Live Feed mode the selected source continuously supplies recent frames to\nthe foreground Omni session. In On Demand mode the foreground model calls\n`appshot` for one selected-source capture. An active visual Proactive task\nalso requests periodic private captures for its Monitor in On Demand mode;\nthe Source/Mode selection does not pause those monitors. The daemon\nreturns its metadata and asset handle through the original\n`function_call_output` continuation; it does not append that snapshot to\nRealtime, commit an audio buffer, or change VAD mode. Pixel-level inspection\nuses the existing handoff path with the returned asset.\n\nScreen Live Feed and visual Proactive monitors capture the **entire selected\ndisplay**, including the desktop, menu bar and Dock, excluding Qwen Live Harness Host's own\nwindows. Choose **Display** under Video Source in Settings. The selection is\nsaved as `visualInput.screenDisplayId` in `config.json`: `primary` (default)\nfollows the system's primary display, or a display UUID selects that device.\nAn explicitly selected display that is disconnected fails without switching to\nanother display. Display changes discard stale captures and reset monitor visual\nbuffers. Both continuous and monitor frames use `liveResolution` (720p by\ndefault), remain aspect-fitted and subject to the existing transport limits;\nfull-display coverage does not mean native pixel resolution. No new init prompt\nis needed. Older Hosts must be updated to support full-display capture.\n\nForeground Screen Appshot and On Demand visual-memory observations keep the\noriginal front-window capture. Appshot still requires Accessibility and Screen\nRecording; the full-display operation needs only Screen Recording. Screen Live\nFeed therefore does not require Accessibility. Camera behavior is unchanged.\n\nScreen captures may also include Appshot accessibility metadata and a PNG\nhandoff asset. Camera source opens a preview/Live Feed stream at\n`cameraResolution` (1280×720 by default). User Appshot requests take a separate\nstill image from that camera track using `cameraSnapshotResolution`; native\nuses the available still-image resolution, with a video-constraint fallback\non devices without still-photo support. The preview settings are restored\nafter fallback capture. Unsupported native capture fails explicitly.\nThe camera JPEG asset keeps its independent snapshot resolution and is\nlimited to 8 MiB. Its transport preview, Live Feed frames, and private Monitor\nframes remain limited to 190 KiB and fitted within 1920×1080. Private Monitor\ncaptures do not take full-resolution still photos. Stop ends the call and its\nmonitors; the visible Camera source may keep a local preview open while idle.\nThe system prompt always follows the latest explicit Source and\nMode and never guesses or combines the unselected source. If the newly selected\nsource needs permission during an active call, the working source remains in\nplace until authorization succeeds, then the switch is applied atomically.\n\n## Status\n\nDeveloped in [Qwen-Live-Harness](https://github.com/QwenLM/Qwen-Live-Harness),\ntracking the [Live split roadmap](https://github.com/QwenLM/qwen-code/issues/10118):\n\n- **M1+M2** (merged): daemon, host stack, base tools, injector, permissions,\n  steering, JSONL logs, Host installer\n- **M4** (merged): AcpAdaptor, multi-backend routing, capability gating\n- **M5** (merged in #10769): protocol v7 playback receipts and the interactive\n  `qwen-live-harness init` wizard\n- **This extension**: protocol v9 visual input and fenced playback receipts,\n  6 configurable Proactive tools, 2 Memory tools with local multi-library\n  storage (both features enabled by default), and configurable desktop controls\n- **M3**: upstream session registry, controller authentication and peer SDK\n  have merged. Live integration is tracked separately from repository migration.\n- Built-in Live retirement is implemented in a companion qwen-code cleanup\n  branch; the new repository owns daemon and Host builds and releases.\n\nThe current source version is 0.3.0 and requires the matching v9 Host under the\nnew application and bundle identities. Build both components from this repository\nuntil the renamed npm package and signed Host are published. Previously released\nartifacts are not an old-name fallback, even if their version or protocol matches.\nReal Qoder account tests require an explicit opt-in:\n`RUN_QODERCLI_SMOKE=1 npx vitest run src/manual/qodercli-acp.test.ts` from this\npackage directory. Ordinary unit and protocol tests do not use live accounts.\n\n## Attribution\n\nThe Proactive and Memory implementations include TypeScript adaptations of\n`qwen-omni-realtime-agent` v0.1.0 (`qwen_omni_realtime_agent/proactive` and\n`qwen_omni_realtime_agent/memory`), Copyright 2026 Alibaba Group Holding Limited,\nlicensed under Apache-2.0. This port modifies those components for Qwen Live Harness's\nDashScope connection, tool authority, playback queue, local storage and Host\ninterface. Original copyright notices are retained in adapted source files.\n","readmeFilename":"README.md"}