From f6bb00c7fe579c28b578ee6d76e30626adb5cc90 Mon Sep 17 00:00:00 2001 From: Lucas1479 Date: Sun, 11 Oct 2026 05:31:19 +1300 Subject: [PATCH 1/2] chore(release): prepare 0.16.0 alpha candidate --- .github/workflows/source-release.yml | 2 +- README.md | 4 +- README_ZH.md | 4 +- docs/alpha-0.16-acceptance.md | 22 ++++++ docs/alpha-0.16.md | 96 ++++++++++++++++++++++++ electron/package-lock.json | 32 ++++---- electron/package.json | 4 +- pyproject.toml | 2 +- release/source_release_policy.json | 15 +++- tools/e2e_codex_app_server_permission.py | 3 +- uv.lock | 2 +- 11 files changed, 159 insertions(+), 27 deletions(-) create mode 100644 docs/alpha-0.16-acceptance.md create mode 100644 docs/alpha-0.16.md diff --git a/.github/workflows/source-release.yml b/.github/workflows/source-release.yml index 4055f9c3..da821114 100644 --- a/.github/workflows/source-release.yml +++ b/.github/workflows/source-release.yml @@ -94,7 +94,7 @@ jobs: working-directory: ${{ env.SOURCE_ROOT }}/electron run: >- node -e "require('node:child_process').execFileSync(require('electron'), - ['scripts/smoke-settings-upgrade.cjs', 'v0.15.0-alpha.0'], + ['scripts/smoke-settings-upgrade.cjs', 'v0.15.2-alpha.0'], {stdio: 'inherit', windowsHide: true, timeout: 45000})" - name: Package extracted Windows sources diff --git a/README.md b/README.md index 59615400..b70ac49e 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@

Bilibili demo Current architecture - 0.15.2 Alpha + 0.16.0 Alpha AGPL-3.0 license
Windows — reference platform @@ -51,7 +51,7 @@ the Host owns identity, state, permissions, persistence, and recovery. > [!IMPORTANT] > This repository contains buildable, runnable source. This branch targets -> **0.15.2 Alpha**, not a packaged desktop release. +> **0.16.0 Alpha**, not a packaged desktop release. > Amadeus first-party code is open-source under the > [GNU Affero General Public License v3.0 (AGPL-3.0)](LICENSE). > Third-party code and external assets retain their own terms. diff --git a/README_ZH.md b/README_ZH.md index 9b9e0ad4..3599b661 100644 --- a/README_ZH.md +++ b/README_ZH.md @@ -10,7 +10,7 @@

Bilibili demo 系统架构 - 0.15.2 Alpha + 0.16.0 Alpha AGPL-3.0 license
Windows — 参考平台 @@ -49,7 +49,7 @@ Amadeus 试图把这些体验连成一个闭环: 持久化与恢复。 > [!IMPORTANT] -> 本仓库包含可构建、可运行的公开源码,本分支为 **0.15.2 Alpha 候选版**, +> 本仓库包含可构建、可运行的公开源码,本分支为 **0.16.0 Alpha**, > 不是带安装器的正式桌面发行版。Amadeus 第一方代码依据 > [GNU Affero General Public License v3.0(AGPL-3.0)](LICENSE) 开源。 > 第三方代码与外部资产保留各自条款。 diff --git a/docs/alpha-0.16-acceptance.md b/docs/alpha-0.16-acceptance.md new file mode 100644 index 00000000..6fbd0694 --- /dev/null +++ b/docs/alpha-0.16-acceptance.md @@ -0,0 +1,22 @@ +# 0.16.0 Alpha acceptance record + +Date: 2026-10-11 (Pacific/Auckland). Preparation baseline: `09aecfb`, including +merged #173 and #174. Comparison release: `v0.15.2-alpha.0`. + +Release preparation uses a clean, isolated Git worktree. Uncommitted local voice +configuration, private runtime state and external media are excluded. + +## Current validation + +Validation is in progress. This record will be updated with observed results +before publication. Historical feature records are not counted as a pass for +the final release candidate. + +## Evidence boundaries + +Deterministic suites, native desktop startup, extracted source installation, +real-model interaction, device playback and human microphone/listening checks +establish different facts. Missing optional dependencies and manual checks are +reported as skipped or unverified, never counted as passes. Raw logs, local +paths, credentials, conversations and runtime media remain outside the source +release; only sanitized aggregate results are recorded here. diff --git a/docs/alpha-0.16.md b/docs/alpha-0.16.md new file mode 100644 index 00000000..7437ee15 --- /dev/null +++ b/docs/alpha-0.16.md @@ -0,0 +1,96 @@ +# 0.16.0 Alpha: unified Chat and local character management + +Tag: `v0.16.0-alpha.0`; Python `0.16.0a0`; Electron `0.16.0-alpha.0`. +This is a source Alpha release. Model weights, character media, reference voices, +Live2D Cubism Core, credentials and desktop installers are not bundled. +The comparison baseline is `v0.15.2-alpha.0`. + +## Changes + +- **One Chat runtime.** Cooperative owns production Chat in both professional + and basic mode. Shared presentation retains streaming, expressions and speech; + the old Original orchestration path is retired. Provider handoffs preserve + shared constraints without expanding the assigned task's scope. +- **Local character management.** Settings can create and edit local roles, + select the next startup identity, and recover explicitly from an invalid role. + Conversations remain owned by their role; accepted Work retains its identity. + Settings groups identity, appearance and the existing Kurisu knowledge controls + under Characters, with voice configuration in its single Voice editor. +- **Experimental Live2D.** Optional local Live2D models use the existing render + and wallpaper path, including expression and mouth signals. Sprite remains + available. Users supply the model and Cubism Core under their respective terms. +- **Desktop and rendering.** Wallpaper and Render transitions share serialized + lifecycle ownership. Texture residency is bounded and BC7 frames can be cached. + SpriteForge framing uses the pack canvas; wallpaper preserves aspect ratio. + Connection editors have consistent save state and dark Render backgrounds. +- **Voice and history.** Speech aggregation accounts for ready audio and the + selected synthesis profile; idle output streams persist between turns. + Continuous voice can return to wake standby, Qwen3-ASR can remain in system + memory, and BERT loads only for Chinese GPT-SoVITS text. V3 emotion references + remain opt-in. Sessions retain complete history while Main Chat receives a + bounded recent window. +- **Configuration and recovery.** Setting declarations and built-in handler + registration share one catalog. Durable state writes, runtime diagnostics and + translation checks are strengthened. Restoring Watching mode preserves an + explicitly disabled vision setting. + +## Install and upgrade + +Use the source ZIP and the installation profile in [README](../README.md). +Extract into a new directory and run `uv sync --locked` with the complete set of +extras for your profile, then `npm ci` and `npm run build` in `electron/`. +Running only the core install command against an existing optional-model +environment removes extras; retain your full profile selection. + +Before upgrading, stop incoming turns and drain or stop owned Work. Back up your +settings and persistent data. Keep external assets separate and configure their +locations in Settings. Do not copy caches or an entire old virtual environment. + +The retired `COOPERATIVE_CHAT_ENABLED=false` no longer selects Original Chat. +Settings explains and acknowledges migration; an obsolete `.env` assignment +must be removed from that file. `COOPERATIVE_WORK_PLANNER_ENABLED=false` still +selects basic Cooperative. There is no hidden Original fallback. + +Pure-local Chat uses `llama_server`; persistent `cli` sessions require migration. +The managed pure-local context defaults to 16384, while Hybrid keeps 4096. +Explicit saved values are preserved. Hybrid now uses its configured local head +alongside the remote role model. See [runtime migration](chat-runtime-convergence.md). + +Role selection applies after restart. Appearance and voice remain application-wide; +creating a role does not create a separate voice, artwork or memory library. +See [character management](character-management.md) and +[Live2D setup](live2d_character_visuals.md). + +For rollback, use a separate checkout or source directory of `v0.15.2-alpha.0`. +Do not overwrite newer history or Work receipts with an old database snapshot. +Acknowledging removal of the retired route setting does not restore that setting +when older software is started. Unknown external Provider outcomes remain unknown. + +## Validation and known limits + +The [0.16 acceptance record](alpha-0.16-acceptance.md) distinguishes this release's +checks from earlier feature evidence. Alpha does not imply that every model, +device, operating system or experimental surface has been qualified. + +- Model-less CI does not establish microphone recognition, audible quality, + acoustic latency, GPU inference or long-duration resource stability. +- Browser cross-domain journeys and pure-local model task interpretation retain + recorded limitations; LM Studio/Ollama capacity behavior remains unqualified. + See the [runtime acceptance boundaries](chat-runtime-convergence.md#evidence-and-remaining-acceptance). +- Live2D and VN remain experimental. Live2D model physics/motions and external + wallpaper hosts depend on the supplied assets and environment. +- Per-role voice/appearance bindings, automatic asset installation and general + cross-session semantic memory are not part of this release. +- Platform and optional GPU-profile support remains limited to the evidence + documented in [installation profiles](install_profiles.md). + +## 中文摘要 + +0.16.0 Alpha 汇总自 0.15.2 以来的运行时与桌面更新:统一 Cooperative Chat, +加入本地角色管理、启动恢复和实验性 Live2D,整理角色与连接设置,改进纹理驻留、 +语音调度、完整历史保存、配置声明及持久化可靠性。 + +这是源码预发布,不包含桌面安装器、模型权重、角色素材、参考语音、Cubism Core +或凭据。升级请使用新目录并保留完整的可选依赖配置;先停止输入和进行中的 Work。 +旧 Original Chat 开关已退役,角色切换需重启,声音与外观仍为应用级设置。 +已知限制和本轮实测范围见验收记录;既有单项测试不代表所有硬件与实验功能均已验收。 diff --git a/electron/package-lock.json b/electron/package-lock.json index dad355f2..fa4a0b99 100644 --- a/electron/package-lock.json +++ b/electron/package-lock.json @@ -1,12 +1,12 @@ { "name": "amadeus-desktop", - "version": "0.15.2-alpha.0", + "version": "0.16.0-alpha.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "amadeus-desktop", - "version": "0.15.2-alpha.0", + "version": "0.16.0-alpha.0", "hasInstallScript": true, "dependencies": { "react": "^19.0.0", @@ -18,7 +18,7 @@ "@types/react": "^19.0.0", "@types/react-dom": "^19.0.0", "@vitejs/plugin-react": "^6.1.1", - "concurrently": "^10.0.5", + "concurrently": "^10.0.6", "electron": "^44.0.0", "electron-builder": "^26.15.3", "tailwindcss": "^4.0.0", @@ -2179,15 +2179,15 @@ "license": "MIT" }, "node_modules/concurrently": { - "version": "10.0.5", - "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-10.0.5.tgz", - "integrity": "sha512-JaP/CoftUrCcAFW/g//RbgEGwlelnEae6cfBLgH6ZdO6s8jPkn6p9SB9u6pdVxYXoiSnFqseOlHfrEfF82TVOg==", + "version": "10.0.6", + "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-10.0.6.tgz", + "integrity": "sha512-qgICXXFp2/3nAebRLmxcINKEXM1mLK3ij/y31P02VTs/KIkddIeC05n73DxOXsXMB9LFSqNU1/na3lDlOhcbIw==", "dev": true, "license": "MIT", "dependencies": { "chalk": "5.6.2", "rxjs": "7.8.2", - "shell-quote": "1.9.0", + "shell-quote": "1.12.0", "supports-color": "10.2.2", "tree-kill": "1.2.2", "yargs": "18.0.0" @@ -3546,9 +3546,9 @@ } }, "node_modules/http-cache-semantics": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", - "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.3.0.tgz", + "integrity": "sha512-M5t5LlJpS1UHMjvwRQVdFHvPISGeLAxNcrWuJkeGh0KxsqCHZ1O3NXZU/8x7cD0BDcGW8kapxMKTvwlqrNkHkA==", "dev": true, "license": "BSD-2-Clause" }, @@ -3744,9 +3744,9 @@ } }, "node_modules/joi": { - "version": "18.2.8", - "resolved": "https://registry.npmjs.org/joi/-/joi-18.2.8.tgz", - "integrity": "sha512-G2TX62h58ZHuwqetJgP2F4ualakqAmZtBYe3jWen7gxQRw5xApX6crnFtuB91WC0c3ESBnva+kGSnb3+6pIQDQ==", + "version": "18.2.9", + "resolved": "https://registry.npmjs.org/joi/-/joi-18.2.9.tgz", + "integrity": "sha512-2mD929bUVKUhOLQQEVhlf6EZ0Mlo0DeRb5MO7cViR9AXLtBauuccEtB1py9Ocxpo/P7ucnh442iY/iOwrh3IQw==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -5744,9 +5744,9 @@ } }, "node_modules/shell-quote": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.9.0.tgz", - "integrity": "sha512-Iov+JwFv/2HcTpcwNMKd8+IWNb8tboQJNQTkAY/LLVK7gGH9jy+LGkVqPxfekHl+yMmiqXszdGWXgkfml7hjqA==", + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.12.0.tgz", + "integrity": "sha512-PcByqNyT/38F2kDNi006HAMRJaULuBzq/FOsw3qdZvX/GA9W/jamDaRskgHjubHiftXK5sIFxLNkvrXUwcof6Q==", "dev": true, "license": "MIT", "engines": { diff --git a/electron/package.json b/electron/package.json index d730e3d8..6d2ce481 100644 --- a/electron/package.json +++ b/electron/package.json @@ -1,6 +1,6 @@ { "name": "amadeus-desktop", - "version": "0.15.2-alpha.0", + "version": "0.16.0-alpha.0", "description": "Amadeus AI Companion — Electron frontend", "type": "module", "main": "dist/main/index.js", @@ -37,7 +37,7 @@ "@types/react": "^19.0.0", "@types/react-dom": "^19.0.0", "@vitejs/plugin-react": "^6.1.1", - "concurrently": "^10.0.5", + "concurrently": "^10.0.6", "electron": "^44.0.0", "electron-builder": "^26.15.3", "tailwindcss": "^4.0.0", diff --git a/pyproject.toml b/pyproject.toml index a597db93..f8db6d4f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "amadeus" -version = "0.15.2a0" +version = "0.16.0a0" description = "Amadeus runtime: voice, chat runtime, provider host, and work ledger." requires-python = ">=3.12,<3.13" diff --git a/release/source_release_policy.json b/release/source_release_policy.json index 44e51b6e..a785d08b 100644 --- a/release/source_release_policy.json +++ b/release/source_release_policy.json @@ -69,7 +69,11 @@ "docs/evidence/routing-accuracy-2026-09-12.json", "docs/alpha-0.15-impact.md", "docs/alpha-0.15.1.md", - "docs/alpha-0.15.2.md" + "docs/alpha-0.15.2.md", + "docs/alpha-0.16.md", + "docs/alpha-0.16-acceptance.md", + "docs/chat-runtime-convergence.md", + "docs/live2d_character_visuals.md" ], "include_roots": [ "characters", @@ -181,6 +185,15 @@ "docs/provider_parent_conversation_handoff_2026-08-31.md", "docs/alpha-0.15.1-acceptance.md", "docs/alpha-0.15.2.md", + "docs/alpha-0.16.md", + "docs/alpha-0.16-acceptance.md", + "docs/chat-runtime-convergence.md", + "docs/live2d_character_visuals.md", + "docs/main-chat-character-prompt.md", + "docs/provider-handoff-context.md", + "docs/tts_adaptive_aggregation.md", + "docs/tts_emotion_references.md", + "docs/texture_cache_implementation_2026-10-05.md", "docs/texture_pipeline_acceptance_2026-10-04.md", "docs/evidence/texture-store-2026-10-04.json", "docs/evidence/texture-egpu-comparison-2026-10-04.json", diff --git a/tools/e2e_codex_app_server_permission.py b/tools/e2e_codex_app_server_permission.py index b373d30c..59ddf824 100644 --- a/tools/e2e_codex_app_server_permission.py +++ b/tools/e2e_codex_app_server_permission.py @@ -277,6 +277,7 @@ def is_next_control_event(event: Any) -> bool: "permission_request_ids": permission_ids, "native_permission_request_ids": sorted(native_request_ids), "target_content": content, + "provider_result": terminal.to_dict(), } ) report["semantic_evidence"] = build_evidence( @@ -297,7 +298,7 @@ def is_next_control_event(event: Any) -> bool: "provider_run_ids": [run_id] if run_id else [], }, manual_acceptance="pending", - notes=f"Official Codex approval callback, Host {args.decision}, same native turn continuation.", + notes=[f"Official Codex approval callback, Host {args.decision}, same native turn continuation."], ) exit_code = 0 if report["status"] == "passed" else 1 except Exception as exc: diff --git a/uv.lock b/uv.lock index 31cc6cec..99c47933 100644 --- a/uv.lock +++ b/uv.lock @@ -195,7 +195,7 @@ wheels = [ [[package]] name = "amadeus" -version = "0.15.2a0" +version = "0.16.0a0" source = { editable = "." } dependencies = [ { name = "aiohttp" }, From 2d6c8a71b6248313aa460e67bb9e8a4a99766e1c Mon Sep 17 00:00:00 2001 From: Lucas1479 Date: Sun, 11 Oct 2026 05:50:39 +1300 Subject: [PATCH 2/2] test(release): qualify live journeys and repair stale probe observations --- docs/alpha-0.16-acceptance.md | 145 ++++++++++++++++++++++- docs/alpha-0.16.md | 3 + tools/e2e_codex_app_server_control.py | 34 +++--- tools/probes/measure_vn_audio_latency.py | 24 ++-- 4 files changed, 178 insertions(+), 28 deletions(-) diff --git a/docs/alpha-0.16-acceptance.md b/docs/alpha-0.16-acceptance.md index 6fbd0694..190d4499 100644 --- a/docs/alpha-0.16-acceptance.md +++ b/docs/alpha-0.16-acceptance.md @@ -6,11 +6,148 @@ merged #173 and #174. Comparison release: `v0.15.2-alpha.0`. Release preparation uses a clean, isolated Git worktree. Uncommitted local voice configuration, private runtime state and external media are excluded. -## Current validation +## Automated and source-package validation -Validation is in progress. This record will be updated with observed results -before publication. Historical feature records are not counted as a pass for -the final release candidate. +The first preparation commit, `f6bb00c`, passed all 17 PR checks across eight +workflows: Python Windows, Electron Windows, Python Linux, Python macOS, local +model installation candidates, Windows ROCm candidate, optional character RAG, +and Source Release. The Source Release job installs locked dependencies, builds +and packages Electron, verifies native settings upgrade, and launches the +unpacked executable using the extracted source ZIP. Final publication must use +the final clean commit and its corresponding manifest/checksum. + +Local checks with Python 3.12.10 and Node 22: + +- Electron: **388/388** tests and TypeScript/Vite production build passed after + the compatible dependency repairs. The existing bundle-size advisory remains. +- Full isolated Python runner executed all discovered suites. Its retained raw + summary was 5358 passed / 20 skipped and a failing exit: `test_system_settings` + had 17 failures and `test_tts_emotion_references` had one. Both failures came + from test-created external-asset junctions while audio qualification was being + prepared. The product correctly rejected paths outside the asset root. + The junctions were removed; settings plus installed Pi runtime/web contracts + then passed **48**, with one optional skip; emotion-reference, Codex approval + cancellation and semantic-evidence tests passed **80**. The separate clean CI + run passed all three full-suite shards. The failed local run is not relabeled + as a green full run. Its aggregate collection and per-suite outcome counts do + not exactly reconcile, so no combined final test-count claim is derived. +- Environment verifier, Ruff, generated architecture views, whitespace checks + and third-party provenance release gate passed. +- Clean source archive: **3882 files**, zero policy errors or warnings at the + first preparation commit. Final release hashes come from the final manifest. + +## Native desktop and upgrade + +- Shipping model-less Electron launch: **11/11** checks passed, covering backend + startup, authentication, navigation, configuration visibility and clean exit. +- Actual settings from `v0.15.2-alpha.0`, with a synthetic native-encrypted + credential, survived upgrade and reopening. No personal profile was migrated. +- Native wallpaper composer and offline startup-settings GUI checks passed; + **13** Windows lifecycle contracts passed. The composer process emitted two + Chromium GPU-state diagnostics while exiting; its interaction assertions + passed. This is not a long-duration GPU or real Lively qualification. +- Extended native role journey: **17/17** checks passed. Two same-name roles + retained distinct IDs; pending selection did not change the running identity; + restart applied selection and edits; malformed-role recovery returned to + Kurisu while preserving the malformed user file; Chat reconnected and owned + processes exited. An initial local harness used the wrong role-directory + environment variable; a second attempt overlapped the asset-junction setup. + Both unsuccessful attempts are retained as instrument/setup failures. The + final run used the documented directory setting and clean model-less assets. + The six extra role assertions use a task-local extension; the ordinary shipped + smoke alone is claimed to cover only its original eleven assertions. + +## Real models, actions and device playback + +- Codex App Server 0.154.0: native **allow and deny each passed 8/8** assertions. + Allow continued the same WorkItem/Attempt and wrote the requested synthetic + file. Deny persisted refusal, closed the permission card and left it absent. + The first isolated attempts lacked the credential helper's explicit env-file + binding; those authentication failures were retained. Corrected tests used + the existing binding and disabled desktop-provider synchronization. The + initial test's managed non-secret provider path was restored afterward. +- Real Chat and Codex Work control: **10/10** assertions passed with the project + opened through the normal Session API. The initial request started one run; + progress inquiry was read-only; amendment delivery was recorded for that same + run; the final file contained exactly the amended text; WorkItem and Attempt + identity remained unchanged. The old harness first asked for a context switch + but never answered the resulting selection, then waited for the retired + `steer_queued` event despite an existing delivered input receipt. These failed + attempts remain recorded. The probe now checks the durable delivery receipt + and actual file result. This does not certify an unattended compound + context-switch-and-execute request or restore the obsolete event. +- Shipping Electron, DeepSeek V4 Flash and seeded AUIP Gomoku: **37/37** + structural checks passed. The journey played a complete round through real + player clicks and model actions, restarted, resigned, concluded, left the app + and returned to ordinary Chat. Accepted receipts, actor/app identity, bounded + experience history and the post-leave conversation were verified. The final + screenshot and role replies were inspected. This run deliberately disabled + TTS; the seeded application is a fixture, not Provider-generated software. +- Actual local GPT-SoVITS v3 checkpoint, Python 3.12.10, Torch 2.6.0+cu124 and + NVIDIA RTX 4070 Ti SUPER: one warmup plus four short VN replies produced nonzero + PCM written to the physical output device through the production VN bridge + and shared speech pipeline. Existing external voice assets and the installed + GPU environment were used with the candidate source in a separate extracted + directory. No media or machine configuration enters the source archive. + +| Delivery | First successful device write | PCM peak | +| --- | ---: | ---: | +| Whole speech body | 2.463 s | 0.349 | +| First segment | 1.962 s | 0.520 | +| First segment | 1.765 s | 0.298 | +| Whole speech body | 1.682 s | 0.353 | + +The old audio probe observed only the streaming method and missed complete-audio +playback. It now observes successful writes on the actual `PyAudio.Stream` class +used by `PyAudio.open`, covering both delivery paths. The old public `Stream` +alias is a different class in the installed PyAudio. Failed instrument runs were +not counted as passes. These timestamps measure device submission, not acoustic +arrival; there is no new latency target, percentile or regression conclusion. +The maintainer deferred a matched previous-release latency comparison for this +release. Human microphone recognition and subjective listening remain unverified. + +Reproduction entry points (use isolated profiles and configured credentials): + +```text +python -X utf8 tools/run_tests.py +npm test # electron/ +npm run build # electron/ +python -X utf8 tools/smoke_electron_model_less.py +python -X utf8 tools/e2e_codex_app_server_permission.py --decision allow_once +python -X utf8 tools/e2e_codex_app_server_permission.py --decision deny +python -X utf8 tools/e2e_codex_app_server_control.py +python -X utf8 tools/probes/measure_vn_audio_latency.py --live-model-and-audio --baseline-delivery whole-speak +``` + +The live Codex tests need `CODEX_APP_SERVER_PROVIDER_AUTH_ENV_FILE` to refer to +the configured credential source; set `CODEX_APP_SERVER_SYNC_DESKTOP_PROVIDER=0` +for isolated tests. Never log or publish that file. The audio command plays real +audio and requires the optional GPU/voice dependencies and separately installed +assets. It must not share asset setup with a clean model-less test run. + +## Dependency audit disposition + +Compatible Electron tooling repairs update concurrently 10.0.5 to 10.0.6 +(shell-quote 1.9.0 to 1.12.0), http-cache-semantics 4.2.0 to 4.3.0, and joi +18.2.8 to 18.2.9. Electron has no remaining high/critical audit findings, but +eight moderate build-tool findings remain in the electron-builder/global-agent/ +roarr/sprintf-js chain. No force downgrade or broad audit suppression was added. + +Pi remains pinned to its qualified 0.86.1 runtime. Its upstream shrinkwrap +installs brace-expansion 5.0.9, retaining the denial-of-service advisories +[GHSA-q2hr-2g5m-vwhr](https://github.com/advisories/GHSA-q2hr-2g5m-vwhr), +[GHSA-qhr7-859c-m2p7](https://github.com/advisories/GHSA-qhr7-859c-m2p7), and +[GHSA-6j4f-fj2g-mc7p](https://github.com/advisories/GHSA-6j4f-fj2g-mc7p). +The audit groups these as one high-severity vulnerable package. A root override +did not repair it. An outer lockfile-only change made the audit green while a +fresh install still contained 5.0.9; that ineffective change was discarded. +Upstream 0.87.1's inspected package also retains 5.0.9. This remains a disclosed +dependency limitation, not a clean Pi audit or a claim of unreachable risk. + +Python core/development audit reported no findings after the existing narrow CI +exemption (`PYSEC-2026-1845`; the tool reported two ignored advisory identities). +The project itself is not audited as a PyPI package. Optional GPU dependencies +were not upgraded or assigned a new vulnerability-free claim. ## Evidence boundaries diff --git a/docs/alpha-0.16.md b/docs/alpha-0.16.md index 7437ee15..329972ad 100644 --- a/docs/alpha-0.16.md +++ b/docs/alpha-0.16.md @@ -83,6 +83,9 @@ device, operating system or experimental surface has been qualified. cross-session semantic memory are not part of this release. - Platform and optional GPU-profile support remains limited to the evidence documented in [installation profiles](install_profiles.md). +- Dependency audits are not clean: the pinned Pi runtime retains a known + brace-expansion denial-of-service risk, and Electron build tooling retains + moderate findings. See the [exact audit disposition](alpha-0.16-acceptance.md#dependency-audit-disposition). ## 中文摘要 diff --git a/tools/e2e_codex_app_server_control.py b/tools/e2e_codex_app_server_control.py index db815c8b..04ea96b9 100644 --- a/tools/e2e_codex_app_server_control.py +++ b/tools/e2e_codex_app_server_control.py @@ -137,7 +137,8 @@ async def _run(args: argparse.Namespace) -> tuple[int, dict[str, Any]]: async with WsProbe(f"ws://127.0.0.1:{port}/ws") as probe: await probe.request( "session.create", - {"session_id": session_id, "title": "Codex active control probe"}, + {"session_id": session_id, "title": "Codex active control probe", + "project_id": project_record.project_id}, ) create_turn, create_after, create_complete = await _send_chat( probe, @@ -146,7 +147,7 @@ async def _run(args: argparse.Namespace) -> tuple[int, dict[str, Any]]: chat_provider=args.chat_provider, timeout_s=args.chat_timeout, text=( - "切换到 control-lab 项目并交给 codex 做:先在项目根运行 " + "交给 codex 做:先在当前项目根运行 " "python slow_gate.py,必须等它结束;然后创建 control.txt," "内容恰好是 ORIGINAL 加一个换行,并读取验证。" ), @@ -198,15 +199,20 @@ async def _run(args: argparse.Namespace) -> tuple[int, dict[str, Any]]: "仍然交给 codex,这是同一项工作的运行中修改。" ), ) - steer = await probe.wait_event( - lambda event: event.method == "provider.event" - and event.params.get("run_id") == run_id - and event.params.get("type") == "run.status" - and event.params.get("payload", {}).get("stage") == "steer_queued", - timeout=args.activity_timeout, - after=amend_after, - description="Codex native steer queued", - ) + # Work amendments use durable input delivery receipts. The retired + # steer_queued presentation event is not a delivery authority fact. + input_receipt = None + async with asyncio.timeout(args.activity_timeout): + while input_receipt is None: + with WorkLedgerStore(ledger_path) as store: + for item in store.list_work_items(limit=20): + for receipt in store.list_provider_inputs(item.work_item_id): + if receipt["input_id"] == amend_turn: + input_receipt = receipt + if input_receipt is None: + await asyncio.sleep(0.1) + if input_receipt["state"] != "delivered" or input_receipt["provider_run_id"] != run_id: + raise RuntimeError("Amendment was not delivered to the original Provider run") amend_created_runs = [ event for event in probe.state.events[amend_after:] @@ -244,10 +250,8 @@ async def _run(args: argparse.Namespace) -> tuple[int, dict[str, Any]]: "status_reply_is_visible": bool( str(status_complete.params.get("full_text") or "").strip() ), - "native_steer_was_queued": int( - steer.params.get("payload", {}).get("revision") or 0 - ) - >= 1, + "amendment_delivered_to_same_run": input_receipt["state"] == "delivered" + and input_receipt["provider_run_id"] == run_id, "amend_did_not_start_second_run": not amend_created_runs, "one_work_item_one_attempt": len(ledger_items) == 1 and len(attempts) == 1, "steered_result_is_current": content == "STEERED\n", diff --git a/tools/probes/measure_vn_audio_latency.py b/tools/probes/measure_vn_audio_latency.py index b56b9a95..17ab516e 100644 --- a/tools/probes/measure_vn_audio_latency.py +++ b/tools/probes/measure_vn_audio_latency.py @@ -81,8 +81,6 @@ def log_event(record): stage = "enqueue_ms" elif "first audio chunk generated" in message: stage = "audio_ready_ms" - elif "first sound started" in message: - stage = "first_sound_ms" if stage: current.setdefault(stage, round((record.created - current["started_at"]) * 1000)) changed.set() @@ -94,16 +92,23 @@ def emit(self, record): observer = Observer() logging.getLogger().addHandler(observer) logging.getLogger("PlaybackManager").setLevel(logging.INFO) - original_write = player.write_audio_async - - async def observe_audio(audio, *positional, **kwargs): + # Both complete/cached playback and streaming reach PortAudio here. The + # async player method covers only streaming and is not device evidence. + import pyaudio + original_write = pyaudio.PyAudio.Stream.write + + def observe_audio(stream, frames, *positional, **kwargs): + result = original_write(stream, frames, *positional, **kwargs) + if stream is not player.stream: + return result import numpy as np - array = audio.cpu().detach().numpy() if hasattr(audio, "detach") else np.asarray(audio) + array = np.frombuffer(frames, dtype=np.float32) if array.size: + current.setdefault("first_device_write_ms", round((time.time() - current["started_at"]) * 1000)) current["peak"] = max(current.get("peak", 0), float(np.abs(array).max())) current["samples"] = current.get("samples", 0) + int(array.size) - await original_write(audio, *positional, **kwargs) - player.write_audio_async = observe_audio + return result + pyaudio.PyAudio.Stream.write = observe_audio async def speak(payload): current.setdefault("speech_submitted_ms", round((time.time() - current["started_at"]) * 1000)) @@ -120,7 +125,7 @@ async def wait_audio(timeout=90): while current.get("last_sentence_id") not in completed: changed.clear() await changed.wait() - if not current.get("first_sound_ms") or not current.get("peak", 0) > 0: + if "first_device_write_ms" not in current or not current.get("peak", 0) > 0: raise AssertionError("No nonzero device-playback evidence; silence placeholders do not pass") workers = [asyncio.create_task(pipeline.play_sentence_worker()), asyncio.create_task(playback.run())] @@ -184,6 +189,7 @@ async def whole_speak(header, text_complete=True): task.cancel() await asyncio.gather(*list(bridge._SUBTITLE_TASKS), return_exceptions=True) player.cleanup() + pyaudio.PyAudio.Stream.write = original_write synthesizer.backend.close() executor.shutdown(wait=True) logging.getLogger().removeHandler(observer)