dsh-starlite 0.1.0-alpha.1 → 0.1.0-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +25 -25
- package/README.md +61 -36
- package/assets/windows-x64/LICENSE +26 -26
- package/assets/windows-x64/THIRD_PARTY_LICENSES.txt +19 -19
- package/assets/windows-x64/dsh-inochi2d-pet.exe +0 -0
- package/assets/windows-x64/manifest.json +3 -3
- package/cordis.patch.yml +3 -3
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/LICENSE
CHANGED
|
@@ -1,25 +1,25 @@
|
|
|
1
|
-
BSD 2-Clause License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2026, StarCried
|
|
4
|
-
All rights reserved.
|
|
5
|
-
|
|
6
|
-
Redistribution and use in source and binary forms, with or without
|
|
7
|
-
modification, are permitted provided that the following conditions are met:
|
|
8
|
-
|
|
9
|
-
1. Redistributions of source code must retain the above copyright notice, this
|
|
10
|
-
list of conditions and the following disclaimer.
|
|
11
|
-
|
|
12
|
-
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
13
|
-
this list of conditions and the following disclaimer in the documentation
|
|
14
|
-
and/or other materials provided with the distribution.
|
|
15
|
-
|
|
16
|
-
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
17
|
-
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
18
|
-
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
19
|
-
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
20
|
-
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
21
|
-
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
22
|
-
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
23
|
-
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
24
|
-
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
25
|
-
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
1
|
+
BSD 2-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026, StarCried
|
|
4
|
+
All rights reserved.
|
|
5
|
+
|
|
6
|
+
Redistribution and use in source and binary forms, with or without
|
|
7
|
+
modification, are permitted provided that the following conditions are met:
|
|
8
|
+
|
|
9
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
10
|
+
list of conditions and the following disclaimer.
|
|
11
|
+
|
|
12
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
13
|
+
this list of conditions and the following disclaimer in the documentation
|
|
14
|
+
and/or other materials provided with the distribution.
|
|
15
|
+
|
|
16
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
17
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
18
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
19
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
20
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
21
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
22
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
23
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
24
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
25
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
package/README.md
CHANGED
|
@@ -1,36 +1,61 @@
|
|
|
1
|
-
# StarLite for DSH
|
|
2
|
-
|
|
3
|
-
用动作和气泡陪伴你的 DSH 工作。**安装一个插件,启动 DSH 即默认使用微光**,无需另装软件或手动导入资源。
|
|
4
|
-
|
|
5
|
-
Alpha `0.1.0-alpha.
|
|
6
|
-
|
|
7
|
-
[先看模型效果](https://starcried.github.io/)(桌面浏览器,无需安装;工作状态与问答需安装插件)。
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
**
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
1
|
+
# StarLite for DSH
|
|
2
|
+
|
|
3
|
+
用动作和气泡陪伴你的 DSH 工作。**安装一个插件,启动 DSH 即默认使用微光**,无需另装软件或手动导入资源。
|
|
4
|
+
|
|
5
|
+
Alpha `0.1.0-alpha.2`,需要 Windows 11 x64、WebView2,以及官方 Windows 桌面版、Windows 原生 CLI/Web 或 WSL2 DSH。
|
|
6
|
+
|
|
7
|
+
[先看模型效果](https://starcried.github.io/)(桌面浏览器,无需安装;工作状态与问答需安装插件)。
|
|
8
|
+
|
|
9
|
+
**快速开始**:[自己安装](#安装与启动) · [让 Agent 安装](#让-agent-帮你)
|
|
10
|
+
|
|
11
|
+
## 安装与启动
|
|
12
|
+
|
|
13
|
+
**Windows 桌面版**:侧栏「插件 → 添加插件」,输入 `dsh-starlite@alpha`,安装并启用;按 DSH 提示重启。
|
|
14
|
+
|
|
15
|
+
**CLI/Web**:在运行 DSH 的 Windows 或 WSL 终端给日常 profile 安装插件,然后启动:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
dsh plugin --profile web add dsh-starlite@alpha
|
|
19
|
+
dsh --profile web
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
将 `web` 换成自己的 profile。插件自动准备 Windows 程序,资源未缓存时自动下载并配置微光;已有 VRM 模型、映射与偏好保留。以后只需启动 DSH。
|
|
23
|
+
|
|
24
|
+
**使用微光即表示同意其独立许可;不同意请在模型列表选择「不显示模型」。** 模型卡片可切换模型或查看许可,「添加 VRM 模型」直接选择文件,详细要求可点「查看导入说明」;设置自动保存。当前仅支持 VRM,Inochi2D 导入和显示已停用。
|
|
25
|
+
|
|
26
|
+
## 让 Agent 帮你
|
|
27
|
+
|
|
28
|
+
把下面这段发给能操作本机的 Agent,它会按[安装指南](https://github.com/StarCried/starlite-dsh/blob/main/docs/user-guide.md)识别环境并完成安装:
|
|
29
|
+
|
|
30
|
+
```text
|
|
31
|
+
请阅读 https://github.com/StarCried/starlite-dsh/blob/main/docs/user-guide.md
|
|
32
|
+
为我正在使用的 DeepSeek Harness 安装 npm 已发布的 dsh-starlite@alpha。
|
|
33
|
+
先识别 Windows 桌面版、Windows CLI/Web 或 WSL2 及实际 profile,使用对应安装入口。
|
|
34
|
+
保留模型和偏好,不升级 DSH、不另开第二个 Host。
|
|
35
|
+
完成不调用付费模型 API 的本地检查,告诉我如何启动和打开设置。
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
<details>
|
|
39
|
+
<summary>只想先了解用法?复制这个提示词</summary>
|
|
40
|
+
|
|
41
|
+
```text
|
|
42
|
+
请阅读 https://github.com/StarCried/starlite-dsh/blob/main/docs/user-guide.md
|
|
43
|
+
结合我的 DSH 安装方式,简要说明如何启动、升级、换模型和设置气泡。
|
|
44
|
+
这次只解释,不安装或修改配置。
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
</details>
|
|
48
|
+
|
|
49
|
+
## 日常使用
|
|
50
|
+
|
|
51
|
+
单击模型回应并查看费用;双击在桌面应用或浏览器打开当前会话,无会话时打开 DSH;拖动身体移动桌宠。右键或托盘打开设置。工作气泡可选精简/详细模式,普通问题可直接在气泡里选择或输入。
|
|
52
|
+
|
|
53
|
+
在 DSH 侧栏「插件」查找 `dsh-starlite`;旧版 DSH 在设置的插件列表中查看。若同一 profile 装过 `dsh-inochi2d-pet`,先移除旧包。桌面版的插件安装与 CLI/Web profile 独立,需要分别安装;一次只连接一个 Host,切换时停止原 Host(桌面版从 DSH 托盘退出),桌宠会在重连期结束后连接等待者。
|
|
54
|
+
|
|
55
|
+
## 进阶
|
|
56
|
+
|
|
57
|
+
[安装、升级与卸载](https://github.com/StarCried/starlite-dsh/blob/main/docs/user-guide.md) · [换模型与动作](https://github.com/StarCried/starlite-dsh/blob/main/docs/model-import.md) · [气泡、费用与隐私](https://github.com/StarCried/starlite-dsh/blob/main/docs/companion-interactions.md)
|
|
58
|
+
|
|
59
|
+
自己的标准人形 VRM 需要有效的人形映射,无动作时自动使用自带基础待机;眨眼和眼球跟随是可选能力,缺少时仍可使用。模型内的动作会自动列出供映射;模型卡片可移除不用的模型,源文件保留,重启不重新添加。桌宠准备、下载或兼容性检查失败时,DSH 仍可正常运行。API Key、用户提示、工具参数/结果和真实会话 ID 不发送给桌宠。桌面版关闭主窗口只隐藏界面,须从 DSH 托盘退出才停止 Host。
|
|
60
|
+
|
|
61
|
+
程序 BSD-2-Clause;默认待机随程序提供,来源见开发文档。微光及其其他动作独立授权,不打包在 npm 插件内。Windows 程序目前未签名。
|
|
@@ -1,26 +1,26 @@
|
|
|
1
|
-
BSD 2-Clause License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2026, StarCried
|
|
4
|
-
All rights reserved.
|
|
5
|
-
|
|
6
|
-
Redistribution and use in source and binary forms, with or without
|
|
7
|
-
modification, are permitted provided that the following conditions are met:
|
|
8
|
-
|
|
9
|
-
1. Redistributions of source code must retain the above copyright notice,
|
|
10
|
-
this list of conditions and the following disclaimer.
|
|
11
|
-
|
|
12
|
-
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
13
|
-
this list of conditions and the following disclaimer in the documentation
|
|
14
|
-
and/or other materials provided with the distribution.
|
|
15
|
-
|
|
16
|
-
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
17
|
-
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
18
|
-
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
19
|
-
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
|
20
|
-
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
21
|
-
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
22
|
-
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
23
|
-
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
24
|
-
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
25
|
-
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
26
|
-
POSSIBILITY OF SUCH DAMAGE.
|
|
1
|
+
BSD 2-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026, StarCried
|
|
4
|
+
All rights reserved.
|
|
5
|
+
|
|
6
|
+
Redistribution and use in source and binary forms, with or without
|
|
7
|
+
modification, are permitted provided that the following conditions are met:
|
|
8
|
+
|
|
9
|
+
1. Redistributions of source code must retain the above copyright notice,
|
|
10
|
+
this list of conditions and the following disclaimer.
|
|
11
|
+
|
|
12
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
13
|
+
this list of conditions and the following disclaimer in the documentation
|
|
14
|
+
and/or other materials provided with the distribution.
|
|
15
|
+
|
|
16
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
17
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
18
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
19
|
+
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
|
20
|
+
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
21
|
+
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
22
|
+
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
23
|
+
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
24
|
+
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
25
|
+
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
26
|
+
POSSIBILITY OF SUCH DAMAGE.
|
|
@@ -74665,23 +74665,23 @@ THE SOFTWARE.
|
|
|
74665
74665
|
==============================================================================
|
|
74666
74666
|
Bundled StarLiteIdle provenance
|
|
74667
74667
|
==============================================================================
|
|
74668
|
-
# Default Idle provenance
|
|
74669
|
-
|
|
74670
|
-
The default-idle.vrma asset is the currently approved Weiguang relaxed standing
|
|
74671
|
-
and breathing motion, offered as StarLiteIdle when importing a humanoid VRM.
|
|
74672
|
-
|
|
74673
|
-
- Source: DesktopPet/desktop/motions/Idle.vrma in the author's local model project.
|
|
74674
|
-
- SHA-256: 7bd5ce630f5f624c274ad2de026ad5365297c353d7f265654ed97656a738d248.
|
|
74675
|
-
- Duration: 6 seconds.
|
|
74676
|
-
- StarCried authored the relaxed pose and final manual editing/bake. The
|
|
74677
|
-
recorded breathing layer originates from Warudo's Human@SM Breathe; this
|
|
74678
|
-
asset must not be described as entirely original.
|
|
74679
|
-
- The user explicitly confirmed redistribution of the selected motions,
|
|
74680
|
-
including this breathing layer, on 2026-10-10 and approved bundling the
|
|
74681
|
-
current Idle for model-only onboarding on 2026-10-14.
|
|
74682
|
-
|
|
74683
|
-
This file records provenance and the user's distribution confirmation; it
|
|
74684
|
-
does not claim that Warudo's full motion library permits redistribution.
|
|
74685
|
-
The model artwork and the other selected motions are not included in this
|
|
74686
|
-
program asset.
|
|
74668
|
+
# Default Idle provenance
|
|
74669
|
+
|
|
74670
|
+
The default-idle.vrma asset is the currently approved Weiguang relaxed standing
|
|
74671
|
+
and breathing motion, offered as StarLiteIdle when importing a humanoid VRM.
|
|
74672
|
+
|
|
74673
|
+
- Source: DesktopPet/desktop/motions/Idle.vrma in the author's local model project.
|
|
74674
|
+
- SHA-256: 7bd5ce630f5f624c274ad2de026ad5365297c353d7f265654ed97656a738d248.
|
|
74675
|
+
- Duration: 6 seconds.
|
|
74676
|
+
- StarCried authored the relaxed pose and final manual editing/bake. The
|
|
74677
|
+
recorded breathing layer originates from Warudo's Human@SM Breathe; this
|
|
74678
|
+
asset must not be described as entirely original.
|
|
74679
|
+
- The user explicitly confirmed redistribution of the selected motions,
|
|
74680
|
+
including this breathing layer, on 2026-10-10 and approved bundling the
|
|
74681
|
+
current Idle for model-only onboarding on 2026-10-14.
|
|
74682
|
+
|
|
74683
|
+
This file records provenance and the user's distribution confirmation; it
|
|
74684
|
+
does not claim that Warudo's full motion library permits redistribution.
|
|
74685
|
+
The model artwork and the other selected motions are not included in this
|
|
74686
|
+
program asset.
|
|
74687
74687
|
|
|
Binary file
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 2,
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.2",
|
|
4
4
|
"platform": "windows-x64",
|
|
5
5
|
"executable": {
|
|
6
6
|
"file": "dsh-inochi2d-pet.exe",
|
|
7
|
-
"sha256": "
|
|
8
|
-
"bytes":
|
|
7
|
+
"sha256": "ba429f395ca855cf6b47ea611876ed038c7a231021d46e7a7e02fdf36f49c711",
|
|
8
|
+
"bytes": 11785728
|
|
9
9
|
},
|
|
10
10
|
"defaultModel": {
|
|
11
11
|
"title": "微光v3.0",
|
package/cordis.patch.yml
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
- insert:
|
|
2
|
-
- id: dsh-starlite
|
|
3
|
-
name: dsh-starlite
|
|
1
|
+
- insert:
|
|
2
|
+
- id: dsh-starlite
|
|
3
|
+
name: dsh-starlite
|
package/dist/index.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/startup-error.ts", "../../protocol/src/billing.ts", "../../protocol/src/interaction.ts", "../../protocol/src/index.ts", "../src/aggregator.ts", "../src/bootstrap.ts", "../src/launcher.ts", "../src/server.ts", "../src/pricing-period.ts", "../src/billing.ts", "../src/questions.ts", "../src/navigation-rpc.ts", "../src/desktop-navigation.ts", "../src/navigation.ts", "../src/adapter.ts", "../src/index.ts"],
|
|
4
|
-
"sourcesContent": ["export type StartupFailure = \"unsupported-platform\" | \"incompatible-api\";\n\n/** Known startup failures have fixed diagnostics; Host errors may contain secrets. */\nexport class DshStartupError extends Error {\n constructor(readonly code: StartupFailure) {\n super(code === \"unsupported-platform\"\n ? \"StarLite for DSH requires Windows 11 x64, either directly or through WSL2 x64 on that Windows host. Other platforms and architectures are unsupported; DSH will continue without the companion.\"\n : \"StarLite for DSH is disabled because this DSH build does not provide the required plugin API. Update DSH and StarLite to compatible versions; DSH will continue without the companion.\");\n this.name = \"DshStartupError\";\n }\n}\n", "/** Read-only financial summary. Never includes credentials or session identity. */\nexport type BillingCurrency = \"CNY\" | \"USD\";\nexport interface BillingSnapshot {\n billingVersion: 1;\n checkedAt: number;\n pricingPeriod?: \"peak\" | \"off-peak\" | \"unknown\";\n balance: {\n status: \"ready\" | \"unconfigured\" | \"unavailable\";\n source?: \"deepseek-official\" | \"deepseek\";\n balances: { currency: BillingCurrency; remaining: number }[];\n };\n today: {\n date: string;\n timeZone: \"Asia/Shanghai\";\n partial: boolean;\n pricedRequests: number;\n unpricedRequests: number;\n pricingDate: string;\n estimates: { currency: BillingCurrency; minimum: number; maximum: number }[];\n };\n}\n\nexport function parseBillingSnapshot(value: unknown): BillingSnapshot {\n const root = record(value);\n const balance = record(root.balance);\n const today = record(root.today);\n if (root.billingVersion !== 1 || !timestamp(root.checkedAt)\n || ![\"ready\", \"unconfigured\", \"unavailable\"].includes(String(balance.status))\n || today.timeZone !== \"Asia/Shanghai\" || typeof today.partial !== \"boolean\"\n || !date(today.date) || !date(today.pricingDate)\n || !count(today.pricedRequests) || !count(today.unpricedRequests)) throw new Error(\"invalid billing summary\");\n const balances = currencies(balance.balances).map(entry => {\n if (!signedAmount(entry.remaining)) throw new Error(\"invalid balance\");\n return {currency: entry.currency as BillingCurrency, remaining: entry.remaining as number};\n });\n if ((balance.status === \"ready\") !== (balances.length > 0)) throw new Error(\"invalid balance status\");\n if (root.pricingPeriod !== undefined && ![\"peak\",\"off-peak\",\"unknown\"].includes(String(root.pricingPeriod))) throw new Error(\"invalid pricing period\");\n if (balance.source !== undefined && (![\"deepseek-official\",\"deepseek\"].includes(String(balance.source)) || balance.status === \"unconfigured\")) throw new Error(\"invalid balance source\");\n const estimates = currencies(today.estimates).map(entry => {\n if (!amount(entry.minimum) || !amount(entry.maximum) || (entry.minimum as number) > (entry.maximum as number)) throw new Error(\"invalid estimate\");\n return {currency: entry.currency as BillingCurrency, minimum: entry.minimum as number, maximum: entry.maximum as number};\n });\n return {\n billingVersion: 1, checkedAt: root.checkedAt as number,\n ...(root.pricingPeriod === undefined ? {} : {pricingPeriod: root.pricingPeriod as NonNullable<BillingSnapshot[\"pricingPeriod\"]>}),\n balance: {status: balance.status as BillingSnapshot[\"balance\"][\"status\"], balances,\n ...(balance.source === undefined ? {} : {source: balance.source as NonNullable<BillingSnapshot[\"balance\"][\"source\"]>})},\n today: {\n date: today.date as string, timeZone: \"Asia/Shanghai\", partial: today.partial,\n pricedRequests: today.pricedRequests as number, unpricedRequests: today.unpricedRequests as number,\n pricingDate: today.pricingDate as string, estimates,\n },\n };\n}\n\nfunction record(value: unknown): Record<string, unknown> {\n if (!value || typeof value !== \"object\" || Array.isArray(value)) throw new Error(\"invalid billing object\");\n return value as Record<string, unknown>;\n}\nfunction currencies(value: unknown): Record<string, unknown>[] {\n if (!Array.isArray(value) || value.length > 2) throw new Error(\"invalid currencies\");\n const entries = value.map(record);\n if (entries.some(entry => entry.currency !== \"CNY\" && entry.currency !== \"USD\")\n || new Set(entries.map(entry => entry.currency)).size !== entries.length) throw new Error(\"invalid currencies\");\n return entries;\n}\nfunction amount(value: unknown): boolean { return typeof value === \"number\" && Number.isFinite(value) && value >= 0 && value <= 1_000_000_000; }\nfunction signedAmount(value: unknown): boolean { return typeof value === \"number\" && Number.isFinite(value) && Math.abs(value) <= 1_000_000_000; }\nfunction count(value: unknown): boolean { return Number.isSafeInteger(value) && (value as number) >= 0 && (value as number) <= 1_000_000; }\nfunction timestamp(value: unknown): boolean { return Number.isSafeInteger(value) && (value as number) >= 0; }\nfunction date(value: unknown): boolean {\n return typeof value === \"string\" && /^\\d{4}-\\d{2}-\\d{2}$/u.test(value)\n && Number.isFinite(Date.parse(value)) && new Date(value).toISOString().slice(0,10) === value;\n}\n", "export const MAX_INTERACTION_BYTES = 32_768;\nexport const ACTIVITIES = [\"thinking\", \"responding\", \"command\", \"editing\", \"reading\", \"searching\", \"compacting\", \"tool\", \"waiting\", \"completed\", \"error\", \"blocked\", \"idle\"] as const;\nexport type PetActivity = typeof ACTIVITIES[number];\nexport interface PetSession { ref: string; title: string }\nexport interface PetQuestion { id: string; question: string; header?: string; detail?: string; options: Array<{label: string; description?: string}>; multiSelect: boolean }\nexport type PetQuestionWait = {state:\"timed\";deadline:number;issuedAt:number}|{state:\"continued\"};\nexport interface PetInteraction { interactionVersion: 1; requestId: string; sessionRef: string; questions: PetQuestion[]; wait?:PetQuestionWait }\nexport interface PetAnswer { requestId: string; sessionRef: string; answers: Array<{id: string; selected: string[]; custom?: string}> }\n\nexport function isOpaqueRef(value: unknown): value is string {\n return typeof value === \"string\" && /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/iu.test(value);\n}\nexport function parsePetSession(value: unknown): PetSession {\n const row = record(value);\n if (!isOpaqueRef(row.ref)) throw Error(\"invalid session reference\");\n return {ref:row.ref,title:text(row.title,320)};\n}\nexport function parsePetInteraction(value: unknown): PetInteraction {\n const row = record(value);\n if(row.interactionVersion!==1 || !isOpaqueRef(row.requestId) || !isOpaqueRef(row.sessionRef)) throw Error(\"invalid interaction identity\");\n const ids=new Set<string>();\n const questions=list(row.questions,8).map(raw=>{\n const item=record(raw),id=text(item.id,128); if(ids.has(id)) throw Error(\"duplicate question\"); ids.add(id);\n const labels=new Set<string>();\n const options=item.options===undefined?[]:list(item.options,20,true).map(rawOption=>{\n const option=record(rawOption),label=text(option.label,1024);if(labels.has(label))throw Error(\"duplicate option\");labels.add(label);\n return {label,...(option.description===undefined?{}:{description:text(option.description,2048)})};\n });\n if(item.multiSelect!==undefined && typeof item.multiSelect!==\"boolean\")throw Error(\"invalid multi-select flag\");\n return {id,question:text(item.question,4096),options,multiSelect:item.multiSelect===true,\n ...(item.header===undefined?{}:{header:text(item.header,128)}),...(item.detail===undefined?{}:{detail:text(item.detail,8192)})};\n });\n let wait:PetQuestionWait|undefined;\n if(row.wait!==undefined){\n const value=record(row.wait);\n if(value.state===\"continued\")wait={state:\"continued\"};\n else if(value.state===\"timed\"&&Number.isSafeInteger(value.deadline)&&Number(value.deadline)>=0&&Number(value.deadline)<=8_640_000_000_000_000\n &&Number.isSafeInteger(value.issuedAt)&&Number(value.issuedAt)>=0&&Number(value.issuedAt)<=Number(value.deadline)&&Number(value.deadline)-Number(value.issuedAt)<=2_147_483_647)wait={state:\"timed\",deadline:Number(value.deadline),issuedAt:Number(value.issuedAt)};\n else throw Error(\"invalid question wait\");\n }\n const result:PetInteraction={interactionVersion:1,requestId:row.requestId,sessionRef:row.sessionRef,questions,...(wait?{wait}:{})};\n bound(result);return result;\n}\nexport function parsePetAnswer(value: unknown, request?: PetInteraction): PetAnswer {\n const row=record(value);if(!isOpaqueRef(row.requestId)||!isOpaqueRef(row.sessionRef))throw Error(\"invalid answer identity\");\n const ids=new Set<string>();\n const answers=list(row.answers,8).map(raw=>{\n const item=record(raw),id=text(item.id,128);if(ids.has(id))throw Error(\"duplicate answer\");ids.add(id);\n const selected=list(item.selected,20,true).map(label=>text(label,1024));\n if(new Set(selected).size!==selected.length)throw Error(\"duplicate selection\");\n const custom=item.custom===undefined?undefined:text(item.custom,8192);\n if(!selected.length&&!custom)throw Error(\"empty answer\");\n return {id,selected,...(custom===undefined?{}:{custom})};\n });\n const result:PetAnswer={requestId:row.requestId,sessionRef:row.sessionRef,answers};bound(result);\n if(request){\n if(result.requestId!==request.requestId||result.sessionRef!==request.sessionRef||answers.length!==request.questions.length)throw Error(\"stale or incomplete answer\");\n for(const item of answers){\n const question=request.questions.find(q=>q.id===item.id);\n if(!question||(!question.multiSelect&&(item.selected.length>1||item.selected.length>0&&item.custom!==undefined)))throw Error(\"invalid selection\");\n if(item.selected.some(label=>!question.options.some(option=>option.label===label)))throw Error(\"unknown option\");\n }\n }\n return result;\n}\nfunction record(value:unknown):Record<string,unknown>{if(!value||typeof value!==\"object\"||Array.isArray(value))throw Error(\"invalid interaction object\");return value as Record<string,unknown>}\nfunction text(value:unknown,max:number):string{\n // Preserve exact option labels and multiline answers; render only as text.\n // oxlint-disable-next-line no-control-regex\n if(typeof value!==\"string\"||!value.trim()||value.length>max||/[\\u0000-\\u0008\\u000b\\u000c\\u000e-\\u001f\\u007f-\\u009f]/u.test(value))throw Error(\"invalid interaction text\");return value;\n}\nfunction list(value:unknown,max:number,empty=false):unknown[]{if(!Array.isArray(value)||value.length>max||(!empty&&!value.length))throw Error(\"invalid interaction list\");return value}\nfunction bound(value:unknown):void{if(new TextEncoder().encode(JSON.stringify(value)).byteLength>MAX_INTERACTION_BYTES)throw Error(\"interaction exceeds wire limit\")}\n", "export { parseBillingSnapshot } from \"./billing.js\";\nexport type { BillingCurrency, BillingSnapshot } from \"./billing.js\";\nexport { ACTIVITIES, MAX_INTERACTION_BYTES, isOpaqueRef, parsePetSession, parsePetInteraction, parsePetAnswer } from \"./interaction.js\";\nexport type { PetActivity, PetSession, PetQuestion, PetQuestionWait, PetInteraction, PetAnswer } from \"./interaction.js\";\nimport { ACTIVITIES, isOpaqueRef, parsePetSession, type PetActivity, type PetSession } from \"./interaction.js\";\n\nexport const PET_PROTOCOL_VERSION = 1 as const;\nexport const DEFAULT_BUBBLE_GRAPHEMES = 120;\nexport const MAX_WIRE_BYTES = 4_096;\nconst MAX_SANITIZE_CODE_UNITS = 16_384;\n\nexport const PET_STATES = [\n \"idle\",\n \"thinking\",\n \"working\",\n \"completed\",\n \"error\",\n \"blocked\",\n \"approval\",\n] as const;\n\nexport type PetState = (typeof PET_STATES)[number];\n\nexport const BUBBLE_KINDS = [\n \"reasoning\",\n \"response\",\n \"tool\",\n \"notice\",\n] as const;\n\nexport type BubbleKind = (typeof BUBBLE_KINDS)[number];\n\nexport interface PetBubble {\n kind: BubbleKind;\n text: string;\n}\n\nexport interface PetSnapshot {\n protocolVersion: typeof PET_PROTOCOL_VERSION;\n sequence: number;\n emittedAt: number;\n state: PetState;\n bubble?: PetBubble;\n session?: PetSession;\n activity?: PetActivity;\n inputRequest?: string;\n}\n\nexport interface PetStateSource {\n start(emit: (snapshot: PetSnapshot) => void): Promise<void>;\n stop(): Promise<void>;\n}\n\nexport interface PetConnectionDescriptor {\n readonly port: number;\n readonly token: string;\n readonly instanceId: string;\n}\n\n/** Strictly validates the one deep-link shape emitted by a local DSH Host. */\nexport function parsePetConnectionUrl(rawUrl: string): PetConnectionDescriptor {\n let url: URL;\n try {\n url = new URL(rawUrl);\n } catch {\n throw new Error(\"pet connection URL is invalid\");\n }\n if (\n url.protocol !== \"dsh-inochi2d-pet:\" ||\n url.hostname !== \"connect\" ||\n (url.pathname !== \"\" && url.pathname !== \"/\") ||\n url.username !== \"\" ||\n url.password !== \"\" ||\n url.port !== \"\" ||\n rawUrl.includes(\"#\")\n ) {\n throw new Error(\"pet connection URL target is invalid\");\n }\n const allowed = new Set([\"port\", \"token\", \"instance\"]);\n for (const key of url.searchParams.keys()) {\n if (!allowed.has(key)) throw new Error(\"pet connection URL contains unknown fields\");\n }\n for (const key of allowed) {\n if (url.searchParams.getAll(key).length !== 1) {\n throw new Error(`pet connection URL field ${key} must occur once`);\n }\n }\n const portText = url.searchParams.get(\"port\") ?? \"\";\n const port = Number(portText);\n const token = url.searchParams.get(\"token\") ?? \"\";\n const instanceId = url.searchParams.get(\"instance\") ?? \"\";\n if (!/^[1-9]\\d{0,4}$/.test(portText) || !Number.isInteger(port) || port > 65_535) {\n throw new Error(\"pet connection port is invalid\");\n }\n if (!/^[A-Za-z0-9_-]{43}$/.test(token)) {\n throw new Error(\"pet connection token is invalid\");\n }\n if (!/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/iu.test(instanceId)) {\n throw new Error(\"pet connection instance is invalid\");\n }\n return { port, token, instanceId };\n}\n\n/**\n * Reduces untrusted model-visible content to short, single-line plain text.\n * This function is deliberately shared by both sides of the WSL boundary.\n */\nexport function sanitizeBubbleText(\n input: unknown,\n maximumGraphemes = DEFAULT_BUBBLE_GRAPHEMES,\n): string {\n if (typeof input !== \"string\" || !Number.isSafeInteger(maximumGraphemes)) {\n return \"\";\n }\n if (maximumGraphemes < 1 || maximumGraphemes > 500) {\n return \"\";\n }\n // DSH-visible output can still be very large. Bound work before Unicode\n // normalization/segmentation, while keeping far more context than the 500\n // grapheme public maximum.\n const bounded = input.length > MAX_SANITIZE_CODE_UNITS\n ? input.slice(0, MAX_SANITIZE_CODE_UNITS)\n : input;\n const normalized = bounded\n .normalize(\"NFC\")\n // These ranges intentionally strip terminal and C0/C1 control bytes.\n // oxlint-disable-next-line no-control-regex\n .replace(/\\u001B(?:\\[[0-?]*[ -/]*[@-~]|\\][^\\u0007]*(?:\\u0007|\\u001B\\\\)?|[@-_])/gu, \"\")\n // oxlint-disable-next-line no-control-regex\n .replace(/[\\u0000-\\u0008\\u000B\\u000C\\u000E-\\u001F\\u007F-\\u009F]/gu, \"\")\n .replace(/\\s+/gu, \" \")\n .trim();\n if (normalized.length === 0) {\n return \"\";\n }\n const graphemes = segmentGraphemes(normalized);\n const truncated = graphemes.length > maximumGraphemes || input.length > MAX_SANITIZE_CODE_UNITS;\n return truncated\n ? `${graphemes.slice(0, maximumGraphemes - 1).join(\"\")}\u2026`\n : normalized;\n}\n\nexport function parsePetSnapshot(value: unknown): PetSnapshot {\n if (!isRecord(value)) {\n throw new Error(\"pet snapshot must be an object\");\n }\n if (value.protocolVersion !== PET_PROTOCOL_VERSION) {\n throw new Error(\"unsupported pet protocol version\");\n }\n if (!isNonNegativeSafeInteger(value.sequence)) {\n throw new Error(\"pet snapshot sequence is invalid\");\n }\n if (!isFiniteTimestamp(value.emittedAt)) {\n throw new Error(\"pet snapshot timestamp is invalid\");\n }\n if (!isPetState(value.state)) {\n throw new Error(\"pet snapshot state is invalid\");\n }\n const bubble = value.bubble === undefined ? undefined : parseBubble(value.bubble);\n const session = value.session === undefined ? undefined : parsePetSession(value.session);\n if(value.activity!==undefined && !(ACTIVITIES as readonly unknown[]).includes(value.activity))throw Error(\"invalid activity\");\n if(value.inputRequest!==undefined && (!session||!isOpaqueRef(value.inputRequest)))throw Error(\"invalid input reference\");\n return {\n protocolVersion: PET_PROTOCOL_VERSION,\n sequence: value.sequence,\n emittedAt: value.emittedAt,\n state: value.state,\n ...(bubble ? { bubble } : {}),\n ...(session ? {session}:{}),\n ...(value.activity===undefined?{}:{activity:value.activity as PetActivity}),\n ...(value.inputRequest===undefined?{}:{inputRequest:value.inputRequest as string}),\n };\n}\n\nexport function parsePetSnapshotJson(json: string): PetSnapshot {\n if (new TextEncoder().encode(json).byteLength > MAX_WIRE_BYTES) {\n throw new Error(\"pet snapshot exceeds the wire limit\");\n }\n return parsePetSnapshot(JSON.parse(json) as unknown);\n}\n\nexport function isPetState(value: unknown): value is PetState {\n return typeof value === \"string\" && (PET_STATES as readonly string[]).includes(value);\n}\n\nfunction parseBubble(value: unknown): PetBubble {\n if (!isRecord(value) || !isBubbleKind(value.kind)) {\n throw new Error(\"pet snapshot bubble is invalid\");\n }\n const text = sanitizeBubbleText(value.text);\n if (text.length === 0) {\n throw new Error(\"pet snapshot bubble text is empty or invalid\");\n }\n return { kind: value.kind, text };\n}\n\nfunction isBubbleKind(value: unknown): value is BubbleKind {\n return (\n typeof value === \"string\" &&\n (BUBBLE_KINDS as readonly string[]).includes(value)\n );\n}\n\nfunction isNonNegativeSafeInteger(value: unknown): value is number {\n return Number.isSafeInteger(value) && (value as number) >= 0;\n}\n\nfunction isFiniteTimestamp(value: unknown): value is number {\n return typeof value === \"number\" && Number.isFinite(value) && value >= 0;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction segmentGraphemes(value: string): string[] {\n if (typeof Intl.Segmenter === \"function\") {\n const segmenter = new Intl.Segmenter(undefined, { granularity: \"grapheme\" });\n return [...segmenter.segment(value)].map(({ segment }) => segment);\n }\n return Array.from(value);\n}\n", "import {\n PET_PROTOCOL_VERSION,\n sanitizeBubbleText,\n type BubbleKind,\n type PetBubble,\n type PetSnapshot,\n type PetState,\n type PetStateSource,\n type PetSession,\n type PetActivity,\n} from \"@starcried/starlite-dsh-protocol\";\nimport {randomUUID} from \"node:crypto\";\n\nconst COMPLETED_LATCH_MS = 6_000;\nconst ERROR_LATCH_MS = 8_000;\nconst CONTENT_THROTTLE_MS = 200;\nconst QUESTION_TOOLS = new Set([\"ask_user_question\", \"request_user_input\"]);\n\ntype Timer = ReturnType<typeof setTimeout>;\n\nexport interface AggregatorClock {\n now(): number;\n setTimeout(callback: () => void, delayMs: number): Timer;\n clearTimeout(timer: Timer): void;\n}\n\nexport type AgentRunStatus = \"idle\" | \"running\";\n\nexport interface DshEventLike {\n readonly type: string;\n readonly data?: unknown;\n}\n\ninterface TimedText {\n readonly text: string;\n readonly at: number;\n readonly endsWithWhitespace: boolean;\n}\n\ninterface TimedTool {\n readonly name: string;\n readonly at: number;\n}\n\ninterface SessionState {\n ref: string;\n title: string;\n compacting: boolean;\n inputs: Map<string, {at:number;blocking:boolean}>;\n activityAt: number;\n running: boolean;\n turnActive: boolean;\n blockedAt: number | undefined;\n completedUntil: number | undefined;\n completedAt: number | undefined;\n errorUntil: number | undefined;\n errorAt: number | undefined;\n reasoning: TimedText | undefined;\n response: TimedText | undefined;\n activeTools: Map<string, TimedTool>;\n approvals: Map<string, TimedTool>;\n}\n\ninterface Candidate {\n readonly session?: PetSession;\n readonly activity?: PetActivity;\n readonly inputRequest?: string;\n readonly state: PetState;\n readonly bubble?: PetBubble;\n readonly at: number;\n}\n\nconst systemClock: AggregatorClock = {\n now: () => Date.now(),\n setTimeout: (callback, delayMs) => setTimeout(callback, delayMs),\n clearTimeout: (timer) => clearTimeout(timer),\n};\n\n/**\n * Reduces every session in one DSH process to the deliberately small wire\n * contract. Event payloads are parsed field-by-field so sensitive DSH fields\n * are neither copied nor serialized accidentally.\n */\nexport class DshStateAggregator implements PetStateSource {\n readonly #clock: AggregatorClock;\n readonly #sessions = new Map<unknown, SessionState>();\n #emit: ((snapshot: PetSnapshot) => void) | undefined;\n #sequence = 0;\n #lastEmittedAt = Number.NEGATIVE_INFINITY;\n #lastKey: string | undefined;\n #lastState: PetState | undefined;\n #lastInput: string | undefined;\n #contentTimer: Timer | undefined;\n #deadlineTimer: Timer | undefined;\n\n constructor(clock: AggregatorClock = systemClock) {\n this.#clock = clock;\n }\n\n async start(emit: (snapshot: PetSnapshot) => void): Promise<void> {\n if (this.#emit) {\n throw new Error(\"DSH pet state source is already started\");\n }\n this.#emit = emit;\n this.#publish(true);\n }\n\n async stop(): Promise<void> {\n this.#clearTimers();\n this.#emit = undefined;\n this.#lastKey = undefined;\n this.#lastState = undefined;\n this.#lastInput = undefined;\n }\n\n handleAgentStatus(key: unknown, status: AgentRunStatus): void {\n const session = this.#session(key);\n session.activityAt = this.#clock.now();\n session.running = status === \"running\";\n this.#changed();\n }\n\n sessionInfo(key: unknown, title?: string): PetSession {\n const session=this.#session(key);\n const clean=sanitizeBubbleText(title,80);\n if(clean && clean!==session.title){session.title=clean;this.#changed();}\n return {ref:session.ref,title:session.title};\n }\n sessionKey(ref: string): unknown {return [...this.#sessions].find(([,session])=>session.ref===ref)?.[0]}\n handleInputRequested(key: unknown, requestId: string, blocking=true): void {\n const session=this.#session(key);session.inputs.set(requestId,{at:this.#clock.now(),blocking});this.#changed();\n }\n handleInputResolved(key: unknown, requestId: string): void {\n if(this.#sessions.get(key)?.inputs.delete(requestId))this.#changed();\n }\n\n handleAgentError(key: unknown): void {\n const now = this.#clock.now();\n const session = this.#session(key);\n session.activityAt = now;\n session.errorAt = now;\n session.errorUntil = now + ERROR_LATCH_MS;\n session.running = false;\n session.turnActive = false;\n session.compacting = false;\n session.activeTools.clear();\n session.approvals.clear();\n this.#changed();\n }\n\n handleAssistantStream(key: unknown, rawFrame: unknown): void {\n const frame = asRecord(rawFrame);\n if (frame?.type !== \"start\" && frame?.type !== \"chunk\") return;\n const session = this.#session(key);\n const now = this.#clock.now();\n if (frame.type === \"start\") {\n // DSH 0.1.5 publishes live chunks outside the durable session log. A new\n // attempt must not concatenate its text with a previous step or retry.\n session.reasoning = undefined;\n session.response = undefined;\n } else {\n handleAssistantChunk(session, frame, now);\n }\n session.activityAt = now;\n this.#changed();\n }\n\n handleApprovalRequested(key: unknown, approvalKey: string, toolName: unknown): void {\n const name = safeToolName(typeof toolName === \"string\" ? toolName : undefined);\n if (!approvalKey || !name) return;\n const session = this.#session(key);\n session.activityAt = this.#clock.now();\n session.approvals.set(`live:${approvalKey}`, {\n name,\n at: session.activityAt,\n });\n this.#changed();\n }\n\n handleApprovalResolved(key: unknown, approvalKey: string): void {\n const session = this.#sessions.get(key);\n if (session?.approvals.delete(`live:${approvalKey}`)) {\n session.activityAt = this.#clock.now();\n this.#changed();\n }\n }\n\n handleSessionEvent(key: unknown, event: DshEventLike): void {\n if (!event || typeof event.type !== \"string\") return;\n const data = asRecord(event.data);\n const now = this.#clock.now();\n const session = this.#session(key);\n\n switch (event.type) {\n case \"session/title\": {\n const title=sanitizeBubbleText(data?.title,80);\n if(title)session.title=title;\n this.#changed();return;\n }\n case \"compaction/start\": session.compacting=true; break;\n case \"compaction/end\": session.compacting=false; break;\n case \"turn/start\":\n resetForTurn(session);\n session.turnActive = true;\n break;\n case \"turn/end\":\n this.#handleTurnEnd(session, turnEndKind(data), now);\n break;\n case \"assistant/chunk\":\n handleAssistantChunk(session, data, now);\n break;\n case \"assistant/message\":\n handleAssistantMessage(session, data, now);\n break;\n case \"tool/call\":\n handleToolStarted(session, data, now, \"callId\");\n break;\n case \"tool/result\":\n handleToolSettled(session, data);\n break;\n case \"tool/code-dispatch-start\":\n case \"tool/ptc-dispatch-start\":\n handleToolStarted(session, data, now, \"subCallId\");\n break;\n case \"tool/code-dispatch\":\n case \"tool/ptc-dispatch\":\n removeCall(session, stringField(data, \"subCallId\"));\n break;\n case \"approval/asked\":\n addApproval(session, data, now);\n break;\n case \"approval/decided\":\n removeApproval(session, stringField(data, \"id\"));\n break;\n case \"step/start\":\n case \"step/end\":\n break;\n default:\n return;\n }\n session.activityAt = now;\n this.#changed();\n }\n\n disposeSession(key: unknown): void {\n if (this.#sessions.delete(key)) this.#changed();\n }\n\n #handleTurnEnd(session: SessionState, reason: string | undefined, now: number): void {\n session.compacting = false;\n session.turnActive = false;\n session.running = false;\n session.activeTools.clear();\n session.approvals.clear();\n session.blockedAt = undefined;\n session.completedAt = undefined;\n session.completedUntil = undefined;\n session.errorAt = undefined;\n session.errorUntil = undefined;\n\n if (reason === \"completed\") {\n session.completedAt = now;\n session.completedUntil = now + COMPLETED_LATCH_MS;\n } else if (reason === \"blocked\") {\n session.blockedAt = now;\n } else if (reason === \"error\") {\n session.errorAt = now;\n session.errorUntil = now + ERROR_LATCH_MS;\n }\n }\n\n #session(key: unknown): SessionState {\n let session = this.#sessions.get(key);\n if (!session) {\n session = {\n ref:randomUUID(), title:\"DSH \u4F1A\u8BDD\", compacting:false, inputs:new Map(),\n activityAt: this.#clock.now(),\n running: false,\n turnActive: false,\n blockedAt: undefined,\n completedUntil: undefined,\n completedAt: undefined,\n errorUntil: undefined,\n errorAt: undefined,\n reasoning: undefined,\n response: undefined,\n activeTools: new Map(),\n approvals: new Map(),\n };\n this.#sessions.set(key, session);\n }\n return session;\n }\n\n #changed(): void {\n this.#scheduleDeadline();\n this.#publish(false);\n }\n\n #publish(force: boolean): void {\n if (!this.#emit) return;\n const now = this.#clock.now();\n const candidate = selectCandidate(this.#sessions.values(), now);\n const key = candidateKey(candidate);\n if (!force && key === this.#lastKey) return;\n\n const stateChanged = candidate.state !== this.#lastState || candidate.inputRequest !== this.#lastInput;\n const elapsed = now - this.#lastEmittedAt;\n if (!force && !stateChanged && elapsed < CONTENT_THROTTLE_MS) {\n if (!this.#contentTimer) {\n this.#contentTimer = this.#clock.setTimeout(() => {\n this.#contentTimer = undefined;\n this.#publish(true);\n }, CONTENT_THROTTLE_MS - Math.max(0, elapsed));\n }\n return;\n }\n\n if (this.#contentTimer) {\n this.#clock.clearTimeout(this.#contentTimer);\n this.#contentTimer = undefined;\n }\n const snapshot: PetSnapshot = {\n protocolVersion: PET_PROTOCOL_VERSION,\n sequence: this.#sequence,\n emittedAt: now,\n state: candidate.state,\n ...(candidate.bubble ? { bubble: candidate.bubble } : {}),\n ...(candidate.session?{session:candidate.session}:{}),\n ...(candidate.activity?{activity:candidate.activity}:{}),\n ...(candidate.inputRequest?{inputRequest:candidate.inputRequest}:{}),\n };\n this.#sequence += 1;\n this.#lastEmittedAt = now;\n this.#lastKey = key;\n this.#lastState = candidate.state;\n this.#lastInput = candidate.inputRequest;\n this.#emit(snapshot);\n }\n\n #scheduleDeadline(): void {\n if (this.#deadlineTimer) {\n this.#clock.clearTimeout(this.#deadlineTimer);\n this.#deadlineTimer = undefined;\n }\n const now = this.#clock.now();\n let next = Number.POSITIVE_INFINITY;\n for (const session of this.#sessions.values()) {\n if (session.completedUntil && session.completedUntil > now) {\n next = Math.min(next, session.completedUntil);\n }\n if (session.errorUntil && session.errorUntil > now) {\n next = Math.min(next, session.errorUntil);\n }\n }\n if (Number.isFinite(next)) {\n this.#deadlineTimer = this.#clock.setTimeout(() => {\n this.#deadlineTimer = undefined;\n this.#changed();\n }, Math.max(1, next - now));\n }\n }\n\n #clearTimers(): void {\n if (this.#contentTimer) this.#clock.clearTimeout(this.#contentTimer);\n if (this.#deadlineTimer) this.#clock.clearTimeout(this.#deadlineTimer);\n this.#contentTimer = undefined;\n this.#deadlineTimer = undefined;\n }\n}\n\nfunction resetForTurn(session: SessionState): void {\n session.compacting = false;\n session.blockedAt = undefined;\n session.completedAt = undefined;\n session.completedUntil = undefined;\n session.errorAt = undefined;\n session.errorUntil = undefined;\n session.reasoning = undefined;\n session.response = undefined;\n session.activeTools.clear();\n session.approvals.clear();\n}\n\nfunction handleAssistantChunk(session: SessionState, data: Record<string, unknown> | undefined, now: number): void {\n const chunk = asRecord(data?.chunk);\n const type = stringField(chunk, \"type\");\n const text = stringField(chunk, \"text\");\n if (!text || (type !== \"reasoning-delta\" && type !== \"text-delta\")) return;\n if (type === \"reasoning-delta\") {\n session.reasoning = appendVisible(session.reasoning, text, now);\n } else {\n session.response = appendVisible(session.response, text, now);\n }\n}\n\nfunction handleAssistantMessage(session: SessionState, data: Record<string, unknown> | undefined, now: number): void {\n const message = asRecord(data?.message);\n if (!message || !Array.isArray(message.content)) return;\n let response: TimedText | undefined;\n let reasoning: TimedText | undefined;\n for (const rawBlock of message.content) {\n const block = asRecord(rawBlock);\n const type = stringField(block, \"type\");\n const text = stringField(block, \"text\");\n if (!text) continue;\n if (type === \"text\") response = appendVisible(response, text, now);\n if (type === \"reasoning\") reasoning = appendVisible(reasoning, text, now);\n }\n if (response) session.response = response;\n if (reasoning) session.reasoning = reasoning;\n}\n\nfunction appendVisible(previous: TimedText | undefined, addition: string, now: number): TimedText | undefined {\n // Wire text is trimmed. Keep one separator bit in memory so a space-only\n // chunk or a chunk ending in whitespace does not join the next word.\n const separator = previous?.endsWithWhitespace ? \" \" : \"\";\n const text = sanitizeBubbleText(`${previous?.text ?? \"\"}${separator}${addition}`);\n return text ? { text, at: now, endsWithWhitespace: /\\s$/u.test(addition) } : previous;\n}\n\nfunction handleToolStarted(\n session: SessionState,\n data: Record<string, unknown> | undefined,\n now: number,\n idField: string,\n): void {\n const id = stringField(data, idField);\n const name = safeToolName(stringField(data, \"name\"));\n if (!id || !name) return;\n const item = { name, at: now };\n if (QUESTION_TOOLS.has(name)) session.approvals.set(`tool:${id}`, item);\n else session.activeTools.set(id, item);\n}\n\nfunction handleToolSettled(session: SessionState, data: Record<string, unknown> | undefined): void {\n const direct = stringField(data, \"callId\");\n const message = asRecord(data?.message);\n const source = asRecord(message?.source);\n removeCall(session, direct ?? stringField(source, \"callId\"));\n}\n\nfunction removeCall(session: SessionState, id: string | undefined): void {\n if (!id) return;\n session.activeTools.delete(id);\n session.approvals.delete(`tool:${id}`);\n}\n\nfunction addApproval(session: SessionState, data: Record<string, unknown> | undefined, now: number): void {\n const id = stringField(data, \"id\");\n const name = safeToolName(stringField(data, \"toolName\"));\n if (id && name) session.approvals.set(`approval:${id}`, { name, at: now });\n}\n\nfunction removeApproval(session: SessionState, id: string | undefined): void {\n if (id) session.approvals.delete(`approval:${id}`);\n}\n\nfunction selectCandidate(sessions: Iterable<SessionState>, now: number): Candidate {\n const all = [...sessions];\n let error: Candidate | undefined;\n let approval: Candidate | undefined;\n let blocked: Candidate | undefined;\n let working: Candidate | undefined;\n let thinking: Candidate | undefined;\n let completed: Candidate | undefined;\n\n for (const session of all) {\n const wrap=(candidate:Candidate,activity:PetActivity):Candidate=>({...candidate,session:{ref:session.ref,title:session.title},activity});\n if (session.errorUntil && session.errorUntil > now) {\n error = newest(error, wrap(notice(\"error\", \"\u53D1\u751F\u9519\u8BEF\uFF0C\u8BF7\u67E5\u770B DSH\", session.errorAt ?? now),\"error\"));\n }\n const input=[...session.inputs].filter(([,item])=>item.blocking).at(-1);\n if(input)approval=newest(approval,{...wrap(notice(\"approval\",\"\u7B49\u5F85\u8F93\u5165\u2026\",input[1].at),\"waiting\"),inputRequest:input[0]});\n const currentApproval = newestMapValue(session.approvals);\n if (currentApproval) {\n if(!input)approval = newest(approval, wrap(notice(\"approval\", QUESTION_TOOLS.has(currentApproval.name)?\"\u7B49\u5F85\u8F93\u5165\u2026\":`\u7B49\u5F85\u6279\u51C6\uFF1A${currentApproval.name}`, currentApproval.at),\"waiting\"));\n }\n if (session.blockedAt !== undefined) {\n blocked = newest(blocked, wrap(notice(\"blocked\", \"\u4EFB\u52A1\u88AB\u963B\u585E\uFF0C\u8BF7\u67E5\u770B DSH\", session.blockedAt),\"blocked\"));\n }\n const currentTool = newestMapValue(session.activeTools);\n if (currentTool) {\n working = newest(working, wrap({\n state: \"working\",\n bubble: bubble(\"tool\", `\u6B63\u5728\u4F7F\u7528 ${currentTool.name}\u2026`),\n at: currentTool.at,\n },toolActivity(currentTool.name)));\n }\n if(session.compacting)working=newest(working,wrap(notice(\"working\",\"\u6B63\u5728\u538B\u7F29\u4E0A\u4E0B\u6587\u2026\",session.activityAt),\"compacting\"));\n if (session.running || session.turnActive) {\n const visible = latestVisible(session);\n thinking = newest(thinking, wrap({\n state: \"thinking\",\n bubble: visible\n ? bubble(visible === session.response ? \"response\" : \"reasoning\", visible.text)\n : bubble(\"notice\", \"\u601D\u8003\u4E2D\u2026\"),\n at: visible?.at ?? session.activityAt,\n },visible===session.response && visible?\"responding\":\"thinking\"));\n }\n if (session.completedUntil && session.completedUntil > now) {\n completed = newest(completed, wrap({\n state: \"completed\",\n bubble: session.response\n ? bubble(\"response\", session.response.text)\n : bubble(\"notice\", \"\u4EFB\u52A1\u5DF2\u5B8C\u6210\"),\n at: session.completedAt ?? now,\n },\"completed\"));\n }\n }\n\n const selected=error ?? approval ?? blocked ?? working ?? thinking ?? completed;\n // Continued questions no longer block the Host's turn. Keep its real state\n // and animation, and expose an answer form only for the selected session.\n // Foreground questions and tool permissions always take precedence.\n if(selected){\n if(selected.state===\"approval\"||selected.state===\"error\")return selected;\n const session=all.find(item=>item.ref===selected.session?.ref);\n const late=session&&[...session.inputs].filter(([,item])=>!item.blocking).at(-1);\n return late?{...selected,inputRequest:late[0]}:selected;\n }\n const last=all.sort((a,b)=>b.activityAt-a.activityAt)[0];\n const late=last&&[...last.inputs].filter(([,item])=>!item.blocking).at(-1);\n return {state:\"idle\",at:now,activity:\"idle\",...(last?{session:{ref:last.ref,title:last.title}}:{}),...(late?{inputRequest:late[0]}:{})};\n}\n\nfunction notice(state: PetState, text: string, at: number): Candidate {\n return { state, bubble: bubble(\"notice\", text), at };\n}\n\nfunction bubble(kind: BubbleKind, rawText: string): PetBubble {\n const text = sanitizeBubbleText(rawText);\n return { kind, text };\n}\n\nfunction latestVisible(session: SessionState): TimedText | undefined {\n if (!session.reasoning) return session.response;\n if (!session.response) return session.reasoning;\n return session.response.at >= session.reasoning.at ? session.response : session.reasoning;\n}\n\nfunction newest(current: Candidate | undefined, next: Candidate): Candidate {\n return !current || next.at >= current.at ? next : current;\n}\n\nfunction newestMapValue(values: Map<string, TimedTool>): TimedTool | undefined {\n let result: TimedTool | undefined;\n for (const item of values.values()) {\n if (!result || item.at >= result.at) result = item;\n }\n return result;\n}\n\nfunction candidateKey(candidate: Candidate): string {\n return JSON.stringify([candidate.state,candidate.bubble,candidate.session,candidate.activity,candidate.inputRequest]);\n}\n\nexport function toolActivity(name: string): PetActivity {\n if(/^(?:bash|pwsh|shell|run_command|execute_command)$/iu.test(name))return \"command\";\n if(/^(?:edit|write)$/iu.test(name)||/(?:str_replace|edit_file|write_file|apply_patch|create_file)/iu.test(name))return \"editing\";\n if(/^(?:read|read_image|ls)$/iu.test(name)||/(?:read_file|list_files|fs_read|fs_list)/iu.test(name))return \"reading\";\n if(/^glob$/iu.test(name)||/(?:search|grep|find_files)/iu.test(name))return \"searching\";\n return \"tool\";\n}\n\nfunction safeToolName(value: string | undefined): string | undefined {\n if (!value) return undefined;\n const clean = sanitizeBubbleText(value, 60);\n return clean || undefined;\n}\n\nfunction asRecord(value: unknown): Record<string, unknown> | undefined {\n return typeof value === \"object\" && value !== null && !Array.isArray(value)\n ? (value as Record<string, unknown>)\n : undefined;\n}\n\nfunction stringField(value: Record<string, unknown> | undefined, key: string): string | undefined {\n const field = value?.[key];\n return typeof field === \"string\" ? field : undefined;\n}\n\nfunction turnEndKind(data: Record<string, unknown> | undefined): string | undefined {\n return stringField(asRecord(data?.reason), \"kind\");\n}\n", "import { createHash, randomUUID } from \"node:crypto\";\r\nimport { constants } from \"node:fs\";\r\nimport { lstat, mkdir, mkdtemp, open, realpath, rename, rm, writeFile } from \"node:fs/promises\";\r\nimport { isAbsolute, join, parse, resolve, sep } from \"node:path\";\r\n\r\nconst LIMIT = 64 * 1024 * 1024;\r\nconst NOTICE_LIMIT = 8 * 1024 * 1024;\r\nconst EXECUTABLE = \"dsh-inochi2d-pet.exe\";\r\nconst NOTICES = [\"LICENSE\", \"THIRD_PARTY_LICENSES.txt\"] as const;\r\nconst RELEASE_REDIRECT_LIMIT = 3;\r\nconst REDIRECT_STATUSES = new Set([301, 302, 303, 307, 308]);\r\n\r\nexport interface CompanionManifest {\r\n readonly schemaVersion: 2;\r\n readonly version: string;\r\n readonly platform: \"windows-x64\";\r\n readonly executable: {readonly file: typeof EXECUTABLE; readonly sha256: string; readonly bytes: number};\r\n readonly defaultModel: {\r\n readonly title: string;\r\n readonly url: string;\r\n readonly sha256: string;\r\n readonly bytes: number;\r\n readonly localTest?: true;\r\n };\r\n}\r\n\r\nexport interface PrepareCompanionOptions {\r\n readonly assetDirectory: string;\r\n readonly cacheDirectory: string;\r\n readonly expectedVersion: string;\r\n readonly signal?: AbortSignal;\r\n readonly fetch?: typeof fetch;\r\n readonly acquireDefaultModel?: boolean;\r\n}\r\n\r\nexport interface PreparedCompanion {\r\n readonly executable: string;\r\n readonly defaultModelReady: boolean;\r\n}\r\n\r\n/** Stage the program separately from the independently licensed VRM model. */\r\nexport async function prepareEmbeddedCompanion(options: PrepareCompanionOptions): Promise<PreparedCompanion> {\r\n options.signal?.throwIfAborted();\r\n const assets = await realpath(options.assetDirectory);\r\n const manifest = parseCompanionManifest(await ordinaryFile(assets, \"manifest.json\", 16_384), options.expectedVersion);\r\n const executable = await ordinaryFile(assets, EXECUTABLE, LIMIT);\r\n assertDigest(executable, manifest.executable);\r\n assertWindowsGuiExecutable(executable);\r\n const notices = await Promise.all(NOTICES.map(name => ordinaryFile(assets, name, NOTICE_LIMIT)));\r\n options.signal?.throwIfAborted();\r\n await ensureSafeDirectory(options.cacheDirectory);\r\n const cacheKey = createHash(\"sha256\").update(`${manifest.executable.sha256}:${manifest.defaultModel.sha256}`).digest(\"hex\");\r\n const directory = join(options.cacheDirectory, `${manifest.version}-${cacheKey}`);\r\n const descriptor = Buffer.from(JSON.stringify({\r\n schemaVersion: 2,\r\n title: manifest.defaultModel.title,\r\n sha256: manifest.defaultModel.sha256,\r\n bytes: manifest.defaultModel.bytes,\r\n file: \"default-model.vrm\",\r\n }));\r\n if (!await exists(directory)) {\r\n const temporary = await mkdtemp(join(options.cacheDirectory, \".stage-\"));\r\n try {\r\n await writeFile(join(temporary, EXECUTABLE), executable, {flag: \"wx\", mode: 0o700});\r\n for (const [index, name] of NOTICES.entries()) {\r\n await writeFile(join(temporary, name), notices[index]!, {flag: \"wx\", mode: 0o600});\r\n }\r\n // Keep the descriptor even when the external download fails, so the UI can explain/retry.\r\n await writeFile(join(temporary, \"default-model.json\"), descriptor, {flag: \"wx\", mode: 0o600});\r\n options.signal?.throwIfAborted();\r\n try { await rename(temporary, directory); }\r\n catch (error) { if (!await exists(directory)) throw error; }\r\n } finally { await rm(temporary, {recursive: true, force: true}); }\r\n }\r\n await assertSafeDirectory(directory);\r\n assertEqual(await ordinaryFile(directory, EXECUTABLE, LIMIT), executable);\r\n for (const [index, name] of NOTICES.entries()) assertEqual(await ordinaryFile(directory, name, NOTICE_LIMIT), notices[index]!);\r\n assertEqual(await ordinaryFile(directory, \"default-model.json\", 16_384), descriptor);\r\n options.signal?.throwIfAborted();\r\n if (options.acquireDefaultModel === false) return {executable: join(directory, EXECUTABLE), defaultModelReady: false};\r\n let defaultModelReady = false;\r\n try {\r\n const modelFile = join(directory, \"default-model.vrm\");\r\n if (await exists(modelFile)) {\r\n const cached = await ordinaryFile(directory, \"default-model.vrm\", LIMIT);\r\n try { assertDigest(cached, manifest.defaultModel); defaultModelReady = true; }\r\n catch { /* Only a bounded ordinary cache file may be repaired from the pinned source. */ }\r\n }\r\n if (!defaultModelReady) await downloadCharacter(manifest.defaultModel, directory, options.fetch ?? fetch, options.signal);\r\n defaultModelReady = true;\r\n } catch {\r\n options.signal?.throwIfAborted();\r\n // The desktop still starts and presents the explicit missing/invalid-character state.\r\n }\r\n options.signal?.throwIfAborted();\r\n return {executable: join(directory, EXECUTABLE), defaultModelReady};\r\n}\r\n\r\nexport function parseCompanionManifest(bytes: Uint8Array, expectedVersion: string): CompanionManifest {\r\n const value: unknown = JSON.parse(Buffer.from(bytes).toString(\"utf8\"));\r\n if (!record(value) || !keys(value, [\"schemaVersion\", \"version\", \"platform\", \"executable\", \"defaultModel\"])\r\n || value.schemaVersion !== 2 || value.platform !== \"windows-x64\"\r\n || typeof value.version !== \"string\" || value.version.length > 64 || !/^\\d+\\.\\d+\\.\\d+(?:-[a-zA-Z0-9.-]+)?$/u.test(value.version)\r\n || value.version !== expectedVersion || !record(value.executable) || !keys(value.executable, [\"file\", \"sha256\", \"bytes\"])\r\n || value.executable.file !== EXECUTABLE || !digest(value.executable)\r\n || !record(value.defaultModel) || !keys(value.defaultModel, [\"title\", \"url\", \"sha256\", \"bytes\"], [\"localTest\"])\r\n || typeof value.defaultModel.title !== \"string\" || value.defaultModel.title.length < 1 || value.defaultModel.title.length > 128\r\n || /[\\p{Cc}]/u.test(value.defaultModel.title) || !digest(value.defaultModel)\r\n || (value.defaultModel.localTest !== undefined && value.defaultModel.localTest !== true)\r\n || typeof value.defaultModel.url !== \"string\" || value.defaultModel.url.length > 2_048\r\n || /[\\p{Cc}\\\\]/u.test(value.defaultModel.url)) {\r\n throw new Error(\"invalid embedded companion manifest\");\r\n }\r\n const url = new URL(value.defaultModel.url);\r\n if (!url.pathname.toLowerCase().endsWith(\".vrm\") || url.username || url.password || url.hash || (url.protocol !== \"https:\"\r\n && !(value.defaultModel.localTest === true && url.protocol === \"http:\" && url.hostname === \"127.0.0.1\"\r\n && /^http:\\/\\/127\\.0\\.0\\.1(?::\\d+)?\\//u.test(value.defaultModel.url)))) {\r\n throw new Error(\"invalid starter character download source\");\r\n }\r\n return value as unknown as CompanionManifest;\r\n}\r\n\r\nexport function assertWindowsGuiExecutable(bytes: Buffer): void {\r\n if (bytes.length < 64 || bytes.readUInt16LE(0) !== 0x5a4d) throw new Error(\"invalid embedded Windows executable\");\r\n const pe = bytes.readUInt32LE(0x3c);\r\n if (pe < 64 || pe + 94 > bytes.length || bytes.readUInt32LE(pe) !== 0x00004550\r\n || bytes.readUInt16LE(pe + 4) !== 0x8664 || bytes.readUInt16LE(pe + 20) < 70\r\n || pe + 24 + bytes.readUInt16LE(pe + 20) > bytes.length || bytes.readUInt16LE(pe + 24) !== 0x20b\r\n || bytes.readUInt16LE(pe + 24 + 68) !== 2) throw new Error(\"embedded executable must be Windows x64 GUI PE32+\");\r\n}\r\n\r\nasync function downloadCharacter(source: CompanionManifest[\"defaultModel\"], directory: string, request: typeof fetch, signal?: AbortSignal): Promise<void> {\r\n const timeout = AbortSignal.timeout(30_000);\r\n const downloadSignal = signal ? AbortSignal.any([signal, timeout]) : timeout;\r\n const response = await characterResponse(source.url, request, downloadSignal);\r\n if (!response.ok || response.redirected || !response.body) {\r\n await response.body?.cancel();\r\n throw new Error(\"starter character download failed\");\r\n }\r\n const length = response.headers.get(\"content-length\");\r\n if (length !== null && (!/^\\d+$/u.test(length) || Number(length) !== source.bytes)) {\r\n await response.body.cancel();\r\n throw new Error(\"starter character download size mismatch\");\r\n }\r\n const temporary = join(directory, `.download-${randomUUID()}`);\r\n let file: Awaited<ReturnType<typeof open>> | undefined;\r\n const reader = response.body.getReader();\r\n try {\r\n file = await open(temporary, \"wx\", 0o600);\r\n const hash = createHash(\"sha256\");\r\n let bytes = 0;\r\n while (true) {\r\n downloadSignal.throwIfAborted();\r\n const item = await reader.read();\r\n if (item.done) break;\r\n bytes += item.value.byteLength;\r\n if (bytes > source.bytes || bytes > LIMIT) throw new Error(\"starter character download exceeds its size limit\");\r\n hash.update(item.value);\r\n await file.writeFile(item.value);\r\n }\r\n if (bytes !== source.bytes || hash.digest(\"hex\") !== source.sha256) throw new Error(\"starter character download integrity mismatch\");\r\n await file.sync();\r\n await file.close(); file = undefined;\r\n downloadSignal.throwIfAborted();\r\n const modelFile = join(directory, \"default-model.vrm\");\r\n if (await exists(modelFile)) {\r\n const cached = await ordinaryFile(directory, \"default-model.vrm\", LIMIT);\r\n try { assertDigest(cached, source); return; }\r\n catch { /* Replace only this validated ordinary cache file, after the replacement has passed its digest. */ }\r\n }\r\n await rename(temporary, modelFile);\r\n } finally {\r\n await reader.cancel().catch(() => undefined);\r\n reader.releaseLock();\r\n await file?.close();\r\n await rm(temporary, {force: true});\r\n }\r\n}\r\n\r\nasync function characterResponse(source: string, request: typeof fetch, signal: AbortSignal): Promise<Response> {\r\n const initial = new URL(source);\r\n const githubRelease = initial.protocol === \"https:\" && initial.hostname === \"github.com\"\r\n && !initial.port && !initial.username && !initial.password && !initial.search && !initial.hash\r\n && /^\\/[a-zA-Z0-9-]+\\/[a-zA-Z0-9_.-]+\\/releases\\/download\\/[^/]+\\/[^/]+\\.vrm$/u.test(initial.pathname);\r\n let current = source;\r\n const visited = new Set([current]);\r\n for (let redirects = 0; ; ++redirects) {\r\n signal.throwIfAborted();\r\n // No cookies, Authorization, or other request headers cross origins. Signed\r\n // asset URLs exist only for this download and never enter cached metadata.\r\n const response = await request(current, {method: \"GET\", redirect: githubRelease ? \"manual\" : \"error\", credentials: \"omit\", signal});\r\n if (!REDIRECT_STATUSES.has(response.status)) return response;\r\n await response.body?.cancel();\r\n const location = response.headers.get(\"location\");\r\n if (!githubRelease || redirects >= RELEASE_REDIRECT_LIMIT || !location) throw new Error(\"starter character redirect rejected\");\r\n let next: URL;\r\n try { next = new URL(location, current); }\r\n catch { throw new Error(\"starter character redirect rejected\"); }\r\n if (next.protocol !== \"https:\" || next.hostname !== \"release-assets.githubusercontent.com\"\r\n || next.port || next.username || next.password || next.hash || visited.has(next.href)) {\r\n throw new Error(\"starter character redirect rejected\");\r\n }\r\n current = next.href;\r\n visited.add(current);\r\n }\r\n}\r\n\r\nasync function ordinaryFile(directory: string, name: string, limit: number): Promise<Buffer> {\r\n await assertSafeDirectory(directory);\r\n const path = join(directory, name), info = await lstat(path);\r\n if (!info.isFile() || info.isSymbolicLink() || info.size < 1 || info.size > limit) throw new Error(\"unsafe companion file\");\r\n const file = await open(path, constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0));\r\n try {\r\n const opened = await file.stat();\r\n if (!opened.isFile() || opened.size !== info.size || opened.size > limit) throw new Error(\"unsafe companion file\");\r\n const bytes = Buffer.alloc(info.size);\r\n let offset = 0;\r\n while (offset < bytes.length) {\r\n const read = await file.read(bytes, offset, bytes.length - offset, offset);\r\n if (read.bytesRead === 0) throw new Error(\"companion file changed while reading\");\r\n offset += read.bytesRead;\r\n }\r\n const extra = await file.read(Buffer.alloc(1), 0, 1, bytes.length);\r\n if (extra.bytesRead !== 0) throw new Error(\"companion file changed while reading\");\r\n return bytes;\r\n } finally { await file.close(); }\r\n}\r\n\r\nasync function ensureSafeDirectory(directory: string): Promise<void> {\r\n if (!isAbsolute(directory)) throw new Error(\"companion cache must be an absolute local directory\");\r\n const absolute = resolve(directory), root = parse(absolute).root;\r\n let current = root;\r\n for (const component of absolute.slice(root.length).split(sep).filter(Boolean)) {\r\n current = join(current, component);\r\n try { await mkdir(current, {mode: 0o700}); }\r\n catch (error) { if (!record(error) || error.code !== \"EEXIST\") throw error; }\r\n await assertSafeDirectory(current);\r\n }\r\n}\r\n\r\nasync function assertSafeDirectory(directory: string): Promise<void> {\r\n const info = await lstat(directory);\r\n if (!info.isDirectory() || info.isSymbolicLink()) throw new Error(\"unsafe companion directory\");\r\n}\r\n\r\nasync function exists(path: string): Promise<boolean> {\r\n try { await lstat(path); return true; }\r\n catch (error) { if (record(error) && error.code === \"ENOENT\") return false; throw error; }\r\n}\r\n\r\nfunction assertDigest(bytes: Buffer, expected: {readonly bytes: number; readonly sha256: string}): void {\r\n if (bytes.length !== expected.bytes || createHash(\"sha256\").update(bytes).digest(\"hex\") !== expected.sha256) throw new Error(\"companion file integrity mismatch\");\r\n}\r\nfunction assertEqual(actual: Buffer, expected: Buffer): void {\r\n if (!actual.equals(expected)) throw new Error(\"cached companion file integrity mismatch\");\r\n}\r\nfunction record(value: unknown): value is Record<string, unknown> { return typeof value === \"object\" && value !== null && !Array.isArray(value); }\r\nfunction keys(value: Record<string, unknown>, required: readonly string[], optional: readonly string[] = []): boolean {\r\n return required.every(key => Object.hasOwn(value, key)) && Object.keys(value).every(key => required.includes(key) || optional.includes(key));\r\n}\r\nfunction digest(value: Record<string, unknown>): boolean {\r\n return typeof value.sha256 === \"string\" && /^[a-f0-9]{64}$/u.test(value.sha256)\r\n && typeof value.bytes === \"number\" && Number.isSafeInteger(value.bytes) && value.bytes >= 1 && value.bytes <= LIMIT;\r\n}\r\n", "import { randomUUID } from \"node:crypto\";\r\nimport { release } from \"node:os\";\r\nimport { posix, win32 } from \"node:path\";\r\nimport { execFile, spawn } from \"node:child_process\";\r\nimport { lstat, open, readFile } from \"node:fs/promises\";\r\nimport { constants } from \"node:fs\";\r\nimport { fileURLToPath } from \"node:url\";\r\nimport { prepareEmbeddedCompanion } from \"./bootstrap.js\";\r\n\r\nexport interface PetConnection {\r\n readonly port: number;\r\n readonly token: string;\r\n readonly instanceId?: string;\r\n}\r\n\r\nexport interface LaunchOptions {\r\n readonly signal?: AbortSignal;\r\n readonly onDefaultModelFailure?: () => void;\r\n readonly host?: WindowsCompanionHost;\r\n}\r\n\r\nexport type WindowsCompanionHost = \"wsl\" | \"windows\";\r\n\r\nexport function isWslEnvironment(environment: NodeJS.ProcessEnv = process.env): boolean {\r\n return Boolean(environment.WSL_INTEROP || environment.WSL_DISTRO_NAME)\r\n || release().toLowerCase().includes(\"microsoft\");\r\n}\r\n\r\nexport function detectCompanionHost(\r\n platform: NodeJS.Platform = process.platform,\r\n architecture: string = process.arch,\r\n inWsl: boolean = isWslEnvironment(),\r\n): WindowsCompanionHost | undefined {\r\n if (architecture !== \"x64\") return undefined;\r\n if (platform === \"win32\") return \"windows\";\r\n return platform === \"linux\" && inWsl ? \"wsl\" : undefined;\r\n}\r\n\r\n/** Child processes need OS launch variables, never provider or Electron/Node options. */\r\nexport function companionEnvironment(source: NodeJS.ProcessEnv): NodeJS.ProcessEnv {\r\n const result: NodeJS.ProcessEnv = {};\r\n for (const name of [\"PATH\", \"Path\", \"HOME\", \"LANG\", \"LC_ALL\", \"SYSTEMROOT\", \"SystemRoot\", \"WINDIR\", \"TEMP\", \"TMP\", \"LOCALAPPDATA\", \"APPDATA\", \"USERPROFILE\", \"WSL_INTEROP\", \"WSL_DISTRO_NAME\"]) {\r\n if (source[name] !== undefined) result[name] = source[name];\r\n }\r\n return result;\r\n}\r\n\r\n/** Start the embedded Windows companion; no installer, registry lookup or elevation. */\r\nexport async function launchWindowsPet(connection: PetConnection, options: LaunchOptions = {}): Promise<void> {\r\n const url = buildConnectionUrl(connection);\r\n const host = options.host ?? detectCompanionHost();\r\n if (!host) throw new Error(\"StarLite requires a Windows x64 or WSL2 x64 DSH Host\");\r\n const paths = host === \"windows\" ? win32 : posix;\r\n options.signal?.throwIfAborted();\r\n let executable: string;\r\n const override = process.env.DSH_INOCHI2D_PET_EXE;\r\n if (override !== undefined) {\r\n const local = validateExecutableOverride(override);\r\n executable = host === \"windows\" ? local : (await execFileText(\"wslpath\", [\"-u\", local], options.signal)).trim();\r\n } else {\r\n const windowsDirectories = (await execFileText(\"powershell.exe\", [\r\n \"-NoLogo\", \"-NoProfile\", \"-NonInteractive\", \"-Command\",\r\n \"[Console]::OutputEncoding = [System.Text.UTF8Encoding]::new(); [Environment]::GetFolderPath('LocalApplicationData'); [Environment]::GetFolderPath('ApplicationData')\",\r\n ], options.signal)).trim().split(/\\r?\\n/u);\r\n if (windowsDirectories.length !== 2 || windowsDirectories.some(path => !validWindowsDirectory(path))) throw new Error(\"Windows application directories are unavailable\");\r\n const windowsCache = windowsDirectories[0]!;\r\n const localCache = host === \"windows\" ? windowsCache : (await execFileText(\"wslpath\", [\"-u\", windowsCache], options.signal)).trim();\r\n const roaming = host === \"windows\" ? windowsDirectories[1]! : (await execFileText(\"wslpath\", [\"-u\", windowsDirectories[1]!], options.signal)).trim();\r\n if (host === \"wsl\" && (!validPosixDirectory(localCache) || !validPosixDirectory(roaming))) throw new Error(\"invalid Windows application directories\");\r\n const acquireDefaultModel = await shouldAcquireDefaultModel(paths.join(roaming, \"com.starcried.dsh-inochi2d-pet\", \"settings.json\"));\r\n const metadata: unknown = JSON.parse(await readFile(new URL(\"../package.json\", import.meta.url), \"utf8\"));\r\n if (typeof metadata !== \"object\" || metadata === null || !(\"version\" in metadata) || typeof metadata.version !== \"string\") {\r\n throw new Error(\"invalid StarLite plugin package version\");\r\n }\r\n const prepared = await prepareEmbeddedCompanion({\r\n assetDirectory: fileURLToPath(new URL(\"../assets/windows-x64/\", import.meta.url)),\r\n cacheDirectory: paths.join(localCache, \"StarCried\", \"StarLite\", \"companions\"),\r\n expectedVersion: metadata.version,\r\n acquireDefaultModel,\r\n ...(options.signal ? {signal: options.signal} : {}),\r\n });\r\n executable = prepared.executable;\r\n if (acquireDefaultModel && !prepared.defaultModelReady) options.onDefaultModelFailure?.();\r\n }\r\n if ((host === \"windows\" && validateExecutableOverride(executable) !== executable)\r\n || (host === \"wsl\" && (!validPosixDirectory(executable) || posix.basename(executable).toLowerCase() !== \"dsh-inochi2d-pet.exe\"))) {\r\n throw new Error(\"StarLite for DSH executable is invalid\");\r\n }\r\n options.signal?.throwIfAborted();\r\n\r\n return new Promise((resolve, reject) => {\r\n const child = spawn(executable, [url], {\r\n // Windows' non-detached child job ends with the Host; the pet must retain\r\n // its own reconnect lease and any queued successor after that exit.\r\n detached: host === \"windows\",\r\n shell: false,\r\n stdio: \"ignore\",\r\n windowsHide: true,\r\n env: companionEnvironment(process.env),\r\n });\r\n let settled = false;\r\n const finish = (error?: Error) => {\r\n if (settled) return;\r\n settled = true;\r\n if (error) reject(error);\r\n else resolve();\r\n };\r\n child.once(\"error\", (error) => finish(error));\r\n child.once(\"spawn\", () => finish());\r\n child.unref();\r\n });\r\n}\r\n\r\nfunction validWindowsDirectory(path: string): boolean {\r\n return /^[a-z]:[\\\\/]/iu.test(path) && win32.isAbsolute(path) && !/[\\p{Cc}\"<>|]/u.test(path)\r\n && !path.split(/[\\\\/]/u).some(part => part === \".\" || part === \"..\");\r\n}\r\n\r\nfunction validPosixDirectory(path: string): boolean {\r\n return posix.isAbsolute(path) && !/[\\p{Cc}]/u.test(path) && !path.split(\"/\").some(part => part === \".\" || part === \"..\");\r\n}\r\n\r\n/** Read only the selected-model policy; never transfer or log companion settings. */\r\nexport async function shouldAcquireDefaultModel(path: string): Promise<boolean> {\r\n const maximum = 4 * 1024 * 1024;\r\n try {\r\n const info = await lstat(path);\r\n if (!info.isFile() || info.isSymbolicLink() || info.size < 1 || info.size > maximum) return false;\r\n const file = await open(path, constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0));\r\n try {\r\n const opened = await file.stat();\r\n if (!opened.isFile() || opened.size !== info.size) return false;\r\n const bytes = Buffer.alloc(info.size + 1);\r\n const result = await file.read(bytes, 0, bytes.length, 0);\r\n if (result.bytesRead !== info.size) return false;\r\n const value: unknown = JSON.parse(bytes.subarray(0, result.bytesRead).toString(\"utf8\"));\r\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) return false;\r\n if (!(\"schemaVersion\" in value)) return false;\r\n if (value.schemaVersion === 4) {\r\n return \"activeModelId\" in value && value.activeModelId === \"weiguang-default\";\r\n }\r\n if (![1, 2, 3].includes(value.schemaVersion as number)) return false;\r\n const preferences = \"preferences\" in value ? value.preferences : undefined;\r\n if (typeof preferences === \"object\" && preferences !== null && \"modelEnabled\" in preferences) return preferences.modelEnabled === true;\r\n return true;\r\n } finally { await file.close(); }\r\n } catch (error) {\r\n return typeof error === \"object\" && error !== null && \"code\" in error && error.code === \"ENOENT\";\r\n }\r\n}\r\n\r\n/** A developer may explicitly select a local build, without changing registrations. */\r\nexport function validateExecutableOverride(override: string): string {\r\n if (!win32.isAbsolute(override)\r\n || !/^[a-z]:[\\\\/]/iu.test(override)\r\n || /[\\p{Cc}\"<>|]/u.test(override) || override.split(/[\\\\/]/u).some(part => part === \".\" || part === \"..\")\r\n || win32.basename(override).toLowerCase() !== \"dsh-inochi2d-pet.exe\") {\r\n throw new Error(\"DSH_INOCHI2D_PET_EXE must name a local Windows companion executable\");\r\n }\r\n return override;\r\n}\r\n\r\nexport function buildConnectionUrl(connection: PetConnection): string {\r\n if (!Number.isInteger(connection.port) || connection.port < 1 || connection.port > 65_535) {\r\n throw new Error(\"invalid DSH pet status port\");\r\n }\r\n if (!/^[A-Za-z0-9_-]{43}$/.test(connection.token)) {\r\n throw new Error(\"invalid DSH pet connection token\");\r\n }\r\n const instanceId = connection.instanceId ?? randomUUID();\r\n if (!/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/iu.test(instanceId)) {\r\n throw new Error(\"invalid DSH pet instance id\");\r\n }\r\n const url = new URL(\"dsh-inochi2d-pet://connect\");\r\n url.searchParams.set(\"port\", String(connection.port));\r\n url.searchParams.set(\"token\", connection.token);\r\n url.searchParams.set(\"instance\", instanceId);\r\n\r\n return url.toString();\r\n}\r\n\r\nfunction execFileText(file: string, arguments_: string[], signal?: AbortSignal): Promise<string> {\r\n return new Promise((resolve, reject) => {\r\n execFile(file, arguments_, {\r\n encoding: \"utf8\",\r\n maxBuffer: 8_192,\r\n timeout: 3_000,\r\n windowsHide: true,\r\n env: companionEnvironment(process.env),\r\n ...(signal ? {signal} : {}),\r\n }, (error, stdout) => {\r\n if (error) reject(error);\r\n else resolve(stdout);\r\n });\r\n });\r\n}\r\n", "import { createServer, type IncomingMessage, type Server, type ServerResponse } from \"node:http\";\nimport { randomBytes, timingSafeEqual } from \"node:crypto\";\nimport {\n MAX_WIRE_BYTES,\n parsePetSnapshot,\n parseBillingSnapshot,\n type PetSnapshot,\n type PetStateSource,\n MAX_INTERACTION_BYTES, isOpaqueRef, parsePetInteraction,\n} from \"@starcried/starlite-dsh-protocol\";\nimport type { BillingSource } from \"./billing.js\";\nimport type {QuestionBridge} from \"./questions.js\";\n\nconst HEARTBEAT_MS = 15_000;\nconst MAX_CLIENTS = 2;\nconst MAX_LISTEN_ATTEMPTS = 16;\n// Fetch implementations reject these ports before a request reaches the\n// server. Port 0 can occasionally select one of them on hosts with a custom\n// ephemeral-port range, which would make an otherwise healthy bridge unusable.\nconst BROWSER_BLOCKED_PORTS = new Set([\n 0, 1, 7, 9, 11, 13, 15, 17, 19, 20, 21, 22, 23, 25, 37, 42, 43, 53, 69,\n 77, 79, 87, 95, 101, 102, 103, 104, 109, 110, 111, 113, 115, 117, 119,\n 123, 135, 137, 139, 143, 161, 179, 389, 427, 465, 512, 513, 514, 515,\n 526, 530, 531, 532, 540, 548, 554, 556, 563, 587, 601, 636, 989, 990,\n 993, 995, 1_719, 1_720, 1_723, 2_049, 3_659, 4_045, 4_190, 5_060,\n 5_061, 6_000, 6_566, 6_665, 6_666, 6_667, 6_668, 6_669, 6_697, 10_080,\n]);\nconst ALLOWED_ORIGINS = new Set([\n \"http://tauri.localhost\",\n \"https://tauri.localhost\",\n \"tauri://localhost\",\n \"http://127.0.0.1:1420\",\n \"http://localhost:1420\",\n]);\n\nexport interface StatusServer {\n readonly port: number;\n readonly token: string;\n close(): Promise<void>;\n}\n\nexport async function startStatusServer(source: PetStateSource, options: {billing?: BillingSource; questions?: QuestionBridge} = {}): Promise<StatusServer> {\n const token = randomBytes(32).toString(\"base64url\");\n const clients = new Set<ServerResponse>();\n let latest: PetSnapshot | undefined;\n let closed = false;\n\n const server = createServer((request, response) => {\n const origin = request.headers.origin;\n if (request.method === \"OPTIONS\") {\n if (!origin || !ALLOWED_ORIGINS.has(origin)) {\n reject(response, 403);\n return;\n }\n response.writeHead(204, corsHeaders(origin));\n response.end();\n return;\n }\n if (!['GET','POST'].includes(request.method??'') || !authorized(request.headers.authorization, token)) {\n reject(response, 401);\n return;\n }\n if (origin && !ALLOWED_ORIGINS.has(origin)) {\n reject(response, 403);\n return;\n }\n\n const path = new URL(request.url ?? \"/\", \"http://127.0.0.1\").pathname;\n if (path === \"/v1/health\" && request.method === \"GET\") {\n response.writeHead(204, {\n \"Cache-Control\": \"no-store\",\n ...(origin ? corsHeaders(origin) : {}),\n });\n response.end();\n return;\n }\n if (path === \"/v1/billing\" && request.method === \"GET\" && options.billing) {\n void options.billing.read().then(snapshot => {\n const json = JSON.stringify(parseBillingSnapshot(snapshot));\n if (Buffer.byteLength(json, \"utf8\") > MAX_WIRE_BYTES) throw new Error(\"billing response too large\");\n if (closed || response.destroyed) return;\n response.writeHead(200, {\n \"Cache-Control\": \"no-store\", \"Content-Type\": \"application/json; charset=utf-8\",\n \"X-Content-Type-Options\": \"nosniff\", ...(origin ? corsHeaders(origin) : {}),\n });\n response.end(json);\n }).catch(() => {if (!response.destroyed && !response.headersSent) reject(response, 503);});\n return;\n }\n if(path === \"/v1/interaction\" && request.method === \"GET\" && options.questions){\n try{\n const query=new URL(request.url??\"/\",\"http://127.0.0.1\").searchParams;\n const id=query.get(\"request\");const pending=isOpaqueRef(id)&&query.getAll(\"request\").length===1?options.questions.read(id):undefined;\n if(!pending){reject(response,409);return;}\n jsonResponse(response,parsePetInteraction(pending),origin);\n }catch{reject(response,409);}\n return;\n }\n if(options.questions && request.method === \"POST\" && [\"/v1/interaction/answer\",\"/v1/interaction/availability\",\"/v1/session/open\",\"/v1/web/open\"].includes(path)){\n const bridge=options.questions;\n void readBody(request).then(async body=>{\n if(closed||response.destroyed)return;\n if(!body||typeof body!==\"object\"||Array.isArray(body))throw Error(\"invalid action\");\n const row=body as Record<string,unknown>;\n if(path===\"/v1/interaction/answer\")await bridge.answer(row);\n else if(path===\"/v1/interaction/availability\"){\n if(typeof row.enabled!==\"boolean\"||Object.keys(row).some(k=>k!==\"enabled\"))throw Error(\"invalid availability\");bridge.setEnabled(row.enabled);\n }else if(path===\"/v1/web/open\"){\n if(Object.keys(row).length!==0)throw Error(\"invalid navigation\");await bridge.openWebUi();\n }else{\n if(!isOpaqueRef(row.sessionRef)||Object.keys(row).some(k=>k!==\"sessionRef\"))throw Error(\"invalid navigation\");await bridge.open(row.sessionRef);\n }\n if(!response.destroyed){response.writeHead(204,{\"Cache-Control\":\"no-store\",...(origin?corsHeaders(origin):{})});response.end();}\n }).catch(()=>{if(!response.destroyed&&!response.headersSent)reject(response,409)});return;\n }\n if (path !== \"/v1/events\" || request.method !== \"GET\") {\n reject(response, 404);\n return;\n }\n if (clients.size >= MAX_CLIENTS) {\n reject(response, 429);\n return;\n }\n\n response.writeHead(200, {\n \"Cache-Control\": \"no-cache, no-store\",\n Connection: \"keep-alive\",\n \"Content-Type\": \"text/event-stream; charset=utf-8\",\n \"X-Accel-Buffering\": \"no\",\n \"X-Content-Type-Options\": \"nosniff\",\n ...(origin ? corsHeaders(origin) : {}),\n });\n response.flushHeaders();\n clients.add(response);\n options.questions?.setConnected(true);\n response.write(\": connected\\n\\n\");\n if (latest) writeSnapshot(response, latest);\n request.on(\"close\", () => {clients.delete(response);options.questions?.setConnected(clients.size>0)});\n });\n\n const heartbeat = setInterval(() => {\n for (const client of clients) client.write(\": heartbeat\\n\\n\");\n }, HEARTBEAT_MS);\n heartbeat.unref();\n\n try {\n await source.start((snapshot) => {\n let canonical: PetSnapshot;\n try {\n // Reconstruct the allow-listed protocol object before serialization.\n // This prevents an accidental structural subtype from carrying a DSH\n // event, path, or other extra field onto the wire.\n canonical = parsePetSnapshot(snapshot);\n } catch {\n return;\n }\n latest = canonical;\n for (const client of clients) writeSnapshot(client, canonical);\n });\n await listenLoopback(server);\n } catch (error) {\n clearInterval(heartbeat);\n await source.stop().catch(() => undefined);\n server.close();\n throw error;\n }\n\n const address = server.address();\n if (!address || typeof address === \"string\") {\n await shutdown(server, clients, source, heartbeat);\n throw new Error(\"DSH pet status server did not receive a TCP port\");\n }\n\n return {\n port: address.port,\n token,\n async close() {\n if (closed) return;\n closed = true;\n options.billing?.close();\n options.questions?.close();\n await shutdown(server, clients, source, heartbeat);\n },\n };\n}\n\nfunction listenLoopback(server: Server): Promise<void> {\n return listenOnSafeLoopbackPort(server);\n}\n\nasync function listenOnSafeLoopbackPort(server: Server): Promise<void> {\n for (let attempt = 0; attempt < MAX_LISTEN_ATTEMPTS; attempt += 1) {\n await listenOnce(server);\n const address = server.address();\n if (address && typeof address !== \"string\" && isBrowserSafePort(address.port)) return;\n await closeServer(server);\n }\n throw new Error(\"DSH pet status server could not allocate a browser-safe port\");\n}\n\nfunction listenOnce(server: Server): Promise<void> {\n return new Promise((resolve, reject) => {\n const onError = (error: Error) => reject(error);\n server.once(\"error\", onError);\n server.listen(0, \"127.0.0.1\", () => {\n server.off(\"error\", onError);\n resolve();\n });\n });\n}\n\nexport function isBrowserSafePort(port: number): boolean {\n return Number.isInteger(port) && port > 0 && port <= 65_535\n && !BROWSER_BLOCKED_PORTS.has(port);\n}\n\nasync function shutdown(\n server: Server,\n clients: Set<ServerResponse>,\n source: PetStateSource,\n heartbeat: ReturnType<typeof setInterval>,\n): Promise<void> {\n clearInterval(heartbeat);\n for (const client of clients) client.destroy();\n clients.clear();\n await Promise.allSettled([source.stop(), closeServer(server)]);\n}\n\nfunction closeServer(server: Server): Promise<void> {\n return new Promise((resolve) => {\n if (!server.listening) {\n resolve();\n return;\n }\n server.close(() => resolve());\n });\n}\n\nfunction writeSnapshot(response: ServerResponse, snapshot: PetSnapshot): void {\n const json = JSON.stringify(snapshot);\n if (Buffer.byteLength(json, \"utf8\") > MAX_WIRE_BYTES) return;\n response.write(`id: ${snapshot.sequence}\\nevent: snapshot\\ndata: ${json}\\n\\n`);\n}\n\nfunction authorized(header: string | undefined, token: string): boolean {\n if (!header?.startsWith(\"Bearer \")) return false;\n const supplied = Buffer.from(header.slice(\"Bearer \".length), \"utf8\");\n const expected = Buffer.from(token, \"utf8\");\n return supplied.length === expected.length && timingSafeEqual(supplied, expected);\n}\n\nfunction corsHeaders(origin: string): Record<string, string> {\n return {\n \"Access-Control-Allow-Headers\": \"Authorization, Content-Type\",\n \"Access-Control-Allow-Methods\": \"GET, POST, OPTIONS\",\n \"Access-Control-Allow-Origin\": origin,\n \"Access-Control-Max-Age\": \"600\",\n Vary: \"Origin\",\n };\n}\n\nfunction jsonResponse(response:ServerResponse,value:unknown,origin:string|undefined):void{\n const json=JSON.stringify(value);if(Buffer.byteLength(json)>MAX_INTERACTION_BYTES){reject(response,413);return;}\n response.writeHead(200,{\"Content-Type\":\"application/json; charset=utf-8\",\"Cache-Control\":\"no-store\",\"X-Content-Type-Options\":\"nosniff\",...(origin?corsHeaders(origin):{})});response.end(json);\n}\nasync function readBody(request:IncomingMessage):Promise<unknown>{\n if(request.headers[\"content-type\"]?.split(\";\")[0]?.trim().toLowerCase()!==\"application/json\")throw Error(\"invalid action content type\");\n if(Number(request.headers[\"content-length\"])>MAX_INTERACTION_BYTES)throw Error(\"action too large\");\n request.setTimeout(5000,()=>request.destroy());\n try{const chunks:Buffer[]=[];let bytes=0;for await(const chunk of request){const data=Buffer.from(chunk);bytes+=data.length;if(bytes>MAX_INTERACTION_BYTES)throw Error(\"action too large\");chunks.push(data)}return JSON.parse(Buffer.concat(chunks).toString(\"utf8\")) as unknown}\n finally{request.setTimeout(0)}\n}\n\nfunction reject(response: ServerResponse, status: number): void {\n response.writeHead(status, {\n \"Cache-Control\": \"no-store\",\n \"Content-Length\": \"0\",\n \"X-Content-Type-Options\": \"nosniff\",\n });\n response.end();\n}\n", "import type {BillingSnapshot} from \"@starcried/starlite-dsh-protocol\";\n\n// Public DeepSeek rule, verified 2026-10-10:\n// https://api-docs.deepseek.com/zh-cn/quick_start/pricing/\n// Mon\u2013Fri 09:00\u201312:00 and 14:00\u201318:00 Beijing, excluding Chinese holidays.\n// Public 2026 holiday schedule:\n// https://www.gov.cn/zhengce/zhengceku/202511/content_7047091.htm\nconst HOLIDAYS_2026 = [\n [\"2026-01-01\",\"2026-01-03\"], [\"2026-02-15\",\"2026-02-23\"],\n [\"2026-04-04\",\"2026-04-06\"], [\"2026-05-01\",\"2026-05-05\"],\n [\"2026-06-19\",\"2026-06-21\"], [\"2026-09-25\",\"2026-09-27\"],\n [\"2026-10-01\",\"2026-10-07\"],\n] as const;\n\n/** Current published tariff period; does not infer past requests' billing time. */\nexport function deepSeekPricingPeriod(time: number): NonNullable<BillingSnapshot[\"pricingPeriod\"]> {\n const beijing = new Date(time + 8 * 60 * 60 * 1000);\n if (!Number.isFinite(beijing.getTime())) return \"unknown\";\n const weekday = beijing.getUTCDay(), minutes = beijing.getUTCHours() * 60 + beijing.getUTCMinutes();\n if (weekday === 0 || weekday === 6 || !(minutes >= 540 && minutes < 720 || minutes >= 840 && minutes < 1080)) return \"off-peak\";\n // Do not guess an unverified year's holiday calendar. Weekend makeup work\n // does not alter DeepSeek's explicit Monday-through-Friday pricing rule.\n if (beijing.getUTCFullYear() !== 2026) return \"unknown\";\n const date = beijing.toISOString().slice(0,10);\n return HOLIDAYS_2026.some(([start,end]) => date >= start && date <= end) ? \"off-peak\" : \"peak\";\n}\n", "import { parseBillingSnapshot, type BillingSnapshot } from \"@starcried/starlite-dsh-protocol\";\nimport {deepSeekPricingPeriod} from \"./pricing-period.js\";\n\nconst PRICING_DATE = \"2026-10-10\";\nconst CURRENCIES = [\"CNY\", \"USD\"] as const;\n// Official peak prices per million tokens, verified on PRICING_DATE.\n// Off-peak is half. A range avoids pretending to know provider billing time\n// at the boundary of tariff periods. Unknown model IDs never inherit a price.\nconst RATES: Record<string, readonly [number, number, number][]> = {\n \"deepseek-flash\": [[.04, 2, 8], [.006, .3, 1.2]],\n \"deepseek-v4-flash\": [[.04, 2, 8], [.006, .3, 1.2]],\n \"deepseek-v4-flash-vision-exp\": [[.04, 2, 8], [.006, .3, 1.2]],\n \"deepseek-v4-pro\": [[.30, 9, 27], [.044, 1.32, 3.96]],\n};\nconst MAX_SESSIONS = 512;\nconst MAX_SESSION_BYTES = 16 * 1024 * 1024;\nconst MAX_EVENTS = 100_000;\nconst MAX_BALANCE_BYTES = 16_384;\n\nexport interface BillingContext {\n get?(name: string): unknown;\n}\ninterface Settings {\n get?(namespace: string): unknown;\n describe?(options: {redactSecrets: true}): unknown;\n}\ninterface Credentials {\n resolve(reference: string): Promise<{value: string} | undefined>;\n readRecord(key: string): Promise<unknown>;\n}\nexport interface BillingPersistence {\n list(options: {signal: AbortSignal}): Promise<readonly {\n header: {id: string}; revision: unknown; sizeBytes?: number;\n }[]>;\n open(id: string, access: \"read\", options: {signal: AbortSignal}): Promise<{\n inheritedEventCount: number;\n read(offset: number, length: number, options: {signal: AbortSignal}): Promise<{events: readonly unknown[]}>;\n close(): Promise<void>;\n }>;\n}\nexport interface BillingSource { read(): Promise<BillingSnapshot>; close(): void; }\ninterface SessionUsage { priced: number; unpriced: number; maximums: number[]; }\ninterface OfficialRoute { provider: \"deepseek-official\" | \"deepseek\"; profile: Record<string, unknown>; }\n\n/** Reads existing DSH services only; never writes credentials or session files. */\nexport class DeepSeekBilling implements BillingSource {\n readonly #context: BillingContext;\n readonly #fetch: typeof fetch;\n readonly #now: () => number;\n readonly #cache = new Map<string, {revision: unknown; date: string; usage: SessionUsage}>();\n #balance: {at: number; value: BillingSnapshot[\"balance\"]} | undefined;\n #snapshot: BillingSnapshot | undefined;\n #pending: Promise<BillingSnapshot> | undefined;\n #abort: AbortController | undefined;\n #closed = false;\n #generation = 0;\n\n constructor(context: BillingContext, fetchImplementation: typeof fetch = fetch, now: () => number = Date.now) {\n this.#context = context; this.#fetch = fetchImplementation; this.#now = now;\n }\n invalidate(accountChanged = false): void {\n ++this.#generation; this.#snapshot = undefined;\n if (accountChanged) {this.#balance = undefined; this.#cache.clear(); this.#abort?.abort();}\n }\n close(): void {this.#closed = true; this.#abort?.abort(); this.#cache.clear(); this.#balance = undefined; this.#snapshot = undefined;}\n read(): Promise<BillingSnapshot> {\n if (this.#closed) return Promise.reject(new Error(\"billing unavailable\"));\n const now = this.#now();\n if (this.#snapshot && this.#snapshot.today.date === billingDate(now) && now - this.#snapshot.checkedAt < 5000) return Promise.resolve(this.#snapshot);\n if (this.#pending) return this.#pending;\n // describe() can announce its first live snapshot. Read it before opening\n // this request so its own settings notification cannot abort the query.\n const routes = officialRoutes(this.#context);\n const generation = this.#generation;\n const abort = new AbortController(); this.#abort = abort;\n const timer = setTimeout(() => abort.abort(), 8000); timer.unref();\n this.#pending = this.#query(now, abort.signal, routes).then(snapshot => {\n if (!this.#closed && generation === this.#generation) this.#snapshot = snapshot;\n return snapshot;\n }).finally(() => {clearTimeout(timer); this.#pending = undefined; this.#abort = undefined;});\n return this.#pending;\n }\n async #query(now: number, signal: AbortSignal, routes: readonly OfficialRoute[]): Promise<BillingSnapshot> {\n const date = billingDate(now);\n const official = routes.length > 0;\n const balancePromise = official ? this.#readBalance(routes, now, signal) : Promise.resolve({status: \"unconfigured\" as const, balances: []});\n const today: BillingSnapshot[\"today\"] = {\n date, timeZone: \"Asia/Shanghai\", partial: !official, pricedRequests: 0, unpricedRequests: 0,\n pricingDate: PRICING_DATE, estimates: CURRENCIES.map(currency => ({currency, minimum: 0, maximum: 0})),\n };\n const persistence = this.#context.get?.(\"sessionPersistence\") as BillingPersistence | undefined;\n if (!official || !persistence || typeof persistence.list !== \"function\" || typeof persistence.open !== \"function\") today.partial = true;\n else {\n try {\n const sessions = await persistence.list({signal});\n if (sessions.length > MAX_SESSIONS) today.partial = true;\n const seen = new Set<string>();\n for (const session of sessions.slice(0, MAX_SESSIONS)) {\n if (signal.aborted) {today.partial = true; break;}\n const id = session.header.id; seen.add(id);\n if (session.sizeBytes !== undefined && session.sizeBytes > MAX_SESSION_BYTES) {today.partial = true; continue;}\n let usage = this.#cache.get(id);\n if (!usage || usage.date !== date || usage.revision !== session.revision) {\n let handle: Awaited<ReturnType<BillingPersistence[\"open\"]>> | undefined;\n try {\n handle = await persistence.open(id, \"read\", {signal});\n const {events} = await handle.read(0, MAX_EVENTS + 1, {signal});\n if (events.length > MAX_EVENTS || !Number.isSafeInteger(handle.inheritedEventCount) || handle.inheritedEventCount < 0 || handle.inheritedEventCount > events.length) {today.partial = true; continue;}\n usage = {revision: session.revision, date, usage: foldDailyUsage(events, handle.inheritedEventCount, date, routes.map(route => route.provider))};\n this.#cache.set(id, usage);\n } catch {today.partial = true; continue;}\n finally {await handle?.close().catch(() => {today.partial = true;});}\n }\n today.pricedRequests += usage.usage.priced; today.unpricedRequests += usage.usage.unpriced;\n today.estimates.forEach((estimate, i) => {estimate.maximum += usage.usage.maximums[i] ?? 0; estimate.minimum = estimate.maximum / 2;});\n }\n for (const id of this.#cache.keys()) if (!seen.has(id)) this.#cache.delete(id);\n } catch {today.partial = true;}\n }\n const balance = await balancePromise;\n if (signal.aborted || this.#closed) throw new Error(\"billing unavailable\");\n return parseBillingSnapshot({billingVersion: 1, checkedAt: now, pricingPeriod: deepSeekPricingPeriod(now), balance, today});\n }\n async #readBalance(routes: readonly OfficialRoute[], now: number, signal: AbortSignal): Promise<BillingSnapshot[\"balance\"]> {\n if (this.#balance && now - this.#balance.at < 60_000) return this.#balance.value;\n let value: BillingSnapshot[\"balance\"];\n let source: BillingSnapshot[\"balance\"][\"source\"];\n try {\n // Prefer DSH's native route, then the explicitly configured pi-ai route.\n // A missing native key must not hide an existing pi-ai credential.\n let key: string | undefined;\n for (const route of routes) {\n key = await resolveKey(this.#context, route);\n if (key) {source = route.provider;break;}\n }\n if (!key) value = {status: \"unconfigured\", balances: []};\n else {\n const request = this.#fetch;\n const response = await request(\"https://api.deepseek.com/user/balance\", {\n method: \"GET\", headers: {Authorization: `Bearer ${key}`}, redirect: \"error\", signal,\n });\n if (!response.ok || !response.headers.get(\"content-type\")?.toLowerCase().includes(\"application/json\") || !response.body) {\n await response.body?.cancel(); throw new Error(\"balance unavailable\");\n }\n const reader = response.body.getReader(); let bytes = 0; let json = \"\";\n const decoder = new TextDecoder();\n try {\n while (true) {\n const {done, value: chunk} = await reader.read(); if (done) break;\n bytes += chunk.byteLength; if (bytes > MAX_BALANCE_BYTES) throw new Error(\"balance too large\");\n json += decoder.decode(chunk, {stream: true});\n }\n json += decoder.decode();\n } finally {await reader.cancel().catch(() => undefined);}\n const raw = record(JSON.parse(json));\n if (typeof raw?.is_available !== \"boolean\" || !Array.isArray(raw.balance_infos) || raw.balance_infos.length < 1 || raw.balance_infos.length > 2) throw new Error(\"invalid balance\");\n const balances = raw.balance_infos.map(item => {\n const entry = record(item);\n if (!entry || ![\"CNY\", \"USD\"].includes(String(entry.currency)) || typeof entry.total_balance !== \"string\" || !/^-?\\d{1,9}(\\.\\d{1,8})?$/u.test(entry.total_balance)) throw new Error(\"invalid balance\");\n return {currency: entry.currency as \"CNY\" | \"USD\", remaining: Number(entry.total_balance)};\n });\n if (new Set(balances.map(item => item.currency)).size !== balances.length) throw new Error(\"duplicate currency\");\n value = {status: \"ready\", balances, ...(source ? {source} : {})};\n }\n } catch {value = {status: \"unavailable\", balances: [], ...(source ? {source} : {})};}\n if (!signal.aborted && !this.#closed) this.#balance = {at: now, value};\n return value;\n }\n}\n\nexport function billingDate(time: number): string {\n return new Date(time + 8 * 60 * 60 * 1000).toISOString().slice(0, 10);\n}\nexport function officialProfile(value: unknown): boolean {\n const profile = record(value); if (!profile) return false;\n if (record(profile.headers) && Object.keys(record(profile.headers)!).some(key => /^(authorization|x-api-key|api-key)$/iu.test(key))) return false;\n if (Array.isArray(profile.models) && profile.models.some(model => {\n const override = record(model)?.baseURL;\n return override !== undefined && !officialEndpoint(override);\n })) return false;\n return profile.baseURL === undefined || officialEndpoint(profile.baseURL); // Built-in deepseek route defaults to official.\n}\nfunction officialEndpoint(value: unknown): boolean {\n if (typeof value !== \"string\") return false;\n try {\n const url = new URL(value);\n return url.origin === \"https://api.deepseek.com\" && !url.username && !url.password && !url.search && !url.hash\n && [\"/\", \"/v1\", \"/v1/\", \"/anthropic\", \"/anthropic/\"].includes(url.pathname);\n } catch {return false;}\n}\nfunction officialRoutes(context: BillingContext): OfficialRoute[] {\n const settings = context.get?.(\"settings\") as Settings | undefined;\n if (!settings) return [];\n try {\n let candidates: OfficialRoute[];\n if (typeof settings.get === \"function\") {\n const native = record(settings.get(\"llm-deepseek\"));\n const pi = record(record(record(settings.get(\"llm-pi-ai\"))?.providers)?.deepseek);\n candidates = [...(native ? [{provider: \"deepseek-official\" as const, profile: native}] : []), ...(pi ? [{provider: \"deepseek\" as const, profile: pi}] : [])];\n } else {\n const llm = context.get?.(\"llm\") as {listConfigurableProviders?(): unknown} | undefined;\n if (typeof settings.describe !== \"function\" || typeof llm?.listConfigurableProviders !== \"function\") return [];\n const directory = llm.listConfigurableProviders();\n const descriptors = settings.describe({redactSecrets: true});\n if (!Array.isArray(directory) || !Array.isArray(descriptors)) return [];\n candidates = [];\n // Account login is a different credential type, never an API-key route.\n for (const provider of [\"deepseek-official\", \"deepseek\"] as const) {\n const entries = directory.map(record).filter(entry => entry?.provider === provider);\n if (entries.length !== 1) continue;\n const entry = entries[0]!;\n if (typeof entry.settingsNs !== \"string\" || !entry.settingsNs || !Array.isArray(entry.settingsPath) || entry.settingsPath.length > 8\n || entry.settingsPath.some(segment => typeof segment !== \"string\" || !segment || [\"__proto__\", \"prototype\", \"constructor\"].includes(segment))) continue;\n const rows = descriptors.map(record).filter(row => row?.ns === entry.settingsNs);\n if (rows.length !== 1) continue;\n let profile = record(rows[0]?.value);\n for (const segment of entry.settingsPath as string[]) profile = profile && Object.hasOwn(profile, segment) ? record(profile[segment]) : undefined;\n if (profile) candidates.push({provider, profile});\n }\n }\n return candidates.filter(route => {\n if (route.provider === \"deepseek\") return officialProfile(route.profile);\n const environment = context.get?.(\"launchEnvironment\") as {get(name: string): {value: string} | undefined} | undefined;\n // Use the same trusted endpoint precedence as the native API-key adapter.\n const baseURL = route.profile.baseURL ?? (environment ? environment.get(\"DEEPSEEK_BASE_URL\")?.value : process.env.DEEPSEEK_BASE_URL)\n ?? (typeof settings.get === \"function\" ? \"https://api.deepseek.com\" : \"https://api.deepseek.com/anthropic\");\n return officialProfile({...route.profile, baseURL});\n });\n } catch {\n return [];\n }\n}\nasync function resolveKey(context: BillingContext, route: OfficialRoute): Promise<string | undefined> {\n const credentials = context.get?.(\"credentials\") as Credentials | undefined;\n if (!credentials) return undefined;\n const reference = route.profile.apiKeyEnv ?? (route.provider === \"deepseek-official\" ? \"DEEPSEEK_API_KEY\" : undefined);\n let value: unknown;\n if (reference !== undefined) {\n if (typeof reference !== \"string\" || !/^[A-Za-z_][A-Za-z0-9_]*$/u.test(reference)) return undefined;\n value = (await credentials.resolve(reference))?.value;\n } else {\n const stored = record(await credentials.readRecord(\"llm-pi-ai/deepseek\"));\n if (stored?.kind === \"api-key\") value = stored.key ?? record(stored.env)?.DEEPSEEK_API_KEY;\n value ??= (await credentials.resolve(\"DEEPSEEK_API_KEY\"))?.value;\n }\n return typeof value === \"string\" && value.length > 0 && value.length <= 4096 && /^[!-~]+$/u.test(value) ? value : undefined;\n}\n\n/** Last settlement wins per attempt; retry-started opens a separate billable slot. */\nexport function foldDailyUsage(events: readonly unknown[], inherited: number, date: string, providers: readonly string[] = [\"deepseek\"]): SessionUsage {\n const result: SessionUsage = {priced: 0, unpriced: 0, maximums: [0, 0]};\n let header: Record<string, unknown> | undefined;\n let last: {turn: unknown; step: unknown; sample: Record<string, unknown> | undefined; route: Record<string, unknown> | undefined; time: number} | undefined;\n const flush = () => {\n if (!last || billingDate(last.time) !== date || (typeof last.route?.provider === \"string\" && !providers.includes(last.route.provider))) {last = undefined; return;}\n const rates = typeof last.route?.model === \"string\" && typeof last.route.provider === \"string\" && providers.includes(last.route.provider) && Object.hasOwn(RATES,last.route.model) ? RATES[last.route.model] : undefined;\n const sample = normalizeUsage(last.sample);\n if (!rates || !sample) result.unpriced++;\n else {\n result.priced++;\n rates.forEach((rate, i) => {result.maximums[i] = (result.maximums[i] ?? 0) + (rate[0] * sample.hit + rate[1] * sample.input + rate[2] * sample.output) / 1_000_000;});\n }\n last = undefined;\n };\n for (let i = 0; i < events.length; i++) {\n const event = record(events[i]); const data = record(event?.data);\n if (event?.type === \"request/header\") header = record(record(data?.header)?.config);\n if (i < inherited) continue;\n if (event?.type === \"llm/retry-started\") {flush(); continue;}\n if (event?.type !== \"assistant/message\" && event?.type !== \"assistant/attempt\") continue;\n if (!data || !count(data.turn) || !count(data.step) || !count(event.time)) continue;\n if (last && (last.turn !== data.turn || last.step !== data.step)) flush();\n let sample = record(data.usage);\n if (!sample && Array.isArray(data.stream)) {\n for (const item of data.stream) {\n const chunk = record(record(item)?.chunk);\n if (chunk?.type === \"usage\") sample = record(chunk.usage);\n }\n }\n const source = record(record(data.message)?.source);\n last = {turn: data.turn, step: data.step, sample: sample ?? last?.sample, route: source ?? header ?? last?.route, time: event.time as number};\n }\n flush(); return result;\n}\nfunction normalizeUsage(sample: Record<string, unknown> | undefined): {input: number; hit: number; output: number} | undefined {\n if (!sample || !count(sample.inputTokens) || !count(sample.outputTokens)\n || !count(sample.cacheReadTokens) || (sample.cacheWriteTokens !== undefined && sample.cacheWriteTokens !== 0)) return undefined;\n const input = sample.inputTokens as number, output = sample.outputTokens as number, hit = sample.cacheReadTokens as number;\n const total = input + output + hit;\n if (!count(total) || (sample.totalTokens !== undefined && sample.totalTokens !== total)\n || (sample.reasoningTokens !== undefined && (!count(sample.reasoningTokens) || (sample.reasoningTokens as number) > output))) return undefined;\n return {input, output, hit};\n}\nfunction count(value: unknown): boolean {return Number.isSafeInteger(value) && (value as number) >= 0;}\nfunction record(value: unknown): Record<string, unknown> | undefined {return value && typeof value === \"object\" && !Array.isArray(value) ? value as Record<string, unknown> : undefined;}\n", "import {randomUUID} from \"node:crypto\";\nimport {parsePetInteraction,parsePetAnswer,type PetInteraction,type PetAnswer} from \"@starcried/starlite-dsh-protocol\";\nimport type {DshStateAggregator} from \"./aggregator.js\";\n\ninterface Services {get?(name:string):unknown}\ninterface QuestionService {\n attachWait(agent:unknown,callId:string,signal:AbortSignal):AsyncIterable<unknown>;\n answer(agent:unknown,callId:string,answer:{answers:PetAnswer[\"answers\"]}):boolean|Promise<boolean>;\n}\ninterface ProjectionService {\n stateOf(session:unknown,key:\"userQuestions\"):unknown;\n onChanged(listener:(session:unknown,key:string)=>void):()=>void;\n}\ninterface AgentDirectory {get(id:string):unknown;roots():readonly unknown[]}\ninterface LateQuestion {agent:unknown;key:unknown;callId:string;request:PetInteraction}\ninterface Pending {\n key:unknown;request:PetInteraction;late?:LateQuestion;deadline?:number;\n finish:(answer:PetAnswer|undefined)=>void;abort:(code?:string)=>void;\n}\nconst MAX_PENDING=32;\nconst MAX_TIMER=2_147_483_647;\n\n/** Ordinary questions only. Tool permission approvals remain owned by DSH. */\nexport class QuestionBridge {\n readonly #pending=new Map<string,Pending>();\n readonly #suppressed=new Map<unknown,Set<string>>();\n #connected=false;#enabled=false;#closed=false;#refreshQueued=false;\n #unsubscribe:(()=>void)|undefined;\n constructor(readonly aggregator:DshStateAggregator,readonly openSession:(key:unknown)=>Promise<void>,readonly services:Services={}){}\n setConnected(value:boolean):void{this.#connected=value;if(!value)this.#delegateAll();else this.refresh();}\n setEnabled(value:boolean):void{this.#enabled=value;if(!value)this.#delegateAll();else this.refresh();}\n close():void{this.#closed=true;this.#delegateAll();this.#unsubscribe?.();this.#unsubscribe=undefined;this.#suppressed.clear();}\n read(id:string):PetInteraction|undefined{\n const pending=this.#pending.get(id),issuedAt=Date.now();\n if(pending?.deadline!==undefined&&issuedAt>=pending.deadline)pending.abort(\"ASK_TIMED_OUT\");\n if(pending?.late&&!this.#currentLate(pending.late))pending.finish(undefined);\n const current=this.#pending.get(id);\n return current?.deadline===undefined?current?.request:{...current.request,wait:{state:\"timed\",deadline:current.deadline,issuedAt}};\n }\n async answer(raw:unknown):Promise<void>{\n const requestId=record(raw)?.requestId;\n const pending=typeof requestId===\"string\"?this.#pending.get(requestId):undefined;\n if(!pending)throw Error(\"question no longer pending\");\n const answer=parsePetAnswer(raw,pending.request);\n if(pending.deadline!==undefined&&Date.now()>=pending.deadline){pending.abort(\"ASK_TIMED_OUT\");throw Error(\"question no longer pending\");}\n if(!pending.late){pending.finish(answer);return;}\n const late=pending.late,service=this.#questionService();\n if(!service||!this.#currentLate(late)){pending.finish(undefined);throw Error(\"question no longer pending\");}\n // Retire the random reference before calling the owner. Its atomic method\n // validates the live root and refuses stale or already queued replies.\n this.#suppress(late.key,late.callId);pending.finish(undefined);\n if(await service.answer(late.agent,late.callId,{answers:answer.answers})!==true)throw Error(\"question no longer pending\");\n }\n async open(ref:string):Promise<void>{\n const key=this.aggregator.sessionKey(ref);if(key===undefined||this.#closed)throw Error(\"session unavailable\");\n const captured=[...this.#pending.values()].filter(item=>item.key===key);\n await this.openSession(key);\n if(this.#closed||this.aggregator.sessionKey(ref)!==key)return;\n // Navigation may fail or outlive its question. Only a successful open can\n // delegate the same captured request; a newer form keeps its own lifetime.\n for(const pending of captured){\n if(this.#pending.get(pending.request.requestId)!==pending)continue;\n if(pending.deadline!==undefined&&Date.now()>=pending.deadline)continue;\n if(pending.late){\n if(!this.#currentLate(pending.late))continue;\n this.#suppress(key,pending.late.callId);\n }\n pending.finish(undefined);\n }\n }\n async openWebUi():Promise<void>{if(this.#closed)throw Error(\"DSH Web UI unavailable\");await this.openSession(undefined);}\n cancelForeground(key:unknown):void{for(const item of this.#pending.values())if(item.key===key&&!item.late)item.abort();}\n cancelSession(key:unknown):void{\n for(const item of this.#pending.values())if(item.key===key)item.abort();\n this.#suppressed.delete(key);\n }\n /** Called after agent changes; projection changes have their own public feed. */\n refresh():void{\n if(this.#closed||!this.#connected||!this.#enabled)return;\n if(!this.#unsubscribe){\n const projections=this.#projectionService();\n if(projections)try{this.#unsubscribe=projections.onChanged((_session,key)=>{if(key===\"userQuestions\")this.refresh();});}catch{return;}\n }\n if(this.#refreshQueued)return;\n this.#refreshQueued=true;\n queueMicrotask(()=>{this.#refreshQueued=false;if(!this.#closed&&this.#connected&&this.#enabled)this.#reconcile();});\n }\n async ask(key:unknown,raw:unknown,next:()=>Promise<unknown>):Promise<unknown>{\n if(this.#closed||!this.#connected||!this.#enabled)return next();\n const source=record(raw);let request:PetInteraction;\n try{request=this.#request(key,source?.questions);}catch{return next();}\n const signal=source?.signal;\n if(signal!==undefined&&!(signal instanceof AbortSignal))return next();\n if(signal?.aborted)throw questionError(\"ASK_ABORTED\");\n const wait=record(source?.wait),timed=wait?.timed===true;\n const callId=validCallId(wait?.callId)?wait.callId:undefined;\n const service=this.#questionService();\n if(source?.wait!==undefined&&(!wait||!callId||(wait.timed!==undefined&&typeof wait.timed!==\"boolean\")))return next();\n if(timed&&(!callId||!service||!this.#liveRoot(source?.agent,key)))return next();\n this.#suppressed.delete(key);\n if(this.#pending.size>=MAX_PENDING){\n const late=[...this.#pending.values()].find(item=>item.late);late?.finish(undefined);\n if(this.#pending.size>=MAX_PENDING)return next();\n }\n let iterator:AsyncIterator<unknown>|undefined;\n const claim=new AbortController();\n let timer:ReturnType<typeof setTimeout>|undefined;\n let published=false,settled=false;\n const answer=await new Promise<PetAnswer|undefined>((resolve,reject)=>{\n const sourceAborted=()=>abort(record(signal?.reason)?.code===\"ASK_TIMED_OUT\"?\"ASK_TIMED_OUT\":\"ASK_ABORTED\");\n const cleanup=()=>{\n this.#pending.delete(request.requestId);signal?.removeEventListener(\"abort\",sourceAborted);\n clearTimeout(timer);claim.abort();\n if(iterator?.return)void iterator.return().catch(()=>undefined);\n if(published)this.aggregator.handleInputResolved(key,request.requestId);\n };\n const finish=(value:PetAnswer|undefined)=>{if(settled)return;settled=true;cleanup();resolve(value);};\n const abort=(code=\"ASK_ABORTED\")=>{if(settled)return;settled=true;cleanup();reject(questionError(code));};\n const pending:Pending={key,request,finish,abort};\n this.#pending.set(request.requestId,pending);\n signal?.addEventListener(\"abort\",sourceAborted,{once:true});\n const publish=()=>{published=true;this.aggregator.handleInputRequested(key,request.requestId);};\n if(!timed){publish();return;}\n void (async()=>{\n try{\n iterator=service!.attachWait(source!.agent,callId!,claim.signal)[Symbol.asyncIterator]();\n const first=await iterator.next();\n if(settled)return;\n const remaining=record(first.value)?.remainingMs;\n if(first.done||!Number.isSafeInteger(remaining)||Number(remaining)<0||Number(remaining)>MAX_TIMER){finish(undefined);return;}\n // Match the official Client: the Host duration starts at receipt of\n // the opening frame, without subtracting time spent obtaining it.\n const issuedAt=Date.now();pending.deadline=issuedAt+Number(remaining);\n // Adding public wait metadata must not bypass the complete wire bound.\n try{request=parsePetInteraction({...request,wait:{state:\"timed\",deadline:pending.deadline,issuedAt}});pending.request=request;}\n catch{finish(undefined);return;}\n if(Date.now()>=pending.deadline){abort(\"ASK_TIMED_OUT\");return;}\n timer=setTimeout(()=>abort(\"ASK_TIMED_OUT\"),pending.deadline-Date.now());timer.unref();publish();\n // The public stream holds the claim until cancellation or settlement.\n await iterator.next();if(!settled)finish(undefined);\n }catch{if(!settled)finish(undefined);}\n })();\n });\n return answer?{answers:answer.answers}:next();\n }\n #request(key:unknown,questions:unknown,wait?:PetInteraction[\"wait\"]):PetInteraction{return parsePetInteraction({interactionVersion:1,requestId:randomUUID(),sessionRef:this.aggregator.sessionInfo(key).ref,questions,...(wait?{wait}:{})});}\n #questionService():QuestionService|undefined{\n const service=this.#service(\"userQuestions\");\n return typeof service?.attachWait===\"function\"&&typeof service.answer===\"function\"?service as unknown as QuestionService:undefined;\n }\n #projectionService():ProjectionService|undefined{\n const service=this.#service(\"sessionProjections\");\n return typeof service?.stateOf===\"function\"&&typeof service.onChanged===\"function\"?service as unknown as ProjectionService:undefined;\n }\n #directory():AgentDirectory|undefined{\n const service=this.#service(\"agents\");\n return typeof service?.get===\"function\"&&typeof service.roots===\"function\"?service as unknown as AgentDirectory:undefined;\n }\n #service(name:string):Record<string,unknown>|undefined{try{return record(this.services.get?.(name));}catch{return undefined;}}\n #liveRoot(agent:unknown,key:unknown):boolean{\n try{const row=record(agent),directory=this.#directory();return Boolean(row&&directory&&row.session===key&&typeof row.id===\"string\"&&directory.get(row.id)===agent&&directory.roots().includes(agent));}catch{return false;}\n }\n #lateQuestions():LateQuestion[]{\n const service=this.#questionService(),projections=this.#projectionService(),directory=this.#directory();\n if(!service||!projections||!directory)return [];\n const result:LateQuestion[]=[];\n try{\n for(const agent of directory.roots().slice(0,128)){\n const key=record(agent)?.session;if(!key||!this.#liveRoot(agent,key))continue;\n const active=record(record(projections.stateOf(key,\"userQuestions\"))?.questions)?.active;\n if(!Array.isArray(active)||active.length>MAX_PENDING)continue;\n for(const raw of active){\n const item=record(raw);if(item?.state!==\"continued\"||!validCallId(item.callId))continue;\n try{result.push({agent,key,callId:item.callId,request:this.#request(key,item.questions,{state:\"continued\"})});}catch{continue;}\n if(result.length>=MAX_PENDING)return result;\n }\n }\n }catch{return [];}\n return result;\n }\n #currentLate(late:LateQuestion):boolean{\n return !this.#closed&&this.#connected&&this.#enabled&&!this.#suppressed.get(late.key)?.has(late.callId)\n &&this.#lateQuestions().some(item=>sameLate(item,late));\n }\n #reconcile():void{\n const current=this.#lateQuestions();\n for(const pending of this.#pending.values())if(pending.late&&!current.some(item=>sameLate(item,pending.late!)))pending.finish(undefined);\n for(const [key,ids] of this.#suppressed){for(const id of ids)if(!current.some(item=>item.key===key&&item.callId===id))ids.delete(id);if(!ids.size)this.#suppressed.delete(key);}\n for(const late of current){\n if(this.#pending.size>=MAX_PENDING)break;\n if(this.#suppressed.get(late.key)?.has(late.callId)||[...this.#pending.values()].some(item=>item.late&&sameLate(item.late,late)))continue;\n const request=late.request;\n const finish=()=>{this.#pending.delete(request.requestId);this.aggregator.handleInputResolved(late.key,request.requestId);};\n this.#pending.set(request.requestId,{key:late.key,request,late,finish,abort:finish});\n this.aggregator.handleInputRequested(late.key,request.requestId,false);\n }\n }\n #suppress(key:unknown,callId:string):void{const ids=this.#suppressed.get(key)??new Set<string>();ids.add(callId);this.#suppressed.set(key,ids);}\n #delegateAll():void{for(const item of this.#pending.values())item.finish(undefined);}\n}\nfunction sameLate(a:LateQuestion,b:LateQuestion):boolean{return a.agent===b.agent&&a.key===b.key&&a.callId===b.callId&&JSON.stringify(a.request.questions)===JSON.stringify(b.request.questions);}\nfunction validCallId(value:unknown):value is string{return typeof value===\"string\"&&value.length>0&&value.length<=256&&!/[\\p{Cc}]/u.test(value);}\nfunction questionError(code:string):Error&{code:string}{return Object.assign(new Error(\"question no longer pending\"),{name:\"UserQuestionError\",code});}\nfunction record(value:unknown):Record<string,unknown>|undefined{return value&&typeof value===\"object\"&&!Array.isArray(value)?value as Record<string,unknown>:undefined;}\r\n", "/** Plugin-owned calls on DSH's public authenticated Connection RPC carrier. */\nexport const NAVIGATION_CHANNEL = \"/starlite-navigation\";\nexport type RpcResult<T = unknown> =\n | {ok: true; value: T}\n | {ok: false; error: {code: string; message: string; details: object}};\nexport interface NavigationCommand {requestRef: string; sessionId: string; expiresAt: number}\nexport interface NavigationRpc {\n call(channel: string, endpoint: string, payload: unknown, signal?: AbortSignal): Promise<RpcResult>;\n}\nexport interface HostNavigationRpc {\n handle(channel: string, handler: (endpoint: string, payload: unknown, signal: AbortSignal, peer: unknown) => Promise<RpcResult>): () => void | Promise<void>;\n}\nexport function opaqueRef(value: unknown): value is string {\n return typeof value === \"string\" && /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(value);\n}\nexport function sessionIdentity(value: unknown): value is string {\n return typeof value === \"string\" && value.length > 0 && value.length <= 256\n && !Array.from(value).some(character => character.charCodeAt(0) < 32);\n}\nexport function object(value: unknown): Record<string, unknown> | undefined {\n return value && typeof value === \"object\" && !Array.isArray(value) ? value as Record<string, unknown> : undefined;\n}\n", "import {randomUUID} from \"node:crypto\";\nimport {object, opaqueRef, type NavigationCommand, type RpcResult} from \"./navigation-rpc.js\";\n\nexport const DESKTOP_NAVIGATION_TIMEOUT_MS = 8_000;\nconst POLL_TIMEOUT_MS = 15_000;\nconst OWNER_GRACE_MS = 750;\ninterface Pending {\n sessionId: string;\n expiresAt: number;\n delivery?: {clientRef: string; requestRef: string};\n finish(opened: boolean): void;\n}\ninterface Owner {\n clientRef: string;\n peer: unknown;\n poll?: {finish(value: NavigationCommand | null): void; cancel(): void};\n grace?: ReturnType<typeof setTimeout>;\n}\nfunction unavailable(): RpcResult {\n return {ok: false, error: {code: \"starlite/navigation-unavailable\", message: \"DSH navigation unavailable\", details: {}}};\n}\nfunction busy(): RpcResult {\n return {ok: false, error: {code: \"starlite/navigation-busy\", message: \"DSH navigation renderer is still connected\", details: {}}};\n}\n\n/** One Desktop renderer owns navigation; a reload may resume only an unexpired request. */\nexport class DesktopNavigationBroker {\n #pending: Pending | undefined;\n #owner: Owner | undefined;\n #closed = false;\n\n request(sessionId: string): {result: Promise<void>; cancel(): void} {\n this.#pending?.finish(false);\n let pending: Pending;\n const result = new Promise<void>((resolve, reject) => {\n const timer = setTimeout(() => pending.finish(false), DESKTOP_NAVIGATION_TIMEOUT_MS);\n timer.unref?.();\n pending = {\n sessionId, expiresAt: Date.now() + DESKTOP_NAVIGATION_TIMEOUT_MS,\n finish: opened => {\n if (this.#pending !== pending) return;\n clearTimeout(timer);\n this.#pending = undefined;\n if (opened) resolve(); else reject(Error(\"DSH Desktop session unavailable\"));\n },\n };\n this.#pending = pending;\n });\n // The opener may reject before the caller begins awaiting the result.\n void result.catch(() => undefined);\n if (this.#closed) pending!.finish(false);\n else this.#deliver();\n return {result, cancel: () => pending!.finish(false)};\n }\n\n async handle(endpoint: string, payload: unknown, signal: AbortSignal, peer: unknown): Promise<RpcResult> {\n const row = object(payload);\n if (this.#closed || signal.aborted || !row || !opaqueRef(row.clientRef) || !peer) return unavailable();\n if (endpoint === \"listen\" && Object.keys(row).length === 1) return this.#listen(row.clientRef, peer, signal);\n const owner = this.#owner;\n if (!owner || owner.clientRef !== row.clientRef || owner.peer !== peer) return unavailable();\n if (endpoint === \"release\" && Object.keys(row).length === 1) {\n this.#release(owner);\n return {ok: true, value: null};\n }\n if (endpoint !== \"ack\" || Object.keys(row).length !== 3 || !opaqueRef(row.requestRef)\n || typeof row.opened !== \"boolean\") return unavailable();\n const pending = this.#pending;\n if (!pending || pending.expiresAt <= Date.now() || pending.delivery?.clientRef !== row.clientRef\n || pending.delivery.requestRef !== row.requestRef) return unavailable();\n pending.finish(row.opened);\n return {ok: true, value: null};\n }\n\n close(): void {\n if (this.#closed) return;\n this.#closed = true;\n this.#pending?.finish(false);\n if (this.#owner) this.#release(this.#owner);\n }\n\n #listen(clientRef: string, peer: unknown, signal: AbortSignal): Promise<RpcResult> {\n let owner = this.#owner;\n if (owner && (owner.clientRef !== clientRef || owner.peer !== peer || owner.poll)) return Promise.resolve(busy());\n if (!owner) {owner = {clientRef, peer}; this.#owner = owner;}\n clearTimeout(owner.grace);\n const currentOwner = owner;\n return new Promise(resolve => {\n let settled = false;\n const timer = setTimeout(() => finish(null), POLL_TIMEOUT_MS);\n timer.unref?.();\n const cancel = () => {\n if (settled) return;\n this.#release(currentOwner);\n };\n const finish = (value: NavigationCommand | null) => {\n if (settled) return;\n settled = true;\n clearTimeout(timer);\n signal.removeEventListener(\"abort\", cancel);\n delete currentOwner.poll;\n if (this.#owner === currentOwner) {\n currentOwner.grace = setTimeout(() => this.#release(currentOwner), OWNER_GRACE_MS);\n currentOwner.grace.unref?.();\n }\n resolve({ok: true, value});\n };\n currentOwner.poll = {finish, cancel};\n signal.addEventListener(\"abort\", cancel, {once: true});\n this.#deliver();\n });\n }\n\n #deliver(): void {\n const pending = this.#pending, owner = this.#owner;\n if (!pending || !owner?.poll || pending.delivery || pending.expiresAt <= Date.now()) return;\n const requestRef = randomUUID();\n pending.delivery = {clientRef: owner.clientRef, requestRef};\n owner.poll.finish({requestRef, sessionId: pending.sessionId, expiresAt: pending.expiresAt});\n }\n\n #release(owner: Owner): void {\n if (this.#owner !== owner) return;\n this.#owner = undefined;\n clearTimeout(owner.grace);\n // A renderer that reloads must receive a new acknowledgement identity.\n if (this.#pending?.delivery?.clientRef === owner.clientRef) delete this.#pending.delivery;\n owner.poll?.finish(null);\n }\n}\n", "import {spawn} from \"node:child_process\";\nimport {DesktopNavigationBroker, DESKTOP_NAVIGATION_TIMEOUT_MS} from \"./desktop-navigation.js\";\nimport {NAVIGATION_CHANNEL, object, sessionIdentity, type HostNavigationRpc} from \"./navigation-rpc.js\";\n\ninterface Services {\n get?(name:string):unknown;\n webServer?: unknown;\n extend?(metadata: {webServer: unknown}): Services;\n inject?(dependencies: string[], callback: (scope: Services) => (() => Promise<void>) | undefined): {dispose(): Promise<void>};\n}\nexport interface DshNavigator {open(key: unknown): Promise<void>; close(): Promise<void>}\n/** Profile ownership, not Electron's executable identity, selects Desktop navigation. */\nexport function createDshNavigator(context: Services, openUrl = openLocalBrowser): DshNavigator {\n if (object(context.get?.(\"profileContext\"))?.name !== \"desktop\") {\n return {open: key => openDshSession(context, key, openUrl), close: async () => undefined};\n }\n let closed = false;\n const active = new Set<AbortController>();\n let binding: {broker: DesktopNavigationBroker; remove(): void | Promise<void>} | undefined;\n const attach = (scope: Services) => {\n if (closed) return;\n const rpc = object(object(scope.get?.(\"connection\"))?.rpc) as unknown as HostNavigationRpc | undefined;\n if (typeof rpc?.handle !== \"function\") throw Error(\"DSH Desktop navigation contract unavailable\");\n const broker = new DesktopNavigationBroker();\n const remove = rpc.handle(NAVIGATION_CHANNEL, (endpoint, payload, signal, peer) => broker.handle(endpoint, payload, signal, peer));\n if (typeof remove !== \"function\") {broker.close(); throw Error(\"DSH Desktop navigation disposal unavailable\");}\n const current = {broker, remove};\n binding = current;\n return async () => {\n if (binding === current) {\n binding = undefined;\n for (const abort of active) abort.abort();\n }\n broker.close();\n await remove();\n };\n };\n // Cordis mounts profile bundles in parallel. The Connection service is not\n // necessarily ready when this plugin starts; own the route through its public\n // dependency lifecycle so withdrawal/replacement also revokes pending targets.\n const injected = context.inject?.([\"connection\", \"webServer\"], scope => {\n try {\n // Connection's public RPC getter captures a Cordis shadow context. Bind\n // the declared carrier as public context metadata so that getter retains\n // this fiber's WebServer instead of reading an uninjected provider scope.\n // extend() leaves the parent and official service untouched.\n if (!scope.extend) return undefined;\n return attach(scope.extend({webServer: scope.webServer}));\n } catch {return undefined;} // Navigation must not stop DSH boot.\n });\n const detach = context.inject ? undefined : attach(context);\n return {\n async open(key) {\n if (closed) throw Error(\"DSH Desktop unavailable\");\n const id = record(key)?.id;\n if (key !== undefined && !sessionIdentity(id)) throw Error(\"DSH session unavailable\");\n if (key !== undefined && !binding) throw Error(\"DSH Desktop session unavailable\");\n const request = key === undefined ? undefined : binding!.broker.request(id as string);\n const abort = new AbortController();\n active.add(abort);\n let timer: ReturnType<typeof setTimeout> | undefined;\n const deadline = new Promise<never>((_resolve, reject) => {\n abort.signal.addEventListener(\"abort\", () => {request?.cancel(); reject(Error(\"DSH Desktop session unavailable\"));}, {once: true});\n timer = setTimeout(() => abort.abort(), DESKTOP_NAVIGATION_TIMEOUT_MS);\n timer.unref?.();\n });\n try {await Promise.race([Promise.all([openUrl(\"dsh://open\", abort.signal), request?.result]), deadline]);}\n catch (error) {request?.cancel(); throw error;}\n finally {active.delete(abort); clearTimeout(timer); abort.abort();}\n },\n async close() {\n if (closed) return;\n closed = true;\n for (const abort of active) abort.abort();\n await injected?.dispose();\n await detach?.();\n },\n };\n}\n/** An undefined key explicitly opens the Web UI home; supplied keys must identify a session. */\nexport async function openDshSession(context:Services,key:unknown,openUrl=openLocalBrowser):Promise<void>{\n const session=record(key), web=record(context.get?.(\"webServer\")), connection=record(context.get?.(\"connection\"));\n if(key!==undefined&&(typeof session?.id!==\"string\"||!session.id||session.id.length>256||Array.from(session.id).some(c=>c.charCodeAt(0)<32)))throw Error(\"DSH session unavailable\");\n if(web?.host!==\"127.0.0.1\"||!Number.isInteger(web.port)||Number(web.port)<1||Number(web.port)>65535\n ||typeof connection?.authenticatedUrl!==\"function\")throw Error(\"DSH Web UI unavailable\");\n const url=new URL(`http://127.0.0.1:${web.port}/`);\n const authenticated=(connection.authenticatedUrl as (base:string)=>unknown).call(connection,url.href);\n if(typeof authenticated!==\"string\")throw Error(\"DSH Web UI unavailable\");\n const target=new URL(authenticated);\n if(target.origin!==url.origin||target.pathname!==\"/\")throw Error(\"DSH Web UI unavailable\");\n // DSH's authentication redirect drops query parameters. A browser-owned\n // fragment survives that redirect and never enters an HTTP request/log.\n target.hash=key===undefined?\"\":new URLSearchParams({companionSession:session!.id as string}).toString();\n await openUrl(target.href);\n}\n// The opener needs OS launcher variables, never DSH/provider credentials.\nexport function browserEnvironment(source:NodeJS.ProcessEnv):NodeJS.ProcessEnv{\n const result:NodeJS.ProcessEnv={};\n for(const name of [\"PATH\",\"HOME\",\"LANG\",\"LC_ALL\",\"DISPLAY\",\"WAYLAND_DISPLAY\",\"XDG_RUNTIME_DIR\",\"XDG_CURRENT_DESKTOP\",\"DBUS_SESSION_BUS_ADDRESS\",\"WSL_INTEROP\",\"WSL_DISTRO_NAME\",\"SYSTEMROOT\",\"SystemRoot\",\"TEMP\",\"TMP\"]){\n if(source[name]!==undefined)result[name]=source[name];\n }\n return result;\n}\n/** Electron needs Node mode only to enter this worker, never for the GUI it opens. */\nexport function browserWorkerScript(openModuleUrl: string): string {\n // wait:true makes PowerShell wait for the GUI to quit. Instead, keep only\n // its launcher alive until it reports whether Start-Process succeeded.\n return `delete process.env.ELECTRON_RUN_AS_NODE;\n const {default:open}=await import(${JSON.stringify(openModuleUrl)});\n const launcher=await open(process.argv[1],{wait:false});\n launcher.ref();\n await new Promise((resolve,reject)=>{\n let settled=false;\n const finish=(code,signal)=>{\n if(settled)return;settled=true;\n launcher.off(\"error\",failed);launcher.off(\"exit\",finish);\n if(code===0&&!signal)resolve();else reject(Error(\"DSH launcher failed\"));\n };\n const failed=()=>finish(null,null);\n launcher.once(\"error\",failed);launcher.once(\"exit\",finish);\n if(launcher.exitCode!==null||launcher.signalCode!==null)finish(launcher.exitCode,launcher.signalCode);\n });`;\n}\nasync function openLocalBrowser(url:string, signal?: AbortSignal):Promise<void>{\n const script = browserWorkerScript(import.meta.resolve(\"open\"));\n await new Promise<void>((resolve,reject)=>{\n const env = browserEnvironment(process.env);\n // Desktop's bundled CLI also uses Electron. Its child must remain in Node\n // mode even though provider credentials and arbitrary Node options are stripped.\n if (process.versions.electron) env.ELECTRON_RUN_AS_NODE = \"1\";\n const child=spawn(process.execPath,[\"--input-type=module\",\"--eval\",script,url],{env,stdio:\"ignore\",windowsHide:true,shell:false,...signal === undefined ? {} : {signal}});\n const timer=setTimeout(()=>{child.kill();reject(Error(\"DSH Web UI could not open\"))},10000);timer.unref();\n child.once(\"error\",()=>{clearTimeout(timer);reject(Error(\"DSH Web UI could not open\"))});\n child.once(\"exit\",code=>{clearTimeout(timer);if(code===0)resolve();else reject(Error(\"DSH Web UI could not open\"))});\n });\n}\nfunction record(value:unknown):Record<string,unknown>|undefined{return value&&typeof value===\"object\"?value as Record<string,unknown>:undefined}\n", "import { randomUUID } from \"node:crypto\";\r\nimport { DshStateAggregator, type AgentRunStatus, type DshEventLike } from \"./aggregator.js\";\r\nimport { detectCompanionHost, launchWindowsPet, type WindowsCompanionHost } from \"./launcher.js\";\r\nimport { startStatusServer, type StatusServer } from \"./server.js\";\r\nimport { DeepSeekBilling, type BillingContext } from \"./billing.js\";\r\nimport {QuestionBridge} from \"./questions.js\";\r\nimport {createDshNavigator} from \"./navigation.js\";\r\nimport {DshStartupError} from \"./startup-error.js\";\r\n\r\ntype Listener = (...arguments_: unknown[]) => unknown;\r\ntype Disposer = () => void | Promise<void>;\r\n\r\ninterface LoggerLike {\r\n info?(message: string): void;\r\n warn?(message: string): void;\r\n}\r\n\r\nexport interface CordisContextLike extends BillingContext {\r\n on(event: string, listener: Listener, options?: {prepend?:boolean}): unknown;\r\n logger?: LoggerLike;\r\n}\r\n\r\nexport interface AdapterDependencies {\r\n readonly host?: () => WindowsCompanionHost | undefined;\r\n readonly startServer?: typeof startStatusServer;\r\n readonly launch?: typeof launchWindowsPet;\r\n readonly openBrowser?: (url:string)=>Promise<void>;\r\n}\r\n\r\nexport async function startDshAdapter(\r\n context: CordisContextLike,\r\n dependencies: AdapterDependencies = {},\r\n): Promise<Disposer> {\r\n if (!context || typeof context.on !== \"function\") {\r\n throw new DshStartupError(\"incompatible-api\");\r\n }\r\n const host = (dependencies.host ?? detectCompanionHost)();\r\n if (!host) {\r\n throw new DshStartupError(\"unsupported-platform\");\r\n }\r\n\r\n const navigator = createDshNavigator(context, dependencies.openBrowser);\r\n const aggregator = new DshStateAggregator();\r\n const billing = new DeepSeekBilling(context);\r\n const questions = new QuestionBridge(aggregator,key=>navigator.open(key),context);\r\n let server: StatusServer;\r\n try {server = await (dependencies.startServer ?? startStatusServer)(aggregator, {billing,questions});}\r\n catch (error) {questions.close(); billing.close(); await navigator.close(); throw error;}\r\n const refreshTitle=(key:unknown)=>{\r\n try{const service=asRecord(context.get?.(\"sessionTitle\"));const value=typeof service?.get===\"function\"?asRecord(service.get.call(service,key)):undefined;\r\n aggregator.sessionInfo(key,typeof value?.title===\"string\"?value.title:undefined);\r\n }catch{aggregator.sessionInfo(key)}\r\n };\r\n const disposers: Disposer[] = [() => navigator.close()];\r\n let approvalSequence = 0;\r\n\r\n try {\r\n subscribeRequired(context, disposers, \"agent/status\", (...arguments_) => {\r\n const payload = asRecord(arguments_[0]);\r\n const agent = payload?.agent;\r\n const status = payload?.status;\r\n if ((status === \"idle\" || status === \"running\") && agent) {\r\n refreshTitle(subjectKey(agent));\r\n aggregator.handleAgentStatus(subjectKey(agent), status as AgentRunStatus);\r\n questions.refresh();\r\n }\r\n });\r\n subscribeRequired(context, disposers, \"agent/error\", (...arguments_) => {\r\n const payload = asRecord(arguments_[0]);\r\n if (payload?.agent) {const key=subjectKey(payload.agent);questions.cancelSession(key);refreshTitle(key);aggregator.handleAgentError(key);}\r\n });\r\n subscribeRequired(context, disposers, \"agent/assistant-stream\", (...arguments_) => {\r\n const payload = asRecord(arguments_[0]);\r\n if (payload?.agent) {\r\n refreshTitle(subjectKey(payload.agent));\r\n aggregator.handleAssistantStream(subjectKey(payload.agent), payload.frame);\r\n }\r\n });\r\n subscribeRequired(context, disposers, \"session/event\", (...arguments_) => {\r\n const session = arguments_[0];\r\n const event = asRecord(arguments_[1]);\r\n if (session && typeof event?.type === \"string\") {\r\n const key=subjectKey(session);refreshTitle(key);\r\n if(event.type===\"turn/end\")questions.cancelForeground(key);\r\n aggregator.handleSessionEvent(subjectKey(session), event as unknown as DshEventLike);\r\n questions.refresh();\r\n if (event.type === \"assistant/message\" || event.type === \"assistant/attempt\") billing.invalidate();\r\n }\r\n });\r\n subscribeRequired(context, disposers, \"approval/request\", async (...arguments_) => {\r\n const request = asRecord(arguments_[0]);\r\n const next = arguments_[1];\r\n if (typeof next !== \"function\") {\r\n // Do not invent a result for a changed waterfall contract. The daily\r\n // declaration guard should catch this before a release reaches users.\r\n return undefined;\r\n }\r\n if (!request?.agent) return next();\r\n const key = subjectKey(request.agent);\r\n const approvalKey = String(approvalSequence);\r\n approvalSequence += 1;\r\n aggregator.handleApprovalRequested(key, approvalKey, request.toolName);\r\n try {\r\n return await next();\r\n } finally {\r\n aggregator.handleApprovalResolved(key, approvalKey);\r\n }\r\n });\r\n subscribeRequired(context,disposers,\"user-questions/request\",(...arguments_)=>{\r\n const request=asRecord(arguments_[0]),next=arguments_[1];\r\n if(typeof next!==\"function\")return undefined;\r\n if(!request?.agent)return next();\r\n const key=subjectKey(request.agent);refreshTitle(key);\r\n return questions.ask(key,request,next as ()=>Promise<unknown>);\r\n },{prepend:true});\r\n subscribeOptional(context, disposers, \"agent/disposed\", (...arguments_) => {\r\n const payload = asRecord(arguments_[0]);\r\n if (payload?.agent) {const key=subjectKey(payload.agent);questions.cancelSession(key);aggregator.disposeSession(key);}\r\n });\r\n for (const event of [\"settings/updated\", \"settings/document-updated\", \"credentials/reference-updated\", \"credentials/record-updated\"]) {\r\n subscribeOptional(context, disposers, event, () => billing.invalidate(true));\r\n }\r\n } catch (error) {\r\n questions.close();billing.close();\r\n await closeAdapter(disposers, server);\r\n throw error;\r\n }\r\n\r\n const launchAbort = new AbortController();\r\n void (dependencies.launch ?? launchWindowsPet)({\r\n port: server.port,\r\n token: server.token,\r\n instanceId: randomUUID(),\r\n }, {\r\n host,\r\n signal: launchAbort.signal,\r\n onDefaultModelFailure: () => {\r\n if (!launchAbort.signal.aborted) context.logger?.warn?.(\r\n \"StarLite could not acquire the starter character. The companion will show recovery instructions; existing models are preserved.\",\r\n );\r\n },\r\n }).catch(() => {\r\n if (launchAbort.signal.aborted) return;\r\n context.logger?.warn?.(\r\n \"StarLite for DSH could not start the Windows companion; DSH will continue without it.\",\r\n );\r\n });\r\n context.logger?.info?.(\"StarLite for DSH status bridge is ready.\");\r\n\r\n let closed = false;\r\n return async () => {\r\n if (closed) return;\r\n closed = true;\r\n launchAbort.abort();\r\n billing.close();\r\n questions.close();\r\n await closeAdapter(disposers, server);\r\n };\r\n}\r\n\r\nfunction subscribeRequired(\r\n context: CordisContextLike,\r\n disposers: Disposer[],\r\n event: string,\r\n listener: Listener,\r\n options?: {prepend?:boolean},\r\n): void {\r\n try {\r\n const result = context.on(event, listener, options);\r\n if (typeof result === \"function\") disposers.push(result as Disposer);\r\n } catch {\r\n throw new DshStartupError(\"incompatible-api\");\r\n }\r\n}\r\n\r\nfunction subscribeOptional(\r\n context: CordisContextLike,\r\n disposers: Disposer[],\r\n event: string,\r\n listener: Listener,\r\n): void {\r\n try {\r\n subscribeRequired(context, disposers, event, listener);\r\n } catch {\r\n // Older preview builds may not expose disposal. Core status events remain\r\n // mandatory, while a missing optional lifecycle signal must not stop DSH.\r\n }\r\n}\r\n\r\nasync function closeAdapter(disposers: Disposer[], server: StatusServer): Promise<void> {\r\n const tasks = disposers.splice(0).reverse().map(async (dispose) => dispose());\r\n tasks.push(server.close());\r\n await Promise.allSettled(tasks);\r\n}\r\n\r\nfunction subjectKey(subject: unknown): unknown {\r\n const record = asRecord(subject);\r\n return record?.session ?? subject;\r\n}\r\n\r\nfunction asRecord(value: unknown): Record<string, unknown> | undefined {\r\n return typeof value === \"object\" && value !== null && !Array.isArray(value)\r\n ? (value as Record<string, unknown>)\r\n : undefined;\r\n}\r\n", "export const name = \"dsh-starlite\";\n\nimport { DshStartupError } from \"./startup-error.js\";\n\r\ninterface PluginContext {\n get?(name: string): unknown;\n on(event: string, listener: (...arguments_: unknown[]) => void): unknown;\r\n logger?: {\r\n warn?(message: string): void;\r\n };\r\n}\r\n\r\n/**\r\n * Compatibility boundary for DSH's developer-preview API. Import and feature\r\n * failures are contained here so the pet can never prevent DSH from booting.\r\n */\r\nexport async function apply(context: PluginContext): Promise<() => Promise<void>> {\r\n try {\r\n const { startDshAdapter } = await import(\"./adapter.js\");\r\n const dispose = await startDshAdapter(context);\r\n return async () => {\r\n await dispose();\r\n };\r\n } catch (error) {\n context.logger?.warn?.(\r\n error instanceof DshStartupError ? error.message\n : \"StarLite for DSH could not start its local status bridge. Restart DSH and check the StarLite installation; DSH will continue without the companion.\",\n );\r\n return async () => undefined;\r\n }\r\n}\r\n"],
|
|
4
|
+
"sourcesContent": ["export type StartupFailure = \"unsupported-platform\" | \"incompatible-api\";\r\n\r\n/** Known startup failures have fixed diagnostics; Host errors may contain secrets. */\r\nexport class DshStartupError extends Error {\r\n constructor(readonly code: StartupFailure) {\r\n super(code === \"unsupported-platform\"\r\n ? \"StarLite for DSH requires Windows 11 x64, either directly or through WSL2 x64 on that Windows host. Other platforms and architectures are unsupported; DSH will continue without the companion.\"\r\n : \"StarLite for DSH is disabled because this DSH build does not provide the required plugin API. Update DSH and StarLite to compatible versions; DSH will continue without the companion.\");\r\n this.name = \"DshStartupError\";\r\n }\r\n}\r\n", "/** Read-only financial summary. Never includes credentials or session identity. */\r\nexport type BillingCurrency = \"CNY\" | \"USD\";\r\nexport interface BillingSnapshot {\r\n billingVersion: 1;\r\n checkedAt: number;\r\n pricingPeriod?: \"peak\" | \"off-peak\" | \"unknown\";\r\n balance: {\r\n status: \"ready\" | \"unconfigured\" | \"unavailable\";\r\n source?: \"deepseek-official\" | \"deepseek\";\r\n balances: { currency: BillingCurrency; remaining: number }[];\r\n };\r\n today: {\r\n date: string;\r\n timeZone: \"Asia/Shanghai\";\r\n partial: boolean;\r\n pricedRequests: number;\r\n unpricedRequests: number;\r\n pricingDate: string;\r\n estimates: { currency: BillingCurrency; minimum: number; maximum: number }[];\r\n };\r\n}\r\n\r\nexport function parseBillingSnapshot(value: unknown): BillingSnapshot {\r\n const root = record(value);\r\n const balance = record(root.balance);\r\n const today = record(root.today);\r\n if (root.billingVersion !== 1 || !timestamp(root.checkedAt)\r\n || ![\"ready\", \"unconfigured\", \"unavailable\"].includes(String(balance.status))\r\n || today.timeZone !== \"Asia/Shanghai\" || typeof today.partial !== \"boolean\"\r\n || !date(today.date) || !date(today.pricingDate)\r\n || !count(today.pricedRequests) || !count(today.unpricedRequests)) throw new Error(\"invalid billing summary\");\r\n const balances = currencies(balance.balances).map(entry => {\r\n if (!signedAmount(entry.remaining)) throw new Error(\"invalid balance\");\r\n return {currency: entry.currency as BillingCurrency, remaining: entry.remaining as number};\r\n });\r\n if ((balance.status === \"ready\") !== (balances.length > 0)) throw new Error(\"invalid balance status\");\r\n if (root.pricingPeriod !== undefined && ![\"peak\",\"off-peak\",\"unknown\"].includes(String(root.pricingPeriod))) throw new Error(\"invalid pricing period\");\r\n if (balance.source !== undefined && (![\"deepseek-official\",\"deepseek\"].includes(String(balance.source)) || balance.status === \"unconfigured\")) throw new Error(\"invalid balance source\");\r\n const estimates = currencies(today.estimates).map(entry => {\r\n if (!amount(entry.minimum) || !amount(entry.maximum) || (entry.minimum as number) > (entry.maximum as number)) throw new Error(\"invalid estimate\");\r\n return {currency: entry.currency as BillingCurrency, minimum: entry.minimum as number, maximum: entry.maximum as number};\r\n });\r\n return {\r\n billingVersion: 1, checkedAt: root.checkedAt as number,\r\n ...(root.pricingPeriod === undefined ? {} : {pricingPeriod: root.pricingPeriod as NonNullable<BillingSnapshot[\"pricingPeriod\"]>}),\r\n balance: {status: balance.status as BillingSnapshot[\"balance\"][\"status\"], balances,\r\n ...(balance.source === undefined ? {} : {source: balance.source as NonNullable<BillingSnapshot[\"balance\"][\"source\"]>})},\r\n today: {\r\n date: today.date as string, timeZone: \"Asia/Shanghai\", partial: today.partial,\r\n pricedRequests: today.pricedRequests as number, unpricedRequests: today.unpricedRequests as number,\r\n pricingDate: today.pricingDate as string, estimates,\r\n },\r\n };\r\n}\r\n\r\nfunction record(value: unknown): Record<string, unknown> {\r\n if (!value || typeof value !== \"object\" || Array.isArray(value)) throw new Error(\"invalid billing object\");\r\n return value as Record<string, unknown>;\r\n}\r\nfunction currencies(value: unknown): Record<string, unknown>[] {\r\n if (!Array.isArray(value) || value.length > 2) throw new Error(\"invalid currencies\");\r\n const entries = value.map(record);\r\n if (entries.some(entry => entry.currency !== \"CNY\" && entry.currency !== \"USD\")\r\n || new Set(entries.map(entry => entry.currency)).size !== entries.length) throw new Error(\"invalid currencies\");\r\n return entries;\r\n}\r\nfunction amount(value: unknown): boolean { return typeof value === \"number\" && Number.isFinite(value) && value >= 0 && value <= 1_000_000_000; }\r\nfunction signedAmount(value: unknown): boolean { return typeof value === \"number\" && Number.isFinite(value) && Math.abs(value) <= 1_000_000_000; }\r\nfunction count(value: unknown): boolean { return Number.isSafeInteger(value) && (value as number) >= 0 && (value as number) <= 1_000_000; }\r\nfunction timestamp(value: unknown): boolean { return Number.isSafeInteger(value) && (value as number) >= 0; }\r\nfunction date(value: unknown): boolean {\r\n return typeof value === \"string\" && /^\\d{4}-\\d{2}-\\d{2}$/u.test(value)\r\n && Number.isFinite(Date.parse(value)) && new Date(value).toISOString().slice(0,10) === value;\r\n}\r\n", "export const MAX_INTERACTION_BYTES = 32_768;\r\nexport const ACTIVITIES = [\"thinking\", \"responding\", \"command\", \"editing\", \"reading\", \"searching\", \"compacting\", \"tool\", \"waiting\", \"completed\", \"error\", \"blocked\", \"idle\"] as const;\r\nexport type PetActivity = typeof ACTIVITIES[number];\r\nexport interface PetSession { ref: string; title: string }\r\nexport interface PetQuestion { id: string; question: string; header?: string; detail?: string; options: Array<{label: string; description?: string}>; multiSelect: boolean }\r\nexport type PetQuestionWait = {state:\"timed\";deadline:number;issuedAt:number}|{state:\"continued\"};\r\nexport interface PetInteraction { interactionVersion: 1; requestId: string; sessionRef: string; questions: PetQuestion[]; wait?:PetQuestionWait }\r\nexport interface PetAnswer { requestId: string; sessionRef: string; answers: Array<{id: string; selected: string[]; custom?: string}> }\r\n\r\nexport function isOpaqueRef(value: unknown): value is string {\r\n return typeof value === \"string\" && /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/iu.test(value);\r\n}\r\nexport function parsePetSession(value: unknown): PetSession {\r\n const row = record(value);\r\n if (!isOpaqueRef(row.ref)) throw Error(\"invalid session reference\");\r\n return {ref:row.ref,title:text(row.title,320)};\r\n}\r\nexport function parsePetInteraction(value: unknown): PetInteraction {\r\n const row = record(value);\r\n if(row.interactionVersion!==1 || !isOpaqueRef(row.requestId) || !isOpaqueRef(row.sessionRef)) throw Error(\"invalid interaction identity\");\r\n const ids=new Set<string>();\r\n const questions=list(row.questions,8).map(raw=>{\r\n const item=record(raw),id=text(item.id,128); if(ids.has(id)) throw Error(\"duplicate question\"); ids.add(id);\r\n const labels=new Set<string>();\r\n const options=item.options===undefined?[]:list(item.options,20,true).map(rawOption=>{\r\n const option=record(rawOption),label=text(option.label,1024);if(labels.has(label))throw Error(\"duplicate option\");labels.add(label);\r\n return {label,...(option.description===undefined?{}:{description:text(option.description,2048)})};\r\n });\r\n if(item.multiSelect!==undefined && typeof item.multiSelect!==\"boolean\")throw Error(\"invalid multi-select flag\");\r\n return {id,question:text(item.question,4096),options,multiSelect:item.multiSelect===true,\r\n ...(item.header===undefined?{}:{header:text(item.header,128)}),...(item.detail===undefined?{}:{detail:text(item.detail,8192)})};\r\n });\r\n let wait:PetQuestionWait|undefined;\r\n if(row.wait!==undefined){\r\n const value=record(row.wait);\r\n if(value.state===\"continued\")wait={state:\"continued\"};\r\n else if(value.state===\"timed\"&&Number.isSafeInteger(value.deadline)&&Number(value.deadline)>=0&&Number(value.deadline)<=8_640_000_000_000_000\r\n &&Number.isSafeInteger(value.issuedAt)&&Number(value.issuedAt)>=0&&Number(value.issuedAt)<=Number(value.deadline)&&Number(value.deadline)-Number(value.issuedAt)<=2_147_483_647)wait={state:\"timed\",deadline:Number(value.deadline),issuedAt:Number(value.issuedAt)};\r\n else throw Error(\"invalid question wait\");\r\n }\r\n const result:PetInteraction={interactionVersion:1,requestId:row.requestId,sessionRef:row.sessionRef,questions,...(wait?{wait}:{})};\r\n bound(result);return result;\r\n}\r\nexport function parsePetAnswer(value: unknown, request?: PetInteraction): PetAnswer {\r\n const row=record(value);if(!isOpaqueRef(row.requestId)||!isOpaqueRef(row.sessionRef))throw Error(\"invalid answer identity\");\r\n const ids=new Set<string>();\r\n const answers=list(row.answers,8).map(raw=>{\r\n const item=record(raw),id=text(item.id,128);if(ids.has(id))throw Error(\"duplicate answer\");ids.add(id);\r\n const selected=list(item.selected,20,true).map(label=>text(label,1024));\r\n if(new Set(selected).size!==selected.length)throw Error(\"duplicate selection\");\r\n const custom=item.custom===undefined?undefined:text(item.custom,8192);\r\n if(!selected.length&&!custom)throw Error(\"empty answer\");\r\n return {id,selected,...(custom===undefined?{}:{custom})};\r\n });\r\n const result:PetAnswer={requestId:row.requestId,sessionRef:row.sessionRef,answers};bound(result);\r\n if(request){\r\n if(result.requestId!==request.requestId||result.sessionRef!==request.sessionRef||answers.length!==request.questions.length)throw Error(\"stale or incomplete answer\");\r\n for(const item of answers){\r\n const question=request.questions.find(q=>q.id===item.id);\r\n if(!question||(!question.multiSelect&&(item.selected.length>1||item.selected.length>0&&item.custom!==undefined)))throw Error(\"invalid selection\");\r\n if(item.selected.some(label=>!question.options.some(option=>option.label===label)))throw Error(\"unknown option\");\r\n }\r\n }\r\n return result;\r\n}\r\nfunction record(value:unknown):Record<string,unknown>{if(!value||typeof value!==\"object\"||Array.isArray(value))throw Error(\"invalid interaction object\");return value as Record<string,unknown>}\r\nfunction text(value:unknown,max:number):string{\r\n // Preserve exact option labels and multiline answers; render only as text.\r\n // oxlint-disable-next-line no-control-regex\r\n if(typeof value!==\"string\"||!value.trim()||value.length>max||/[\\u0000-\\u0008\\u000b\\u000c\\u000e-\\u001f\\u007f-\\u009f]/u.test(value))throw Error(\"invalid interaction text\");return value;\r\n}\r\nfunction list(value:unknown,max:number,empty=false):unknown[]{if(!Array.isArray(value)||value.length>max||(!empty&&!value.length))throw Error(\"invalid interaction list\");return value}\r\nfunction bound(value:unknown):void{if(new TextEncoder().encode(JSON.stringify(value)).byteLength>MAX_INTERACTION_BYTES)throw Error(\"interaction exceeds wire limit\")}\r\n", "export { parseBillingSnapshot } from \"./billing.js\";\r\nexport type { BillingCurrency, BillingSnapshot } from \"./billing.js\";\r\nexport { ACTIVITIES, MAX_INTERACTION_BYTES, isOpaqueRef, parsePetSession, parsePetInteraction, parsePetAnswer } from \"./interaction.js\";\r\nexport type { PetActivity, PetSession, PetQuestion, PetQuestionWait, PetInteraction, PetAnswer } from \"./interaction.js\";\r\nimport { ACTIVITIES, isOpaqueRef, parsePetSession, type PetActivity, type PetSession } from \"./interaction.js\";\r\n\r\nexport const PET_PROTOCOL_VERSION = 1 as const;\r\nexport const DEFAULT_BUBBLE_GRAPHEMES = 120;\r\nexport const MAX_WIRE_BYTES = 4_096;\r\nconst MAX_SANITIZE_CODE_UNITS = 16_384;\r\n\r\nexport const PET_STATES = [\r\n \"idle\",\r\n \"thinking\",\r\n \"working\",\r\n \"completed\",\r\n \"error\",\r\n \"blocked\",\r\n \"approval\",\r\n] as const;\r\n\r\nexport type PetState = (typeof PET_STATES)[number];\r\n\r\nexport const BUBBLE_KINDS = [\r\n \"reasoning\",\r\n \"response\",\r\n \"tool\",\r\n \"notice\",\r\n] as const;\r\n\r\nexport type BubbleKind = (typeof BUBBLE_KINDS)[number];\r\n\r\nexport interface PetBubble {\r\n kind: BubbleKind;\r\n text: string;\r\n}\r\n\r\nexport interface PetSnapshot {\r\n protocolVersion: typeof PET_PROTOCOL_VERSION;\r\n sequence: number;\r\n emittedAt: number;\r\n state: PetState;\r\n bubble?: PetBubble;\r\n session?: PetSession;\r\n activity?: PetActivity;\r\n inputRequest?: string;\r\n}\r\n\r\nexport interface PetStateSource {\r\n start(emit: (snapshot: PetSnapshot) => void): Promise<void>;\r\n stop(): Promise<void>;\r\n}\r\n\r\nexport interface PetConnectionDescriptor {\r\n readonly port: number;\r\n readonly token: string;\r\n readonly instanceId: string;\r\n}\r\n\r\n/** Strictly validates the one deep-link shape emitted by a local DSH Host. */\r\nexport function parsePetConnectionUrl(rawUrl: string): PetConnectionDescriptor {\r\n let url: URL;\r\n try {\r\n url = new URL(rawUrl);\r\n } catch {\r\n throw new Error(\"pet connection URL is invalid\");\r\n }\r\n if (\r\n url.protocol !== \"dsh-inochi2d-pet:\" ||\r\n url.hostname !== \"connect\" ||\r\n (url.pathname !== \"\" && url.pathname !== \"/\") ||\r\n url.username !== \"\" ||\r\n url.password !== \"\" ||\r\n url.port !== \"\" ||\r\n rawUrl.includes(\"#\")\r\n ) {\r\n throw new Error(\"pet connection URL target is invalid\");\r\n }\r\n const allowed = new Set([\"port\", \"token\", \"instance\"]);\r\n for (const key of url.searchParams.keys()) {\r\n if (!allowed.has(key)) throw new Error(\"pet connection URL contains unknown fields\");\r\n }\r\n for (const key of allowed) {\r\n if (url.searchParams.getAll(key).length !== 1) {\r\n throw new Error(`pet connection URL field ${key} must occur once`);\r\n }\r\n }\r\n const portText = url.searchParams.get(\"port\") ?? \"\";\r\n const port = Number(portText);\r\n const token = url.searchParams.get(\"token\") ?? \"\";\r\n const instanceId = url.searchParams.get(\"instance\") ?? \"\";\r\n if (!/^[1-9]\\d{0,4}$/.test(portText) || !Number.isInteger(port) || port > 65_535) {\r\n throw new Error(\"pet connection port is invalid\");\r\n }\r\n if (!/^[A-Za-z0-9_-]{43}$/.test(token)) {\r\n throw new Error(\"pet connection token is invalid\");\r\n }\r\n if (!/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/iu.test(instanceId)) {\r\n throw new Error(\"pet connection instance is invalid\");\r\n }\r\n return { port, token, instanceId };\r\n}\r\n\r\n/**\r\n * Reduces untrusted model-visible content to short, single-line plain text.\r\n * This function is deliberately shared by both sides of the WSL boundary.\r\n */\r\nexport function sanitizeBubbleText(\r\n input: unknown,\r\n maximumGraphemes = DEFAULT_BUBBLE_GRAPHEMES,\r\n): string {\r\n if (typeof input !== \"string\" || !Number.isSafeInteger(maximumGraphemes)) {\r\n return \"\";\r\n }\r\n if (maximumGraphemes < 1 || maximumGraphemes > 500) {\r\n return \"\";\r\n }\r\n // DSH-visible output can still be very large. Bound work before Unicode\r\n // normalization/segmentation, while keeping far more context than the 500\r\n // grapheme public maximum.\r\n const bounded = input.length > MAX_SANITIZE_CODE_UNITS\r\n ? input.slice(0, MAX_SANITIZE_CODE_UNITS)\r\n : input;\r\n const normalized = bounded\r\n .normalize(\"NFC\")\r\n // These ranges intentionally strip terminal and C0/C1 control bytes.\r\n // oxlint-disable-next-line no-control-regex\r\n .replace(/\\u001B(?:\\[[0-?]*[ -/]*[@-~]|\\][^\\u0007]*(?:\\u0007|\\u001B\\\\)?|[@-_])/gu, \"\")\r\n // oxlint-disable-next-line no-control-regex\r\n .replace(/[\\u0000-\\u0008\\u000B\\u000C\\u000E-\\u001F\\u007F-\\u009F]/gu, \"\")\r\n .replace(/\\s+/gu, \" \")\r\n .trim();\r\n if (normalized.length === 0) {\r\n return \"\";\r\n }\r\n const graphemes = segmentGraphemes(normalized);\r\n const truncated = graphemes.length > maximumGraphemes || input.length > MAX_SANITIZE_CODE_UNITS;\r\n return truncated\r\n ? `${graphemes.slice(0, maximumGraphemes - 1).join(\"\")}\u2026`\r\n : normalized;\r\n}\r\n\r\nexport function parsePetSnapshot(value: unknown): PetSnapshot {\r\n if (!isRecord(value)) {\r\n throw new Error(\"pet snapshot must be an object\");\r\n }\r\n if (value.protocolVersion !== PET_PROTOCOL_VERSION) {\r\n throw new Error(\"unsupported pet protocol version\");\r\n }\r\n if (!isNonNegativeSafeInteger(value.sequence)) {\r\n throw new Error(\"pet snapshot sequence is invalid\");\r\n }\r\n if (!isFiniteTimestamp(value.emittedAt)) {\r\n throw new Error(\"pet snapshot timestamp is invalid\");\r\n }\r\n if (!isPetState(value.state)) {\r\n throw new Error(\"pet snapshot state is invalid\");\r\n }\r\n const bubble = value.bubble === undefined ? undefined : parseBubble(value.bubble);\r\n const session = value.session === undefined ? undefined : parsePetSession(value.session);\r\n if(value.activity!==undefined && !(ACTIVITIES as readonly unknown[]).includes(value.activity))throw Error(\"invalid activity\");\r\n if(value.inputRequest!==undefined && (!session||!isOpaqueRef(value.inputRequest)))throw Error(\"invalid input reference\");\r\n return {\r\n protocolVersion: PET_PROTOCOL_VERSION,\r\n sequence: value.sequence,\r\n emittedAt: value.emittedAt,\r\n state: value.state,\r\n ...(bubble ? { bubble } : {}),\r\n ...(session ? {session}:{}),\r\n ...(value.activity===undefined?{}:{activity:value.activity as PetActivity}),\r\n ...(value.inputRequest===undefined?{}:{inputRequest:value.inputRequest as string}),\r\n };\r\n}\r\n\r\nexport function parsePetSnapshotJson(json: string): PetSnapshot {\r\n if (new TextEncoder().encode(json).byteLength > MAX_WIRE_BYTES) {\r\n throw new Error(\"pet snapshot exceeds the wire limit\");\r\n }\r\n return parsePetSnapshot(JSON.parse(json) as unknown);\r\n}\r\n\r\nexport function isPetState(value: unknown): value is PetState {\r\n return typeof value === \"string\" && (PET_STATES as readonly string[]).includes(value);\r\n}\r\n\r\nfunction parseBubble(value: unknown): PetBubble {\r\n if (!isRecord(value) || !isBubbleKind(value.kind)) {\r\n throw new Error(\"pet snapshot bubble is invalid\");\r\n }\r\n const text = sanitizeBubbleText(value.text);\r\n if (text.length === 0) {\r\n throw new Error(\"pet snapshot bubble text is empty or invalid\");\r\n }\r\n return { kind: value.kind, text };\r\n}\r\n\r\nfunction isBubbleKind(value: unknown): value is BubbleKind {\r\n return (\r\n typeof value === \"string\" &&\r\n (BUBBLE_KINDS as readonly string[]).includes(value)\r\n );\r\n}\r\n\r\nfunction isNonNegativeSafeInteger(value: unknown): value is number {\r\n return Number.isSafeInteger(value) && (value as number) >= 0;\r\n}\r\n\r\nfunction isFiniteTimestamp(value: unknown): value is number {\r\n return typeof value === \"number\" && Number.isFinite(value) && value >= 0;\r\n}\r\n\r\nfunction isRecord(value: unknown): value is Record<string, unknown> {\r\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\r\n}\r\n\r\nfunction segmentGraphemes(value: string): string[] {\r\n if (typeof Intl.Segmenter === \"function\") {\r\n const segmenter = new Intl.Segmenter(undefined, { granularity: \"grapheme\" });\r\n return [...segmenter.segment(value)].map(({ segment }) => segment);\r\n }\r\n return Array.from(value);\r\n}\r\n", "import {\r\n PET_PROTOCOL_VERSION,\r\n sanitizeBubbleText,\r\n type BubbleKind,\r\n type PetBubble,\r\n type PetSnapshot,\r\n type PetState,\r\n type PetStateSource,\r\n type PetSession,\r\n type PetActivity,\r\n} from \"@starcried/starlite-dsh-protocol\";\r\nimport {randomUUID} from \"node:crypto\";\r\n\r\nconst COMPLETED_LATCH_MS = 6_000;\r\nconst ERROR_LATCH_MS = 8_000;\r\nconst CONTENT_THROTTLE_MS = 200;\r\nconst QUESTION_TOOLS = new Set([\"ask_user_question\", \"request_user_input\"]);\r\n\r\ntype Timer = ReturnType<typeof setTimeout>;\r\n\r\nexport interface AggregatorClock {\r\n now(): number;\r\n setTimeout(callback: () => void, delayMs: number): Timer;\r\n clearTimeout(timer: Timer): void;\r\n}\r\n\r\nexport type AgentRunStatus = \"idle\" | \"running\";\r\n\r\nexport interface DshEventLike {\r\n readonly type: string;\r\n readonly data?: unknown;\r\n}\r\n\r\ninterface TimedText {\r\n readonly text: string;\r\n readonly at: number;\r\n readonly endsWithWhitespace: boolean;\r\n}\r\n\r\ninterface TimedTool {\r\n readonly name: string;\r\n readonly at: number;\r\n}\r\n\r\ninterface SessionState {\r\n ref: string;\r\n title: string;\r\n compacting: boolean;\r\n inputs: Map<string, {at:number;blocking:boolean}>;\r\n activityAt: number;\r\n running: boolean;\r\n turnActive: boolean;\r\n blockedAt: number | undefined;\r\n completedUntil: number | undefined;\r\n completedAt: number | undefined;\r\n errorUntil: number | undefined;\r\n errorAt: number | undefined;\r\n reasoning: TimedText | undefined;\r\n response: TimedText | undefined;\r\n activeTools: Map<string, TimedTool>;\r\n approvals: Map<string, TimedTool>;\r\n}\r\n\r\ninterface Candidate {\r\n readonly session?: PetSession;\r\n readonly activity?: PetActivity;\r\n readonly inputRequest?: string;\r\n readonly state: PetState;\r\n readonly bubble?: PetBubble;\r\n readonly at: number;\r\n}\r\n\r\nconst systemClock: AggregatorClock = {\r\n now: () => Date.now(),\r\n setTimeout: (callback, delayMs) => setTimeout(callback, delayMs),\r\n clearTimeout: (timer) => clearTimeout(timer),\r\n};\r\n\r\n/**\r\n * Reduces every session in one DSH process to the deliberately small wire\r\n * contract. Event payloads are parsed field-by-field so sensitive DSH fields\r\n * are neither copied nor serialized accidentally.\r\n */\r\nexport class DshStateAggregator implements PetStateSource {\r\n readonly #clock: AggregatorClock;\r\n readonly #sessions = new Map<unknown, SessionState>();\r\n #emit: ((snapshot: PetSnapshot) => void) | undefined;\r\n #sequence = 0;\r\n #lastEmittedAt = Number.NEGATIVE_INFINITY;\r\n #lastKey: string | undefined;\r\n #lastState: PetState | undefined;\r\n #lastInput: string | undefined;\r\n #contentTimer: Timer | undefined;\r\n #deadlineTimer: Timer | undefined;\r\n\r\n constructor(clock: AggregatorClock = systemClock) {\r\n this.#clock = clock;\r\n }\r\n\r\n async start(emit: (snapshot: PetSnapshot) => void): Promise<void> {\r\n if (this.#emit) {\r\n throw new Error(\"DSH pet state source is already started\");\r\n }\r\n this.#emit = emit;\r\n this.#publish(true);\r\n }\r\n\r\n async stop(): Promise<void> {\r\n this.#clearTimers();\r\n this.#emit = undefined;\r\n this.#lastKey = undefined;\r\n this.#lastState = undefined;\r\n this.#lastInput = undefined;\r\n }\r\n\r\n handleAgentStatus(key: unknown, status: AgentRunStatus): void {\r\n const session = this.#session(key);\r\n session.activityAt = this.#clock.now();\r\n session.running = status === \"running\";\r\n this.#changed();\r\n }\r\n\r\n sessionInfo(key: unknown, title?: string): PetSession {\r\n const session=this.#session(key);\r\n const clean=sanitizeBubbleText(title,80);\r\n if(clean && clean!==session.title){session.title=clean;this.#changed();}\r\n return {ref:session.ref,title:session.title};\r\n }\r\n sessionKey(ref: string): unknown {return [...this.#sessions].find(([,session])=>session.ref===ref)?.[0]}\r\n handleInputRequested(key: unknown, requestId: string, blocking=true): void {\r\n const session=this.#session(key);session.inputs.set(requestId,{at:this.#clock.now(),blocking});this.#changed();\r\n }\r\n handleInputResolved(key: unknown, requestId: string): void {\r\n if(this.#sessions.get(key)?.inputs.delete(requestId))this.#changed();\r\n }\r\n\r\n handleAgentError(key: unknown): void {\r\n const now = this.#clock.now();\r\n const session = this.#session(key);\r\n session.activityAt = now;\r\n session.errorAt = now;\r\n session.errorUntil = now + ERROR_LATCH_MS;\r\n session.running = false;\r\n session.turnActive = false;\r\n session.compacting = false;\r\n session.activeTools.clear();\r\n session.approvals.clear();\r\n this.#changed();\r\n }\r\n\r\n handleAssistantStream(key: unknown, rawFrame: unknown): void {\r\n const frame = asRecord(rawFrame);\r\n if (frame?.type !== \"start\" && frame?.type !== \"chunk\") return;\r\n const session = this.#session(key);\r\n const now = this.#clock.now();\r\n if (frame.type === \"start\") {\r\n // DSH 0.1.5 publishes live chunks outside the durable session log. A new\r\n // attempt must not concatenate its text with a previous step or retry.\r\n session.reasoning = undefined;\r\n session.response = undefined;\r\n } else {\r\n handleAssistantChunk(session, frame, now);\r\n }\r\n session.activityAt = now;\r\n this.#changed();\r\n }\r\n\r\n handleApprovalRequested(key: unknown, approvalKey: string, toolName: unknown): void {\r\n const name = safeToolName(typeof toolName === \"string\" ? toolName : undefined);\r\n if (!approvalKey || !name) return;\r\n const session = this.#session(key);\r\n session.activityAt = this.#clock.now();\r\n session.approvals.set(`live:${approvalKey}`, {\r\n name,\r\n at: session.activityAt,\r\n });\r\n this.#changed();\r\n }\r\n\r\n handleApprovalResolved(key: unknown, approvalKey: string): void {\r\n const session = this.#sessions.get(key);\r\n if (session?.approvals.delete(`live:${approvalKey}`)) {\r\n session.activityAt = this.#clock.now();\r\n this.#changed();\r\n }\r\n }\r\n\r\n handleSessionEvent(key: unknown, event: DshEventLike): void {\r\n if (!event || typeof event.type !== \"string\") return;\r\n const data = asRecord(event.data);\r\n const now = this.#clock.now();\r\n const session = this.#session(key);\r\n\r\n switch (event.type) {\r\n case \"session/title\": {\r\n const title=sanitizeBubbleText(data?.title,80);\r\n if(title)session.title=title;\r\n this.#changed();return;\r\n }\r\n case \"compaction/start\": session.compacting=true; break;\r\n case \"compaction/end\": session.compacting=false; break;\r\n case \"turn/start\":\r\n resetForTurn(session);\r\n session.turnActive = true;\r\n break;\r\n case \"turn/end\":\r\n this.#handleTurnEnd(session, turnEndKind(data), now);\r\n break;\r\n case \"assistant/chunk\":\r\n handleAssistantChunk(session, data, now);\r\n break;\r\n case \"assistant/message\":\r\n handleAssistantMessage(session, data, now);\r\n break;\r\n case \"tool/call\":\r\n handleToolStarted(session, data, now, \"callId\");\r\n break;\r\n case \"tool/result\":\r\n handleToolSettled(session, data);\r\n break;\r\n case \"tool/code-dispatch-start\":\r\n case \"tool/ptc-dispatch-start\":\r\n handleToolStarted(session, data, now, \"subCallId\");\r\n break;\r\n case \"tool/code-dispatch\":\r\n case \"tool/ptc-dispatch\":\r\n removeCall(session, stringField(data, \"subCallId\"));\r\n break;\r\n case \"approval/asked\":\r\n addApproval(session, data, now);\r\n break;\r\n case \"approval/decided\":\r\n removeApproval(session, stringField(data, \"id\"));\r\n break;\r\n case \"step/start\":\r\n case \"step/end\":\r\n break;\r\n default:\r\n return;\r\n }\r\n session.activityAt = now;\r\n this.#changed();\r\n }\r\n\r\n disposeSession(key: unknown): void {\r\n if (this.#sessions.delete(key)) this.#changed();\r\n }\r\n\r\n #handleTurnEnd(session: SessionState, reason: string | undefined, now: number): void {\r\n session.compacting = false;\r\n session.turnActive = false;\r\n session.running = false;\r\n session.activeTools.clear();\r\n session.approvals.clear();\r\n session.blockedAt = undefined;\r\n session.completedAt = undefined;\r\n session.completedUntil = undefined;\r\n session.errorAt = undefined;\r\n session.errorUntil = undefined;\r\n\r\n if (reason === \"completed\") {\r\n session.completedAt = now;\r\n session.completedUntil = now + COMPLETED_LATCH_MS;\r\n } else if (reason === \"blocked\") {\r\n session.blockedAt = now;\r\n } else if (reason === \"error\") {\r\n session.errorAt = now;\r\n session.errorUntil = now + ERROR_LATCH_MS;\r\n }\r\n }\r\n\r\n #session(key: unknown): SessionState {\r\n let session = this.#sessions.get(key);\r\n if (!session) {\r\n session = {\r\n ref:randomUUID(), title:\"DSH \u4F1A\u8BDD\", compacting:false, inputs:new Map(),\r\n activityAt: this.#clock.now(),\r\n running: false,\r\n turnActive: false,\r\n blockedAt: undefined,\r\n completedUntil: undefined,\r\n completedAt: undefined,\r\n errorUntil: undefined,\r\n errorAt: undefined,\r\n reasoning: undefined,\r\n response: undefined,\r\n activeTools: new Map(),\r\n approvals: new Map(),\r\n };\r\n this.#sessions.set(key, session);\r\n }\r\n return session;\r\n }\r\n\r\n #changed(): void {\r\n this.#scheduleDeadline();\r\n this.#publish(false);\r\n }\r\n\r\n #publish(force: boolean): void {\r\n if (!this.#emit) return;\r\n const now = this.#clock.now();\r\n const candidate = selectCandidate(this.#sessions.values(), now);\r\n const key = candidateKey(candidate);\r\n if (!force && key === this.#lastKey) return;\r\n\r\n const stateChanged = candidate.state !== this.#lastState || candidate.inputRequest !== this.#lastInput;\r\n const elapsed = now - this.#lastEmittedAt;\r\n if (!force && !stateChanged && elapsed < CONTENT_THROTTLE_MS) {\r\n if (!this.#contentTimer) {\r\n this.#contentTimer = this.#clock.setTimeout(() => {\r\n this.#contentTimer = undefined;\r\n this.#publish(true);\r\n }, CONTENT_THROTTLE_MS - Math.max(0, elapsed));\r\n }\r\n return;\r\n }\r\n\r\n if (this.#contentTimer) {\r\n this.#clock.clearTimeout(this.#contentTimer);\r\n this.#contentTimer = undefined;\r\n }\r\n const snapshot: PetSnapshot = {\r\n protocolVersion: PET_PROTOCOL_VERSION,\r\n sequence: this.#sequence,\r\n emittedAt: now,\r\n state: candidate.state,\r\n ...(candidate.bubble ? { bubble: candidate.bubble } : {}),\r\n ...(candidate.session?{session:candidate.session}:{}),\r\n ...(candidate.activity?{activity:candidate.activity}:{}),\r\n ...(candidate.inputRequest?{inputRequest:candidate.inputRequest}:{}),\r\n };\r\n this.#sequence += 1;\r\n this.#lastEmittedAt = now;\r\n this.#lastKey = key;\r\n this.#lastState = candidate.state;\r\n this.#lastInput = candidate.inputRequest;\r\n this.#emit(snapshot);\r\n }\r\n\r\n #scheduleDeadline(): void {\r\n if (this.#deadlineTimer) {\r\n this.#clock.clearTimeout(this.#deadlineTimer);\r\n this.#deadlineTimer = undefined;\r\n }\r\n const now = this.#clock.now();\r\n let next = Number.POSITIVE_INFINITY;\r\n for (const session of this.#sessions.values()) {\r\n if (session.completedUntil && session.completedUntil > now) {\r\n next = Math.min(next, session.completedUntil);\r\n }\r\n if (session.errorUntil && session.errorUntil > now) {\r\n next = Math.min(next, session.errorUntil);\r\n }\r\n }\r\n if (Number.isFinite(next)) {\r\n this.#deadlineTimer = this.#clock.setTimeout(() => {\r\n this.#deadlineTimer = undefined;\r\n this.#changed();\r\n }, Math.max(1, next - now));\r\n }\r\n }\r\n\r\n #clearTimers(): void {\r\n if (this.#contentTimer) this.#clock.clearTimeout(this.#contentTimer);\r\n if (this.#deadlineTimer) this.#clock.clearTimeout(this.#deadlineTimer);\r\n this.#contentTimer = undefined;\r\n this.#deadlineTimer = undefined;\r\n }\r\n}\r\n\r\nfunction resetForTurn(session: SessionState): void {\r\n session.compacting = false;\r\n session.blockedAt = undefined;\r\n session.completedAt = undefined;\r\n session.completedUntil = undefined;\r\n session.errorAt = undefined;\r\n session.errorUntil = undefined;\r\n session.reasoning = undefined;\r\n session.response = undefined;\r\n session.activeTools.clear();\r\n session.approvals.clear();\r\n}\r\n\r\nfunction handleAssistantChunk(session: SessionState, data: Record<string, unknown> | undefined, now: number): void {\r\n const chunk = asRecord(data?.chunk);\r\n const type = stringField(chunk, \"type\");\r\n const text = stringField(chunk, \"text\");\r\n if (!text || (type !== \"reasoning-delta\" && type !== \"text-delta\")) return;\r\n if (type === \"reasoning-delta\") {\r\n session.reasoning = appendVisible(session.reasoning, text, now);\r\n } else {\r\n session.response = appendVisible(session.response, text, now);\r\n }\r\n}\r\n\r\nfunction handleAssistantMessage(session: SessionState, data: Record<string, unknown> | undefined, now: number): void {\r\n const message = asRecord(data?.message);\r\n if (!message || !Array.isArray(message.content)) return;\r\n let response: TimedText | undefined;\r\n let reasoning: TimedText | undefined;\r\n for (const rawBlock of message.content) {\r\n const block = asRecord(rawBlock);\r\n const type = stringField(block, \"type\");\r\n const text = stringField(block, \"text\");\r\n if (!text) continue;\r\n if (type === \"text\") response = appendVisible(response, text, now);\r\n if (type === \"reasoning\") reasoning = appendVisible(reasoning, text, now);\r\n }\r\n if (response) session.response = response;\r\n if (reasoning) session.reasoning = reasoning;\r\n}\r\n\r\nfunction appendVisible(previous: TimedText | undefined, addition: string, now: number): TimedText | undefined {\r\n // Wire text is trimmed. Keep one separator bit in memory so a space-only\r\n // chunk or a chunk ending in whitespace does not join the next word.\r\n const separator = previous?.endsWithWhitespace ? \" \" : \"\";\r\n const text = sanitizeBubbleText(`${previous?.text ?? \"\"}${separator}${addition}`);\r\n return text ? { text, at: now, endsWithWhitespace: /\\s$/u.test(addition) } : previous;\r\n}\r\n\r\nfunction handleToolStarted(\r\n session: SessionState,\r\n data: Record<string, unknown> | undefined,\r\n now: number,\r\n idField: string,\r\n): void {\r\n const id = stringField(data, idField);\r\n const name = safeToolName(stringField(data, \"name\"));\r\n if (!id || !name) return;\r\n const item = { name, at: now };\r\n if (QUESTION_TOOLS.has(name)) session.approvals.set(`tool:${id}`, item);\r\n else session.activeTools.set(id, item);\r\n}\r\n\r\nfunction handleToolSettled(session: SessionState, data: Record<string, unknown> | undefined): void {\r\n const direct = stringField(data, \"callId\");\r\n const message = asRecord(data?.message);\r\n const source = asRecord(message?.source);\r\n removeCall(session, direct ?? stringField(source, \"callId\"));\r\n}\r\n\r\nfunction removeCall(session: SessionState, id: string | undefined): void {\r\n if (!id) return;\r\n session.activeTools.delete(id);\r\n session.approvals.delete(`tool:${id}`);\r\n}\r\n\r\nfunction addApproval(session: SessionState, data: Record<string, unknown> | undefined, now: number): void {\r\n const id = stringField(data, \"id\");\r\n const name = safeToolName(stringField(data, \"toolName\"));\r\n if (id && name) session.approvals.set(`approval:${id}`, { name, at: now });\r\n}\r\n\r\nfunction removeApproval(session: SessionState, id: string | undefined): void {\r\n if (id) session.approvals.delete(`approval:${id}`);\r\n}\r\n\r\nfunction selectCandidate(sessions: Iterable<SessionState>, now: number): Candidate {\r\n const all = [...sessions];\r\n let error: Candidate | undefined;\r\n let approval: Candidate | undefined;\r\n let blocked: Candidate | undefined;\r\n let working: Candidate | undefined;\r\n let thinking: Candidate | undefined;\r\n let completed: Candidate | undefined;\r\n\r\n for (const session of all) {\r\n const wrap=(candidate:Candidate,activity:PetActivity):Candidate=>({...candidate,session:{ref:session.ref,title:session.title},activity});\r\n if (session.errorUntil && session.errorUntil > now) {\r\n error = newest(error, wrap(notice(\"error\", \"\u53D1\u751F\u9519\u8BEF\uFF0C\u8BF7\u67E5\u770B DSH\", session.errorAt ?? now),\"error\"));\r\n }\r\n const input=[...session.inputs].filter(([,item])=>item.blocking).at(-1);\r\n if(input)approval=newest(approval,{...wrap(notice(\"approval\",\"\u7B49\u5F85\u8F93\u5165\u2026\",input[1].at),\"waiting\"),inputRequest:input[0]});\r\n const currentApproval = newestMapValue(session.approvals);\r\n if (currentApproval) {\r\n if(!input)approval = newest(approval, wrap(notice(\"approval\", QUESTION_TOOLS.has(currentApproval.name)?\"\u7B49\u5F85\u8F93\u5165\u2026\":`\u7B49\u5F85\u6279\u51C6\uFF1A${currentApproval.name}`, currentApproval.at),\"waiting\"));\r\n }\r\n if (session.blockedAt !== undefined) {\r\n blocked = newest(blocked, wrap(notice(\"blocked\", \"\u4EFB\u52A1\u88AB\u963B\u585E\uFF0C\u8BF7\u67E5\u770B DSH\", session.blockedAt),\"blocked\"));\r\n }\r\n const currentTool = newestMapValue(session.activeTools);\r\n if (currentTool) {\r\n working = newest(working, wrap({\r\n state: \"working\",\r\n bubble: bubble(\"tool\", `\u6B63\u5728\u4F7F\u7528 ${currentTool.name}\u2026`),\r\n at: currentTool.at,\r\n },toolActivity(currentTool.name)));\r\n }\r\n if(session.compacting)working=newest(working,wrap(notice(\"working\",\"\u6B63\u5728\u538B\u7F29\u4E0A\u4E0B\u6587\u2026\",session.activityAt),\"compacting\"));\r\n if (session.running || session.turnActive) {\r\n const visible = latestVisible(session);\r\n thinking = newest(thinking, wrap({\r\n state: \"thinking\",\r\n bubble: visible\r\n ? bubble(visible === session.response ? \"response\" : \"reasoning\", visible.text)\r\n : bubble(\"notice\", \"\u601D\u8003\u4E2D\u2026\"),\r\n at: visible?.at ?? session.activityAt,\r\n },visible===session.response && visible?\"responding\":\"thinking\"));\r\n }\r\n if (session.completedUntil && session.completedUntil > now) {\r\n completed = newest(completed, wrap({\r\n state: \"completed\",\r\n bubble: session.response\r\n ? bubble(\"response\", session.response.text)\r\n : bubble(\"notice\", \"\u4EFB\u52A1\u5DF2\u5B8C\u6210\"),\r\n at: session.completedAt ?? now,\r\n },\"completed\"));\r\n }\r\n }\r\n\r\n const selected=error ?? approval ?? blocked ?? working ?? thinking ?? completed;\r\n // Continued questions no longer block the Host's turn. Keep its real state\r\n // and animation, and expose an answer form only for the selected session.\r\n // Foreground questions and tool permissions always take precedence.\r\n if(selected){\r\n if(selected.state===\"approval\"||selected.state===\"error\")return selected;\r\n const session=all.find(item=>item.ref===selected.session?.ref);\r\n const late=session&&[...session.inputs].filter(([,item])=>!item.blocking).at(-1);\r\n return late?{...selected,inputRequest:late[0]}:selected;\r\n }\r\n const last=all.sort((a,b)=>b.activityAt-a.activityAt)[0];\r\n const late=last&&[...last.inputs].filter(([,item])=>!item.blocking).at(-1);\r\n return {state:\"idle\",at:now,activity:\"idle\",...(last?{session:{ref:last.ref,title:last.title}}:{}),...(late?{inputRequest:late[0]}:{})};\r\n}\r\n\r\nfunction notice(state: PetState, text: string, at: number): Candidate {\r\n return { state, bubble: bubble(\"notice\", text), at };\r\n}\r\n\r\nfunction bubble(kind: BubbleKind, rawText: string): PetBubble {\r\n const text = sanitizeBubbleText(rawText);\r\n return { kind, text };\r\n}\r\n\r\nfunction latestVisible(session: SessionState): TimedText | undefined {\r\n if (!session.reasoning) return session.response;\r\n if (!session.response) return session.reasoning;\r\n return session.response.at >= session.reasoning.at ? session.response : session.reasoning;\r\n}\r\n\r\nfunction newest(current: Candidate | undefined, next: Candidate): Candidate {\r\n return !current || next.at >= current.at ? next : current;\r\n}\r\n\r\nfunction newestMapValue(values: Map<string, TimedTool>): TimedTool | undefined {\r\n let result: TimedTool | undefined;\r\n for (const item of values.values()) {\r\n if (!result || item.at >= result.at) result = item;\r\n }\r\n return result;\r\n}\r\n\r\nfunction candidateKey(candidate: Candidate): string {\r\n return JSON.stringify([candidate.state,candidate.bubble,candidate.session,candidate.activity,candidate.inputRequest]);\r\n}\r\n\r\nexport function toolActivity(name: string): PetActivity {\r\n if(/^(?:bash|pwsh|shell|run_command|execute_command)$/iu.test(name))return \"command\";\r\n if(/^(?:edit|write)$/iu.test(name)||/(?:str_replace|edit_file|write_file|apply_patch|create_file)/iu.test(name))return \"editing\";\r\n if(/^(?:read|read_image|ls)$/iu.test(name)||/(?:read_file|list_files|fs_read|fs_list)/iu.test(name))return \"reading\";\r\n if(/^glob$/iu.test(name)||/(?:search|grep|find_files)/iu.test(name))return \"searching\";\r\n return \"tool\";\r\n}\r\n\r\nfunction safeToolName(value: string | undefined): string | undefined {\r\n if (!value) return undefined;\r\n const clean = sanitizeBubbleText(value, 60);\r\n return clean || undefined;\r\n}\r\n\r\nfunction asRecord(value: unknown): Record<string, unknown> | undefined {\r\n return typeof value === \"object\" && value !== null && !Array.isArray(value)\r\n ? (value as Record<string, unknown>)\r\n : undefined;\r\n}\r\n\r\nfunction stringField(value: Record<string, unknown> | undefined, key: string): string | undefined {\r\n const field = value?.[key];\r\n return typeof field === \"string\" ? field : undefined;\r\n}\r\n\r\nfunction turnEndKind(data: Record<string, unknown> | undefined): string | undefined {\r\n return stringField(asRecord(data?.reason), \"kind\");\r\n}\r\n", "import { createHash, randomUUID } from \"node:crypto\";\r\nimport { constants } from \"node:fs\";\r\nimport { lstat, mkdir, mkdtemp, open, realpath, rename, rm, writeFile } from \"node:fs/promises\";\r\nimport { isAbsolute, join, parse, resolve, sep } from \"node:path\";\r\n\r\nconst LIMIT = 64 * 1024 * 1024;\r\nconst NOTICE_LIMIT = 8 * 1024 * 1024;\r\nconst EXECUTABLE = \"dsh-inochi2d-pet.exe\";\r\nconst NOTICES = [\"LICENSE\", \"THIRD_PARTY_LICENSES.txt\"] as const;\r\nconst RELEASE_REDIRECT_LIMIT = 3;\r\nconst REDIRECT_STATUSES = new Set([301, 302, 303, 307, 308]);\r\n\r\nexport interface CompanionManifest {\r\n readonly schemaVersion: 2;\r\n readonly version: string;\r\n readonly platform: \"windows-x64\";\r\n readonly executable: {readonly file: typeof EXECUTABLE; readonly sha256: string; readonly bytes: number};\r\n readonly defaultModel: {\r\n readonly title: string;\r\n readonly url: string;\r\n readonly sha256: string;\r\n readonly bytes: number;\r\n readonly localTest?: true;\r\n };\r\n}\r\n\r\nexport interface PrepareCompanionOptions {\r\n readonly assetDirectory: string;\r\n readonly cacheDirectory: string;\r\n readonly expectedVersion: string;\r\n readonly signal?: AbortSignal;\r\n readonly fetch?: typeof fetch;\r\n readonly acquireDefaultModel?: boolean;\r\n}\r\n\r\nexport interface PreparedCompanion {\r\n readonly executable: string;\r\n readonly defaultModelReady: boolean;\r\n}\r\n\r\n/** Stage the program separately from the independently licensed VRM model. */\r\nexport async function prepareEmbeddedCompanion(options: PrepareCompanionOptions): Promise<PreparedCompanion> {\r\n options.signal?.throwIfAborted();\r\n const assets = await realpath(options.assetDirectory);\r\n const manifest = parseCompanionManifest(await ordinaryFile(assets, \"manifest.json\", 16_384), options.expectedVersion);\r\n const executable = await ordinaryFile(assets, EXECUTABLE, LIMIT);\r\n assertDigest(executable, manifest.executable);\r\n assertWindowsGuiExecutable(executable);\r\n const notices = await Promise.all(NOTICES.map(name => ordinaryFile(assets, name, NOTICE_LIMIT)));\r\n options.signal?.throwIfAborted();\r\n await ensureSafeDirectory(options.cacheDirectory);\r\n const cacheKey = createHash(\"sha256\").update(`${manifest.executable.sha256}:${manifest.defaultModel.sha256}`).digest(\"hex\");\r\n const directory = join(options.cacheDirectory, `${manifest.version}-${cacheKey}`);\r\n const descriptor = Buffer.from(JSON.stringify({\r\n schemaVersion: 2,\r\n title: manifest.defaultModel.title,\r\n sha256: manifest.defaultModel.sha256,\r\n bytes: manifest.defaultModel.bytes,\r\n file: \"default-model.vrm\",\r\n }));\r\n if (!await exists(directory)) {\r\n const temporary = await mkdtemp(join(options.cacheDirectory, \".stage-\"));\r\n try {\r\n await writeFile(join(temporary, EXECUTABLE), executable, {flag: \"wx\", mode: 0o700});\r\n for (const [index, name] of NOTICES.entries()) {\r\n await writeFile(join(temporary, name), notices[index]!, {flag: \"wx\", mode: 0o600});\r\n }\r\n // Keep the descriptor even when the external download fails, so the UI can explain/retry.\r\n await writeFile(join(temporary, \"default-model.json\"), descriptor, {flag: \"wx\", mode: 0o600});\r\n options.signal?.throwIfAborted();\r\n try { await rename(temporary, directory); }\r\n catch (error) { if (!await exists(directory)) throw error; }\r\n } finally { await rm(temporary, {recursive: true, force: true}); }\r\n }\r\n await assertSafeDirectory(directory);\r\n assertEqual(await ordinaryFile(directory, EXECUTABLE, LIMIT), executable);\r\n for (const [index, name] of NOTICES.entries()) assertEqual(await ordinaryFile(directory, name, NOTICE_LIMIT), notices[index]!);\r\n assertEqual(await ordinaryFile(directory, \"default-model.json\", 16_384), descriptor);\r\n options.signal?.throwIfAborted();\r\n if (options.acquireDefaultModel === false) return {executable: join(directory, EXECUTABLE), defaultModelReady: false};\r\n let defaultModelReady = false;\r\n try {\r\n const modelFile = join(directory, \"default-model.vrm\");\r\n if (await exists(modelFile)) {\r\n const cached = await ordinaryFile(directory, \"default-model.vrm\", LIMIT);\r\n try { assertDigest(cached, manifest.defaultModel); defaultModelReady = true; }\r\n catch { /* Only a bounded ordinary cache file may be repaired from the pinned source. */ }\r\n }\r\n if (!defaultModelReady) await downloadCharacter(manifest.defaultModel, directory, options.fetch ?? fetch, options.signal);\r\n defaultModelReady = true;\r\n } catch {\r\n options.signal?.throwIfAborted();\r\n // The desktop still starts and presents the explicit missing/invalid-character state.\r\n }\r\n options.signal?.throwIfAborted();\r\n return {executable: join(directory, EXECUTABLE), defaultModelReady};\r\n}\r\n\r\nexport function parseCompanionManifest(bytes: Uint8Array, expectedVersion: string): CompanionManifest {\r\n const value: unknown = JSON.parse(Buffer.from(bytes).toString(\"utf8\"));\r\n if (!record(value) || !keys(value, [\"schemaVersion\", \"version\", \"platform\", \"executable\", \"defaultModel\"])\r\n || value.schemaVersion !== 2 || value.platform !== \"windows-x64\"\r\n || typeof value.version !== \"string\" || value.version.length > 64 || !/^\\d+\\.\\d+\\.\\d+(?:-[a-zA-Z0-9.-]+)?$/u.test(value.version)\r\n || value.version !== expectedVersion || !record(value.executable) || !keys(value.executable, [\"file\", \"sha256\", \"bytes\"])\r\n || value.executable.file !== EXECUTABLE || !digest(value.executable)\r\n || !record(value.defaultModel) || !keys(value.defaultModel, [\"title\", \"url\", \"sha256\", \"bytes\"], [\"localTest\"])\r\n || typeof value.defaultModel.title !== \"string\" || value.defaultModel.title.length < 1 || value.defaultModel.title.length > 128\r\n || /[\\p{Cc}]/u.test(value.defaultModel.title) || !digest(value.defaultModel)\r\n || (value.defaultModel.localTest !== undefined && value.defaultModel.localTest !== true)\r\n || typeof value.defaultModel.url !== \"string\" || value.defaultModel.url.length > 2_048\r\n || /[\\p{Cc}\\\\]/u.test(value.defaultModel.url)) {\r\n throw new Error(\"invalid embedded companion manifest\");\r\n }\r\n const url = new URL(value.defaultModel.url);\r\n if (!url.pathname.toLowerCase().endsWith(\".vrm\") || url.username || url.password || url.hash || (url.protocol !== \"https:\"\r\n && !(value.defaultModel.localTest === true && url.protocol === \"http:\" && url.hostname === \"127.0.0.1\"\r\n && /^http:\\/\\/127\\.0\\.0\\.1(?::\\d+)?\\//u.test(value.defaultModel.url)))) {\r\n throw new Error(\"invalid starter character download source\");\r\n }\r\n return value as unknown as CompanionManifest;\r\n}\r\n\r\nexport function assertWindowsGuiExecutable(bytes: Buffer): void {\r\n if (bytes.length < 64 || bytes.readUInt16LE(0) !== 0x5a4d) throw new Error(\"invalid embedded Windows executable\");\r\n const pe = bytes.readUInt32LE(0x3c);\r\n if (pe < 64 || pe + 94 > bytes.length || bytes.readUInt32LE(pe) !== 0x00004550\r\n || bytes.readUInt16LE(pe + 4) !== 0x8664 || bytes.readUInt16LE(pe + 20) < 70\r\n || pe + 24 + bytes.readUInt16LE(pe + 20) > bytes.length || bytes.readUInt16LE(pe + 24) !== 0x20b\r\n || bytes.readUInt16LE(pe + 24 + 68) !== 2) throw new Error(\"embedded executable must be Windows x64 GUI PE32+\");\r\n}\r\n\r\nasync function downloadCharacter(source: CompanionManifest[\"defaultModel\"], directory: string, request: typeof fetch, signal?: AbortSignal): Promise<void> {\r\n const timeout = AbortSignal.timeout(30_000);\r\n const downloadSignal = signal ? AbortSignal.any([signal, timeout]) : timeout;\r\n const response = await characterResponse(source.url, request, downloadSignal);\r\n if (!response.ok || response.redirected || !response.body) {\r\n await response.body?.cancel();\r\n throw new Error(\"starter character download failed\");\r\n }\r\n const length = response.headers.get(\"content-length\");\r\n if (length !== null && (!/^\\d+$/u.test(length) || Number(length) !== source.bytes)) {\r\n await response.body.cancel();\r\n throw new Error(\"starter character download size mismatch\");\r\n }\r\n const temporary = join(directory, `.download-${randomUUID()}`);\r\n let file: Awaited<ReturnType<typeof open>> | undefined;\r\n const reader = response.body.getReader();\r\n try {\r\n file = await open(temporary, \"wx\", 0o600);\r\n const hash = createHash(\"sha256\");\r\n let bytes = 0;\r\n while (true) {\r\n downloadSignal.throwIfAborted();\r\n const item = await reader.read();\r\n if (item.done) break;\r\n bytes += item.value.byteLength;\r\n if (bytes > source.bytes || bytes > LIMIT) throw new Error(\"starter character download exceeds its size limit\");\r\n hash.update(item.value);\r\n await file.writeFile(item.value);\r\n }\r\n if (bytes !== source.bytes || hash.digest(\"hex\") !== source.sha256) throw new Error(\"starter character download integrity mismatch\");\r\n await file.sync();\r\n await file.close(); file = undefined;\r\n downloadSignal.throwIfAborted();\r\n const modelFile = join(directory, \"default-model.vrm\");\r\n if (await exists(modelFile)) {\r\n const cached = await ordinaryFile(directory, \"default-model.vrm\", LIMIT);\r\n try { assertDigest(cached, source); return; }\r\n catch { /* Replace only this validated ordinary cache file, after the replacement has passed its digest. */ }\r\n }\r\n await rename(temporary, modelFile);\r\n } finally {\r\n await reader.cancel().catch(() => undefined);\r\n reader.releaseLock();\r\n await file?.close();\r\n await rm(temporary, {force: true});\r\n }\r\n}\r\n\r\nasync function characterResponse(source: string, request: typeof fetch, signal: AbortSignal): Promise<Response> {\r\n const initial = new URL(source);\r\n const githubRelease = initial.protocol === \"https:\" && initial.hostname === \"github.com\"\r\n && !initial.port && !initial.username && !initial.password && !initial.search && !initial.hash\r\n && /^\\/[a-zA-Z0-9-]+\\/[a-zA-Z0-9_.-]+\\/releases\\/download\\/[^/]+\\/[^/]+\\.vrm$/u.test(initial.pathname);\r\n let current = source;\r\n const visited = new Set([current]);\r\n for (let redirects = 0; ; ++redirects) {\r\n signal.throwIfAborted();\r\n // No cookies, Authorization, or other request headers cross origins. Signed\r\n // asset URLs exist only for this download and never enter cached metadata.\r\n const response = await request(current, {method: \"GET\", redirect: githubRelease ? \"manual\" : \"error\", credentials: \"omit\", signal});\r\n if (!REDIRECT_STATUSES.has(response.status)) return response;\r\n await response.body?.cancel();\r\n const location = response.headers.get(\"location\");\r\n if (!githubRelease || redirects >= RELEASE_REDIRECT_LIMIT || !location) throw new Error(\"starter character redirect rejected\");\r\n let next: URL;\r\n try { next = new URL(location, current); }\r\n catch { throw new Error(\"starter character redirect rejected\"); }\r\n if (next.protocol !== \"https:\" || next.hostname !== \"release-assets.githubusercontent.com\"\r\n || next.port || next.username || next.password || next.hash || visited.has(next.href)) {\r\n throw new Error(\"starter character redirect rejected\");\r\n }\r\n current = next.href;\r\n visited.add(current);\r\n }\r\n}\r\n\r\nasync function ordinaryFile(directory: string, name: string, limit: number): Promise<Buffer> {\r\n await assertSafeDirectory(directory);\r\n const path = join(directory, name), info = await lstat(path);\r\n if (!info.isFile() || info.isSymbolicLink() || info.size < 1 || info.size > limit) throw new Error(\"unsafe companion file\");\r\n const file = await open(path, constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0));\r\n try {\r\n const opened = await file.stat();\r\n if (!opened.isFile() || opened.size !== info.size || opened.size > limit) throw new Error(\"unsafe companion file\");\r\n const bytes = Buffer.alloc(info.size);\r\n let offset = 0;\r\n while (offset < bytes.length) {\r\n const read = await file.read(bytes, offset, bytes.length - offset, offset);\r\n if (read.bytesRead === 0) throw new Error(\"companion file changed while reading\");\r\n offset += read.bytesRead;\r\n }\r\n const extra = await file.read(Buffer.alloc(1), 0, 1, bytes.length);\r\n if (extra.bytesRead !== 0) throw new Error(\"companion file changed while reading\");\r\n return bytes;\r\n } finally { await file.close(); }\r\n}\r\n\r\nasync function ensureSafeDirectory(directory: string): Promise<void> {\r\n if (!isAbsolute(directory)) throw new Error(\"companion cache must be an absolute local directory\");\r\n const absolute = resolve(directory), root = parse(absolute).root;\r\n let current = root;\r\n for (const component of absolute.slice(root.length).split(sep).filter(Boolean)) {\r\n current = join(current, component);\r\n try { await mkdir(current, {mode: 0o700}); }\r\n catch (error) { if (!record(error) || error.code !== \"EEXIST\") throw error; }\r\n await assertSafeDirectory(current);\r\n }\r\n}\r\n\r\nasync function assertSafeDirectory(directory: string): Promise<void> {\r\n const info = await lstat(directory);\r\n if (!info.isDirectory() || info.isSymbolicLink()) throw new Error(\"unsafe companion directory\");\r\n}\r\n\r\nasync function exists(path: string): Promise<boolean> {\r\n try { await lstat(path); return true; }\r\n catch (error) { if (record(error) && error.code === \"ENOENT\") return false; throw error; }\r\n}\r\n\r\nfunction assertDigest(bytes: Buffer, expected: {readonly bytes: number; readonly sha256: string}): void {\r\n if (bytes.length !== expected.bytes || createHash(\"sha256\").update(bytes).digest(\"hex\") !== expected.sha256) throw new Error(\"companion file integrity mismatch\");\r\n}\r\nfunction assertEqual(actual: Buffer, expected: Buffer): void {\r\n if (!actual.equals(expected)) throw new Error(\"cached companion file integrity mismatch\");\r\n}\r\nfunction record(value: unknown): value is Record<string, unknown> { return typeof value === \"object\" && value !== null && !Array.isArray(value); }\r\nfunction keys(value: Record<string, unknown>, required: readonly string[], optional: readonly string[] = []): boolean {\r\n return required.every(key => Object.hasOwn(value, key)) && Object.keys(value).every(key => required.includes(key) || optional.includes(key));\r\n}\r\nfunction digest(value: Record<string, unknown>): boolean {\r\n return typeof value.sha256 === \"string\" && /^[a-f0-9]{64}$/u.test(value.sha256)\r\n && typeof value.bytes === \"number\" && Number.isSafeInteger(value.bytes) && value.bytes >= 1 && value.bytes <= LIMIT;\r\n}\r\n", "import { randomUUID } from \"node:crypto\";\r\nimport { release } from \"node:os\";\r\nimport { posix, win32 } from \"node:path\";\r\nimport { execFile, spawn } from \"node:child_process\";\r\nimport { lstat, open, readFile } from \"node:fs/promises\";\r\nimport { constants } from \"node:fs\";\r\nimport { fileURLToPath } from \"node:url\";\r\nimport { prepareEmbeddedCompanion } from \"./bootstrap.js\";\r\n\r\nexport interface PetConnection {\r\n readonly port: number;\r\n readonly token: string;\r\n readonly instanceId?: string;\r\n}\r\n\r\nexport interface LaunchOptions {\r\n readonly signal?: AbortSignal;\r\n readonly onDefaultModelFailure?: () => void;\r\n readonly host?: WindowsCompanionHost;\r\n}\r\n\r\nexport type WindowsCompanionHost = \"wsl\" | \"windows\";\r\n\r\nexport function isWslEnvironment(environment: NodeJS.ProcessEnv = process.env): boolean {\r\n return Boolean(environment.WSL_INTEROP || environment.WSL_DISTRO_NAME)\r\n || release().toLowerCase().includes(\"microsoft\");\r\n}\r\n\r\nexport function detectCompanionHost(\r\n platform: NodeJS.Platform = process.platform,\r\n architecture: string = process.arch,\r\n inWsl: boolean = isWslEnvironment(),\r\n): WindowsCompanionHost | undefined {\r\n if (architecture !== \"x64\") return undefined;\r\n if (platform === \"win32\") return \"windows\";\r\n return platform === \"linux\" && inWsl ? \"wsl\" : undefined;\r\n}\r\n\r\n/** Child processes need OS launch variables, never provider or Electron/Node options. */\r\nexport function companionEnvironment(source: NodeJS.ProcessEnv): NodeJS.ProcessEnv {\r\n const result: NodeJS.ProcessEnv = {};\r\n for (const name of [\"PATH\", \"Path\", \"HOME\", \"LANG\", \"LC_ALL\", \"SYSTEMROOT\", \"SystemRoot\", \"WINDIR\", \"TEMP\", \"TMP\", \"LOCALAPPDATA\", \"APPDATA\", \"USERPROFILE\", \"WSL_INTEROP\", \"WSL_DISTRO_NAME\"]) {\r\n if (source[name] !== undefined) result[name] = source[name];\r\n }\r\n return result;\r\n}\r\n\r\n/** Start the embedded Windows companion; no installer, registry lookup or elevation. */\r\nexport async function launchWindowsPet(connection: PetConnection, options: LaunchOptions = {}): Promise<void> {\r\n const url = buildConnectionUrl(connection);\r\n const host = options.host ?? detectCompanionHost();\r\n if (!host) throw new Error(\"StarLite requires a Windows x64 or WSL2 x64 DSH Host\");\r\n const paths = host === \"windows\" ? win32 : posix;\r\n options.signal?.throwIfAborted();\r\n let executable: string;\r\n const override = process.env.DSH_INOCHI2D_PET_EXE;\r\n if (override !== undefined) {\r\n const local = validateExecutableOverride(override);\r\n executable = host === \"windows\" ? local : (await execFileText(\"wslpath\", [\"-u\", local], options.signal)).trim();\r\n } else {\r\n const windowsDirectories = (await execFileText(\"powershell.exe\", [\r\n \"-NoLogo\", \"-NoProfile\", \"-NonInteractive\", \"-Command\",\r\n \"[Console]::OutputEncoding = [System.Text.UTF8Encoding]::new(); [Environment]::GetFolderPath('LocalApplicationData'); [Environment]::GetFolderPath('ApplicationData')\",\r\n ], options.signal)).trim().split(/\\r?\\n/u);\r\n if (windowsDirectories.length !== 2 || windowsDirectories.some(path => !validWindowsDirectory(path))) throw new Error(\"Windows application directories are unavailable\");\r\n const windowsCache = windowsDirectories[0]!;\r\n const localCache = host === \"windows\" ? windowsCache : (await execFileText(\"wslpath\", [\"-u\", windowsCache], options.signal)).trim();\r\n const roaming = host === \"windows\" ? windowsDirectories[1]! : (await execFileText(\"wslpath\", [\"-u\", windowsDirectories[1]!], options.signal)).trim();\r\n if (host === \"wsl\" && (!validPosixDirectory(localCache) || !validPosixDirectory(roaming))) throw new Error(\"invalid Windows application directories\");\r\n const acquireDefaultModel = await shouldAcquireDefaultModel(paths.join(roaming, \"com.starcried.dsh-inochi2d-pet\", \"settings.json\"));\r\n const metadata: unknown = JSON.parse(await readFile(new URL(\"../package.json\", import.meta.url), \"utf8\"));\r\n if (typeof metadata !== \"object\" || metadata === null || !(\"version\" in metadata) || typeof metadata.version !== \"string\") {\r\n throw new Error(\"invalid StarLite plugin package version\");\r\n }\r\n const prepared = await prepareEmbeddedCompanion({\r\n assetDirectory: fileURLToPath(new URL(\"../assets/windows-x64/\", import.meta.url)),\r\n cacheDirectory: paths.join(localCache, \"StarCried\", \"StarLite\", \"companions\"),\r\n expectedVersion: metadata.version,\r\n acquireDefaultModel,\r\n ...(options.signal ? {signal: options.signal} : {}),\r\n });\r\n executable = prepared.executable;\r\n if (acquireDefaultModel && !prepared.defaultModelReady) options.onDefaultModelFailure?.();\r\n }\r\n if ((host === \"windows\" && validateExecutableOverride(executable) !== executable)\r\n || (host === \"wsl\" && (!validPosixDirectory(executable) || posix.basename(executable).toLowerCase() !== \"dsh-inochi2d-pet.exe\"))) {\r\n throw new Error(\"StarLite for DSH executable is invalid\");\r\n }\r\n options.signal?.throwIfAborted();\r\n\r\n return new Promise((resolve, reject) => {\r\n const child = spawn(executable, [url], {\r\n // Windows' non-detached child job ends with the Host; the pet must retain\r\n // its own reconnect lease and any queued successor after that exit.\r\n detached: host === \"windows\",\r\n shell: false,\r\n stdio: \"ignore\",\r\n windowsHide: true,\r\n env: companionEnvironment(process.env),\r\n });\r\n let settled = false;\r\n const finish = (error?: Error) => {\r\n if (settled) return;\r\n settled = true;\r\n if (error) reject(error);\r\n else resolve();\r\n };\r\n child.once(\"error\", (error) => finish(error));\r\n child.once(\"spawn\", () => finish());\r\n child.unref();\r\n });\r\n}\r\n\r\nfunction validWindowsDirectory(path: string): boolean {\r\n return /^[a-z]:[\\\\/]/iu.test(path) && win32.isAbsolute(path) && !/[\\p{Cc}\"<>|]/u.test(path)\r\n && !path.split(/[\\\\/]/u).some(part => part === \".\" || part === \"..\");\r\n}\r\n\r\nfunction validPosixDirectory(path: string): boolean {\r\n return posix.isAbsolute(path) && !/[\\p{Cc}]/u.test(path) && !path.split(\"/\").some(part => part === \".\" || part === \"..\");\r\n}\r\n\r\n/** Read only the selected-model policy; never transfer or log companion settings. */\r\nexport async function shouldAcquireDefaultModel(path: string): Promise<boolean> {\r\n const maximum = 4 * 1024 * 1024;\r\n try {\r\n const info = await lstat(path);\r\n if (!info.isFile() || info.isSymbolicLink() || info.size < 1 || info.size > maximum) return false;\r\n const file = await open(path, constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0));\r\n try {\r\n const opened = await file.stat();\r\n if (!opened.isFile() || opened.size !== info.size) return false;\r\n const bytes = Buffer.alloc(info.size + 1);\r\n const result = await file.read(bytes, 0, bytes.length, 0);\r\n if (result.bytesRead !== info.size) return false;\r\n const value: unknown = JSON.parse(bytes.subarray(0, result.bytesRead).toString(\"utf8\"));\r\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) return false;\r\n if (!(\"schemaVersion\" in value)) return false;\r\n if (value.schemaVersion === 4) {\r\n return \"activeModelId\" in value && value.activeModelId === \"weiguang-default\";\r\n }\r\n if (![1, 2, 3].includes(value.schemaVersion as number)) return false;\r\n const preferences = \"preferences\" in value ? value.preferences : undefined;\r\n if (typeof preferences === \"object\" && preferences !== null && \"modelEnabled\" in preferences) return preferences.modelEnabled === true;\r\n return true;\r\n } finally { await file.close(); }\r\n } catch (error) {\r\n return typeof error === \"object\" && error !== null && \"code\" in error && error.code === \"ENOENT\";\r\n }\r\n}\r\n\r\n/** A developer may explicitly select a local build, without changing registrations. */\r\nexport function validateExecutableOverride(override: string): string {\r\n if (!win32.isAbsolute(override)\r\n || !/^[a-z]:[\\\\/]/iu.test(override)\r\n || /[\\p{Cc}\"<>|]/u.test(override) || override.split(/[\\\\/]/u).some(part => part === \".\" || part === \"..\")\r\n || win32.basename(override).toLowerCase() !== \"dsh-inochi2d-pet.exe\") {\r\n throw new Error(\"DSH_INOCHI2D_PET_EXE must name a local Windows companion executable\");\r\n }\r\n return override;\r\n}\r\n\r\nexport function buildConnectionUrl(connection: PetConnection): string {\r\n if (!Number.isInteger(connection.port) || connection.port < 1 || connection.port > 65_535) {\r\n throw new Error(\"invalid DSH pet status port\");\r\n }\r\n if (!/^[A-Za-z0-9_-]{43}$/.test(connection.token)) {\r\n throw new Error(\"invalid DSH pet connection token\");\r\n }\r\n const instanceId = connection.instanceId ?? randomUUID();\r\n if (!/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/iu.test(instanceId)) {\r\n throw new Error(\"invalid DSH pet instance id\");\r\n }\r\n const url = new URL(\"dsh-inochi2d-pet://connect\");\r\n url.searchParams.set(\"port\", String(connection.port));\r\n url.searchParams.set(\"token\", connection.token);\r\n url.searchParams.set(\"instance\", instanceId);\r\n\r\n return url.toString();\r\n}\r\n\r\nfunction execFileText(file: string, arguments_: string[], signal?: AbortSignal): Promise<string> {\r\n return new Promise((resolve, reject) => {\r\n execFile(file, arguments_, {\r\n encoding: \"utf8\",\r\n maxBuffer: 8_192,\r\n timeout: 3_000,\r\n windowsHide: true,\r\n env: companionEnvironment(process.env),\r\n ...(signal ? {signal} : {}),\r\n }, (error, stdout) => {\r\n if (error) reject(error);\r\n else resolve(stdout);\r\n });\r\n });\r\n}\r\n", "import { createServer, type IncomingMessage, type Server, type ServerResponse } from \"node:http\";\r\nimport { randomBytes, timingSafeEqual } from \"node:crypto\";\r\nimport {\r\n MAX_WIRE_BYTES,\r\n parsePetSnapshot,\r\n parseBillingSnapshot,\r\n type PetSnapshot,\r\n type PetStateSource,\r\n MAX_INTERACTION_BYTES, isOpaqueRef, parsePetInteraction,\r\n} from \"@starcried/starlite-dsh-protocol\";\r\nimport type { BillingSource } from \"./billing.js\";\r\nimport type {QuestionBridge} from \"./questions.js\";\r\n\r\nconst HEARTBEAT_MS = 15_000;\r\nconst MAX_CLIENTS = 2;\r\nconst MAX_LISTEN_ATTEMPTS = 16;\r\n// Fetch implementations reject these ports before a request reaches the\r\n// server. Port 0 can occasionally select one of them on hosts with a custom\r\n// ephemeral-port range, which would make an otherwise healthy bridge unusable.\r\nconst BROWSER_BLOCKED_PORTS = new Set([\r\n 0, 1, 7, 9, 11, 13, 15, 17, 19, 20, 21, 22, 23, 25, 37, 42, 43, 53, 69,\r\n 77, 79, 87, 95, 101, 102, 103, 104, 109, 110, 111, 113, 115, 117, 119,\r\n 123, 135, 137, 139, 143, 161, 179, 389, 427, 465, 512, 513, 514, 515,\r\n 526, 530, 531, 532, 540, 548, 554, 556, 563, 587, 601, 636, 989, 990,\r\n 993, 995, 1_719, 1_720, 1_723, 2_049, 3_659, 4_045, 4_190, 5_060,\r\n 5_061, 6_000, 6_566, 6_665, 6_666, 6_667, 6_668, 6_669, 6_697, 10_080,\r\n]);\r\nconst ALLOWED_ORIGINS = new Set([\r\n \"http://tauri.localhost\",\r\n \"https://tauri.localhost\",\r\n \"tauri://localhost\",\r\n \"http://127.0.0.1:1420\",\r\n \"http://localhost:1420\",\r\n]);\r\n\r\nexport interface StatusServer {\r\n readonly port: number;\r\n readonly token: string;\r\n close(): Promise<void>;\r\n}\r\n\r\nexport async function startStatusServer(source: PetStateSource, options: {billing?: BillingSource; questions?: QuestionBridge} = {}): Promise<StatusServer> {\r\n const token = randomBytes(32).toString(\"base64url\");\r\n const clients = new Set<ServerResponse>();\r\n let latest: PetSnapshot | undefined;\r\n let closed = false;\r\n\r\n const server = createServer((request, response) => {\r\n const origin = request.headers.origin;\r\n if (request.method === \"OPTIONS\") {\r\n if (!origin || !ALLOWED_ORIGINS.has(origin)) {\r\n reject(response, 403);\r\n return;\r\n }\r\n response.writeHead(204, corsHeaders(origin));\r\n response.end();\r\n return;\r\n }\r\n if (!['GET','POST'].includes(request.method??'') || !authorized(request.headers.authorization, token)) {\r\n reject(response, 401);\r\n return;\r\n }\r\n if (origin && !ALLOWED_ORIGINS.has(origin)) {\r\n reject(response, 403);\r\n return;\r\n }\r\n\r\n const path = new URL(request.url ?? \"/\", \"http://127.0.0.1\").pathname;\r\n if (path === \"/v1/health\" && request.method === \"GET\") {\r\n response.writeHead(204, {\r\n \"Cache-Control\": \"no-store\",\r\n ...(origin ? corsHeaders(origin) : {}),\r\n });\r\n response.end();\r\n return;\r\n }\r\n if (path === \"/v1/billing\" && request.method === \"GET\" && options.billing) {\r\n void options.billing.read().then(snapshot => {\r\n const json = JSON.stringify(parseBillingSnapshot(snapshot));\r\n if (Buffer.byteLength(json, \"utf8\") > MAX_WIRE_BYTES) throw new Error(\"billing response too large\");\r\n if (closed || response.destroyed) return;\r\n response.writeHead(200, {\r\n \"Cache-Control\": \"no-store\", \"Content-Type\": \"application/json; charset=utf-8\",\r\n \"X-Content-Type-Options\": \"nosniff\", ...(origin ? corsHeaders(origin) : {}),\r\n });\r\n response.end(json);\r\n }).catch(() => {if (!response.destroyed && !response.headersSent) reject(response, 503);});\r\n return;\r\n }\r\n if(path === \"/v1/interaction\" && request.method === \"GET\" && options.questions){\r\n try{\r\n const query=new URL(request.url??\"/\",\"http://127.0.0.1\").searchParams;\r\n const id=query.get(\"request\");const pending=isOpaqueRef(id)&&query.getAll(\"request\").length===1?options.questions.read(id):undefined;\r\n if(!pending){reject(response,409);return;}\r\n jsonResponse(response,parsePetInteraction(pending),origin);\r\n }catch{reject(response,409);}\r\n return;\r\n }\r\n if(options.questions && request.method === \"POST\" && [\"/v1/interaction/answer\",\"/v1/interaction/availability\",\"/v1/session/open\",\"/v1/web/open\"].includes(path)){\r\n const bridge=options.questions;\r\n void readBody(request).then(async body=>{\r\n if(closed||response.destroyed)return;\r\n if(!body||typeof body!==\"object\"||Array.isArray(body))throw Error(\"invalid action\");\r\n const row=body as Record<string,unknown>;\r\n if(path===\"/v1/interaction/answer\")await bridge.answer(row);\r\n else if(path===\"/v1/interaction/availability\"){\r\n if(typeof row.enabled!==\"boolean\"||Object.keys(row).some(k=>k!==\"enabled\"))throw Error(\"invalid availability\");bridge.setEnabled(row.enabled);\r\n }else if(path===\"/v1/web/open\"){\r\n if(Object.keys(row).length!==0)throw Error(\"invalid navigation\");await bridge.openWebUi();\r\n }else{\r\n if(!isOpaqueRef(row.sessionRef)||Object.keys(row).some(k=>k!==\"sessionRef\"))throw Error(\"invalid navigation\");await bridge.open(row.sessionRef);\r\n }\r\n if(!response.destroyed){response.writeHead(204,{\"Cache-Control\":\"no-store\",...(origin?corsHeaders(origin):{})});response.end();}\r\n }).catch(()=>{if(!response.destroyed&&!response.headersSent)reject(response,409)});return;\r\n }\r\n if (path !== \"/v1/events\" || request.method !== \"GET\") {\r\n reject(response, 404);\r\n return;\r\n }\r\n if (clients.size >= MAX_CLIENTS) {\r\n reject(response, 429);\r\n return;\r\n }\r\n\r\n response.writeHead(200, {\r\n \"Cache-Control\": \"no-cache, no-store\",\r\n Connection: \"keep-alive\",\r\n \"Content-Type\": \"text/event-stream; charset=utf-8\",\r\n \"X-Accel-Buffering\": \"no\",\r\n \"X-Content-Type-Options\": \"nosniff\",\r\n ...(origin ? corsHeaders(origin) : {}),\r\n });\r\n response.flushHeaders();\r\n clients.add(response);\r\n options.questions?.setConnected(true);\r\n response.write(\": connected\\n\\n\");\r\n if (latest) writeSnapshot(response, latest);\r\n request.on(\"close\", () => {clients.delete(response);options.questions?.setConnected(clients.size>0)});\r\n });\r\n\r\n const heartbeat = setInterval(() => {\r\n for (const client of clients) client.write(\": heartbeat\\n\\n\");\r\n }, HEARTBEAT_MS);\r\n heartbeat.unref();\r\n\r\n try {\r\n await source.start((snapshot) => {\r\n let canonical: PetSnapshot;\r\n try {\r\n // Reconstruct the allow-listed protocol object before serialization.\r\n // This prevents an accidental structural subtype from carrying a DSH\r\n // event, path, or other extra field onto the wire.\r\n canonical = parsePetSnapshot(snapshot);\r\n } catch {\r\n return;\r\n }\r\n latest = canonical;\r\n for (const client of clients) writeSnapshot(client, canonical);\r\n });\r\n await listenLoopback(server);\r\n } catch (error) {\r\n clearInterval(heartbeat);\r\n await source.stop().catch(() => undefined);\r\n server.close();\r\n throw error;\r\n }\r\n\r\n const address = server.address();\r\n if (!address || typeof address === \"string\") {\r\n await shutdown(server, clients, source, heartbeat);\r\n throw new Error(\"DSH pet status server did not receive a TCP port\");\r\n }\r\n\r\n return {\r\n port: address.port,\r\n token,\r\n async close() {\r\n if (closed) return;\r\n closed = true;\r\n options.billing?.close();\r\n options.questions?.close();\r\n await shutdown(server, clients, source, heartbeat);\r\n },\r\n };\r\n}\r\n\r\nfunction listenLoopback(server: Server): Promise<void> {\r\n return listenOnSafeLoopbackPort(server);\r\n}\r\n\r\nasync function listenOnSafeLoopbackPort(server: Server): Promise<void> {\r\n for (let attempt = 0; attempt < MAX_LISTEN_ATTEMPTS; attempt += 1) {\r\n await listenOnce(server);\r\n const address = server.address();\r\n if (address && typeof address !== \"string\" && isBrowserSafePort(address.port)) return;\r\n await closeServer(server);\r\n }\r\n throw new Error(\"DSH pet status server could not allocate a browser-safe port\");\r\n}\r\n\r\nfunction listenOnce(server: Server): Promise<void> {\r\n return new Promise((resolve, reject) => {\r\n const onError = (error: Error) => reject(error);\r\n server.once(\"error\", onError);\r\n server.listen(0, \"127.0.0.1\", () => {\r\n server.off(\"error\", onError);\r\n resolve();\r\n });\r\n });\r\n}\r\n\r\nexport function isBrowserSafePort(port: number): boolean {\r\n return Number.isInteger(port) && port > 0 && port <= 65_535\r\n && !BROWSER_BLOCKED_PORTS.has(port);\r\n}\r\n\r\nasync function shutdown(\r\n server: Server,\r\n clients: Set<ServerResponse>,\r\n source: PetStateSource,\r\n heartbeat: ReturnType<typeof setInterval>,\r\n): Promise<void> {\r\n clearInterval(heartbeat);\r\n for (const client of clients) client.destroy();\r\n clients.clear();\r\n await Promise.allSettled([source.stop(), closeServer(server)]);\r\n}\r\n\r\nfunction closeServer(server: Server): Promise<void> {\r\n return new Promise((resolve) => {\r\n if (!server.listening) {\r\n resolve();\r\n return;\r\n }\r\n server.close(() => resolve());\r\n });\r\n}\r\n\r\nfunction writeSnapshot(response: ServerResponse, snapshot: PetSnapshot): void {\r\n const json = JSON.stringify(snapshot);\r\n if (Buffer.byteLength(json, \"utf8\") > MAX_WIRE_BYTES) return;\r\n response.write(`id: ${snapshot.sequence}\\nevent: snapshot\\ndata: ${json}\\n\\n`);\r\n}\r\n\r\nfunction authorized(header: string | undefined, token: string): boolean {\r\n if (!header?.startsWith(\"Bearer \")) return false;\r\n const supplied = Buffer.from(header.slice(\"Bearer \".length), \"utf8\");\r\n const expected = Buffer.from(token, \"utf8\");\r\n return supplied.length === expected.length && timingSafeEqual(supplied, expected);\r\n}\r\n\r\nfunction corsHeaders(origin: string): Record<string, string> {\r\n return {\r\n \"Access-Control-Allow-Headers\": \"Authorization, Content-Type\",\r\n \"Access-Control-Allow-Methods\": \"GET, POST, OPTIONS\",\r\n \"Access-Control-Allow-Origin\": origin,\r\n \"Access-Control-Max-Age\": \"600\",\r\n Vary: \"Origin\",\r\n };\r\n}\r\n\r\nfunction jsonResponse(response:ServerResponse,value:unknown,origin:string|undefined):void{\r\n const json=JSON.stringify(value);if(Buffer.byteLength(json)>MAX_INTERACTION_BYTES){reject(response,413);return;}\r\n response.writeHead(200,{\"Content-Type\":\"application/json; charset=utf-8\",\"Cache-Control\":\"no-store\",\"X-Content-Type-Options\":\"nosniff\",...(origin?corsHeaders(origin):{})});response.end(json);\r\n}\r\nasync function readBody(request:IncomingMessage):Promise<unknown>{\r\n if(request.headers[\"content-type\"]?.split(\";\")[0]?.trim().toLowerCase()!==\"application/json\")throw Error(\"invalid action content type\");\r\n if(Number(request.headers[\"content-length\"])>MAX_INTERACTION_BYTES)throw Error(\"action too large\");\r\n request.setTimeout(5000,()=>request.destroy());\r\n try{const chunks:Buffer[]=[];let bytes=0;for await(const chunk of request){const data=Buffer.from(chunk);bytes+=data.length;if(bytes>MAX_INTERACTION_BYTES)throw Error(\"action too large\");chunks.push(data)}return JSON.parse(Buffer.concat(chunks).toString(\"utf8\")) as unknown}\r\n finally{request.setTimeout(0)}\r\n}\r\n\r\nfunction reject(response: ServerResponse, status: number): void {\r\n response.writeHead(status, {\r\n \"Cache-Control\": \"no-store\",\r\n \"Content-Length\": \"0\",\r\n \"X-Content-Type-Options\": \"nosniff\",\r\n });\r\n response.end();\r\n}\r\n", "import type {BillingSnapshot} from \"@starcried/starlite-dsh-protocol\";\r\n\r\n// Public DeepSeek rule, verified 2026-10-10:\r\n// https://api-docs.deepseek.com/zh-cn/quick_start/pricing/\r\n// Mon\u2013Fri 09:00\u201312:00 and 14:00\u201318:00 Beijing, excluding Chinese holidays.\r\n// Public 2026 holiday schedule:\r\n// https://www.gov.cn/zhengce/zhengceku/202511/content_7047091.htm\r\nconst HOLIDAYS_2026 = [\r\n [\"2026-01-01\",\"2026-01-03\"], [\"2026-02-15\",\"2026-02-23\"],\r\n [\"2026-04-04\",\"2026-04-06\"], [\"2026-05-01\",\"2026-05-05\"],\r\n [\"2026-06-19\",\"2026-06-21\"], [\"2026-09-25\",\"2026-09-27\"],\r\n [\"2026-10-01\",\"2026-10-07\"],\r\n] as const;\r\n\r\n/** Current published tariff period; does not infer past requests' billing time. */\r\nexport function deepSeekPricingPeriod(time: number): NonNullable<BillingSnapshot[\"pricingPeriod\"]> {\r\n const beijing = new Date(time + 8 * 60 * 60 * 1000);\r\n if (!Number.isFinite(beijing.getTime())) return \"unknown\";\r\n const weekday = beijing.getUTCDay(), minutes = beijing.getUTCHours() * 60 + beijing.getUTCMinutes();\r\n if (weekday === 0 || weekday === 6 || !(minutes >= 540 && minutes < 720 || minutes >= 840 && minutes < 1080)) return \"off-peak\";\r\n // Do not guess an unverified year's holiday calendar. Weekend makeup work\r\n // does not alter DeepSeek's explicit Monday-through-Friday pricing rule.\r\n if (beijing.getUTCFullYear() !== 2026) return \"unknown\";\r\n const date = beijing.toISOString().slice(0,10);\r\n return HOLIDAYS_2026.some(([start,end]) => date >= start && date <= end) ? \"off-peak\" : \"peak\";\r\n}\r\n", "import { parseBillingSnapshot, type BillingSnapshot } from \"@starcried/starlite-dsh-protocol\";\r\nimport {deepSeekPricingPeriod} from \"./pricing-period.js\";\r\n\r\nconst PRICING_DATE = \"2026-10-10\";\r\nconst CURRENCIES = [\"CNY\", \"USD\"] as const;\r\n// Official peak prices per million tokens, verified on PRICING_DATE.\r\n// Off-peak is half. A range avoids pretending to know provider billing time\r\n// at the boundary of tariff periods. Unknown model IDs never inherit a price.\r\nconst RATES: Record<string, readonly [number, number, number][]> = {\r\n \"deepseek-flash\": [[.04, 2, 8], [.006, .3, 1.2]],\r\n \"deepseek-v4-flash\": [[.04, 2, 8], [.006, .3, 1.2]],\r\n \"deepseek-v4-flash-vision-exp\": [[.04, 2, 8], [.006, .3, 1.2]],\r\n \"deepseek-v4-pro\": [[.30, 9, 27], [.044, 1.32, 3.96]],\r\n};\r\nconst MAX_SESSIONS = 512;\r\nconst MAX_SESSION_BYTES = 16 * 1024 * 1024;\r\nconst MAX_EVENTS = 100_000;\r\nconst MAX_BALANCE_BYTES = 16_384;\r\n\r\nexport interface BillingContext {\r\n get?(name: string): unknown;\r\n}\r\ninterface Settings {\r\n get?(namespace: string): unknown;\r\n describe?(options: {redactSecrets: true}): unknown;\r\n}\r\ninterface Credentials {\r\n resolve(reference: string): Promise<{value: string} | undefined>;\r\n readRecord(key: string): Promise<unknown>;\r\n}\r\nexport interface BillingPersistence {\r\n list(options: {signal: AbortSignal}): Promise<readonly {\r\n header: {id: string}; revision: unknown; sizeBytes?: number;\r\n }[]>;\r\n open(id: string, access: \"read\", options: {signal: AbortSignal}): Promise<{\r\n inheritedEventCount: number;\r\n read(offset: number, length: number, options: {signal: AbortSignal}): Promise<{events: readonly unknown[]}>;\r\n close(): Promise<void>;\r\n }>;\r\n}\r\nexport interface BillingSource { read(): Promise<BillingSnapshot>; close(): void; }\r\ninterface SessionUsage { priced: number; unpriced: number; maximums: number[]; }\r\ninterface OfficialRoute { provider: \"deepseek-official\" | \"deepseek\"; profile: Record<string, unknown>; }\r\n\r\n/** Reads existing DSH services only; never writes credentials or session files. */\r\nexport class DeepSeekBilling implements BillingSource {\r\n readonly #context: BillingContext;\r\n readonly #fetch: typeof fetch;\r\n readonly #now: () => number;\r\n readonly #cache = new Map<string, {revision: unknown; date: string; usage: SessionUsage}>();\r\n #balance: {at: number; value: BillingSnapshot[\"balance\"]} | undefined;\r\n #snapshot: BillingSnapshot | undefined;\r\n #pending: Promise<BillingSnapshot> | undefined;\r\n #abort: AbortController | undefined;\r\n #closed = false;\r\n #generation = 0;\r\n\r\n constructor(context: BillingContext, fetchImplementation: typeof fetch = fetch, now: () => number = Date.now) {\r\n this.#context = context; this.#fetch = fetchImplementation; this.#now = now;\r\n }\r\n invalidate(accountChanged = false): void {\r\n ++this.#generation; this.#snapshot = undefined;\r\n if (accountChanged) {this.#balance = undefined; this.#cache.clear(); this.#abort?.abort();}\r\n }\r\n close(): void {this.#closed = true; this.#abort?.abort(); this.#cache.clear(); this.#balance = undefined; this.#snapshot = undefined;}\r\n read(): Promise<BillingSnapshot> {\r\n if (this.#closed) return Promise.reject(new Error(\"billing unavailable\"));\r\n const now = this.#now();\r\n if (this.#snapshot && this.#snapshot.today.date === billingDate(now) && now - this.#snapshot.checkedAt < 5000) return Promise.resolve(this.#snapshot);\r\n if (this.#pending) return this.#pending;\r\n // describe() can announce its first live snapshot. Read it before opening\r\n // this request so its own settings notification cannot abort the query.\r\n const routes = officialRoutes(this.#context);\r\n const generation = this.#generation;\r\n const abort = new AbortController(); this.#abort = abort;\r\n const timer = setTimeout(() => abort.abort(), 8000); timer.unref();\r\n this.#pending = this.#query(now, abort.signal, routes).then(snapshot => {\r\n if (!this.#closed && generation === this.#generation) this.#snapshot = snapshot;\r\n return snapshot;\r\n }).finally(() => {clearTimeout(timer); this.#pending = undefined; this.#abort = undefined;});\r\n return this.#pending;\r\n }\r\n async #query(now: number, signal: AbortSignal, routes: readonly OfficialRoute[]): Promise<BillingSnapshot> {\r\n const date = billingDate(now);\r\n const official = routes.length > 0;\r\n const balancePromise = official ? this.#readBalance(routes, now, signal) : Promise.resolve({status: \"unconfigured\" as const, balances: []});\r\n const today: BillingSnapshot[\"today\"] = {\r\n date, timeZone: \"Asia/Shanghai\", partial: !official, pricedRequests: 0, unpricedRequests: 0,\r\n pricingDate: PRICING_DATE, estimates: CURRENCIES.map(currency => ({currency, minimum: 0, maximum: 0})),\r\n };\r\n const persistence = this.#context.get?.(\"sessionPersistence\") as BillingPersistence | undefined;\r\n if (!official || !persistence || typeof persistence.list !== \"function\" || typeof persistence.open !== \"function\") today.partial = true;\r\n else {\r\n try {\r\n const sessions = await persistence.list({signal});\r\n if (sessions.length > MAX_SESSIONS) today.partial = true;\r\n const seen = new Set<string>();\r\n for (const session of sessions.slice(0, MAX_SESSIONS)) {\r\n if (signal.aborted) {today.partial = true; break;}\r\n const id = session.header.id; seen.add(id);\r\n if (session.sizeBytes !== undefined && session.sizeBytes > MAX_SESSION_BYTES) {today.partial = true; continue;}\r\n let usage = this.#cache.get(id);\r\n if (!usage || usage.date !== date || usage.revision !== session.revision) {\r\n let handle: Awaited<ReturnType<BillingPersistence[\"open\"]>> | undefined;\r\n try {\r\n handle = await persistence.open(id, \"read\", {signal});\r\n const {events} = await handle.read(0, MAX_EVENTS + 1, {signal});\r\n if (events.length > MAX_EVENTS || !Number.isSafeInteger(handle.inheritedEventCount) || handle.inheritedEventCount < 0 || handle.inheritedEventCount > events.length) {today.partial = true; continue;}\r\n usage = {revision: session.revision, date, usage: foldDailyUsage(events, handle.inheritedEventCount, date, routes.map(route => route.provider))};\r\n this.#cache.set(id, usage);\r\n } catch {today.partial = true; continue;}\r\n finally {await handle?.close().catch(() => {today.partial = true;});}\r\n }\r\n today.pricedRequests += usage.usage.priced; today.unpricedRequests += usage.usage.unpriced;\r\n today.estimates.forEach((estimate, i) => {estimate.maximum += usage.usage.maximums[i] ?? 0; estimate.minimum = estimate.maximum / 2;});\r\n }\r\n for (const id of this.#cache.keys()) if (!seen.has(id)) this.#cache.delete(id);\r\n } catch {today.partial = true;}\r\n }\r\n const balance = await balancePromise;\r\n if (signal.aborted || this.#closed) throw new Error(\"billing unavailable\");\r\n return parseBillingSnapshot({billingVersion: 1, checkedAt: now, pricingPeriod: deepSeekPricingPeriod(now), balance, today});\r\n }\r\n async #readBalance(routes: readonly OfficialRoute[], now: number, signal: AbortSignal): Promise<BillingSnapshot[\"balance\"]> {\r\n if (this.#balance && now - this.#balance.at < 60_000) return this.#balance.value;\r\n let value: BillingSnapshot[\"balance\"];\r\n let source: BillingSnapshot[\"balance\"][\"source\"];\r\n try {\r\n // Prefer DSH's native route, then the explicitly configured pi-ai route.\r\n // A missing native key must not hide an existing pi-ai credential.\r\n let key: string | undefined;\r\n for (const route of routes) {\r\n key = await resolveKey(this.#context, route);\r\n if (key) {source = route.provider;break;}\r\n }\r\n if (!key) value = {status: \"unconfigured\", balances: []};\r\n else {\r\n const request = this.#fetch;\r\n const response = await request(\"https://api.deepseek.com/user/balance\", {\r\n method: \"GET\", headers: {Authorization: `Bearer ${key}`}, redirect: \"error\", signal,\r\n });\r\n if (!response.ok || !response.headers.get(\"content-type\")?.toLowerCase().includes(\"application/json\") || !response.body) {\r\n await response.body?.cancel(); throw new Error(\"balance unavailable\");\r\n }\r\n const reader = response.body.getReader(); let bytes = 0; let json = \"\";\r\n const decoder = new TextDecoder();\r\n try {\r\n while (true) {\r\n const {done, value: chunk} = await reader.read(); if (done) break;\r\n bytes += chunk.byteLength; if (bytes > MAX_BALANCE_BYTES) throw new Error(\"balance too large\");\r\n json += decoder.decode(chunk, {stream: true});\r\n }\r\n json += decoder.decode();\r\n } finally {await reader.cancel().catch(() => undefined);}\r\n const raw = record(JSON.parse(json));\r\n if (typeof raw?.is_available !== \"boolean\" || !Array.isArray(raw.balance_infos) || raw.balance_infos.length < 1 || raw.balance_infos.length > 2) throw new Error(\"invalid balance\");\r\n const balances = raw.balance_infos.map(item => {\r\n const entry = record(item);\r\n if (!entry || ![\"CNY\", \"USD\"].includes(String(entry.currency)) || typeof entry.total_balance !== \"string\" || !/^-?\\d{1,9}(\\.\\d{1,8})?$/u.test(entry.total_balance)) throw new Error(\"invalid balance\");\r\n return {currency: entry.currency as \"CNY\" | \"USD\", remaining: Number(entry.total_balance)};\r\n });\r\n if (new Set(balances.map(item => item.currency)).size !== balances.length) throw new Error(\"duplicate currency\");\r\n value = {status: \"ready\", balances, ...(source ? {source} : {})};\r\n }\r\n } catch {value = {status: \"unavailable\", balances: [], ...(source ? {source} : {})};}\r\n if (!signal.aborted && !this.#closed) this.#balance = {at: now, value};\r\n return value;\r\n }\r\n}\r\n\r\nexport function billingDate(time: number): string {\r\n return new Date(time + 8 * 60 * 60 * 1000).toISOString().slice(0, 10);\r\n}\r\nexport function officialProfile(value: unknown): boolean {\r\n const profile = record(value); if (!profile) return false;\r\n if (record(profile.headers) && Object.keys(record(profile.headers)!).some(key => /^(authorization|x-api-key|api-key)$/iu.test(key))) return false;\r\n if (Array.isArray(profile.models) && profile.models.some(model => {\r\n const override = record(model)?.baseURL;\r\n return override !== undefined && !officialEndpoint(override);\r\n })) return false;\r\n return profile.baseURL === undefined || officialEndpoint(profile.baseURL); // Built-in deepseek route defaults to official.\r\n}\r\nfunction officialEndpoint(value: unknown): boolean {\r\n if (typeof value !== \"string\") return false;\r\n try {\r\n const url = new URL(value);\r\n return url.origin === \"https://api.deepseek.com\" && !url.username && !url.password && !url.search && !url.hash\r\n && [\"/\", \"/v1\", \"/v1/\", \"/anthropic\", \"/anthropic/\"].includes(url.pathname);\r\n } catch {return false;}\r\n}\r\nfunction officialRoutes(context: BillingContext): OfficialRoute[] {\r\n const settings = context.get?.(\"settings\") as Settings | undefined;\r\n if (!settings) return [];\r\n try {\r\n let candidates: OfficialRoute[];\r\n if (typeof settings.get === \"function\") {\r\n const native = record(settings.get(\"llm-deepseek\"));\r\n const pi = record(record(record(settings.get(\"llm-pi-ai\"))?.providers)?.deepseek);\r\n candidates = [...(native ? [{provider: \"deepseek-official\" as const, profile: native}] : []), ...(pi ? [{provider: \"deepseek\" as const, profile: pi}] : [])];\r\n } else {\r\n const llm = context.get?.(\"llm\") as {listConfigurableProviders?(): unknown} | undefined;\r\n if (typeof settings.describe !== \"function\" || typeof llm?.listConfigurableProviders !== \"function\") return [];\r\n const directory = llm.listConfigurableProviders();\r\n const descriptors = settings.describe({redactSecrets: true});\r\n if (!Array.isArray(directory) || !Array.isArray(descriptors)) return [];\r\n candidates = [];\r\n // Account login is a different credential type, never an API-key route.\r\n for (const provider of [\"deepseek-official\", \"deepseek\"] as const) {\r\n const entries = directory.map(record).filter(entry => entry?.provider === provider);\r\n if (entries.length !== 1) continue;\r\n const entry = entries[0]!;\r\n if (typeof entry.settingsNs !== \"string\" || !entry.settingsNs || !Array.isArray(entry.settingsPath) || entry.settingsPath.length > 8\r\n || entry.settingsPath.some(segment => typeof segment !== \"string\" || !segment || [\"__proto__\", \"prototype\", \"constructor\"].includes(segment))) continue;\r\n const rows = descriptors.map(record).filter(row => row?.ns === entry.settingsNs);\r\n if (rows.length !== 1) continue;\r\n let profile = record(rows[0]?.value);\r\n for (const segment of entry.settingsPath as string[]) profile = profile && Object.hasOwn(profile, segment) ? record(profile[segment]) : undefined;\r\n if (profile) candidates.push({provider, profile});\r\n }\r\n }\r\n return candidates.filter(route => {\r\n if (route.provider === \"deepseek\") return officialProfile(route.profile);\r\n const environment = context.get?.(\"launchEnvironment\") as {get(name: string): {value: string} | undefined} | undefined;\r\n // Use the same trusted endpoint precedence as the native API-key adapter.\r\n const baseURL = route.profile.baseURL ?? (environment ? environment.get(\"DEEPSEEK_BASE_URL\")?.value : process.env.DEEPSEEK_BASE_URL)\r\n ?? (typeof settings.get === \"function\" ? \"https://api.deepseek.com\" : \"https://api.deepseek.com/anthropic\");\r\n return officialProfile({...route.profile, baseURL});\r\n });\r\n } catch {\r\n return [];\r\n }\r\n}\r\nasync function resolveKey(context: BillingContext, route: OfficialRoute): Promise<string | undefined> {\r\n const credentials = context.get?.(\"credentials\") as Credentials | undefined;\r\n if (!credentials) return undefined;\r\n const reference = route.profile.apiKeyEnv ?? (route.provider === \"deepseek-official\" ? \"DEEPSEEK_API_KEY\" : undefined);\r\n let value: unknown;\r\n if (reference !== undefined) {\r\n if (typeof reference !== \"string\" || !/^[A-Za-z_][A-Za-z0-9_]*$/u.test(reference)) return undefined;\r\n value = (await credentials.resolve(reference))?.value;\r\n } else {\r\n const stored = record(await credentials.readRecord(\"llm-pi-ai/deepseek\"));\r\n if (stored?.kind === \"api-key\") value = stored.key ?? record(stored.env)?.DEEPSEEK_API_KEY;\r\n value ??= (await credentials.resolve(\"DEEPSEEK_API_KEY\"))?.value;\r\n }\r\n return typeof value === \"string\" && value.length > 0 && value.length <= 4096 && /^[!-~]+$/u.test(value) ? value : undefined;\r\n}\r\n\r\n/** Last settlement wins per attempt; retry-started opens a separate billable slot. */\r\nexport function foldDailyUsage(events: readonly unknown[], inherited: number, date: string, providers: readonly string[] = [\"deepseek\"]): SessionUsage {\r\n const result: SessionUsage = {priced: 0, unpriced: 0, maximums: [0, 0]};\r\n let header: Record<string, unknown> | undefined;\r\n let last: {turn: unknown; step: unknown; sample: Record<string, unknown> | undefined; route: Record<string, unknown> | undefined; time: number} | undefined;\r\n const flush = () => {\r\n if (!last || billingDate(last.time) !== date || (typeof last.route?.provider === \"string\" && !providers.includes(last.route.provider))) {last = undefined; return;}\r\n const rates = typeof last.route?.model === \"string\" && typeof last.route.provider === \"string\" && providers.includes(last.route.provider) && Object.hasOwn(RATES,last.route.model) ? RATES[last.route.model] : undefined;\r\n const sample = normalizeUsage(last.sample);\r\n if (!rates || !sample) result.unpriced++;\r\n else {\r\n result.priced++;\r\n rates.forEach((rate, i) => {result.maximums[i] = (result.maximums[i] ?? 0) + (rate[0] * sample.hit + rate[1] * sample.input + rate[2] * sample.output) / 1_000_000;});\r\n }\r\n last = undefined;\r\n };\r\n for (let i = 0; i < events.length; i++) {\r\n const event = record(events[i]); const data = record(event?.data);\r\n if (event?.type === \"request/header\") header = record(record(data?.header)?.config);\r\n if (i < inherited) continue;\r\n if (event?.type === \"llm/retry-started\") {flush(); continue;}\r\n if (event?.type !== \"assistant/message\" && event?.type !== \"assistant/attempt\") continue;\r\n if (!data || !count(data.turn) || !count(data.step) || !count(event.time)) continue;\r\n if (last && (last.turn !== data.turn || last.step !== data.step)) flush();\r\n let sample = record(data.usage);\r\n if (!sample && Array.isArray(data.stream)) {\r\n for (const item of data.stream) {\r\n const chunk = record(record(item)?.chunk);\r\n if (chunk?.type === \"usage\") sample = record(chunk.usage);\r\n }\r\n }\r\n const source = record(record(data.message)?.source);\r\n last = {turn: data.turn, step: data.step, sample: sample ?? last?.sample, route: source ?? header ?? last?.route, time: event.time as number};\r\n }\r\n flush(); return result;\r\n}\r\nfunction normalizeUsage(sample: Record<string, unknown> | undefined): {input: number; hit: number; output: number} | undefined {\r\n if (!sample || !count(sample.inputTokens) || !count(sample.outputTokens)\r\n || !count(sample.cacheReadTokens) || (sample.cacheWriteTokens !== undefined && sample.cacheWriteTokens !== 0)) return undefined;\r\n const input = sample.inputTokens as number, output = sample.outputTokens as number, hit = sample.cacheReadTokens as number;\r\n const total = input + output + hit;\r\n if (!count(total) || (sample.totalTokens !== undefined && sample.totalTokens !== total)\r\n || (sample.reasoningTokens !== undefined && (!count(sample.reasoningTokens) || (sample.reasoningTokens as number) > output))) return undefined;\r\n return {input, output, hit};\r\n}\r\nfunction count(value: unknown): boolean {return Number.isSafeInteger(value) && (value as number) >= 0;}\r\nfunction record(value: unknown): Record<string, unknown> | undefined {return value && typeof value === \"object\" && !Array.isArray(value) ? value as Record<string, unknown> : undefined;}\r\n", "import {randomUUID} from \"node:crypto\";\r\nimport {parsePetInteraction,parsePetAnswer,type PetInteraction,type PetAnswer} from \"@starcried/starlite-dsh-protocol\";\r\nimport type {DshStateAggregator} from \"./aggregator.js\";\r\n\r\ninterface Services {get?(name:string):unknown}\r\ninterface QuestionService {\r\n attachWait(agent:unknown,callId:string,signal:AbortSignal):AsyncIterable<unknown>;\r\n answer(agent:unknown,callId:string,answer:{answers:PetAnswer[\"answers\"]}):boolean|Promise<boolean>;\r\n}\r\ninterface ProjectionService {\r\n stateOf(session:unknown,key:\"userQuestions\"):unknown;\r\n onChanged(listener:(session:unknown,key:string)=>void):()=>void;\r\n}\r\ninterface AgentDirectory {get(id:string):unknown;roots():readonly unknown[]}\r\ninterface LateQuestion {agent:unknown;key:unknown;callId:string;request:PetInteraction}\r\ninterface Pending {\r\n key:unknown;request:PetInteraction;late?:LateQuestion;deadline?:number;\r\n finish:(answer:PetAnswer|undefined)=>void;abort:(code?:string)=>void;\r\n}\r\nconst MAX_PENDING=32;\r\nconst MAX_TIMER=2_147_483_647;\r\n\r\n/** Ordinary questions only. Tool permission approvals remain owned by DSH. */\r\nexport class QuestionBridge {\r\n readonly #pending=new Map<string,Pending>();\r\n readonly #suppressed=new Map<unknown,Set<string>>();\r\n #connected=false;#enabled=false;#closed=false;#refreshQueued=false;\r\n #unsubscribe:(()=>void)|undefined;\r\n constructor(readonly aggregator:DshStateAggregator,readonly openSession:(key:unknown)=>Promise<void>,readonly services:Services={}){}\r\n setConnected(value:boolean):void{this.#connected=value;if(!value)this.#delegateAll();else this.refresh();}\r\n setEnabled(value:boolean):void{this.#enabled=value;if(!value)this.#delegateAll();else this.refresh();}\r\n close():void{this.#closed=true;this.#delegateAll();this.#unsubscribe?.();this.#unsubscribe=undefined;this.#suppressed.clear();}\r\n read(id:string):PetInteraction|undefined{\r\n const pending=this.#pending.get(id),issuedAt=Date.now();\r\n if(pending?.deadline!==undefined&&issuedAt>=pending.deadline)pending.abort(\"ASK_TIMED_OUT\");\r\n if(pending?.late&&!this.#currentLate(pending.late))pending.finish(undefined);\r\n const current=this.#pending.get(id);\r\n return current?.deadline===undefined?current?.request:{...current.request,wait:{state:\"timed\",deadline:current.deadline,issuedAt}};\r\n }\r\n async answer(raw:unknown):Promise<void>{\r\n const requestId=record(raw)?.requestId;\r\n const pending=typeof requestId===\"string\"?this.#pending.get(requestId):undefined;\r\n if(!pending)throw Error(\"question no longer pending\");\r\n const answer=parsePetAnswer(raw,pending.request);\r\n if(pending.deadline!==undefined&&Date.now()>=pending.deadline){pending.abort(\"ASK_TIMED_OUT\");throw Error(\"question no longer pending\");}\r\n if(!pending.late){pending.finish(answer);return;}\r\n const late=pending.late,service=this.#questionService();\r\n if(!service||!this.#currentLate(late)){pending.finish(undefined);throw Error(\"question no longer pending\");}\r\n // Retire the random reference before calling the owner. Its atomic method\r\n // validates the live root and refuses stale or already queued replies.\r\n this.#suppress(late.key,late.callId);pending.finish(undefined);\r\n if(await service.answer(late.agent,late.callId,{answers:answer.answers})!==true)throw Error(\"question no longer pending\");\r\n }\r\n async open(ref:string):Promise<void>{\r\n const key=this.aggregator.sessionKey(ref);if(key===undefined||this.#closed)throw Error(\"session unavailable\");\r\n const captured=[...this.#pending.values()].filter(item=>item.key===key);\r\n await this.openSession(key);\r\n if(this.#closed||this.aggregator.sessionKey(ref)!==key)return;\r\n // Navigation may fail or outlive its question. Only a successful open can\r\n // delegate the same captured request; a newer form keeps its own lifetime.\r\n for(const pending of captured){\r\n if(this.#pending.get(pending.request.requestId)!==pending)continue;\r\n if(pending.deadline!==undefined&&Date.now()>=pending.deadline)continue;\r\n if(pending.late){\r\n if(!this.#currentLate(pending.late))continue;\r\n this.#suppress(key,pending.late.callId);\r\n }\r\n pending.finish(undefined);\r\n }\r\n }\r\n async openWebUi():Promise<void>{if(this.#closed)throw Error(\"DSH Web UI unavailable\");await this.openSession(undefined);}\r\n cancelForeground(key:unknown):void{for(const item of this.#pending.values())if(item.key===key&&!item.late)item.abort();}\r\n cancelSession(key:unknown):void{\r\n for(const item of this.#pending.values())if(item.key===key)item.abort();\r\n this.#suppressed.delete(key);\r\n }\r\n /** Called after agent changes; projection changes have their own public feed. */\r\n refresh():void{\r\n if(this.#closed||!this.#connected||!this.#enabled)return;\r\n if(!this.#unsubscribe){\r\n const projections=this.#projectionService();\r\n if(projections)try{this.#unsubscribe=projections.onChanged((_session,key)=>{if(key===\"userQuestions\")this.refresh();});}catch{return;}\r\n }\r\n if(this.#refreshQueued)return;\r\n this.#refreshQueued=true;\r\n queueMicrotask(()=>{this.#refreshQueued=false;if(!this.#closed&&this.#connected&&this.#enabled)this.#reconcile();});\r\n }\r\n async ask(key:unknown,raw:unknown,next:()=>Promise<unknown>):Promise<unknown>{\r\n if(this.#closed||!this.#connected||!this.#enabled)return next();\r\n const source=record(raw);let request:PetInteraction;\r\n try{request=this.#request(key,source?.questions);}catch{return next();}\r\n const signal=source?.signal;\r\n if(signal!==undefined&&!(signal instanceof AbortSignal))return next();\r\n if(signal?.aborted)throw questionError(\"ASK_ABORTED\");\r\n const wait=record(source?.wait),timed=wait?.timed===true;\r\n const callId=validCallId(wait?.callId)?wait.callId:undefined;\r\n const service=this.#questionService();\r\n if(source?.wait!==undefined&&(!wait||!callId||(wait.timed!==undefined&&typeof wait.timed!==\"boolean\")))return next();\r\n if(timed&&(!callId||!service||!this.#liveRoot(source?.agent,key)))return next();\r\n this.#suppressed.delete(key);\r\n if(this.#pending.size>=MAX_PENDING){\r\n const late=[...this.#pending.values()].find(item=>item.late);late?.finish(undefined);\r\n if(this.#pending.size>=MAX_PENDING)return next();\r\n }\r\n let iterator:AsyncIterator<unknown>|undefined;\r\n const claim=new AbortController();\r\n let timer:ReturnType<typeof setTimeout>|undefined;\r\n let published=false,settled=false;\r\n const answer=await new Promise<PetAnswer|undefined>((resolve,reject)=>{\r\n const sourceAborted=()=>abort(record(signal?.reason)?.code===\"ASK_TIMED_OUT\"?\"ASK_TIMED_OUT\":\"ASK_ABORTED\");\r\n const cleanup=()=>{\r\n this.#pending.delete(request.requestId);signal?.removeEventListener(\"abort\",sourceAborted);\r\n clearTimeout(timer);claim.abort();\r\n if(iterator?.return)void iterator.return().catch(()=>undefined);\r\n if(published)this.aggregator.handleInputResolved(key,request.requestId);\r\n };\r\n const finish=(value:PetAnswer|undefined)=>{if(settled)return;settled=true;cleanup();resolve(value);};\r\n const abort=(code=\"ASK_ABORTED\")=>{if(settled)return;settled=true;cleanup();reject(questionError(code));};\r\n const pending:Pending={key,request,finish,abort};\r\n this.#pending.set(request.requestId,pending);\r\n signal?.addEventListener(\"abort\",sourceAborted,{once:true});\r\n const publish=()=>{published=true;this.aggregator.handleInputRequested(key,request.requestId);};\r\n if(!timed){publish();return;}\r\n void (async()=>{\r\n try{\r\n iterator=service!.attachWait(source!.agent,callId!,claim.signal)[Symbol.asyncIterator]();\r\n const first=await iterator.next();\r\n if(settled)return;\r\n const remaining=record(first.value)?.remainingMs;\r\n if(first.done||!Number.isSafeInteger(remaining)||Number(remaining)<0||Number(remaining)>MAX_TIMER){finish(undefined);return;}\r\n // Match the official Client: the Host duration starts at receipt of\r\n // the opening frame, without subtracting time spent obtaining it.\r\n const issuedAt=Date.now();pending.deadline=issuedAt+Number(remaining);\r\n // Adding public wait metadata must not bypass the complete wire bound.\r\n try{request=parsePetInteraction({...request,wait:{state:\"timed\",deadline:pending.deadline,issuedAt}});pending.request=request;}\r\n catch{finish(undefined);return;}\r\n if(Date.now()>=pending.deadline){abort(\"ASK_TIMED_OUT\");return;}\r\n timer=setTimeout(()=>abort(\"ASK_TIMED_OUT\"),pending.deadline-Date.now());timer.unref();publish();\r\n // The public stream holds the claim until cancellation or settlement.\r\n await iterator.next();if(!settled)finish(undefined);\r\n }catch{if(!settled)finish(undefined);}\r\n })();\r\n });\r\n return answer?{answers:answer.answers}:next();\r\n }\r\n #request(key:unknown,questions:unknown,wait?:PetInteraction[\"wait\"]):PetInteraction{return parsePetInteraction({interactionVersion:1,requestId:randomUUID(),sessionRef:this.aggregator.sessionInfo(key).ref,questions,...(wait?{wait}:{})});}\r\n #questionService():QuestionService|undefined{\r\n const service=this.#service(\"userQuestions\");\r\n return typeof service?.attachWait===\"function\"&&typeof service.answer===\"function\"?service as unknown as QuestionService:undefined;\r\n }\r\n #projectionService():ProjectionService|undefined{\r\n const service=this.#service(\"sessionProjections\");\r\n return typeof service?.stateOf===\"function\"&&typeof service.onChanged===\"function\"?service as unknown as ProjectionService:undefined;\r\n }\r\n #directory():AgentDirectory|undefined{\r\n const service=this.#service(\"agents\");\r\n return typeof service?.get===\"function\"&&typeof service.roots===\"function\"?service as unknown as AgentDirectory:undefined;\r\n }\r\n #service(name:string):Record<string,unknown>|undefined{try{return record(this.services.get?.(name));}catch{return undefined;}}\r\n #liveRoot(agent:unknown,key:unknown):boolean{\r\n try{const row=record(agent),directory=this.#directory();return Boolean(row&&directory&&row.session===key&&typeof row.id===\"string\"&&directory.get(row.id)===agent&&directory.roots().includes(agent));}catch{return false;}\r\n }\r\n #lateQuestions():LateQuestion[]{\r\n const service=this.#questionService(),projections=this.#projectionService(),directory=this.#directory();\r\n if(!service||!projections||!directory)return [];\r\n const result:LateQuestion[]=[];\r\n try{\r\n for(const agent of directory.roots().slice(0,128)){\r\n const key=record(agent)?.session;if(!key||!this.#liveRoot(agent,key))continue;\r\n const active=record(record(projections.stateOf(key,\"userQuestions\"))?.questions)?.active;\r\n if(!Array.isArray(active)||active.length>MAX_PENDING)continue;\r\n for(const raw of active){\r\n const item=record(raw);if(item?.state!==\"continued\"||!validCallId(item.callId))continue;\r\n try{result.push({agent,key,callId:item.callId,request:this.#request(key,item.questions,{state:\"continued\"})});}catch{continue;}\r\n if(result.length>=MAX_PENDING)return result;\r\n }\r\n }\r\n }catch{return [];}\r\n return result;\r\n }\r\n #currentLate(late:LateQuestion):boolean{\r\n return !this.#closed&&this.#connected&&this.#enabled&&!this.#suppressed.get(late.key)?.has(late.callId)\r\n &&this.#lateQuestions().some(item=>sameLate(item,late));\r\n }\r\n #reconcile():void{\r\n const current=this.#lateQuestions();\r\n for(const pending of this.#pending.values())if(pending.late&&!current.some(item=>sameLate(item,pending.late!)))pending.finish(undefined);\r\n for(const [key,ids] of this.#suppressed){for(const id of ids)if(!current.some(item=>item.key===key&&item.callId===id))ids.delete(id);if(!ids.size)this.#suppressed.delete(key);}\r\n for(const late of current){\r\n if(this.#pending.size>=MAX_PENDING)break;\r\n if(this.#suppressed.get(late.key)?.has(late.callId)||[...this.#pending.values()].some(item=>item.late&&sameLate(item.late,late)))continue;\r\n const request=late.request;\r\n const finish=()=>{this.#pending.delete(request.requestId);this.aggregator.handleInputResolved(late.key,request.requestId);};\r\n this.#pending.set(request.requestId,{key:late.key,request,late,finish,abort:finish});\r\n this.aggregator.handleInputRequested(late.key,request.requestId,false);\r\n }\r\n }\r\n #suppress(key:unknown,callId:string):void{const ids=this.#suppressed.get(key)??new Set<string>();ids.add(callId);this.#suppressed.set(key,ids);}\r\n #delegateAll():void{for(const item of this.#pending.values())item.finish(undefined);}\r\n}\r\nfunction sameLate(a:LateQuestion,b:LateQuestion):boolean{return a.agent===b.agent&&a.key===b.key&&a.callId===b.callId&&JSON.stringify(a.request.questions)===JSON.stringify(b.request.questions);}\r\nfunction validCallId(value:unknown):value is string{return typeof value===\"string\"&&value.length>0&&value.length<=256&&!/[\\p{Cc}]/u.test(value);}\r\nfunction questionError(code:string):Error&{code:string}{return Object.assign(new Error(\"question no longer pending\"),{name:\"UserQuestionError\",code});}\r\nfunction record(value:unknown):Record<string,unknown>|undefined{return value&&typeof value===\"object\"&&!Array.isArray(value)?value as Record<string,unknown>:undefined;}\r\n", "/** Plugin-owned calls on DSH's public authenticated Connection RPC carrier. */\r\nexport const NAVIGATION_CHANNEL = \"/starlite-navigation\";\r\nexport type RpcResult<T = unknown> =\r\n | {ok: true; value: T}\r\n | {ok: false; error: {code: string; message: string; details: object}};\r\nexport interface NavigationCommand {requestRef: string; sessionId: string; expiresAt: number}\r\nexport interface NavigationRpc {\r\n call(channel: string, endpoint: string, payload: unknown, signal?: AbortSignal): Promise<RpcResult>;\r\n}\r\nexport interface HostNavigationRpc {\r\n handle(channel: string, handler: (endpoint: string, payload: unknown, signal: AbortSignal, peer: unknown) => Promise<RpcResult>): () => void | Promise<void>;\r\n}\r\nexport function opaqueRef(value: unknown): value is string {\r\n return typeof value === \"string\" && /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(value);\r\n}\r\nexport function sessionIdentity(value: unknown): value is string {\r\n return typeof value === \"string\" && value.length > 0 && value.length <= 256\r\n && !Array.from(value).some(character => character.charCodeAt(0) < 32);\r\n}\r\nexport function object(value: unknown): Record<string, unknown> | undefined {\r\n return value && typeof value === \"object\" && !Array.isArray(value) ? value as Record<string, unknown> : undefined;\r\n}\r\n", "import {randomUUID} from \"node:crypto\";\r\nimport {object, opaqueRef, type NavigationCommand, type RpcResult} from \"./navigation-rpc.js\";\r\n\r\nexport const DESKTOP_NAVIGATION_TIMEOUT_MS = 8_000;\r\nconst POLL_TIMEOUT_MS = 15_000;\r\nconst OWNER_GRACE_MS = 750;\r\ninterface Pending {\r\n sessionId: string;\r\n expiresAt: number;\r\n delivery?: {clientRef: string; requestRef: string};\r\n finish(opened: boolean): void;\r\n}\r\ninterface Owner {\r\n clientRef: string;\r\n peer: unknown;\r\n poll?: {finish(value: NavigationCommand | null): void; cancel(): void};\r\n grace?: ReturnType<typeof setTimeout>;\r\n}\r\nfunction unavailable(): RpcResult {\r\n return {ok: false, error: {code: \"starlite/navigation-unavailable\", message: \"DSH navigation unavailable\", details: {}}};\r\n}\r\nfunction busy(): RpcResult {\r\n return {ok: false, error: {code: \"starlite/navigation-busy\", message: \"DSH navigation renderer is still connected\", details: {}}};\r\n}\r\n\r\n/** One Desktop renderer owns navigation; a reload may resume only an unexpired request. */\r\nexport class DesktopNavigationBroker {\r\n #pending: Pending | undefined;\r\n #owner: Owner | undefined;\r\n #closed = false;\r\n\r\n request(sessionId: string): {result: Promise<void>; cancel(): void} {\r\n this.#pending?.finish(false);\r\n let pending: Pending;\r\n const result = new Promise<void>((resolve, reject) => {\r\n const timer = setTimeout(() => pending.finish(false), DESKTOP_NAVIGATION_TIMEOUT_MS);\r\n timer.unref?.();\r\n pending = {\r\n sessionId, expiresAt: Date.now() + DESKTOP_NAVIGATION_TIMEOUT_MS,\r\n finish: opened => {\r\n if (this.#pending !== pending) return;\r\n clearTimeout(timer);\r\n this.#pending = undefined;\r\n if (opened) resolve(); else reject(Error(\"DSH Desktop session unavailable\"));\r\n },\r\n };\r\n this.#pending = pending;\r\n });\r\n // The opener may reject before the caller begins awaiting the result.\r\n void result.catch(() => undefined);\r\n if (this.#closed) pending!.finish(false);\r\n else this.#deliver();\r\n return {result, cancel: () => pending!.finish(false)};\r\n }\r\n\r\n async handle(endpoint: string, payload: unknown, signal: AbortSignal, peer: unknown): Promise<RpcResult> {\r\n const row = object(payload);\r\n if (this.#closed || signal.aborted || !row || !opaqueRef(row.clientRef) || !peer) return unavailable();\r\n if (endpoint === \"listen\" && Object.keys(row).length === 1) return this.#listen(row.clientRef, peer, signal);\r\n const owner = this.#owner;\r\n if (!owner || owner.clientRef !== row.clientRef || owner.peer !== peer) return unavailable();\r\n if (endpoint === \"release\" && Object.keys(row).length === 1) {\r\n this.#release(owner);\r\n return {ok: true, value: null};\r\n }\r\n if (endpoint !== \"ack\" || Object.keys(row).length !== 3 || !opaqueRef(row.requestRef)\r\n || typeof row.opened !== \"boolean\") return unavailable();\r\n const pending = this.#pending;\r\n if (!pending || pending.expiresAt <= Date.now() || pending.delivery?.clientRef !== row.clientRef\r\n || pending.delivery.requestRef !== row.requestRef) return unavailable();\r\n pending.finish(row.opened);\r\n return {ok: true, value: null};\r\n }\r\n\r\n close(): void {\r\n if (this.#closed) return;\r\n this.#closed = true;\r\n this.#pending?.finish(false);\r\n if (this.#owner) this.#release(this.#owner);\r\n }\r\n\r\n #listen(clientRef: string, peer: unknown, signal: AbortSignal): Promise<RpcResult> {\r\n let owner = this.#owner;\r\n if (owner && (owner.clientRef !== clientRef || owner.peer !== peer || owner.poll)) return Promise.resolve(busy());\r\n if (!owner) {owner = {clientRef, peer}; this.#owner = owner;}\r\n clearTimeout(owner.grace);\r\n const currentOwner = owner;\r\n return new Promise(resolve => {\r\n let settled = false;\r\n const timer = setTimeout(() => finish(null), POLL_TIMEOUT_MS);\r\n timer.unref?.();\r\n const cancel = () => {\r\n if (settled) return;\r\n this.#release(currentOwner);\r\n };\r\n const finish = (value: NavigationCommand | null) => {\r\n if (settled) return;\r\n settled = true;\r\n clearTimeout(timer);\r\n signal.removeEventListener(\"abort\", cancel);\r\n delete currentOwner.poll;\r\n if (this.#owner === currentOwner) {\r\n currentOwner.grace = setTimeout(() => this.#release(currentOwner), OWNER_GRACE_MS);\r\n currentOwner.grace.unref?.();\r\n }\r\n resolve({ok: true, value});\r\n };\r\n currentOwner.poll = {finish, cancel};\r\n signal.addEventListener(\"abort\", cancel, {once: true});\r\n this.#deliver();\r\n });\r\n }\r\n\r\n #deliver(): void {\r\n const pending = this.#pending, owner = this.#owner;\r\n if (!pending || !owner?.poll || pending.delivery || pending.expiresAt <= Date.now()) return;\r\n const requestRef = randomUUID();\r\n pending.delivery = {clientRef: owner.clientRef, requestRef};\r\n owner.poll.finish({requestRef, sessionId: pending.sessionId, expiresAt: pending.expiresAt});\r\n }\r\n\r\n #release(owner: Owner): void {\r\n if (this.#owner !== owner) return;\r\n this.#owner = undefined;\r\n clearTimeout(owner.grace);\r\n // A renderer that reloads must receive a new acknowledgement identity.\r\n if (this.#pending?.delivery?.clientRef === owner.clientRef) delete this.#pending.delivery;\r\n owner.poll?.finish(null);\r\n }\r\n}\r\n", "import {spawn} from \"node:child_process\";\r\nimport {DesktopNavigationBroker, DESKTOP_NAVIGATION_TIMEOUT_MS} from \"./desktop-navigation.js\";\r\nimport {NAVIGATION_CHANNEL, object, sessionIdentity, type HostNavigationRpc} from \"./navigation-rpc.js\";\r\n\r\ninterface Services {\r\n get?(name:string):unknown;\r\n webServer?: unknown;\r\n extend?(metadata: {webServer: unknown}): Services;\r\n inject?(dependencies: string[], callback: (scope: Services) => (() => Promise<void>) | undefined): {dispose(): Promise<void>};\r\n}\r\nexport interface DshNavigator {open(key: unknown): Promise<void>; close(): Promise<void>}\r\n/** Profile ownership, not Electron's executable identity, selects Desktop navigation. */\r\nexport function createDshNavigator(context: Services, openUrl = openLocalBrowser): DshNavigator {\r\n if (object(context.get?.(\"profileContext\"))?.name !== \"desktop\") {\r\n return {open: key => openDshSession(context, key, openUrl), close: async () => undefined};\r\n }\r\n let closed = false;\r\n const active = new Set<AbortController>();\r\n let binding: {broker: DesktopNavigationBroker; remove(): void | Promise<void>} | undefined;\r\n const attach = (scope: Services) => {\r\n if (closed) return;\r\n const rpc = object(object(scope.get?.(\"connection\"))?.rpc) as unknown as HostNavigationRpc | undefined;\r\n if (typeof rpc?.handle !== \"function\") throw Error(\"DSH Desktop navigation contract unavailable\");\r\n const broker = new DesktopNavigationBroker();\r\n const remove = rpc.handle(NAVIGATION_CHANNEL, (endpoint, payload, signal, peer) => broker.handle(endpoint, payload, signal, peer));\r\n if (typeof remove !== \"function\") {broker.close(); throw Error(\"DSH Desktop navigation disposal unavailable\");}\r\n const current = {broker, remove};\r\n binding = current;\r\n return async () => {\r\n if (binding === current) {\r\n binding = undefined;\r\n for (const abort of active) abort.abort();\r\n }\r\n broker.close();\r\n await remove();\r\n };\r\n };\r\n // Cordis mounts profile bundles in parallel. The Connection service is not\r\n // necessarily ready when this plugin starts; own the route through its public\r\n // dependency lifecycle so withdrawal/replacement also revokes pending targets.\r\n const injected = context.inject?.([\"connection\", \"webServer\"], scope => {\r\n try {\r\n // Connection's public RPC getter captures a Cordis shadow context. Bind\r\n // the declared carrier as public context metadata so that getter retains\r\n // this fiber's WebServer instead of reading an uninjected provider scope.\r\n // extend() leaves the parent and official service untouched.\r\n if (!scope.extend) return undefined;\r\n return attach(scope.extend({webServer: scope.webServer}));\r\n } catch {return undefined;} // Navigation must not stop DSH boot.\r\n });\r\n const detach = context.inject ? undefined : attach(context);\r\n return {\r\n async open(key) {\r\n if (closed) throw Error(\"DSH Desktop unavailable\");\r\n const id = record(key)?.id;\r\n if (key !== undefined && !sessionIdentity(id)) throw Error(\"DSH session unavailable\");\r\n if (key !== undefined && !binding) throw Error(\"DSH Desktop session unavailable\");\r\n const request = key === undefined ? undefined : binding!.broker.request(id as string);\r\n const abort = new AbortController();\r\n active.add(abort);\r\n let timer: ReturnType<typeof setTimeout> | undefined;\r\n const deadline = new Promise<never>((_resolve, reject) => {\r\n abort.signal.addEventListener(\"abort\", () => {request?.cancel(); reject(Error(\"DSH Desktop session unavailable\"));}, {once: true});\r\n timer = setTimeout(() => abort.abort(), DESKTOP_NAVIGATION_TIMEOUT_MS);\r\n timer.unref?.();\r\n });\r\n try {await Promise.race([Promise.all([openUrl(\"dsh://open\", abort.signal), request?.result]), deadline]);}\r\n catch (error) {request?.cancel(); throw error;}\r\n finally {active.delete(abort); clearTimeout(timer); abort.abort();}\r\n },\r\n async close() {\r\n if (closed) return;\r\n closed = true;\r\n for (const abort of active) abort.abort();\r\n await injected?.dispose();\r\n await detach?.();\r\n },\r\n };\r\n}\r\n/** An undefined key explicitly opens the Web UI home; supplied keys must identify a session. */\r\nexport async function openDshSession(context:Services,key:unknown,openUrl=openLocalBrowser):Promise<void>{\r\n const session=record(key), web=record(context.get?.(\"webServer\")), connection=record(context.get?.(\"connection\"));\r\n if(key!==undefined&&(typeof session?.id!==\"string\"||!session.id||session.id.length>256||Array.from(session.id).some(c=>c.charCodeAt(0)<32)))throw Error(\"DSH session unavailable\");\r\n if(web?.host!==\"127.0.0.1\"||!Number.isInteger(web.port)||Number(web.port)<1||Number(web.port)>65535\r\n ||typeof connection?.authenticatedUrl!==\"function\")throw Error(\"DSH Web UI unavailable\");\r\n const url=new URL(`http://127.0.0.1:${web.port}/`);\r\n const authenticated=(connection.authenticatedUrl as (base:string)=>unknown).call(connection,url.href);\r\n if(typeof authenticated!==\"string\")throw Error(\"DSH Web UI unavailable\");\r\n const target=new URL(authenticated);\r\n if(target.origin!==url.origin||target.pathname!==\"/\")throw Error(\"DSH Web UI unavailable\");\r\n // DSH's authentication redirect drops query parameters. A browser-owned\r\n // fragment survives that redirect and never enters an HTTP request/log.\r\n target.hash=key===undefined?\"\":new URLSearchParams({companionSession:session!.id as string}).toString();\r\n await openUrl(target.href);\r\n}\r\n// The opener needs OS launcher variables, never DSH/provider credentials.\r\nexport function browserEnvironment(source:NodeJS.ProcessEnv):NodeJS.ProcessEnv{\r\n const result:NodeJS.ProcessEnv={};\r\n for(const name of [\"PATH\",\"HOME\",\"LANG\",\"LC_ALL\",\"DISPLAY\",\"WAYLAND_DISPLAY\",\"XDG_RUNTIME_DIR\",\"XDG_CURRENT_DESKTOP\",\"DBUS_SESSION_BUS_ADDRESS\",\"WSL_INTEROP\",\"WSL_DISTRO_NAME\",\"SYSTEMROOT\",\"SystemRoot\",\"TEMP\",\"TMP\"]){\r\n if(source[name]!==undefined)result[name]=source[name];\r\n }\r\n return result;\r\n}\r\n/** Electron needs Node mode only to enter this worker, never for the GUI it opens. */\r\nexport function browserWorkerScript(openModuleUrl: string): string {\r\n // wait:true makes PowerShell wait for the GUI to quit. Instead, keep only\r\n // its launcher alive until it reports whether Start-Process succeeded.\r\n return `delete process.env.ELECTRON_RUN_AS_NODE;\r\n const {default:open}=await import(${JSON.stringify(openModuleUrl)});\r\n const launcher=await open(process.argv[1],{wait:false});\r\n launcher.ref();\r\n await new Promise((resolve,reject)=>{\r\n let settled=false;\r\n const finish=(code,signal)=>{\r\n if(settled)return;settled=true;\r\n launcher.off(\"error\",failed);launcher.off(\"exit\",finish);\r\n if(code===0&&!signal)resolve();else reject(Error(\"DSH launcher failed\"));\r\n };\r\n const failed=()=>finish(null,null);\r\n launcher.once(\"error\",failed);launcher.once(\"exit\",finish);\r\n if(launcher.exitCode!==null||launcher.signalCode!==null)finish(launcher.exitCode,launcher.signalCode);\r\n });`;\r\n}\r\nasync function openLocalBrowser(url:string, signal?: AbortSignal):Promise<void>{\r\n const script = browserWorkerScript(import.meta.resolve(\"open\"));\r\n await new Promise<void>((resolve,reject)=>{\r\n const env = browserEnvironment(process.env);\r\n // Desktop's bundled CLI also uses Electron. Its child must remain in Node\r\n // mode even though provider credentials and arbitrary Node options are stripped.\r\n if (process.versions.electron) env.ELECTRON_RUN_AS_NODE = \"1\";\r\n const child=spawn(process.execPath,[\"--input-type=module\",\"--eval\",script,url],{env,stdio:\"ignore\",windowsHide:true,shell:false,...signal === undefined ? {} : {signal}});\r\n const timer=setTimeout(()=>{child.kill();reject(Error(\"DSH Web UI could not open\"))},10000);timer.unref();\r\n child.once(\"error\",()=>{clearTimeout(timer);reject(Error(\"DSH Web UI could not open\"))});\r\n child.once(\"exit\",code=>{clearTimeout(timer);if(code===0)resolve();else reject(Error(\"DSH Web UI could not open\"))});\r\n });\r\n}\r\nfunction record(value:unknown):Record<string,unknown>|undefined{return value&&typeof value===\"object\"?value as Record<string,unknown>:undefined}\r\n", "import { randomUUID } from \"node:crypto\";\r\nimport { DshStateAggregator, type AgentRunStatus, type DshEventLike } from \"./aggregator.js\";\r\nimport { detectCompanionHost, launchWindowsPet, type WindowsCompanionHost } from \"./launcher.js\";\r\nimport { startStatusServer, type StatusServer } from \"./server.js\";\r\nimport { DeepSeekBilling, type BillingContext } from \"./billing.js\";\r\nimport {QuestionBridge} from \"./questions.js\";\r\nimport {createDshNavigator} from \"./navigation.js\";\r\nimport {DshStartupError} from \"./startup-error.js\";\r\n\r\ntype Listener = (...arguments_: unknown[]) => unknown;\r\ntype Disposer = () => void | Promise<void>;\r\n\r\ninterface LoggerLike {\r\n info?(message: string): void;\r\n warn?(message: string): void;\r\n}\r\n\r\nexport interface CordisContextLike extends BillingContext {\r\n on(event: string, listener: Listener, options?: {prepend?:boolean}): unknown;\r\n logger?: LoggerLike;\r\n}\r\n\r\nexport interface AdapterDependencies {\r\n readonly host?: () => WindowsCompanionHost | undefined;\r\n readonly startServer?: typeof startStatusServer;\r\n readonly launch?: typeof launchWindowsPet;\r\n readonly openBrowser?: (url:string)=>Promise<void>;\r\n}\r\n\r\nexport async function startDshAdapter(\r\n context: CordisContextLike,\r\n dependencies: AdapterDependencies = {},\r\n): Promise<Disposer> {\r\n if (!context || typeof context.on !== \"function\") {\r\n throw new DshStartupError(\"incompatible-api\");\r\n }\r\n const host = (dependencies.host ?? detectCompanionHost)();\r\n if (!host) {\r\n throw new DshStartupError(\"unsupported-platform\");\r\n }\r\n\r\n const navigator = createDshNavigator(context, dependencies.openBrowser);\r\n const aggregator = new DshStateAggregator();\r\n const billing = new DeepSeekBilling(context);\r\n const questions = new QuestionBridge(aggregator,key=>navigator.open(key),context);\r\n let server: StatusServer;\r\n try {server = await (dependencies.startServer ?? startStatusServer)(aggregator, {billing,questions});}\r\n catch (error) {questions.close(); billing.close(); await navigator.close(); throw error;}\r\n const refreshTitle=(key:unknown)=>{\r\n try{const service=asRecord(context.get?.(\"sessionTitle\"));const value=typeof service?.get===\"function\"?asRecord(service.get.call(service,key)):undefined;\r\n aggregator.sessionInfo(key,typeof value?.title===\"string\"?value.title:undefined);\r\n }catch{aggregator.sessionInfo(key)}\r\n };\r\n const disposers: Disposer[] = [() => navigator.close()];\r\n let approvalSequence = 0;\r\n\r\n try {\r\n subscribeRequired(context, disposers, \"agent/status\", (...arguments_) => {\r\n const payload = asRecord(arguments_[0]);\r\n const agent = payload?.agent;\r\n const status = payload?.status;\r\n if ((status === \"idle\" || status === \"running\") && agent) {\r\n refreshTitle(subjectKey(agent));\r\n aggregator.handleAgentStatus(subjectKey(agent), status as AgentRunStatus);\r\n questions.refresh();\r\n }\r\n });\r\n subscribeRequired(context, disposers, \"agent/error\", (...arguments_) => {\r\n const payload = asRecord(arguments_[0]);\r\n if (payload?.agent) {const key=subjectKey(payload.agent);questions.cancelSession(key);refreshTitle(key);aggregator.handleAgentError(key);}\r\n });\r\n subscribeRequired(context, disposers, \"agent/assistant-stream\", (...arguments_) => {\r\n const payload = asRecord(arguments_[0]);\r\n if (payload?.agent) {\r\n refreshTitle(subjectKey(payload.agent));\r\n aggregator.handleAssistantStream(subjectKey(payload.agent), payload.frame);\r\n }\r\n });\r\n subscribeRequired(context, disposers, \"session/event\", (...arguments_) => {\r\n const session = arguments_[0];\r\n const event = asRecord(arguments_[1]);\r\n if (session && typeof event?.type === \"string\") {\r\n const key=subjectKey(session);refreshTitle(key);\r\n if(event.type===\"turn/end\")questions.cancelForeground(key);\r\n aggregator.handleSessionEvent(subjectKey(session), event as unknown as DshEventLike);\r\n questions.refresh();\r\n if (event.type === \"assistant/message\" || event.type === \"assistant/attempt\") billing.invalidate();\r\n }\r\n });\r\n subscribeRequired(context, disposers, \"approval/request\", async (...arguments_) => {\r\n const request = asRecord(arguments_[0]);\r\n const next = arguments_[1];\r\n if (typeof next !== \"function\") {\r\n // Do not invent a result for a changed waterfall contract. The daily\r\n // declaration guard should catch this before a release reaches users.\r\n return undefined;\r\n }\r\n if (!request?.agent) return next();\r\n const key = subjectKey(request.agent);\r\n const approvalKey = String(approvalSequence);\r\n approvalSequence += 1;\r\n aggregator.handleApprovalRequested(key, approvalKey, request.toolName);\r\n try {\r\n return await next();\r\n } finally {\r\n aggregator.handleApprovalResolved(key, approvalKey);\r\n }\r\n });\r\n subscribeRequired(context,disposers,\"user-questions/request\",(...arguments_)=>{\r\n const request=asRecord(arguments_[0]),next=arguments_[1];\r\n if(typeof next!==\"function\")return undefined;\r\n if(!request?.agent)return next();\r\n const key=subjectKey(request.agent);refreshTitle(key);\r\n return questions.ask(key,request,next as ()=>Promise<unknown>);\r\n },{prepend:true});\r\n subscribeOptional(context, disposers, \"agent/disposed\", (...arguments_) => {\r\n const payload = asRecord(arguments_[0]);\r\n if (payload?.agent) {const key=subjectKey(payload.agent);questions.cancelSession(key);aggregator.disposeSession(key);}\r\n });\r\n for (const event of [\"settings/updated\", \"settings/document-updated\", \"credentials/reference-updated\", \"credentials/record-updated\"]) {\r\n subscribeOptional(context, disposers, event, () => billing.invalidate(true));\r\n }\r\n } catch (error) {\r\n questions.close();billing.close();\r\n await closeAdapter(disposers, server);\r\n throw error;\r\n }\r\n\r\n const launchAbort = new AbortController();\r\n void (dependencies.launch ?? launchWindowsPet)({\r\n port: server.port,\r\n token: server.token,\r\n instanceId: randomUUID(),\r\n }, {\r\n host,\r\n signal: launchAbort.signal,\r\n onDefaultModelFailure: () => {\r\n if (!launchAbort.signal.aborted) context.logger?.warn?.(\r\n \"StarLite could not acquire the starter character. The companion will show recovery instructions; existing models are preserved.\",\r\n );\r\n },\r\n }).catch(() => {\r\n if (launchAbort.signal.aborted) return;\r\n context.logger?.warn?.(\r\n \"StarLite for DSH could not start the Windows companion; DSH will continue without it.\",\r\n );\r\n });\r\n context.logger?.info?.(\"StarLite for DSH status bridge is ready.\");\r\n\r\n let closed = false;\r\n return async () => {\r\n if (closed) return;\r\n closed = true;\r\n launchAbort.abort();\r\n billing.close();\r\n questions.close();\r\n await closeAdapter(disposers, server);\r\n };\r\n}\r\n\r\nfunction subscribeRequired(\r\n context: CordisContextLike,\r\n disposers: Disposer[],\r\n event: string,\r\n listener: Listener,\r\n options?: {prepend?:boolean},\r\n): void {\r\n try {\r\n const result = context.on(event, listener, options);\r\n if (typeof result === \"function\") disposers.push(result as Disposer);\r\n } catch {\r\n throw new DshStartupError(\"incompatible-api\");\r\n }\r\n}\r\n\r\nfunction subscribeOptional(\r\n context: CordisContextLike,\r\n disposers: Disposer[],\r\n event: string,\r\n listener: Listener,\r\n): void {\r\n try {\r\n subscribeRequired(context, disposers, event, listener);\r\n } catch {\r\n // Older preview builds may not expose disposal. Core status events remain\r\n // mandatory, while a missing optional lifecycle signal must not stop DSH.\r\n }\r\n}\r\n\r\nasync function closeAdapter(disposers: Disposer[], server: StatusServer): Promise<void> {\r\n const tasks = disposers.splice(0).reverse().map(async (dispose) => dispose());\r\n tasks.push(server.close());\r\n await Promise.allSettled(tasks);\r\n}\r\n\r\nfunction subjectKey(subject: unknown): unknown {\r\n const record = asRecord(subject);\r\n return record?.session ?? subject;\r\n}\r\n\r\nfunction asRecord(value: unknown): Record<string, unknown> | undefined {\r\n return typeof value === \"object\" && value !== null && !Array.isArray(value)\r\n ? (value as Record<string, unknown>)\r\n : undefined;\r\n}\r\n", "export const name = \"dsh-starlite\";\r\n\r\nimport { DshStartupError } from \"./startup-error.js\";\r\n\r\ninterface PluginContext {\r\n get?(name: string): unknown;\r\n on(event: string, listener: (...arguments_: unknown[]) => void): unknown;\r\n logger?: {\r\n warn?(message: string): void;\r\n };\r\n}\r\n\r\n/**\r\n * Compatibility boundary for DSH's developer-preview API. Import and feature\r\n * failures are contained here so the pet can never prevent DSH from booting.\r\n */\r\nexport async function apply(context: PluginContext): Promise<() => Promise<void>> {\r\n try {\r\n const { startDshAdapter } = await import(\"./adapter.js\");\r\n const dispose = await startDshAdapter(context);\r\n return async () => {\r\n await dispose();\r\n };\r\n } catch (error) {\r\n context.logger?.warn?.(\r\n error instanceof DshStartupError ? error.message\r\n : \"StarLite for DSH could not start its local status bridge. Restart DSH and check the StarLite installation; DSH will continue without the companion.\",\r\n );\r\n return async () => undefined;\r\n }\r\n}\r\n"],
|
|
5
5
|
"mappings": "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",
|
|
6
6
|
"names": ["record", "value", "bubble", "text", "text", "name", "late", "randomUUID", "name", "record", "randomUUID", "lstat", "open", "constants", "name", "resolve", "reject", "resolve", "reject", "date", "record", "entry", "date", "count", "init_billing", "randomUUID", "record", "resolve", "reject", "name", "randomUUID", "resolve", "reject", "spawn", "record", "reject", "name", "resolve", "randomUUID", "asRecord", "record", "init_billing", "startDshAdapter"]
|
|
7
7
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-starlite",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.2",
|
|
4
4
|
"description": "StarLite for DSH: a lightweight Windows companion managed by one DSH plugin",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "BSD-2-Clause",
|
|
@@ -31,8 +31,8 @@
|
|
|
31
31
|
"@types/node": "^24.10.0",
|
|
32
32
|
"esbuild": "0.28.2",
|
|
33
33
|
"typescript": "7.0.2",
|
|
34
|
-
"vitest": "4.1.
|
|
35
|
-
"@starcried/starlite-dsh-protocol": "0.1.0-alpha.
|
|
34
|
+
"vitest": "4.1.12",
|
|
35
|
+
"@starcried/starlite-dsh-protocol": "0.1.0-alpha.2"
|
|
36
36
|
},
|
|
37
37
|
"dsh": {
|
|
38
38
|
"bundle": {
|