{"owner":"AlkaidLab","repo":"foundation-sunshine","hasSkills":true,"totalSkillsCount":4,"totalTokensCount":5633,"categories":["plugin-manifest","subagent-persona"],"hasMcp":false,"mcpConfig":null,"found":["tools/sunshine-ds5-sidecar/README.md","src_assets/common/assets/web/utils/agents/gameLibrary/README.md","src_assets/common/assets/web/utils/agents/diagnostics/README.md","src_assets/common/assets/web/utils/agents/core/README.md"],"skills":{"tools/sunshine-ds5-sidecar/README.md":"# Sunshine virtual device host\n\nThis optional Windows helper isolates Sunshine from the third-party\nHIDMaestro runtime. The shipping executable remains\n`Sunshine.Ds5Sidecar.exe` for component-upgrade compatibility, while the\nprocess now uses a `VirtualDeviceHostServer` and `DeviceRegistry` internally.\nIt owns virtual devices and exposes the versioned `SDS5` named-pipe protocol.\nThe optional component package includes patched HIDMaestro Core and its notices.\n\nBuild the compatible Foundation Core patch and the sidecar:\n\n```powershell\nscripts/build-ds5-sidecar.ps1\n```\n\nThe version 1.4.2 component ZIP includes patched `HIDMaestro.Core.dll`, its\nlicenses, and the complete .NET runtime. The main Sunshine package contains only\nthe pinned ZIP download manifest. The Control Panel automatically updates older\ninstalled components when streaming is idle, using the existing administrator\nhelper, verified staging and whole-component rollback. Optional USB/IP transport\ninstallation remains part of explicit install/repair, not automatic updates.\nThe sidecar uses normal .NET loading and requires the v1.12.0 runtime and\ndevice-ownership, externally managed USB/IP and `FoundationDualSenseReports=host-clock-v1`\nmarkers even for `--probe`. Sunshine validates the\ninstalled component without replacing DLLs.\nThe upstream SDK API, assembly version and embedded resources are preserved; see\n[`../hidmaestro-core-patch/README.md`](../hidmaestro-core-patch/README.md).\n\nRead-only capability probe:\n\n```powershell\ndotnet Sunshine.Ds5Sidecar.dll --probe\n```\n\nDeterministic protocol ABI, microphone queue/runtime-contract, four-channel\nlayout, and channel-isolation checks (no elevation or virtual device required):\n\n```powershell\ndotnet Sunshine.Ds5Sidecar.dll --self-check\n```\n\nUSB/IP installation and repair belong to the shared component manager. Device\ncreation only checks the approved driver and fails with a shared transport error\nwhen it is missing or incompatible. The ZIP carries `component-compatibility.json`,\nthe same contract used by the GUI installer. The staged upgrade acceptance test\nrequires elevation and an idle host, and creates/releases two composite devices:\n\n```powershell\nSunshine.Ds5Sidecar.exe --self-test transport --result transport-acceptance.json\n```\n\nThe production process must be launched elevated and placed in the Sunshine\nJob Object. The pipe accepts a single elevated client of the creating user;\nnon-elevated callers are rejected at connect time and dropped without\nending the sidecar.\nInput, touch, motion and battery updates submit their combined state directly\non receipt under the session state lock. They do not use a thread-pool timer:\neven a 1 ms timer can run at roughly 15.6 ms intervals on Windows and reduce\ncontinuous input to about 64 Hz. The sidecar preserves the incoming update\ncadence; it does not synthesize repeated reports to inflate polling-rate tests.\nUSB reports include rolling 8-bit and 32-bit sequence numbers, device time and\nthe USB-connected status. Device and motion timestamps use the same host QPC\nepoch at 3 MHz. Button, touch and battery reports advance device time while\nretaining the latest motion sample timestamp. Network receive time still stands\nin for the client's physical sampling time; these metadata corrections have not\nbeen verified to resolve Genshin's camera jumps.\nThe pipe reader reuses a receive buffer for headers and payloads, dispatching\ncomplete frames as soon as they arrive. This removes separate header/body reads\nand per-input payload allocations without adding a batching delay. Microphone\nPCM retains its own copy because its queue outlives the receive buffer. The\ndevice-free `--self-check` covers fragmented/packed frames, length validation,\nEOF, cancellation and retained PCM ownership.\nThe reader uses one async state machine per pending frame. Standard axis\nbindings are resolved once through the SDK at controller creation and update\nthe existing state dictionary in place. This avoids rebuilding the SDK's\nstick/trigger lists and axis dictionary on every input update. `--self-check`\ncompares the cached mapping against the SDK's complete bundled profile catalog\nand checks that steady-state axis updates allocate no managed objects.\nThe pinned .NET 10 runtime config sets\n`System.Threading.ThreadPool.UnfairSemaphoreSpinLimit` to zero for this process.\nPool workers wait for a signal instead of spinning between short input bursts.\nThis adds no report-rate limiter or timer. Because it trades spinning for OS\nwakeups, CPU and report-interval tests should be repeated when changing the\nbundled runtime or enabling additional concurrent input/audio workloads.\nDisconnecting the owning pipe disposes every device created by\nthat connection. Standard `dualsense` uses UMDF2; `dualsense-composite`\nenables the USB composite HID/audio profile and authored haptics PCM.\nHID-only attaches use `dualsense` with the native 273-byte descriptor and\nGame Pad usage (0x05). HIDMaestro v1.12.0 no longer writes the incorrect global\nGameInput mapping for JSON profiles and removes exact legacy SDK tables\n(issue #1056), so the former Joystick usage override is unnecessary. Input\nreports retain `alwaysArmed` for touch and motion data. Windows decides how\nto expose the device; native Game Pad usage does not guarantee a WGI Gamepad.\nThe optional Genshin compatibility attach flag derives a third profile from\n`dualsense-composite` at runtime. It preserves the Sony VID/PID, descriptors,\nand four-channel layout while changing only the USB product string from\n`DualSense Wireless Controller` to the launch-model `Wireless Controller`.\nThe runtime profile starts the USB speaker control unmuted at its declared\nmaximum and commits the active endpoint's 4-channel, available-speaker and\nfull-range masks as quadraphonic (`0x33`) through Core Audio. This matches the\nthree settings applied by completing Windows' speaker setup wizard as required\nby Genshin.\nEndpoint enumeration uses the Windows SDK's `IMMDeviceCollection` IID, and\nthe Windows 10+ policy property calls include the effects-store argument.\n`NativeMethods.txt` requests the public audio COM interfaces, structures and\nproperty keys from Microsoft CsWin32. The generator and SDK metadata are pinned\nin `packages.lock.json`; publishing restores in locked mode and adds no runtime\nlibrary dependency. `CoreAudio` shares endpoint enumeration and reference cleanup\nbetween the default-device monitor and WASAPI probe. Only the undocumented\nWindows 10+ policy interface remains hand-declared, inside `AudioPolicyConfig`.\nCore Audio references are released per acquisition so configuration readback\ncannot invalidate the concurrent default-device monitor. Endpoint detection\nalso recognizes HIDMaestro's synthetic USB serial under the official USB/IP\nhost controller, without requiring runtime ownership stamping.\nThe transport acceptance test creates both the normal composite profile and\nthe Genshin compatibility profile. It reads back the endpoint format and both\nspeaker masks for the endpoint created by that attach before passing. It waits\nfor that endpoint to become inactive before the next round, so a stale endpoint\ncannot satisfy another mode's check.\nThe sidecar advertises this support through a protocol capability bit so an\nolder runtime cannot silently accept an ineffective setting.\nThe v1 wire contract reserves capability bits and message numbers for the\nvirtual microphone. Normal startup intentionally does not advertise them;\nonly the explicit development prototype can enable those capabilities.\n\nThe Phase 2 composite-profile microphone path is development-only and requires\nboth the exact HIDMaestro 1.12.0.0 runtime and an explicit opt-in. Its elevated\nattach/PCM/flush/destroy smoke test is:\n\n```powershell\ndotnet Sunshine.Ds5Sidecar.dll --self-test microphone-prototype\n```\n\nFor an end-to-end developer check, `microphone-capture` snapshots the active\ncapture endpoints, creates the prototype, opens only its newly added endpoint\nthrough WASAPI, streams a 440 Hz signal for five seconds, and requires observed\nhost streaming, zero submit errors, captured frames, and non-zero PCM before it\npasses:\n\n```powershell\ndotnet Sunshine.Ds5Sidecar.dll --self-test microphone-capture\n```\n\nThis temporary path also enumerates the composite profile's HID and render\ninterfaces; it is not the final capture-only virtual microphone product.\nThe composite session monitors every Windows default render and capture role.\nIf Windows selects a HIDMaestro-backed virtual DualSense endpoint as a default,\nthe helper reports the policy violation and exits; Sunshine then performs its\nsingle recovery attach in HID-only DS5 mode. This read-only fail-closed guard\navoids undocumented audio-policy writes and never changes a user's defaults.\n\nThe trigger effects and lightbar of an output report a game writes to the\nvirtual pad are read the way the hardware reads them: only when the report's\nvalidity byte for that field is set. A game leaves the fields it is not\nprogramming zero, and reading those zeros as an instruction would cancel an\neffect the game just armed and strobe a held color. A validity byte the decoder\ndoes not expose governs nothing, so the fields it would gate are read\nunconditionally. The motor bytes are read unconditionally as well, because their\nzeros can cancel a rumble that is still playing while gating them can drop a\nstop that has no other way to arrive.\n","src_assets/common/assets/web/utils/agents/gameLibrary/README.md":"# 游戏资源搜刮 Agent / Skills 体系\n\n这套代码负责在 WebUI 的应用扫描流程中，对扫描到的游戏资源做 AI 辅助整理：复用用户已确认的修正、清洗游戏标题、生成封面搜索词、匹配封面，并把低置信度结果留给用户复核。\n\n## 分层设计\n\n- 通用底座：`utils/agents/core/agentCore.js` 提供 agent runner、skill 校验、capability registry、默认启用能力计算和用户选择归一化。\n- 领域适配：`utils/agents/gameLibrary/gameLibraryCuratorAgent.js` 定义游戏资源搜刮的 skill id、capability 文案、默认 skills 和 `apps` context。\n- 领域能力：`skills/` 下的每个文件只实现一个游戏资源处理能力。\n- 领域策略：`policies/` 放不依赖 UI 的判断规则，例如复核队列阈值。\n\n游戏资源之外的新能力不应该塞进 `gameLibrary`。如果要接入日志诊断、配置建议、库维护等能力，应新增自己的 domain agent，并复用 `agents/core`。\n\n## 入口与边界\n\n- UI 入口：`views/Apps.vue` 展示扫描结果和用户可选的 AI skill 开关。\n- 组合逻辑：`composables/useApps.js` 调用扫描能力，并运行 `runGameLibraryCuratorAgent()`。\n- Agent 入口：`utils/agents/gameLibrary/gameLibraryCuratorAgent.js` 注册游戏能力，底层执行由 `utils/agents/core/agentCore.js` 完成。\n- AI 请求：WebUI 只调用 `/api/ai/chat/completions`，不保存独立 LLM 配置。\n- 配置入口：LLM provider、base URL、model、API key 等配置统一由 control panel 的米塔页面维护，并同步到 Sunshine 后端 `/api/ai/config`。\n\n不要在 WebUI 扫描页新增第二套 AI 配置入口。扫描页只负责选择启用哪些 game-library skills。\n\n## 当前数据流\n\n1. `scanDirectory()` 或 `scanGameLibraries()` 得到扫描结果。\n2. `applyGameLibraryOverrides()` 先套用用户此前确认过的名称和封面。\n3. `withScanKeys()` 给本轮扫描结果加 `__scan-key`，用于异步更新时稳定定位条目。\n4. `runGameLibraryCuratorAgent()` 根据用户启用的 skill id 顺序运行 skills。\n5. `onTitlesEnhanced()` 和 `onCoverResolved()` 增量更新扫描结果弹窗。\n6. 用户快速添加、批量添加或编辑保存时，`rememberGameLibraryApp()` 记录本次确认结果，供下次扫描复用。\n\n## 内置 Skills\n\n| Skill id | 阶段 | 默认 | 用户可选 | 职责 |\n| --- | --- | --- | --- | --- |\n| `game.scan.memory` | `memory` | 开启 | 否 | 应用已确认的扫描覆盖记录，并在用户确认后学习新记录。 |\n| `game.title.normalize` | `metadata` | 开启 | 是 | 通过 LLM 清洗标题、输出规范名、搜索词和名称置信度。 |\n| `game.cover.select` | `asset` | 开启 | 是 | 搜索候选封面，再用 LLM 或本地 fallback 选择最佳封面。 |\n\n必选能力用 `required: true` 标记。`normalizeGameLibrarySkillIds()` 会保证 required skill 始终启用，并过滤未知 id。\n\n### 封面证据校准\n\n`game.cover.select` 不直接信任模型给出的封面置信度。`coverSelectionAi.js` 会先计算候选封面与规范名、显示名、原始名、搜索词之间的本地匹配证据：\n\n- `exact-title`：候选标题与已知名称完全一致。\n- `prefix-title` / `contains-title`：候选标题与已知名称存在强前缀或包含关系。\n- `token-overlap`：标题 token 高度重合。\n- `source-prior`：只有来源先验，没有足够标题证据。\n\nAI 选择封面后，`calibrateCoverConfidence()` 会用这份证据校准 `ai-cover-confidence`，并写入 `cover-match-confidence`、`cover-match-relation`、`cover-match-reason`。这样模型理解语义，本地证据负责防止同名游戏、DLC、工具、原声带等弱匹配以高置信度通过。审核队列会优先使用 `ai-cover-confidence`，缺失时回退到 `cover-match-confidence`。\n\n## Skill 契约\n\n每个 skill 至少需要：\n\n```js\n{\n  id: 'game.example.skill',\n  type: 'metadata',\n  label: 'Example skill',\n  async run(context) {\n    return {\n      ...context,\n      apps: nextApps,\n      stats: { ...(context.stats || {}), exampleCount },\n    }\n  },\n}\n```\n\n`context` 字段约定：\n\n- `apps`：当前扫描结果数组。skill 应返回新数组或等价的更新结果。\n- `events`：调试事件数组。skill 可以 push `{ skillId, type, ... }`。\n- `stats`：聚合统计，例如 `titleChanges`、`coversFound`、`skillFailures`。\n- `options`：运行时回调，例如 `onTitlesEnhanced`、`onCoverResolved`、`onSkillError`。\n\nAgent 默认会捕获单个 skill 的异常，记录 `skill:error` 事件并继续后续 skill。只有传入 `stopOnSkillError: true` 时才会中断整轮。\n\n## 新增其他 Domain Agent\n\n新增游戏资源之外的能力时，优先建立独立目录，例如：\n\n```text\nutils/agents/diagnostics/\nutils/agents/configAdvisor/\n```\n\n推荐结构：\n\n```js\nimport {\n  createAgent,\n  createAgentSkill,\n  createSkillRegistry,\n} from '../core/agentCore.js'\n\nexport const DIAGNOSTICS_AGENT_ID = 'diagnostics'\n\nexport function createDiagnosticsSkill(definition) {\n  return createAgentSkill(definition, {\n    skillSubject: 'Diagnostics skills',\n    runSubject: 'Diagnostics skill',\n  })\n}\n\nconst registry = createSkillRegistry({\n  baseCapabilities: DIAGNOSTICS_CAPABILITIES,\n  createSkill: createDiagnosticsSkill,\n  duplicateMessage: 'Diagnostics skill already registered',\n})\n\nexport function createDiagnosticsAgent(options = {}) {\n  return createAgent({\n    id: DIAGNOSTICS_AGENT_ID,\n    skills: options.skills || createDefaultDiagnosticsSkills(),\n    createContext(input, runOptions) {\n      return { input, events: [], stats: {}, options: runOptions }\n    },\n  })\n}\n```\n\n每个 domain agent 应自己定义 context 形状、capability 文案、默认启用规则和 UI 接入点。通用 core 不应该知道具体业务字段。\n\n## 新增内置 Skill\n\n1. 在 `skills/` 下新增 `xxxSkill.js`，导出稳定的 `XXX_SKILL_ID` 和 `createXxxSkill()`。\n2. 在 `gameLibraryCuratorAgent.js` 中导入并加入：\n   - `GAME_LIBRARY_SKILL_IDS`\n   - `GAME_LIBRARY_AGENT_CAPABILITIES`\n   - `createDefaultGameLibrarySkills()`\n3. 给 capability 填好：\n   - `stage`\n   - Font Awesome `icon`\n   - 英文 `label`\n   - 中文 `labels.zh`\n   - `defaultEnabled`\n   - `userSelectable`\n   - 必要时 `required`\n4. 在 `tests/gameLibraryCuratorAgent.test.js` 增加顺序、选择、失败隔离、label/icon 和默认启用测试。\n5. 如涉及 UI 文案或状态，补充 `Apps.vue` / `ScanResultModal.vue` 的行为验证。\n\n## 注册扩展 Skill\n\n实验性或外部能力可以通过 `registerGameLibrarySkillExtension()` 注册，不需要改默认内置列表：\n\n```js\nimport {\n  registerGameLibrarySkillExtension,\n} from './gameLibraryCuratorAgent.js'\n\nconst unregister = registerGameLibrarySkillExtension({\n  skill: {\n    id: 'game.example.annotate',\n    type: 'metadata',\n    label: 'Example annotation',\n    async run(context) {\n      return {\n        ...context,\n        apps: context.apps.map((app) => ({ ...app, example: true })),\n      }\n    },\n  },\n  capability: {\n    icon: 'fa-vial',\n    label: 'Example annotation',\n    labels: { zh: '示例标注' },\n    defaultEnabled: false,\n    userSelectable: true,\n  },\n})\n```\n\n扩展 id 必须全局唯一。测试结束或插件卸载时调用 `unregister()`，避免污染后续运行。\n\n## AI Prompt 与语言\n\nPrompt 语言由 `aiLocale.js` 根据当前页面语言解析：\n\n- `getCurrentLocale()` 优先读 `document.documentElement.lang`，其次读浏览器语言。\n- `buildLocalizedInstruction()` 要求模型用当前语言输出面向用户的短理由。\n- 标题清洗和封面选择 prompt 都必须返回 JSON，不允许 markdown。\n\n维护 prompt 时要优先约束输出 schema、置信度和不确定时的保守行为。不要把 provider、model、key 等配置写进 prompt 或扫描页。\n\n## 缓存与复核\n\n- `aiCache.js` 使用 localStorage，访问失败时自动降级到内存缓存。\n- 元数据缓存 namespace：`game-metadata:v1`。\n- 封面选择缓存 namespace：`cover-selection:v1`。\n- 调整 prompt、schema 或影响结果含义时，要提升 cache version。\n- `reviewQueuePolicy.js` 负责判断是否进入复核队列。默认阈值：\n  - 名称置信度低于 `0.82`\n  - 封面置信度低于 `0.7`\n  - 游戏缺少规范名或封面\n\n## 容错原则\n\n- 扫描结果应先展示，再异步增强，AI 不可用时不能阻塞用户添加应用。\n- 单个 batch 或单个封面候选失败时，应跳过该部分并保留原始结果。\n- 用户确认过的 `user-override` 和已有封面优先级最高。\n- 异步更新必须用 `__scan-key` 或 `getGameResourceKey()` 稳定定位，避免重复名称、重复命令导致错位。\n\n## 维护检查清单\n\n改动这套体系后至少运行：\n\n```powershell\nnpm run test:webui\nnpm run build\ngit diff --check\n```\n\n重点测试文件：\n\n- `tests/agentCore.test.js`\n- `tests/gameLibraryCuratorAgent.test.js`\n- `tests/gameMetadataAi.test.js`\n- `tests/aiCache.test.js`\n- `tests/scanOverrides.test.js`\n\n如果改了 control panel 的 AI 配置或 provider preset，还要在 `src_assets/common/sunshine-control-panel` 内运行对应构建和测试命令。\n","src_assets/common/assets/web/utils/agents/diagnostics/README.md":"# Diagnostics Agent\n\n`diagnostics` 是面向 Troubleshooting 的日志诊断 domain agent。它复用 `agents/core`，但只处理日志和诊断发现，不保存 AI 配置，也不直接调用 LLM。\n\n## 当前能力\n\n| Skill id | 阶段 | 默认 | 职责 |\n| --- | --- | --- | --- |\n| `diagnostics.logs.severity` | `log-analysis` | 开启 | 统计 fatal、error、warning 日志行，并保留少量证据。 |\n| `diagnostics.logs.patterns` | `log-analysis` | 开启 | 检测编码器、显示捕获、网络连接、端口绑定和配置风险模式。 |\n| `diagnostics.logs.remediation` | `log-analysis` | 开启 | 根据本地 findings 生成可执行修复建议。 |\n\n## Context\n\n```js\n{\n  logs: '',\n  findings: [],\n  suggestions: [],\n  severitySummary: null,\n  events: [],\n  stats: {},\n  options: runOptions,\n}\n```\n\n`findings` 和 `suggestions` 会作为本地预诊断显示在 AI 日志诊断弹窗中，并作为上下文传给 `/api/ai/chat/completions`。这样即使 AI 配置不可用，用户仍能看到本地规则发现和基础修复建议。\n\n## 边界\n\n- LLM provider、model 和 key 仍由 control panel 的米塔页面统一配置。\n- WebUI 诊断弹窗只消费共享 Sunshine AI proxy。\n- 本地规则只提供可解释线索，不自动修复配置或重启服务。\n","src_assets/common/assets/web/utils/agents/core/README.md":"# Agent Core\n\n`agents/core` 是 WebUI 里可复用的 agent/skill 底座。它不包含任何游戏、日志、配置或 UI 业务字段，只负责通用执行模型。\n\n## 提供什么\n\n- `createAgent()`：顺序运行 skills，支持 `enabledSkills` 选择、`stopOnSkillError` 中断和默认失败隔离。\n- `createAgentSkill()`：校验 skill id 和 `run(context)`。\n- `createSkillRegistry()`：注册扩展 skill，并生成对应 capability。\n- capability helpers：获取默认启用项、可选择项、图标、文案和归一化后的启用列表。\n\n## Context 约定\n\nCore 只要求 context 可以被 skill 接收并返回。推荐每个 domain 保持这些通用字段：\n\n```js\n{\n  events: [],\n  stats: {},\n  options: runOptions,\n}\n```\n\n业务字段由 domain 自己定义，例如：\n\n- `gameLibrary` 使用 `apps`。\n- `diagnostics` 使用 `logs`、`findings` 和 `severitySummary`。\n\n## 新建 Domain Agent\n\n每个 domain agent 应该自己提供：\n\n- 稳定的 agent id。\n- 稳定的 skill id 常量。\n- capability 列表和本地化文案。\n- `createXxxSkill()` 包装，用于给错误信息加 domain 名称。\n- `createDefaultXxxSkills()`。\n- `createXxxAgent()`，通过 `createContext()` 定义自己的 context 形状。\n- `registerXxxSkillExtension()`，如果允许外部扩展。\n\nCore 不应该直接 import 任何 domain 文件。\n"},"files":{"tools/sunshine-ds5-sidecar/README.md":"# Sunshine virtual device host\n\nThis optional Windows helper isolates Sunshine from the third-party\nHIDMaestro runtime. The shipping executable remains\n`Sunshine.Ds5Sidecar.exe` for component-upgrade compatibility, while the\nprocess now uses a `VirtualDeviceHostServer` and `DeviceRegistry` internally.\nIt owns virtual devices and exposes the versioned `SDS5` named-pipe protocol.\nThe optional component package includes patched HIDMaestro Core and its notices.\n\nBuild the compatible Foundation Core patch and the sidecar:\n\n```powershell\nscripts/build-ds5-sidecar.ps1\n```\n\nThe version 1.4.2 component ZIP includes patched `HIDMaestro.Core.dll`, its\nlicenses, and the complete .NET runtime. The main Sunshine package contains only\nthe pinned ZIP download manifest. The Control Panel automatically updates older\ninstalled components when streaming is idle, using the existing administrator\nhelper, verified staging and whole-component rollback. Optional USB/IP transport\ninstallation remains part of explicit install/repair, not automatic updates.\nThe sidecar uses normal .NET loading and requires the v1.12.0 runtime and\ndevice-ownership, externally managed USB/IP and `FoundationDualSenseReports=host-clock-v1`\nmarkers even for `--probe`. Sunshine validates the\ninstalled component without replacing DLLs.\nThe upstream SDK API, assembly version and embedded resources are preserved; see\n[`../hidmaestro-core-patch/README.md`](../hidmaestro-core-patch/README.md).\n\nRead-only capability probe:\n\n```powershell\ndotnet Sunshine.Ds5Sidecar.dll --probe\n```\n\nDeterministic protocol ABI, microphone queue/runtime-contract, four-channel\nlayout, and channel-isolation checks (no elevation or virtual device required):\n\n```powershell\ndotnet Sunshine.Ds5Sidecar.dll --self-check\n```\n\nUSB/IP installation and repair belong to the shared component manager. Device\ncreation only checks the approved driver and fails with a shared transport error\nwhen it is missing or incompatible. The ZIP carries `component-compatibility.json`,\nthe same contract used by the GUI installer. The staged upgrade acceptance test\nrequires elevation and an idle host, and creates/releases two composite devices:\n\n```powershell\nSunshine.Ds5Sidecar.exe --self-test transport --result transport-acceptance.json\n```\n\nThe production process must be launched elevated and placed in the Sunshine\nJob Object. The pipe accepts a single elevated client of the creating user;\nnon-elevated callers are rejected at connect time and dropped without\nending the sidecar.\nInput, touch, motion and battery updates submit their combined state directly\non receipt under the session state lock. They do not use a thread-pool timer:\neven a 1 ms timer can run at roughly 15.6 ms intervals on Windows and reduce\ncontinuous input to about 64 Hz. The sidecar preserves the incoming update\ncadence; it does not synthesize repeated reports to inflate polling-rate tests.\nUSB reports include rolling 8-bit and 32-bit sequence numbers, device time and\nthe USB-connected status. Device and motion timestamps use the same host QPC\nepoch at 3 MHz. Button, touch and battery reports advance device time while\nretaining the latest motion sample timestamp. Network receive time still stands\nin for the client's physical sampling time; these metadata corrections have not\nbeen verified to resolve Genshin's camera jumps.\nThe pipe reader reuses a receive buffer for headers and payloads, dispatching\ncomplete frames as soon as they arrive. This removes separate header/body reads\nand per-input payload allocations without adding a batching delay. Microphone\nPCM retains its own copy because its queue outlives the receive buffer. The\ndevice-free `--self-check` covers fragmented/packed frames, length validation,\nEOF, cancellation and retained PCM ownership.\nThe reader uses one async state machine per pending frame. Standard axis\nbindings are resolved once through the SDK at controller creation and update\nthe existing state dictionary in place. This avoids rebuilding the SDK's\nstick/trigger lists and axis dictionary on every input update. `--self-check`\ncompares the cached mapping against the SDK's complete bundled profile catalog\nand checks that steady-state axis updates allocate no managed objects.\nThe pinned .NET 10 runtime config sets\n`System.Threading.ThreadPool.UnfairSemaphoreSpinLimit` to zero for this process.\nPool workers wait for a signal instead of spinning between short input bursts.\nThis adds no report-rate limiter or timer. Because it trades spinning for OS\nwakeups, CPU and report-interval tests should be repeated when changing the\nbundled runtime or enabling additional concurrent input/audio workloads.\nDisconnecting the owning pipe disposes every device created by\nthat connection. Standard `dualsense` uses UMDF2; `dualsense-composite`\nenables the USB composite HID/audio profile and authored haptics PCM.\nHID-only attaches use `dualsense` with the native 273-byte descriptor and\nGame Pad usage (0x05). HIDMaestro v1.12.0 no longer writes the incorrect global\nGameInput mapping for JSON profiles and removes exact legacy SDK tables\n(issue #1056), so the former Joystick usage override is unnecessary. Input\nreports retain `alwaysArmed` for touch and motion data. Windows decides how\nto expose the device; native Game Pad usage does not guarantee a WGI Gamepad.\nThe optional Genshin compatibility attach flag derives a third profile from\n`dualsense-composite` at runtime. It preserves the Sony VID/PID, descriptors,\nand four-channel layout while changing only the USB product string from\n`DualSense Wireless Controller` to the launch-model `Wireless Controller`.\nThe runtime profile starts the USB speaker control unmuted at its declared\nmaximum and commits the active endpoint's 4-channel, available-speaker and\nfull-range masks as quadraphonic (`0x33`) through Core Audio. This matches the\nthree settings applied by completing Windows' speaker setup wizard as required\nby Genshin.\nEndpoint enumeration uses the Windows SDK's `IMMDeviceCollection` IID, and\nthe Windows 10+ policy property calls include the effects-store argument.\n`NativeMethods.txt` requests the public audio COM interfaces, structures and\nproperty keys from Microsoft CsWin32. The generator and SDK metadata are pinned\nin `packages.lock.json`; publishing restores in locked mode and adds no runtime\nlibrary dependency. `CoreAudio` shares endpoint enumeration and reference cleanup\nbetween the default-device monitor and WASAPI probe. Only the undocumented\nWindows 10+ policy interface remains hand-declared, inside `AudioPolicyConfig`.\nCore Audio references are released per acquisition so configuration readback\ncannot invalidate the concurrent default-device monitor. Endpoint detection\nalso recognizes HIDMaestro's synthetic USB serial under the official USB/IP\nhost controller, without requiring runtime ownership stamping.\nThe transport acceptance test creates both the normal composite profile and\nthe Genshin compatibility profile. It reads back the endpoint format and both\nspeaker masks for the endpoint created by that attach before passing. It waits\nfor that endpoint to become inactive before the next round, so a stale endpoint\ncannot satisfy another mode's check.\nThe sidecar advertises this support through a protocol capability bit so an\nolder runtime cannot silently accept an ineffective setting.\nThe v1 wire contract reserves capability bits and message numbers for the\nvirtual microphone. Normal startup intentionally does not advertise them;\nonly the explicit development prototype can enable those capabilities.\n\nThe Phase 2 composite-profile microphone path is development-only and requires\nboth the exact HIDMaestro 1.12.0.0 runtime and an explicit opt-in. Its elevated\nattach/PCM/flush/destroy smoke test is:\n\n```powershell\ndotnet Sunshine.Ds5Sidecar.dll --self-test microphone-prototype\n```\n\nFor an end-to-end developer check, `microphone-capture` snapshots the active\ncapture endpoints, creates the prototype, opens only its newly added endpoint\nthrough WASAPI, streams a 440 Hz signal for five seconds, and requires observed\nhost streaming, zero submit errors, captured frames, and non-zero PCM before it\npasses:\n\n```powershell\ndotnet Sunshine.Ds5Sidecar.dll --self-test microphone-capture\n```\n\nThis temporary path also enumerates the composite profile's HID and render\ninterfaces; it is not the final capture-only virtual microphone product.\nThe composite session monitors every Windows default render and capture role.\nIf Windows selects a HIDMaestro-backed virtual DualSense endpoint as a default,\nthe helper reports the policy violation and exits; Sunshine then performs its\nsingle recovery attach in HID-only DS5 mode. This read-only fail-closed guard\navoids undocumented audio-policy writes and never changes a user's defaults.\n\nThe trigger effects and lightbar of an output report a game writes to the\nvirtual pad are read the way the hardware reads them: only when the report's\nvalidity byte for that field is set. A game leaves the fields it is not\nprogramming zero, and reading those zeros as an instruction would cancel an\neffect the game just armed and strobe a held color. A validity byte the decoder\ndoes not expose governs nothing, so the fields it would gate are read\nunconditionally. The motor bytes are read unconditionally as well, because their\nzeros can cancel a rumble that is still playing while gating them can drop a\nstop that has no other way to arrive.\n","src_assets/common/assets/web/utils/agents/gameLibrary/README.md":"# 游戏资源搜刮 Agent / Skills 体系\n\n这套代码负责在 WebUI 的应用扫描流程中，对扫描到的游戏资源做 AI 辅助整理：复用用户已确认的修正、清洗游戏标题、生成封面搜索词、匹配封面，并把低置信度结果留给用户复核。\n\n## 分层设计\n\n- 通用底座：`utils/agents/core/agentCore.js` 提供 agent runner、skill 校验、capability registry、默认启用能力计算和用户选择归一化。\n- 领域适配：`utils/agents/gameLibrary/gameLibraryCuratorAgent.js` 定义游戏资源搜刮的 skill id、capability 文案、默认 skills 和 `apps` context。\n- 领域能力：`skills/` 下的每个文件只实现一个游戏资源处理能力。\n- 领域策略：`policies/` 放不依赖 UI 的判断规则，例如复核队列阈值。\n\n游戏资源之外的新能力不应该塞进 `gameLibrary`。如果要接入日志诊断、配置建议、库维护等能力，应新增自己的 domain agent，并复用 `agents/core`。\n\n## 入口与边界\n\n- UI 入口：`views/Apps.vue` 展示扫描结果和用户可选的 AI skill 开关。\n- 组合逻辑：`composables/useApps.js` 调用扫描能力，并运行 `runGameLibraryCuratorAgent()`。\n- Agent 入口：`utils/agents/gameLibrary/gameLibraryCuratorAgent.js` 注册游戏能力，底层执行由 `utils/agents/core/agentCore.js` 完成。\n- AI 请求：WebUI 只调用 `/api/ai/chat/completions`，不保存独立 LLM 配置。\n- 配置入口：LLM provider、base URL、model、API key 等配置统一由 control panel 的米塔页面维护，并同步到 Sunshine 后端 `/api/ai/config`。\n\n不要在 WebUI 扫描页新增第二套 AI 配置入口。扫描页只负责选择启用哪些 game-library skills。\n\n## 当前数据流\n\n1. `scanDirectory()` 或 `scanGameLibraries()` 得到扫描结果。\n2. `applyGameLibraryOverrides()` 先套用用户此前确认过的名称和封面。\n3. `withScanKeys()` 给本轮扫描结果加 `__scan-key`，用于异步更新时稳定定位条目。\n4. `runGameLibraryCuratorAgent()` 根据用户启用的 skill id 顺序运行 skills。\n5. `onTitlesEnhanced()` 和 `onCoverResolved()` 增量更新扫描结果弹窗。\n6. 用户快速添加、批量添加或编辑保存时，`rememberGameLibraryApp()` 记录本次确认结果，供下次扫描复用。\n\n## 内置 Skills\n\n| Skill id | 阶段 | 默认 | 用户可选 | 职责 |\n| --- | --- | --- | --- | --- |\n| `game.scan.memory` | `memory` | 开启 | 否 | 应用已确认的扫描覆盖记录，并在用户确认后学习新记录。 |\n| `game.title.normalize` | `metadata` | 开启 | 是 | 通过 LLM 清洗标题、输出规范名、搜索词和名称置信度。 |\n| `game.cover.select` | `asset` | 开启 | 是 | 搜索候选封面，再用 LLM 或本地 fallback 选择最佳封面。 |\n\n必选能力用 `required: true` 标记。`normalizeGameLibrarySkillIds()` 会保证 required skill 始终启用，并过滤未知 id。\n\n### 封面证据校准\n\n`game.cover.select` 不直接信任模型给出的封面置信度。`coverSelectionAi.js` 会先计算候选封面与规范名、显示名、原始名、搜索词之间的本地匹配证据：\n\n- `exact-title`：候选标题与已知名称完全一致。\n- `prefix-title` / `contains-title`：候选标题与已知名称存在强前缀或包含关系。\n- `token-overlap`：标题 token 高度重合。\n- `source-prior`：只有来源先验，没有足够标题证据。\n\nAI 选择封面后，`calibrateCoverConfidence()` 会用这份证据校准 `ai-cover-confidence`，并写入 `cover-match-confidence`、`cover-match-relation`、`cover-match-reason`。这样模型理解语义，本地证据负责防止同名游戏、DLC、工具、原声带等弱匹配以高置信度通过。审核队列会优先使用 `ai-cover-confidence`，缺失时回退到 `cover-match-confidence`。\n\n## Skill 契约\n\n每个 skill 至少需要：\n\n```js\n{\n  id: 'game.example.skill',\n  type: 'metadata',\n  label: 'Example skill',\n  async run(context) {\n    return {\n      ...context,\n      apps: nextApps,\n      stats: { ...(context.stats || {}), exampleCount },\n    }\n  },\n}\n```\n\n`context` 字段约定：\n\n- `apps`：当前扫描结果数组。skill 应返回新数组或等价的更新结果。\n- `events`：调试事件数组。skill 可以 push `{ skillId, type, ... }`。\n- `stats`：聚合统计，例如 `titleChanges`、`coversFound`、`skillFailures`。\n- `options`：运行时回调，例如 `onTitlesEnhanced`、`onCoverResolved`、`onSkillError`。\n\nAgent 默认会捕获单个 skill 的异常，记录 `skill:error` 事件并继续后续 skill。只有传入 `stopOnSkillError: true` 时才会中断整轮。\n\n## 新增其他 Domain Agent\n\n新增游戏资源之外的能力时，优先建立独立目录，例如：\n\n```text\nutils/agents/diagnostics/\nutils/agents/configAdvisor/\n```\n\n推荐结构：\n\n```js\nimport {\n  createAgent,\n  createAgentSkill,\n  createSkillRegistry,\n} from '../core/agentCore.js'\n\nexport const DIAGNOSTICS_AGENT_ID = 'diagnostics'\n\nexport function createDiagnosticsSkill(definition) {\n  return createAgentSkill(definition, {\n    skillSubject: 'Diagnostics skills',\n    runSubject: 'Diagnostics skill',\n  })\n}\n\nconst registry = createSkillRegistry({\n  baseCapabilities: DIAGNOSTICS_CAPABILITIES,\n  createSkill: createDiagnosticsSkill,\n  duplicateMessage: 'Diagnostics skill already registered',\n})\n\nexport function createDiagnosticsAgent(options = {}) {\n  return createAgent({\n    id: DIAGNOSTICS_AGENT_ID,\n    skills: options.skills || createDefaultDiagnosticsSkills(),\n    createContext(input, runOptions) {\n      return { input, events: [], stats: {}, options: runOptions }\n    },\n  })\n}\n```\n\n每个 domain agent 应自己定义 context 形状、capability 文案、默认启用规则和 UI 接入点。通用 core 不应该知道具体业务字段。\n\n## 新增内置 Skill\n\n1. 在 `skills/` 下新增 `xxxSkill.js`，导出稳定的 `XXX_SKILL_ID` 和 `createXxxSkill()`。\n2. 在 `gameLibraryCuratorAgent.js` 中导入并加入：\n   - `GAME_LIBRARY_SKILL_IDS`\n   - `GAME_LIBRARY_AGENT_CAPABILITIES`\n   - `createDefaultGameLibrarySkills()`\n3. 给 capability 填好：\n   - `stage`\n   - Font Awesome `icon`\n   - 英文 `label`\n   - 中文 `labels.zh`\n   - `defaultEnabled`\n   - `userSelectable`\n   - 必要时 `required`\n4. 在 `tests/gameLibraryCuratorAgent.test.js` 增加顺序、选择、失败隔离、label/icon 和默认启用测试。\n5. 如涉及 UI 文案或状态，补充 `Apps.vue` / `ScanResultModal.vue` 的行为验证。\n\n## 注册扩展 Skill\n\n实验性或外部能力可以通过 `registerGameLibrarySkillExtension()` 注册，不需要改默认内置列表：\n\n```js\nimport {\n  registerGameLibrarySkillExtension,\n} from './gameLibraryCuratorAgent.js'\n\nconst unregister = registerGameLibrarySkillExtension({\n  skill: {\n    id: 'game.example.annotate',\n    type: 'metadata',\n    label: 'Example annotation',\n    async run(context) {\n      return {\n        ...context,\n        apps: context.apps.map((app) => ({ ...app, example: true })),\n      }\n    },\n  },\n  capability: {\n    icon: 'fa-vial',\n    label: 'Example annotation',\n    labels: { zh: '示例标注' },\n    defaultEnabled: false,\n    userSelectable: true,\n  },\n})\n```\n\n扩展 id 必须全局唯一。测试结束或插件卸载时调用 `unregister()`，避免污染后续运行。\n\n## AI Prompt 与语言\n\nPrompt 语言由 `aiLocale.js` 根据当前页面语言解析：\n\n- `getCurrentLocale()` 优先读 `document.documentElement.lang`，其次读浏览器语言。\n- `buildLocalizedInstruction()` 要求模型用当前语言输出面向用户的短理由。\n- 标题清洗和封面选择 prompt 都必须返回 JSON，不允许 markdown。\n\n维护 prompt 时要优先约束输出 schema、置信度和不确定时的保守行为。不要把 provider、model、key 等配置写进 prompt 或扫描页。\n\n## 缓存与复核\n\n- `aiCache.js` 使用 localStorage，访问失败时自动降级到内存缓存。\n- 元数据缓存 namespace：`game-metadata:v1`。\n- 封面选择缓存 namespace：`cover-selection:v1`。\n- 调整 prompt、schema 或影响结果含义时，要提升 cache version。\n- `reviewQueuePolicy.js` 负责判断是否进入复核队列。默认阈值：\n  - 名称置信度低于 `0.82`\n  - 封面置信度低于 `0.7`\n  - 游戏缺少规范名或封面\n\n## 容错原则\n\n- 扫描结果应先展示，再异步增强，AI 不可用时不能阻塞用户添加应用。\n- 单个 batch 或单个封面候选失败时，应跳过该部分并保留原始结果。\n- 用户确认过的 `user-override` 和已有封面优先级最高。\n- 异步更新必须用 `__scan-key` 或 `getGameResourceKey()` 稳定定位，避免重复名称、重复命令导致错位。\n\n## 维护检查清单\n\n改动这套体系后至少运行：\n\n```powershell\nnpm run test:webui\nnpm run build\ngit diff --check\n```\n\n重点测试文件：\n\n- `tests/agentCore.test.js`\n- `tests/gameLibraryCuratorAgent.test.js`\n- `tests/gameMetadataAi.test.js`\n- `tests/aiCache.test.js`\n- `tests/scanOverrides.test.js`\n\n如果改了 control panel 的 AI 配置或 provider preset，还要在 `src_assets/common/sunshine-control-panel` 内运行对应构建和测试命令。\n","src_assets/common/assets/web/utils/agents/diagnostics/README.md":"# Diagnostics Agent\n\n`diagnostics` 是面向 Troubleshooting 的日志诊断 domain agent。它复用 `agents/core`，但只处理日志和诊断发现，不保存 AI 配置，也不直接调用 LLM。\n\n## 当前能力\n\n| Skill id | 阶段 | 默认 | 职责 |\n| --- | --- | --- | --- |\n| `diagnostics.logs.severity` | `log-analysis` | 开启 | 统计 fatal、error、warning 日志行，并保留少量证据。 |\n| `diagnostics.logs.patterns` | `log-analysis` | 开启 | 检测编码器、显示捕获、网络连接、端口绑定和配置风险模式。 |\n| `diagnostics.logs.remediation` | `log-analysis` | 开启 | 根据本地 findings 生成可执行修复建议。 |\n\n## Context\n\n```js\n{\n  logs: '',\n  findings: [],\n  suggestions: [],\n  severitySummary: null,\n  events: [],\n  stats: {},\n  options: runOptions,\n}\n```\n\n`findings` 和 `suggestions` 会作为本地预诊断显示在 AI 日志诊断弹窗中，并作为上下文传给 `/api/ai/chat/completions`。这样即使 AI 配置不可用，用户仍能看到本地规则发现和基础修复建议。\n\n## 边界\n\n- LLM provider、model 和 key 仍由 control panel 的米塔页面统一配置。\n- WebUI 诊断弹窗只消费共享 Sunshine AI proxy。\n- 本地规则只提供可解释线索，不自动修复配置或重启服务。\n","src_assets/common/assets/web/utils/agents/core/README.md":"# Agent Core\n\n`agents/core` 是 WebUI 里可复用的 agent/skill 底座。它不包含任何游戏、日志、配置或 UI 业务字段，只负责通用执行模型。\n\n## 提供什么\n\n- `createAgent()`：顺序运行 skills，支持 `enabledSkills` 选择、`stopOnSkillError` 中断和默认失败隔离。\n- `createAgentSkill()`：校验 skill id 和 `run(context)`。\n- `createSkillRegistry()`：注册扩展 skill，并生成对应 capability。\n- capability helpers：获取默认启用项、可选择项、图标、文案和归一化后的启用列表。\n\n## Context 约定\n\nCore 只要求 context 可以被 skill 接收并返回。推荐每个 domain 保持这些通用字段：\n\n```js\n{\n  events: [],\n  stats: {},\n  options: runOptions,\n}\n```\n\n业务字段由 domain 自己定义，例如：\n\n- `gameLibrary` 使用 `apps`。\n- `diagnostics` 使用 `logs`、`findings` 和 `severitySummary`。\n\n## 新建 Domain Agent\n\n每个 domain agent 应该自己提供：\n\n- 稳定的 agent id。\n- 稳定的 skill id 常量。\n- capability 列表和本地化文案。\n- `createXxxSkill()` 包装，用于给错误信息加 domain 名称。\n- `createDefaultXxxSkills()`。\n- `createXxxAgent()`，通过 `createContext()` 定义自己的 context 形状。\n- `registerXxxSkillExtension()`，如果允许外部扩展。\n\nCore 不应该直接 import 任何 domain 文件。\n"},"items":[{"name":"README.md","path":"tools/sunshine-ds5-sidecar/README.md","rawUrl":"https://raw.githubusercontent.com/AlkaidLab/foundation-sunshine/HEAD/tools/sunshine-ds5-sidecar/README.md","title":"sunshine-ds5-sidecar Documentation","category":"plugin-manifest","format":"markdown","content":"# Sunshine virtual device host\n\nThis optional Windows helper isolates Sunshine from the third-party\nHIDMaestro runtime. The shipping executable remains\n`Sunshine.Ds5Sidecar.exe` for component-upgrade compatibility, while the\nprocess now uses a `VirtualDeviceHostServer` and `DeviceRegistry` internally.\nIt owns virtual devices and exposes the versioned `SDS5` named-pipe protocol.\nThe optional component package includes patched HIDMaestro Core and its notices.\n\nBuild the compatible Foundation Core patch and the sidecar:\n\n```powershell\nscripts/build-ds5-sidecar.ps1\n```\n\nThe version 1.4.2 component ZIP includes patched `HIDMaestro.Core.dll`, its\nlicenses, and the complete .NET runtime. The main Sunshine package contains only\nthe pinned ZIP download manifest. The Control Panel automatically updates older\ninstalled components when streaming is idle, using the existing administrator\nhelper, verified staging and whole-component rollback. Optional USB/IP transport\ninstallation remains part of explicit install/repair, not automatic updates.\nThe sidecar uses normal .NET loading and requires the v1.12.0 runtime and\ndevice-ownership, externally managed USB/IP and `FoundationDualSenseReports=host-clock-v1`\nmarkers even for `--probe`. Sunshine validates the\ninstalled component without replacing DLLs.\nThe upstream SDK API, assembly version and embedded resources are preserved; see\n[`../hidmaestro-core-patch/README.md`](../hidmaestro-core-patch/README.md).\n\nRead-only capability probe:\n\n```powershell\ndotnet Sunshine.Ds5Sidecar.dll --probe\n```\n\nDeterministic protocol ABI, microphone queue/runtime-contract, four-channel\nlayout, and channel-isolation checks (no elevation or virtual device required):\n\n```powershell\ndotnet Sunshine.Ds5Sidecar.dll --self-check\n```\n\nUSB/IP installation and repair belong to the shared component manager. Device\ncreation only checks the approved driver and fails with a shared transport error\nwhen it is missing or incompatible. The ZIP carries `component-compatibility.json`,\nthe same contract used by the GUI installer. The staged upgrade acceptance test\nrequires elevation and an idle host, and creates/releases two composite devices:\n\n```powershell\nSunshine.Ds5Sidecar.exe --self-test transport --result transport-acceptance.json\n```\n\nThe production process must be launched elevated and placed in the Sunshine\nJob Object. The pipe accepts a single elevated client of the creating user;\nnon-elevated callers are rejected at connect time and dropped without\nending the sidecar.\nInput, touch, motion and battery updates submit their combined state directly\non receipt under the session state lock. They do not use a thread-pool timer:\neven a 1 ms timer can run at roughly 15.6 ms intervals on Windows and reduce\ncontinuous input to about 64 Hz. The sidecar preserves the incoming update\ncadence; it does not synthesize repeated reports to inflate polling-rate tests.\nUSB reports include rolling 8-bit and 32-bit sequence numbers, device time and\nthe USB-connected status. Device and motion timestamps use the same host QPC\nepoch at 3 MHz. Button, touch and battery reports advance device time while\nretaining the latest motion sample timestamp. Network receive time still stands\nin for the client's physical sampling time; these metadata corrections have not\nbeen verified to resolve Genshin's camera jumps.\nThe pipe reader reuses a receive buffer for headers and payloads, dispatching\ncomplete frames as soon as they arrive. This removes separate header/body reads\nand per-input payload allocations without adding a batching delay. Microphone\nPCM retains its own copy because its queue outlives the receive buffer. The\ndevice-free `--self-check` covers fragmented/packed frames, length validation,\nEOF, cancellation and retained PCM ownership.\nThe reader uses one async state machine per pending frame. Standard axis\nbindings are resolved once through the SDK at controller creation and update\nthe existing state dictionary in place. This avoids rebuilding the SDK's\nstick/trigger lists and axis dictionary on every input update. `--self-check`\ncompares the cached mapping against the SDK's complete bundled profile catalog\nand checks that steady-state axis updates allocate no managed objects.\nThe pinned .NET 10 runtime config sets\n`System.Threading.ThreadPool.UnfairSemaphoreSpinLimit` to zero for this process.\nPool workers wait for a signal instead of spinning between short input bursts.\nThis adds no report-rate limiter or timer. Because it trades spinning for OS\nwakeups, CPU and report-interval tests should be repeated when changing the\nbundled runtime or enabling additional concurrent input/audio workloads.\nDisconnecting the owning pipe disposes every device created by\nthat connection. Standard `dualsense` uses UMDF2; `dualsense-composite`\nenables the USB composite HID/audio profile and authored haptics PCM.\nHID-only attaches use `dualsense` with the native 273-byte descriptor and\nGame Pad usage (0x05). HIDMaestro v1.12.0 no longer writes the incorrect global\nGameInput mapping for JSON profiles and removes exact legacy SDK tables\n(issue #1056), so the former Joystick usage override is unnecessary. Input\nreports retain `alwaysArmed` for touch and motion data. Windows decides how\nto expose the device; native Game Pad usage does not guarantee a WGI Gamepad.\nThe optional Genshin compatibility attach flag derives a third profile from\n`dualsense-composite` at runtime. It preserves the Sony VID/PID, descriptors,\nand four-channel layout while changing only the USB product string from\n`DualSense Wireless Controller` to the launch-model `Wireless Controller`.\nThe runtime profile starts the USB speaker control unmuted at its declared\nmaximum and commits the active endpoint's 4-channel, available-speaker and\nfull-range masks as quadraphonic (`0x33`) through Core Audio. This matches the\nthree settings applied by completing Windows' speaker setup wizard as required\nby Genshin.\nEndpoint enumeration uses the Windows SDK's `IMMDeviceCollection` IID, and\nthe Windows 10+ policy property calls include the effects-store argument.\n`NativeMethods.txt` requests the public audio COM interfaces, structures and\nproperty keys from Microsoft CsWin32. The generator and SDK metadata are pinned\nin `packages.lock.json`; publishing restores in locked mode and adds no runtime\nlibrary dependency. `CoreAudio` shares endpoint enumeration and reference cleanup\nbetween the default-device monitor and WASAPI probe. Only the undocumented\nWindows 10+ policy interface remains hand-declared, inside `AudioPolicyConfig`.\nCore Audio references are released per acquisition so configuration readback\ncannot invalidate the concurrent default-device monitor. Endpoint detection\nalso recognizes HIDMaestro's synthetic USB serial under the official USB/IP\nhost controller, without requiring runtime ownership stamping.\nThe transport acceptance test creates both the normal composite profile and\nthe Genshin compatibility profile. It reads back the endpoint format and both\nspeaker masks for the endpoint created by that attach before passing. It waits\nfor that endpoint to become inactive before the next round, so a stale endpoint\ncannot satisfy another mode's check.\nThe sidecar advertises this support through a protocol capability bit so an\nolder runtime cannot silently accept an ineffective setting.\nThe v1 wire contract reserves capability bits and message numbers for the\nvirtual microphone. Normal startup intentionally does not advertise them;\nonly the explicit development prototype can enable those capabilities.\n\nThe Phase 2 composite-profile microphone path is development-only and requires\nboth the exact HIDMaestro 1.12.0.0 runtime and an explicit opt-in. Its elevated\nattach/PCM/flush/destroy smoke test is:\n\n```powershell\ndotnet Sunshine.Ds5Sidecar.dll --self-test microphone-prototype\n```\n\nFor an end-to-end developer check, `microphone-capture` snapshots the active\ncapture endpoints, creates the prototype, opens only its newly added endpoint\nthrough WASAPI, streams a 440 Hz signal for five seconds, and requires observed\nhost streaming, zero submit errors, captured frames, and non-zero PCM before it\npasses:\n\n```powershell\ndotnet Sunshine.Ds5Sidecar.dll --self-test microphone-capture\n```\n\nThis temporary path also enumerates the composite profile's HID and render\ninterfaces; it is not the final capture-only virtual microphone product.\nThe composite session monitors every Windows default render and capture role.\nIf Windows selects a HIDMaestro-backed virtual DualSense endpoint as a default,\nthe helper reports the policy violation and exits; Sunshine then performs its\nsingle recovery attach in HID-only DS5 mode. This read-only fail-closed guard\navoids undocumented audio-policy writes and never changes a user's defaults.\n\nThe trigger effects and lightbar of an output report a game writes to the\nvirtual pad are read the way the hardware reads them: only when the report's\nvalidity byte for that field is set. A game leaves the fields it is not\nprogramming zero, and reading those zeros as an instruction would cancel an\neffect the game just armed and strobe a held color. A validity byte the decoder\ndoes not expose governs nothing, so the fields it would gate are read\nunconditionally. The motor bytes are read unconditionally as well, because their\nzeros can cancel a rumble that is still playing while gating them can drop a\nstop that has no other way to arrive.\n","isInternal":false,"tokens":1947,"sizeBytes":9436},{"name":"README.md","path":"src_assets/common/assets/web/utils/agents/core/README.md","rawUrl":"https://raw.githubusercontent.com/AlkaidLab/foundation-sunshine/HEAD/src_assets/common/assets/web/utils/agents/core/README.md","title":"Subagent: readme","category":"subagent-persona","format":"markdown","content":"# Agent Core\n\n`agents/core` 是 WebUI 里可复用的 agent/skill 底座。它不包含任何游戏、日志、配置或 UI 业务字段，只负责通用执行模型。\n\n## 提供什么\n\n- `createAgent()`：顺序运行 skills，支持 `enabledSkills` 选择、`stopOnSkillError` 中断和默认失败隔离。\n- `createAgentSkill()`：校验 skill id 和 `run(context)`。\n- `createSkillRegistry()`：注册扩展 skill，并生成对应 capability。\n- capability helpers：获取默认启用项、可选择项、图标、文案和归一化后的启用列表。\n\n## Context 约定\n\nCore 只要求 context 可以被 skill 接收并返回。推荐每个 domain 保持这些通用字段：\n\n```js\n{\n  events: [],\n  stats: {},\n  options: runOptions,\n}\n```\n\n业务字段由 domain 自己定义，例如：\n\n- `gameLibrary` 使用 `apps`。\n- `diagnostics` 使用 `logs`、`findings` 和 `severitySummary`。\n\n## 新建 Domain Agent\n\n每个 domain agent 应该自己提供：\n\n- 稳定的 agent id。\n- 稳定的 skill id 常量。\n- capability 列表和本地化文案。\n- `createXxxSkill()` 包装，用于给错误信息加 domain 名称。\n- `createDefaultXxxSkills()`。\n- `createXxxAgent()`，通过 `createContext()` 定义自己的 context 形状。\n- `registerXxxSkillExtension()`，如果允许外部扩展。\n\nCore 不应该直接 import 任何 domain 文件。\n","isInternal":false,"tokens":413,"sizeBytes":1371},{"name":"README.md","path":"src_assets/common/assets/web/utils/agents/diagnostics/README.md","rawUrl":"https://raw.githubusercontent.com/AlkaidLab/foundation-sunshine/HEAD/src_assets/common/assets/web/utils/agents/diagnostics/README.md","title":"Subagent: readme","category":"subagent-persona","format":"markdown","content":"# Diagnostics Agent\n\n`diagnostics` 是面向 Troubleshooting 的日志诊断 domain agent。它复用 `agents/core`，但只处理日志和诊断发现，不保存 AI 配置，也不直接调用 LLM。\n\n## 当前能力\n\n| Skill id | 阶段 | 默认 | 职责 |\n| --- | --- | --- | --- |\n| `diagnostics.logs.severity` | `log-analysis` | 开启 | 统计 fatal、error、warning 日志行，并保留少量证据。 |\n| `diagnostics.logs.patterns` | `log-analysis` | 开启 | 检测编码器、显示捕获、网络连接、端口绑定和配置风险模式。 |\n| `diagnostics.logs.remediation` | `log-analysis` | 开启 | 根据本地 findings 生成可执行修复建议。 |\n\n## Context\n\n```js\n{\n  logs: '',\n  findings: [],\n  suggestions: [],\n  severitySummary: null,\n  events: [],\n  stats: {},\n  options: runOptions,\n}\n```\n\n`findings` 和 `suggestions` 会作为本地预诊断显示在 AI 日志诊断弹窗中，并作为上下文传给 `/api/ai/chat/completions`。这样即使 AI 配置不可用，用户仍能看到本地规则发现和基础修复建议。\n\n## 边界\n\n- LLM provider、model 和 key 仍由 control panel 的米塔页面统一配置。\n- WebUI 诊断弹窗只消费共享 Sunshine AI proxy。\n- 本地规则只提供可解释线索，不自动修复配置或重启服务。\n","isInternal":false,"tokens":413,"sizeBytes":1297},{"name":"README.md","path":"src_assets/common/assets/web/utils/agents/gameLibrary/README.md","rawUrl":"https://raw.githubusercontent.com/AlkaidLab/foundation-sunshine/HEAD/src_assets/common/assets/web/utils/agents/gameLibrary/README.md","title":"Subagent: readme","category":"subagent-persona","format":"markdown","content":"# 游戏资源搜刮 Agent / Skills 体系\n\n这套代码负责在 WebUI 的应用扫描流程中，对扫描到的游戏资源做 AI 辅助整理：复用用户已确认的修正、清洗游戏标题、生成封面搜索词、匹配封面，并把低置信度结果留给用户复核。\n\n## 分层设计\n\n- 通用底座：`utils/agents/core/agentCore.js` 提供 agent runner、skill 校验、capability registry、默认启用能力计算和用户选择归一化。\n- 领域适配：`utils/agents/gameLibrary/gameLibraryCuratorAgent.js` 定义游戏资源搜刮的 skill id、capability 文案、默认 skills 和 `apps` context。\n- 领域能力：`skills/` 下的每个文件只实现一个游戏资源处理能力。\n- 领域策略：`policies/` 放不依赖 UI 的判断规则，例如复核队列阈值。\n\n游戏资源之外的新能力不应该塞进 `gameLibrary`。如果要接入日志诊断、配置建议、库维护等能力，应新增自己的 domain agent，并复用 `agents/core`。\n\n## 入口与边界\n\n- UI 入口：`views/Apps.vue` 展示扫描结果和用户可选的 AI skill 开关。\n- 组合逻辑：`composables/useApps.js` 调用扫描能力，并运行 `runGameLibraryCuratorAgent()`。\n- Agent 入口：`utils/agents/gameLibrary/gameLibraryCuratorAgent.js` 注册游戏能力，底层执行由 `utils/agents/core/agentCore.js` 完成。\n- AI 请求：WebUI 只调用 `/api/ai/chat/completions`，不保存独立 LLM 配置。\n- 配置入口：LLM provider、base URL、model、API key 等配置统一由 control panel 的米塔页面维护，并同步到 Sunshine 后端 `/api/ai/config`。\n\n不要在 WebUI 扫描页新增第二套 AI 配置入口。扫描页只负责选择启用哪些 game-library skills。\n\n## 当前数据流\n\n1. `scanDirectory()` 或 `scanGameLibraries()` 得到扫描结果。\n2. `applyGameLibraryOverrides()` 先套用用户此前确认过的名称和封面。\n3. `withScanKeys()` 给本轮扫描结果加 `__scan-key`，用于异步更新时稳定定位条目。\n4. `runGameLibraryCuratorAgent()` 根据用户启用的 skill id 顺序运行 skills。\n5. `onTitlesEnhanced()` 和 `onCoverResolved()` 增量更新扫描结果弹窗。\n6. 用户快速添加、批量添加或编辑保存时，`rememberGameLibraryApp()` 记录本次确认结果，供下次扫描复用。\n\n## 内置 Skills\n\n| Skill id | 阶段 | 默认 | 用户可选 | 职责 |\n| --- | --- | --- | --- | --- |\n| `game.scan.memory` | `memory` | 开启 | 否 | 应用已确认的扫描覆盖记录，并在用户确认后学习新记录。 |\n| `game.title.normalize` | `metadata` | 开启 | 是 | 通过 LLM 清洗标题、输出规范名、搜索词和名称置信度。 |\n| `game.cover.select` | `asset` | 开启 | 是 | 搜索候选封面，再用 LLM 或本地 fallback 选择最佳封面。 |\n\n必选能力用 `required: true` 标记。`normalizeGameLibrarySkillIds()` 会保证 required skill 始终启用，并过滤未知 id。\n\n### 封面证据校准\n\n`game.cover.select` 不直接信任模型给出的封面置信度。`coverSelectionAi.js` 会先计算候选封面与规范名、显示名、原始名、搜索词之间的本地匹配证据：\n\n- `exact-title`：候选标题与已知名称完全一致。\n- `prefix-title` / `contains-title`：候选标题与已知名称存在强前缀或包含关系。\n- `token-overlap`：标题 token 高度重合。\n- `source-prior`：只有来源先验，没有足够标题证据。\n\nAI 选择封面后，`calibrateCoverConfidence()` 会用这份证据校准 `ai-cover-confidence`，并写入 `cover-match-confidence`、`cover-match-relation`、`cover-match-reason`。这样模型理解语义，本地证据负责防止同名游戏、DLC、工具、原声带等弱匹配以高置信度通过。审核队列会优先使用 `ai-cover-confidence`，缺失时回退到 `cover-match-confidence`。\n\n## Skill 契约\n\n每个 skill 至少需要：\n\n```js\n{\n  id: 'game.example.skill',\n  type: 'metadata',\n  label: 'Example skill',\n  async run(context) {\n    return {\n      ...context,\n      apps: nextApps,\n      stats: { ...(context.stats || {}), exampleCount },\n    }\n  },\n}\n```\n\n`context` 字段约定：\n\n- `apps`：当前扫描结果数组。skill 应返回新数组或等价的更新结果。\n- `events`：调试事件数组。skill 可以 push `{ skillId, type, ... }`。\n- `stats`：聚合统计，例如 `titleChanges`、`coversFound`、`skillFailures`。\n- `options`：运行时回调，例如 `onTitlesEnhanced`、`onCoverResolved`、`onSkillError`。\n\nAgent 默认会捕获单个 skill 的异常，记录 `skill:error` 事件并继续后续 skill。只有传入 `stopOnSkillError: true` 时才会中断整轮。\n\n## 新增其他 Domain Agent\n\n新增游戏资源之外的能力时，优先建立独立目录，例如：\n\n```text\nutils/agents/diagnostics/\nutils/agents/configAdvisor/\n```\n\n推荐结构：\n\n```js\nimport {\n  createAgent,\n  createAgentSkill,\n  createSkillRegistry,\n} from '../core/agentCore.js'\n\nexport const DIAGNOSTICS_AGENT_ID = 'diagnostics'\n\nexport function createDiagnosticsSkill(definition) {\n  return createAgentSkill(definition, {\n    skillSubject: 'Diagnostics skills',\n    runSubject: 'Diagnostics skill',\n  })\n}\n\nconst registry = createSkillRegistry({\n  baseCapabilities: DIAGNOSTICS_CAPABILITIES,\n  createSkill: createDiagnosticsSkill,\n  duplicateMessage: 'Diagnostics skill already registered',\n})\n\nexport function createDiagnosticsAgent(options = {}) {\n  return createAgent({\n    id: DIAGNOSTICS_AGENT_ID,\n    skills: options.skills || createDefaultDiagnosticsSkills(),\n    createContext(input, runOptions) {\n      return { input, events: [], stats: {}, options: runOptions }\n    },\n  })\n}\n```\n\n每个 domain agent 应自己定义 context 形状、capability 文案、默认启用规则和 UI 接入点。通用 core 不应该知道具体业务字段。\n\n## 新增内置 Skill\n\n1. 在 `skills/` 下新增 `xxxSkill.js`，导出稳定的 `XXX_SKILL_ID` 和 `createXxxSkill()`。\n2. 在 `gameLibraryCuratorAgent.js` 中导入并加入：\n   - `GAME_LIBRARY_SKILL_IDS`\n   - `GAME_LIBRARY_AGENT_CAPABILITIES`\n   - `createDefaultGameLibrarySkills()`\n3. 给 capability 填好：\n   - `stage`\n   - Font Awesome `icon`\n   - 英文 `label`\n   - 中文 `labels.zh`\n   - `defaultEnabled`\n   - `userSelectable`\n   - 必要时 `required`\n4. 在 `tests/gameLibraryCuratorAgent.test.js` 增加顺序、选择、失败隔离、label/icon 和默认启用测试。\n5. 如涉及 UI 文案或状态，补充 `Apps.vue` / `ScanResultModal.vue` 的行为验证。\n\n## 注册扩展 Skill\n\n实验性或外部能力可以通过 `registerGameLibrarySkillExtension()` 注册，不需要改默认内置列表：\n\n```js\nimport {\n  registerGameLibrarySkillExtension,\n} from './gameLibraryCuratorAgent.js'\n\nconst unregister = registerGameLibrarySkillExtension({\n  skill: {\n    id: 'game.example.annotate',\n    type: 'metadata',\n    label: 'Example annotation',\n    async run(context) {\n      return {\n        ...context,\n        apps: context.apps.map((app) => ({ ...app, example: true })),\n      }\n    },\n  },\n  capability: {\n    icon: 'fa-vial',\n    label: 'Example annotation',\n    labels: { zh: '示例标注' },\n    defaultEnabled: false,\n    userSelectable: true,\n  },\n})\n```\n\n扩展 id 必须全局唯一。测试结束或插件卸载时调用 `unregister()`，避免污染后续运行。\n\n## AI Prompt 与语言\n\nPrompt 语言由 `aiLocale.js` 根据当前页面语言解析：\n\n- `getCurrentLocale()` 优先读 `document.documentElement.lang`，其次读浏览器语言。\n- `buildLocalizedInstruction()` 要求模型用当前语言输出面向用户的短理由。\n- 标题清洗和封面选择 prompt 都必须返回 JSON，不允许 markdown。\n\n维护 prompt 时要优先约束输出 schema、置信度和不确定时的保守行为。不要把 provider、model、key 等配置写进 prompt 或扫描页。\n\n## 缓存与复核\n\n- `aiCache.js` 使用 localStorage，访问失败时自动降级到内存缓存。\n- 元数据缓存 namespace：`game-metadata:v1`。\n- 封面选择缓存 namespace：`cover-selection:v1`。\n- 调整 prompt、schema 或影响结果含义时，要提升 cache version。\n- `reviewQueuePolicy.js` 负责判断是否进入复核队列。默认阈值：\n  - 名称置信度低于 `0.82`\n  - 封面置信度低于 `0.7`\n  - 游戏缺少规范名或封面\n\n## 容错原则\n\n- 扫描结果应先展示，再异步增强，AI 不可用时不能阻塞用户添加应用。\n- 单个 batch 或单个封面候选失败时，应跳过该部分并保留原始结果。\n- 用户确认过的 `user-override` 和已有封面优先级最高。\n- 异步更新必须用 `__scan-key` 或 `getGameResourceKey()` 稳定定位，避免重复名称、重复命令导致错位。\n\n## 维护检查清单\n\n改动这套体系后至少运行：\n\n```powershell\nnpm run test:webui\nnpm run build\ngit diff --check\n```\n\n重点测试文件：\n\n- `tests/agentCore.test.js`\n- `tests/gameLibraryCuratorAgent.test.js`\n- `tests/gameMetadataAi.test.js`\n- `tests/aiCache.test.js`\n- `tests/scanOverrides.test.js`\n\n如果改了 control panel 的 AI 配置或 provider preset，还要在 `src_assets/common/sunshine-control-panel` 内运行对应构建和测试命令。\n","isInternal":false,"tokens":2860,"sizeBytes":9235}],"systemPromptSnippet":"<yakaai_skills repo=\"AlkaidLab/foundation-sunshine\">\n<!-- File: tools/sunshine-ds5-sidecar/README.md (Tokens: ~1947 | Category: plugin-manifest) -->\n# Sunshine virtual device host\n\nThis optional Windows helper isolates Sunshine from the third-party\nHIDMaestro runtime. The shipping executable remains\n`Sunshine.Ds5Sidecar.exe` for component-upgrade compatibility, while the\nprocess now uses a `VirtualDeviceHostServer` and `DeviceRegistry` internally.\nIt owns virtual devices and exposes the versioned `SDS5` named-pipe protocol.\nThe optional component package includes patched HIDMaestro Core and its notices.\n\nBuild the compatible Foundation Core patch and the sidecar:\n\n```powershell\nscripts/build-ds5-sidecar.ps1\n```\n\nThe version 1.4.2 component ZIP includes patched `HIDMaestro.Core.dll`, its\nlicenses, and the complete .NET runtime. The main Sunshine package contains only\nthe pinned ZIP download manifest. The Control Panel automatically updates older\ninstalled components when streaming is idle, using the existing administrator\nhelper, verified staging and whole-component rollback. Optional USB/IP transport\ninstallation remains part of explicit install/repair, not automatic updates.\nThe sidecar uses normal .NET loading and requires the v1.12.0 runtime and\ndevice-ownership, externally managed USB/IP and `FoundationDualSenseReports=host-clock-v1`\nmarkers even for `--probe`. Sunshine validates the\ninstalled component without replacing DLLs.\nThe upstream SDK API, assembly version and embedded resources are preserved; see\n[`../hidmaestro-core-patch/README.md`](../hidmaestro-core-patch/README.md).\n\nRead-only capability probe:\n\n```powershell\ndotnet Sunshine.Ds5Sidecar.dll --probe\n```\n\nDeterministic protocol ABI, microphone queue/runtime-contract, four-channel\nlayout, and channel-isolation checks (no elevation or virtual device required):\n\n```powershell\ndotnet Sunshine.Ds5Sidecar.dll --self-check\n```\n\nUSB/IP installation and repair belong to the shared component manager. Device\ncreation only checks the approved driver and fails with a shared transport error\nwhen it is missing or incompatible. The ZIP carries `component-compatibility.json`,\nthe same contract used by the GUI installer. The staged upgrade acceptance test\nrequires elevation and an idle host, and creates/releases two composite devices:\n\n```powershell\nSunshine.Ds5Sidecar.exe --self-test transport --result transport-acceptance.json\n```\n\nThe production process must be launched elevated and placed in the Sunshine\nJob Object. The pipe accepts a single elevated client of the creating user;\nnon-elevated callers are rejected at connect time and dropped without\nending the sidecar.\nInput, touch, motion and battery updates submit their combined state directly\non receipt under the session state lock. They do not use a thread-pool timer:\neven a 1 ms timer can run at roughly 15.6 ms intervals on Windows and reduce\ncontinuous input to about 64 Hz. The sidecar preserves the incoming update\ncadence; it does not synthesize repeated reports to inflate polling-rate tests.\nUSB reports include rolling 8-bit and 32-bit sequence numbers, device time and\nthe USB-connected status. Device and motion timestamps use the same host QPC\nepoch at 3 MHz. Button, touch and battery reports advance device time while\nretaining the latest motion sample timestamp. Network receive time still stands\nin for the client's physical sampling time; these metadata corrections have not\nbeen verified to resolve Genshin's camera jumps.\nThe pipe reader reuses a receive buffer for headers and payloads, dispatching\ncomplete frames as soon as they arrive. This removes separate header/body reads\nand per-input payload allocations without adding a batching delay. Microphone\nPCM retains its own copy because its queue outlives the receive buffer. The\ndevice-free `--self-check` covers fragmented/packed frames, length validation,\nEOF, cancellation and retained PCM ownership.\nThe reader uses one async state machine per pending frame. Standard axis\nbindings are resolved once through the SDK at controller creation and update\nthe existing state dictionary in place. This avoids rebuilding the SDK's\nstick/trigger lists and axis dictionary on every input update. `--self-check`\ncompares the cached mapping against the SDK's complete bundled profile catalog\nand checks that steady-state axis updates allocate no managed objects.\nThe pinned .NET 10 runtime config sets\n`System.Threading.ThreadPool.UnfairSemaphoreSpinLimit` to zero for this process.\nPool workers wait for a signal instead of spinning between short input bursts.\nThis adds no report-rate limiter or timer. Because it trades spinning for OS\nwakeups, CPU and report-interval tests should be repeated when changing the\nbundled runtime or enabling additional concurrent input/audio workloads.\nDisconnecting the owning pipe disposes every device created by\nthat connection. Standard `dualsense` uses UMDF2; `dualsense-composite`\nenables the USB composite HID/audio profile and authored haptics PCM.\nHID-only attaches use `dualsense` with the native 273-byte descriptor and\nGame Pad usage (0x05). HIDMaestro v1.12.0 no longer writes the incorrect global\nGameInput mapping for JSON profiles and removes exact legacy SDK tables\n(issue #1056), so the former Joystick usage override is unnecessary. Input\nreports retain `alwaysArmed` for touch and motion data. Windows decides how\nto expose the device; native Game Pad usage does not guarantee a WGI Gamepad.\nThe optional Genshin compatibility attach flag derives a third profile from\n`dualsense-composite` at runtime. It preserves the Sony VID/PID, descriptors,\nand four-channel layout while changing only the USB product string from\n`DualSense Wireless Controller` to the launch-model `Wireless Controller`.\nThe runtime profile starts the USB speaker control unmuted at its declared\nmaximum and commits the active endpoint's 4-channel, available-speaker and\nfull-range masks as quadraphonic (`0x33`) through Core Audio. This matches the\nthree settings applied by completing Windows' speaker setup wizard as required\nby Genshin.\nEndpoint enumeration uses the Windows SDK's `IMMDeviceCollection` IID, and\nthe Windows 10+ policy property calls include the effects-store argument.\n`NativeMethods.txt` requests the public audio COM interfaces, structures and\nproperty keys from Microsoft CsWin32. The generator and SDK metadata are pinned\nin `packages.lock.json`; publishing restores in locked mode and adds no runtime\nlibrary dependency. `CoreAudio` shares endpoint enumeration and reference cleanup\nbetween the default-device monitor and WASAPI probe. Only the undocumented\nWindows 10+ policy interface remains hand-declared, inside `AudioPolicyConfig`.\nCore Audio references are released per acquisition so configuration readback\ncannot invalidate the concurrent default-device monitor. Endpoint detection\nalso recognizes HIDMaestro's synthetic USB serial under the official USB/IP\nhost controller, without requiring runtime ownership stamping.\nThe transport acceptance test creates both the normal composite profile and\nthe Genshin compatibility profile. It reads back the endpoint format and both\nspeaker masks for the endpoint created by that attach before passing. It waits\nfor that endpoint to become inactive before the next round, so a stale endpoint\ncannot satisfy another mode's check.\nThe sidecar advertises this support through a protocol capability bit so an\nolder runtime cannot silently accept an ineffective setting.\nThe v1 wire contract reserves capability bits and message numbers for the\nvirtual microphone. Normal startup intentionally does not advertise them;\nonly the explicit development prototype can enable those capabilities.\n\nThe Phase 2 composite-profile microphone path is development-only and requires\nboth the exact HIDMaestro 1.12.0.0 runtime and an explicit opt-in. Its elevated\nattach/PCM/flush/destroy smoke test is:\n\n```powershell\ndotnet Sunshine.Ds5Sidecar.dll --self-test microphone-prototype\n```\n\nFor an end-to-end developer check, `microphone-capture` snapshots the active\ncapture endpoints, creates the prototype, opens only its newly added endpoint\nthrough WASAPI, streams a 440 Hz signal for five seconds, and requires observed\nhost streaming, zero submit errors, captured frames, and non-zero PCM before it\npasses:\n\n```powershell\ndotnet Sunshine.Ds5Sidecar.dll --self-test microphone-capture\n```\n\nThis temporary path also enumerates the composite profile's HID and render\ninterfaces; it is not the final capture-only virtual microphone product.\nThe composite session monitors every Windows default render and capture role.\nIf Windows selects a HIDMaestro-backed virtual DualSense endpoint as a default,\nthe helper reports the policy violation and exits; Sunshine then performs its\nsingle recovery attach in HID-only DS5 mode. This read-only fail-closed guard\navoids undocumented audio-policy writes and never changes a user's defaults.\n\nThe trigger effects and lightbar of an output report a game writes to the\nvirtual pad are read the way the hardware reads them: only when the report's\nvalidity byte for that field is set. A game leaves the fields it is not\nprogramming zero, and reading those zeros as an instruction would cancel an\neffect the game just armed and strobe a held color. A validity byte the decoder\ndoes not expose governs nothing, so the fields it would gate are read\nunconditionally. The motor bytes are read unconditionally as well, because their\nzeros can cancel a rumble that is still playing while gating them can drop a\nstop that has no other way to arrive.\n\n<!-- File: src_assets/common/assets/web/utils/agents/core/README.md (Tokens: ~413 | Category: subagent-persona) -->\n# Agent Core\n\n`agents/core` 是 WebUI 里可复用的 agent/skill 底座。它不包含任何游戏、日志、配置或 UI 业务字段，只负责通用执行模型。\n\n## 提供什么\n\n- `createAgent()`：顺序运行 skills，支持 `enabledSkills` 选择、`stopOnSkillError` 中断和默认失败隔离。\n- `createAgentSkill()`：校验 skill id 和 `run(context)`。\n- `createSkillRegistry()`：注册扩展 skill，并生成对应 capability。\n- capability helpers：获取默认启用项、可选择项、图标、文案和归一化后的启用列表。\n\n## Context 约定\n\nCore 只要求 context 可以被 skill 接收并返回。推荐每个 domain 保持这些通用字段：\n\n```js\n{\n  events: [],\n  stats: {},\n  options: runOptions,\n}\n```\n\n业务字段由 domain 自己定义，例如：\n\n- `gameLibrary` 使用 `apps`。\n- `diagnostics` 使用 `logs`、`findings` 和 `severitySummary`。\n\n## 新建 Domain Agent\n\n每个 domain agent 应该自己提供：\n\n- 稳定的 agent id。\n- 稳定的 skill id 常量。\n- capability 列表和本地化文案。\n- `createXxxSkill()` 包装，用于给错误信息加 domain 名称。\n- `createDefaultXxxSkills()`。\n- `createXxxAgent()`，通过 `createContext()` 定义自己的 context 形状。\n- `registerXxxSkillExtension()`，如果允许外部扩展。\n\nCore 不应该直接 import 任何 domain 文件。\n\n<!-- File: src_assets/common/assets/web/utils/agents/diagnostics/README.md (Tokens: ~413 | Category: subagent-persona) -->\n# Diagnostics Agent\n\n`diagnostics` 是面向 Troubleshooting 的日志诊断 domain agent。它复用 `agents/core`，但只处理日志和诊断发现，不保存 AI 配置，也不直接调用 LLM。\n\n## 当前能力\n\n| Skill id | 阶段 | 默认 | 职责 |\n| --- | --- | --- | --- |\n| `diagnostics.logs.severity` | `log-analysis` | 开启 | 统计 fatal、error、warning 日志行，并保留少量证据。 |\n| `diagnostics.logs.patterns` | `log-analysis` | 开启 | 检测编码器、显示捕获、网络连接、端口绑定和配置风险模式。 |\n| `diagnostics.logs.remediation` | `log-analysis` | 开启 | 根据本地 findings 生成可执行修复建议。 |\n\n## Context\n\n```js\n{\n  logs: '',\n  findings: [],\n  suggestions: [],\n  severitySummary: null,\n  events: [],\n  stats: {},\n  options: runOptions,\n}\n```\n\n`findings` 和 `suggestions` 会作为本地预诊断显示在 AI 日志诊断弹窗中，并作为上下文传给 `/api/ai/chat/completions`。这样即使 AI 配置不可用，用户仍能看到本地规则发现和基础修复建议。\n\n## 边界\n\n- LLM provider、model 和 key 仍由 control panel 的米塔页面统一配置。\n- WebUI 诊断弹窗只消费共享 Sunshine AI proxy。\n- 本地规则只提供可解释线索，不自动修复配置或重启服务。\n\n<!-- File: src_assets/common/assets/web/utils/agents/gameLibrary/README.md (Tokens: ~2860 | Category: subagent-persona) -->\n# 游戏资源搜刮 Agent / Skills 体系\n\n这套代码负责在 WebUI 的应用扫描流程中，对扫描到的游戏资源做 AI 辅助整理：复用用户已确认的修正、清洗游戏标题、生成封面搜索词、匹配封面，并把低置信度结果留给用户复核。\n\n## 分层设计\n\n- 通用底座：`utils/agents/core/agentCore.js` 提供 agent runner、skill 校验、capability registry、默认启用能力计算和用户选择归一化。\n- 领域适配：`utils/agents/gameLibrary/gameLibraryCuratorAgent.js` 定义游戏资源搜刮的 skill id、capability 文案、默认 skills 和 `apps` context。\n- 领域能力：`skills/` 下的每个文件只实现一个游戏资源处理能力。\n- 领域策略：`policies/` 放不依赖 UI 的判断规则，例如复核队列阈值。\n\n游戏资源之外的新能力不应该塞进 `gameLibrary`。如果要接入日志诊断、配置建议、库维护等能力，应新增自己的 domain agent，并复用 `agents/core`。\n\n## 入口与边界\n\n- UI 入口：`views/Apps.vue` 展示扫描结果和用户可选的 AI skill 开关。\n- 组合逻辑：`composables/useApps.js` 调用扫描能力，并运行 `runGameLibraryCuratorAgent()`。\n- Agent 入口：`utils/agents/gameLibrary/gameLibraryCuratorAgent.js` 注册游戏能力，底层执行由 `utils/agents/core/agentCore.js` 完成。\n- AI 请求：WebUI 只调用 `/api/ai/chat/completions`，不保存独立 LLM 配置。\n- 配置入口：LLM provider、base URL、model、API key 等配置统一由 control panel 的米塔页面维护，并同步到 Sunshine 后端 `/api/ai/config`。\n\n不要在 WebUI 扫描页新增第二套 AI 配置入口。扫描页只负责选择启用哪些 game-library skills。\n\n## 当前数据流\n\n1. `scanDirectory()` 或 `scanGameLibraries()` 得到扫描结果。\n2. `applyGameLibraryOverrides()` 先套用用户此前确认过的名称和封面。\n3. `withScanKeys()` 给本轮扫描结果加 `__scan-key`，用于异步更新时稳定定位条目。\n4. `runGameLibraryCuratorAgent()` 根据用户启用的 skill id 顺序运行 skills。\n5. `onTitlesEnhanced()` 和 `onCoverResolved()` 增量更新扫描结果弹窗。\n6. 用户快速添加、批量添加或编辑保存时，`rememberGameLibraryApp()` 记录本次确认结果，供下次扫描复用。\n\n## 内置 Skills\n\n| Skill id | 阶段 | 默认 | 用户可选 | 职责 |\n| --- | --- | --- | --- | --- |\n| `game.scan.memory` | `memory` | 开启 | 否 | 应用已确认的扫描覆盖记录，并在用户确认后学习新记录。 |\n| `game.title.normalize` | `metadata` | 开启 | 是 | 通过 LLM 清洗标题、输出规范名、搜索词和名称置信度。 |\n| `game.cover.select` | `asset` | 开启 | 是 | 搜索候选封面，再用 LLM 或本地 fallback 选择最佳封面。 |\n\n必选能力用 `required: true` 标记。`normalizeGameLibrarySkillIds()` 会保证 required skill 始终启用，并过滤未知 id。\n\n### 封面证据校准\n\n`game.cover.select` 不直接信任模型给出的封面置信度。`coverSelectionAi.js` 会先计算候选封面与规范名、显示名、原始名、搜索词之间的本地匹配证据：\n\n- `exact-title`：候选标题与已知名称完全一致。\n- `prefix-title` / `contains-title`：候选标题与已知名称存在强前缀或包含关系。\n- `token-overlap`：标题 token 高度重合。\n- `source-prior`：只有来源先验，没有足够标题证据。\n\nAI 选择封面后，`calibrateCoverConfidence()` 会用这份证据校准 `ai-cover-confidence`，并写入 `cover-match-confidence`、`cover-match-relation`、`cover-match-reason`。这样模型理解语义，本地证据负责防止同名游戏、DLC、工具、原声带等弱匹配以高置信度通过。审核队列会优先使用 `ai-cover-confidence`，缺失时回退到 `cover-match-confidence`。\n\n## Skill 契约\n\n每个 skill 至少需要：\n\n```js\n{\n  id: 'game.example.skill',\n  type: 'metadata',\n  label: 'Example skill',\n  async run(context) {\n    return {\n      ...context,\n      apps: nextApps,\n      stats: { ...(context.stats || {}), exampleCount },\n    }\n  },\n}\n```\n\n`context` 字段约定：\n\n- `apps`：当前扫描结果数组。skill 应返回新数组或等价的更新结果。\n- `events`：调试事件数组。skill 可以 push `{ skillId, type, ... }`。\n- `stats`：聚合统计，例如 `titleChanges`、`coversFound`、`skillFailures`。\n- `options`：运行时回调，例如 `onTitlesEnhanced`、`onCoverResolved`、`onSkillError`。\n\nAgent 默认会捕获单个 skill 的异常，记录 `skill:error` 事件并继续后续 skill。只有传入 `stopOnSkillError: true` 时才会中断整轮。\n\n## 新增其他 Domain Agent\n\n新增游戏资源之外的能力时，优先建立独立目录，例如：\n\n```text\nutils/agents/diagnostics/\nutils/agents/configAdvisor/\n```\n\n推荐结构：\n\n```js\nimport {\n  createAgent,\n  createAgentSkill,\n  createSkillRegistry,\n} from '../core/agentCore.js'\n\nexport const DIAGNOSTICS_AGENT_ID = 'diagnostics'\n\nexport function createDiagnosticsSkill(definition) {\n  return createAgentSkill(definition, {\n    skillSubject: 'Diagnostics skills',\n    runSubject: 'Diagnostics skill',\n  })\n}\n\nconst registry = createSkillRegistry({\n  baseCapabilities: DIAGNOSTICS_CAPABILITIES,\n  createSkill: createDiagnosticsSkill,\n  duplicateMessage: 'Diagnostics skill already registered',\n})\n\nexport function createDiagnosticsAgent(options = {}) {\n  return createAgent({\n    id: DIAGNOSTICS_AGENT_ID,\n    skills: options.skills || createDefaultDiagnosticsSkills(),\n    createContext(input, runOptions) {\n      return { input, events: [], stats: {}, options: runOptions }\n    },\n  })\n}\n```\n\n每个 domain agent 应自己定义 context 形状、capability 文案、默认启用规则和 UI 接入点。通用 core 不应该知道具体业务字段。\n\n## 新增内置 Skill\n\n1. 在 `skills/` 下新增 `xxxSkill.js`，导出稳定的 `XXX_SKILL_ID` 和 `createXxxSkill()`。\n2. 在 `gameLibraryCuratorAgent.js` 中导入并加入：\n   - `GAME_LIBRARY_SKILL_IDS`\n   - `GAME_LIBRARY_AGENT_CAPABILITIES`\n   - `createDefaultGameLibrarySkills()`\n3. 给 capability 填好：\n   - `stage`\n   - Font Awesome `icon`\n   - 英文 `label`\n   - 中文 `labels.zh`\n   - `defaultEnabled`\n   - `userSelectable`\n   - 必要时 `required`\n4. 在 `tests/gameLibraryCuratorAgent.test.js` 增加顺序、选择、失败隔离、label/icon 和默认启用测试。\n5. 如涉及 UI 文案或状态，补充 `Apps.vue` / `ScanResultModal.vue` 的行为验证。\n\n## 注册扩展 Skill\n\n实验性或外部能力可以通过 `registerGameLibrarySkillExtension()` 注册，不需要改默认内置列表：\n\n```js\nimport {\n  registerGameLibrarySkillExtension,\n} from './gameLibraryCuratorAgent.js'\n\nconst unregister = registerGameLibrarySkillExtension({\n  skill: {\n    id: 'game.example.annotate',\n    type: 'metadata',\n    label: 'Example annotation',\n    async run(context) {\n      return {\n        ...context,\n        apps: context.apps.map((app) => ({ ...app, example: true })),\n      }\n    },\n  },\n  capability: {\n    icon: 'fa-vial',\n    label: 'Example annotation',\n    labels: { zh: '示例标注' },\n    defaultEnabled: false,\n    userSelectable: true,\n  },\n})\n```\n\n扩展 id 必须全局唯一。测试结束或插件卸载时调用 `unregister()`，避免污染后续运行。\n\n## AI Prompt 与语言\n\nPrompt 语言由 `aiLocale.js` 根据当前页面语言解析：\n\n- `getCurrentLocale()` 优先读 `document.documentElement.lang`，其次读浏览器语言。\n- `buildLocalizedInstruction()` 要求模型用当前语言输出面向用户的短理由。\n- 标题清洗和封面选择 prompt 都必须返回 JSON，不允许 markdown。\n\n维护 prompt 时要优先约束输出 schema、置信度和不确定时的保守行为。不要把 provider、model、key 等配置写进 prompt 或扫描页。\n\n## 缓存与复核\n\n- `aiCache.js` 使用 localStorage，访问失败时自动降级到内存缓存。\n- 元数据缓存 namespace：`game-metadata:v1`。\n- 封面选择缓存 namespace：`cover-selection:v1`。\n- 调整 prompt、schema 或影响结果含义时，要提升 cache version。\n- `reviewQueuePolicy.js` 负责判断是否进入复核队列。默认阈值：\n  - 名称置信度低于 `0.82`\n  - 封面置信度低于 `0.7`\n  - 游戏缺少规范名或封面\n\n## 容错原则\n\n- 扫描结果应先展示，再异步增强，AI 不可用时不能阻塞用户添加应用。\n- 单个 batch 或单个封面候选失败时，应跳过该部分并保留原始结果。\n- 用户确认过的 `user-override` 和已有封面优先级最高。\n- 异步更新必须用 `__scan-key` 或 `getGameResourceKey()` 稳定定位，避免重复名称、重复命令导致错位。\n\n## 维护检查清单\n\n改动这套体系后至少运行：\n\n```powershell\nnpm run test:webui\nnpm run build\ngit diff --check\n```\n\n重点测试文件：\n\n- `tests/agentCore.test.js`\n- `tests/gameLibraryCuratorAgent.test.js`\n- `tests/gameMetadataAi.test.js`\n- `tests/aiCache.test.js`\n- `tests/scanOverrides.test.js`\n\n如果改了 control panel 的 AI 配置或 provider preset，还要在 `src_assets/common/sunshine-control-panel` 内运行对应构建和测试命令。\n</yakaai_skills>"}