dsh-plugin-prompt-tool 0.3.0 → 0.3.1

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/README.md CHANGED
@@ -1,53 +1,48 @@
1
1
  # 提示词工具(dsh-plugin-prompt-tool)
2
2
 
3
- DSH 插件:为 DeepSeek Harness 提供三层提示词规范注入,并通过 Web 界面在线编辑。核心是“最优组合”首轮锚定:保持官方 Minimal(极简)训练分布的首轮条件,在模型轨迹稳定后再恢复完整能力。
3
+ 把「Anchored Standard + dsh-router-standard 最优组合」做成 DSH 全家桶里的一键安装插件:插件启动时生成并维护 `~/.dsh/.agent-presets/prompt-tool/` 预设,首轮模型请求只看到官方 Minimal 精确双工具——持久 `bash` 与 `str_replace_editor`——和正确的 persona,没有运行时上下文与指令注入;锚定建立后进入 resident 目录,恢复常规注入,并在确认轨迹后注入 `preset.md`。同时提供 Web 界面在线编辑 `preset.md` / `AGENTS.md`、切换全部开关,以及可开关的 skills 技能层。全部通过官方插件接口实现,不修改 DSH 源码。
4
4
 
5
- ## 来源与参考
5
+ ## 原理
6
6
 
7
- | 项目 | 关系 | 复用内容 |
8
- |---|---|---|
9
- | [xiaobright/dsh-anchored-standard](https://github.com/xiaobright/dsh-anchored-standard) | 上游,内联快照 | 两阶段首轮锚定、tool-bootstrap、context-gate、resident 目录与按需解锁 |
10
- | [yjh051108/dsh-router-standard](https://github.com/yjh051108/dsh-router-standard) | 参考 | 首轮结构、追加任务引导、Flash 弱路由人设与三锚、子代理方案 |
11
- | [yjh051108/dsh-super-injector](https://github.com/yjh051108/dsh-super-injector) | 参考 | 缓存铁律、工具面成本原则 |
7
+ DeepSeek V4 会强烈依赖 API 中可见的**首轮工具目录与 persona** 选择执行轨迹。完整 Standard 目录与自动注入在场会破坏 Minimal 轨迹,而全程 Minimal 又会失去重型工具。本项目把「首次轨迹选择」与「后续完整工具能力」拆开:
12
8
 
13
- 上游文件固化在 `upstream/dsh-anchored-standard/`(含 `LICENSE`、`NOTICE`、`REVISION`),
14
- `pnpm sync:anchored` 更新。
9
+ 1. **干净首轮**:`context-gate` 清空首轮 runtime-context 并剥离自动注入;`tool-bootstrap` 只暴露 Minimal 双工具;`router-first-turn` 按主会话模型替换 persona——Pro 使用训练原句,Flash 自动采用 dsh-router-standard 的 Flash 弱路由人设(build/fix 分类 + 回顾锚 + 反跑题锚 + 先深想再产出)。
10
+ 2. **晋升**:首次持久 `tool/call` `assistant/message`(先到者为准)落地后,目录进入 resident 集——bootstrap 双工具 + 发现工具 + 已解锁工具;上下文与注入恢复,重型工具经 `dev_tool_search` 按需解锁。
11
+ 3. **任务引导**:可选 `near-anchor` 在首条真实用户消息后追加一次近距离首句引导(we/let 按任务自动选择);Flash 主会话晋升后 `router-guide` 按任务复杂度追加每轮深度引导。
12
+ 4. **提示词注入**:we 锚定确认后把 `preset.md` 作为用户消息注入一次;we 未确认最多等一轮兜底,绝不卡死。
13
+ 5. **子代理**:目录直接全量放行(仍受调用方工具白名单过滤);可选 `subagentFlash` 固定 Flash 路由 + 任务分类人设 + 三锚。
15
14
 
16
- ## 项目说明
15
+ 阶段全部从持久 session events 推导,resume / reload 不丢失状态。
17
16
 
18
- - **常驻层**:把 `AGENTS.md` 作为受管块写入 `~/.dsh/AGENTS.md`;开启 `injectAgentsPrompt` 时,以 `instruction-hint` 的位置每会话注入一次。
19
- - **按需层**:扫描 `skills/*/SKILL.md` 注册可开关技能,内容只来自技能自身。
20
- - **锚定层**:生成独立的 `~/.dsh/.agent-presets/prompt-tool/` 预设,承载首轮工具引导、上下文闸门、任务引导注入与提示词注入。
21
- - **Web 界面**:自建回环设置桥,在线编辑 `preset.md` 与 `AGENTS.md`,切换全部开关;不占用模型设置区。
22
- - **TUI(终端界面)命令**:`/prompt-tool status` 查看状态,`/prompt-tool on|off|toggle <开关>` 切换。
23
- - **上游管理**:上游 dsh-anchored-standard 以内联快照形式放在 `upstream/`,`pnpm sync:anchored` 刷新;不再使用 git 子模块。
17
+ ## 稳定化控制
24
18
 
25
- ## 构建 / 装载
19
+ 全部在生成 preset `agent.cordis.yml` 行中配置:
26
20
 
27
- ```sh
28
- pnpm install
29
- pnpm build # 生成 lib/
30
- pnpm typecheck # Host Client 两个 tsc program
31
- pnpm lint # oxlint
32
- pnpm test # pnpm build + node --test
33
- pnpm prepare # npm publish / git install 前自动触发
34
- pnpm sync:anchored # 从上游 main 刷新内联快照(可加 ref 参数)
35
- ```
21
+ - `bootstrapMaxTokens`:首轮输出封顶。`0` = 本项目默认不设封顶(Web 界面显示 256000,即不设上限);正整数 = 请求 #1 的 `maxTokens`,晋升后自动剥离,不会焊进后续请求。不锁定 1024,任意正整数均可。
22
+ - `context-gate`:未晋升期间关闭两条统一注入路径,pre-step 只保留 claimed 批次 + `allowKinds`(放行用户技能手势与 `near-anchor` / `router-guide`);晋升后差分恢复一条 runtime-context 消息。
23
+ - `tool-bootstrap`:`promoteOn: either` 避免纯文字首答永久困在双工具;compaction 后回到 bootstrap + `compactionTools` 受控阶段。
24
+ - `router-first-turn`:只替换 persona 段,保留 plan-mode 段与第三方 section;首轮隐藏 `mnemon:*` 自动注入段,晋升后恢复。
25
+ - `near-anchor`:行为引导放在真实用户消息之后(近距离零衰减),只要求首句一次;复杂规划任务放行 Let 深度路径,日常任务锚 We。
26
+ - `router-guide`:简单任务快速收敛,复杂任务深度引导;子代理不注入,首轮不注入。
27
+ - `prompt-injector`:注入状态以持久事件为准,跨进程重启 / 插件热重载不重复注入。
28
+ - `injectAgentsPrompt`:开启时用 `AGENTS.md` 内容替换上游 instruction-hint 提示文本;关闭时保留上游原版 hint。
29
+ - `skillSwitches`:扫描 `skills/*/SKILL.md` 注册可开关技能,未列出的技能默认开启。
30
+ - `subagentFlash`:检测不到 DeepSeek 模型路由时 Web/TUI 禁用并强制降级为关闭。
36
31
 
37
- 安装到 `prompt-tool` profile:
32
+ ## 安装
38
33
 
39
34
  ```sh
40
35
  # 1. 先安装宿主服务(提供 webServer 等本插件依赖的服务)
41
36
  dsh plugin --profile prompt-tool add @deepseek-ai/dsh-web-app
42
37
  # 或使用 dsh-tui:dsh plugin --profile prompt-tool add @deepseek-harness-tui/dsh-tui
43
38
 
44
- # 2. npm 安装本插件(官方插件指令,自动加入 profile bundles)
39
+ # 2. 安装本插件(官方插件指令,自动加入 profile bundles)
45
40
  dsh plugin --profile prompt-tool add dsh-plugin-prompt-tool
46
41
 
47
- # 3. 本地开发安装(任选其一,会覆盖上一步的 registry 依赖)
42
+ # 3. 本地开发安装(任选其一,会覆盖 registry 依赖)
48
43
  dsh plugin --profile prompt-tool add link:<本仓库绝对路径>
49
44
 
50
- # 4. 启动方式(任选)
45
+ # 4. 启动
51
46
  dsh --profile prompt-tool
52
47
  dsh web
53
48
  dsh-tui
@@ -56,45 +51,51 @@ dsh-tui
56
51
  dsh plugin --profile prompt-tool remove dsh-plugin-prompt-tool
57
52
  ```
58
53
 
59
- 本插件声明了 `dsh.bundle`,所以官方 `add` 会自动把它追加进
60
- `dsh.profile.bundles`;`dsh-base` 与 `dsh-web-app` 不会由本插件写入。
61
- 如果启动时报 `waiting for service: webServer`,说明该 profile 缺少宿主
62
- 服务,先执行第 1 步。等价的 profile 清单如下:
63
-
64
- ```jsonc
65
- // <DSH_HOME>/profiles/prompt-tool/package.json
66
- "dsh": {
67
- "profile": {
68
- "bundles": [
69
- "@deepseek-ai/dsh-base",
70
- "@deepseek-ai/dsh-web-app",
71
- "dsh-plugin-prompt-tool"
72
- ]
73
- }
74
- }
75
- ```
54
+ 装完**完整重启 `dsh web`**,新建空 session,预设选择 **prompt-tool**。插件会在启动时生成并刷新 `~/.dsh/.agent-presets/prompt-tool/`(升级插件后重启即自动更新)。
55
+
56
+ ## 验证
76
57
 
77
- 临时调试可用官方 `--patch` 覆盖层(不落盘、不改 profile):
58
+ 导出 session JSONL,检查 `request/header`:
59
+
60
+ - 第一份 header 的 `tools` 应恰好是 `["bash", "str_replace_editor"]`;
61
+ - 第一轮只包含用户消息与首句锚点:没有 workspace 指令 baseline、运行时快照、skill 目录消息;
62
+ - 首次工具调用或首次助手回复后,下一份变更 header 应包含 resident 目录:bootstrap 双工具 + `dev_tool_search` / `skill_search` / `skill_load` + 已解锁工具;
63
+ - 此后的请求保持 resident 集,只经 `dev_tool_search` 显式解锁增长;
64
+ - `we` 锚定确认后,事件流出现一次 `source.plugin === 'prompt-injector'` 的消息;未确认时最多等一轮兜底。
65
+
66
+ 本项目测试:
78
67
 
79
68
  ```sh
80
- dsh --profile prompt-tool --patch <cordis.yml>
69
+ pnpm test # pnpm build + node --test
70
+ pnpm typecheck # Host 与 Client 两个 tsc program
71
+ pnpm lint # oxlint
81
72
  ```
82
73
 
83
- 完全重启 dsh 后,新建会话并选择 prompt-tool 预设。
74
+ ## 行为与限制
84
75
 
85
- ## 原理
76
+ - Windows:DSH 的 PTY 后端仅支持 linux/darwin,持久 shell 组禁用;phase-1 的 `bash` 切换为 `custom-bash`——同名且 schema 与 Minimal 兼容,经普通跨平台子进程通道调起 Git Bash(运行时探测安装路径,不硬编码)。
77
+ - Linux/macOS:持久 shell 的 `shellPath` 自适应——`/bin/bash` 存在时保持默认,不存在(如 NixOS)回退 PATH 里的 `bash`。
78
+ - 首轮能力类提问可能基于被裁剪的双工具视图作答,晋升后由后续工具纠正;需要时可开启任务引导或直接首轮问任务类问题。
79
+ - 工具目录只变化一次(晋升点),因此第一、二次请求之间发生一次前缀缓存变化;之后每次 `dev_tool_search` 解锁再变化。
80
+ - preset 与 shell 访问具有相同信任等级,安装前可自行审阅 `upstream/` 与生成目录。
81
+ - 插件只监听本机回环设置桥,不发起网络请求,也不增加遥测。
82
+ - 不要在已经产生内容的会话中途切换 preset。
83
+ - 需要 DSH 0.1.0-rc.5+(preset 机制与 `system-prompt/assemble` 钩子)。
86
84
 
87
- 模型的首轮请求结构决定整条会话轨迹。上游实测:官方 Minimal 首轮(训练原句 + 持久 `bash` + `str_replace_editor`,无输出封顶)稳定锚定 we 轨迹;完整 Standard 目录与自动注入在场会破坏锚定。
85
+ ## 构建与上游管理
88
86
 
89
- 本插件把“首轮轨迹选择”和“后续完整能力”拆开:
87
+ ```sh
88
+ pnpm install
89
+ pnpm build # 生成 lib/
90
+ pnpm prepare # npm publish / git install 前自动触发
91
+ pnpm sync:anchored # 从上游 main 刷新内联快照(可加 ref 参数)
92
+ ```
90
93
 
91
- 1. **干净首轮**:`context-gate` 清空首轮自动上下文并剥离自动注入;`tool-bootstrap` 只暴露 Minimal 两个工具;`router-first-turn` 按主会话模型替换 persona——Pro 用训练原句,Flash 自动采用 dsh-router-standardFlash 弱路由人设(build/fix 分类 + 三锚 + 先深想再产出)。
92
- 2. **晋升**:首次工具调用或助手回复落地后,目录进入 resident(常驻)集,上下文与注入恢复;工具按需解锁。
93
- 3. **追加任务引导**:可选 `near-anchor`,在首条真实用户消息之后追加一次引导——默认按任务自动选择 we/let 首句,也可固定使用自定义文本。Flash 主会话晋升后,`router-guide` 还会在每个真实用户消息后追加固定深度引导;首句与每轮分别有独立的自定义开关和文本框。
94
- 4. **提示词注入**:we 锚定确认后,把 `preset.md` 作为用户消息注入一次;未确认最多等一轮兜底。
95
- 5. **子代理**:目录直接全量放行(实际仍受调用方工具白名单过滤);开启 `subagentFlash` 时,采用 dsh-router-standard 的 Flash 方案——固定 Flash 路由、任务分类人设与三锚。
94
+ 上游 `dsh-anchored-standard` 以内联快照形式固化在 `upstream/dsh-anchored-standard/`(含 `LICENSE`、`NOTICE`、`REVISION`),对应提交见 `REVISION`。上游已转入维护期(2026-08-17);其 Project2 98/99/99 成绩来自当前实现之前的旧配置(issue #60),轨迹锚定有独立复现(#65),能力增益在小样本下未决(#51)。
96
95
 
97
- 主会话首轮始终只有两个工具;模型性能不受记忆、技能等自动注入影响,完整能力在轨迹稳定后恢复。
96
+ ## 许可
97
+
98
+ 插件本体 MIT(Czerror)。首轮锚定机制源自 [xiaobright/dsh-anchored-standard](https://github.com/xiaobright/dsh-anchored-standard)(MIT),任务引导与 Flash 方案参考 [yjh051108/dsh-router-standard](https://github.com/yjh051108/dsh-router-standard)(MIT),缓存与工具面成本原则参考 [yjh051108/dsh-super-injector](https://github.com/yjh051108/dsh-super-injector)(MIT)。上游 `agent.cordis.yml` 基于 DeepSeek Harness Standard 预设修改,原始 DeepSeek 版权与 MIT 声明保留在 `upstream/dsh-anchored-standard/NOTICE` 与 `LICENSE`。
98
99
 
99
100
  ## 配置参考
100
101
 
@@ -108,19 +109,19 @@ dsh --profile prompt-tool --patch <cordis.yml>
108
109
  text: '' # 覆盖 preset.md 文本;默认读项目文件
109
110
  agentsText: '' # 覆盖 AGENTS.md 文本;默认读项目文件
110
111
  injectAgentsPrompt: false # 用 AGENTS.md 替换 instruction-hint 提示文本
111
- writeAgents: true # 写 ~/.dsh/AGENTS.md 受管块(默认 true)
112
- writePreset: true # 启用锚定预设(总开关,默认 true)
113
- injectPrompt: true # we 确认后注入 preset.md(默认 true)
112
+ writeAgents: true # 写 ~/.dsh/AGENTS.md 受管块
113
+ writePreset: true # 启用锚定预设(总开关)
114
+ injectPrompt: true # we 确认后注入 preset.md
114
115
  skillSwitches: {} # 按 skills/* 目录名开关,未列出默认开启
115
- anchorFirstTurn: false # 追加任务引导(默认关闭)
116
+ anchorFirstTurn: false # 追加任务引导
116
117
  anchorText: '' # 自定义引导文本(首句)
117
118
  anchorCustom: false # 使用自定义引导(首句)
118
- guideText: '' # 自定义引导文本(每轮);默认写入自动引导(简单/复杂两段)
119
- guideCustom: false # 使用自定义引导(每轮;仅 Flash 主会话晋升后生效)
120
- subagentFlash: false # 子代理固定 Flash 模型(默认关闭)
119
+ guideText: '' # 自定义引导文本(每轮)
120
+ guideCustom: false # 使用自定义引导(每轮)
121
+ subagentFlash: false # 子代理固定 Flash 模型
121
122
  subagentFlashProvider: 'deepseek-official'
122
123
  subagentFlashModel: 'deepseek-v4-flash'
123
- customBashPath: 'bash.exe' # 自定义 bash 路径(默认 PATH 查找)
124
+ bootstrapMaxTokens: 0 # 首轮输出封顶:0=关闭;正整数=请求 #1 maxTokens
124
125
  skillsDir: '' # 可选技能目录(默认包内 skills/)
125
126
  skillRankBase: 250 # 技能候选排序基数
126
127
  residentAgentsPath: '' # 默认 ~/.dsh/AGENTS.md
@@ -129,22 +130,11 @@ dsh --profile prompt-tool --patch <cordis.yml>
129
130
  fallbackText: '' # preset.md 缺失或不可读时的回退文本
130
131
  ```
131
132
 
132
- 字段说明:
133
-
134
- - **启用锚定预设**:总开关。开启时生成并刷新 `~/.dsh/.agent-presets/prompt-tool/`;关闭时移除已生成目录,锚定相关开关失效。
135
- - **追加任务引导**:开启时挂载 `near-anchor`,在首条真实用户消息后追加一次任务引导。
136
- - **使用自定义引导(首句)**:开启时固定使用 `anchorText`;关闭时按任务自动选择 we/let 首句。开启且文本为空时不注入。
137
- - **使用自定义引导(每轮)**:开启时每轮固定使用 `guideText`,Pro 与 Flash 都注入(便于对照测试);关闭时按任务自动选择快速收敛/深度引导,仅 Flash 主会话晋升后由 router-guide 注入。留空则不注入。
138
- - **子代理固定 Flash 模型**:开启时采用 dsh-router-standard 的 Flash 子代理方案——通用子代理行加固定 Flash 路由、任务分类人设与三锚;宿主直派子代理(含 dsh-mnemon)自动补 Flash 路由,调用方显式模型优先。检测不到 DeepSeek 模型路由时 Web/TUI 禁用,且运行时强制降级为关闭。
139
- - **自定义 bash 路径**:上游若带作者机器固定路径,生成 preset 时归一化为该值,默认 PATH 查找 `bash.exe`。
140
- - 其他字段语义见上方配置注释;设置文件优先级高于挂载配置,首次安装时用项目文件内容初始化设置。
141
-
142
- ## 版权
133
+ TUI 命令:
143
134
 
144
- 本项目采用 MIT 许可证。
145
-
146
- - 首轮锚定机制源自 [xiaobright/dsh-anchored-standard](https://github.com/xiaobright/dsh-anchored-standard)(MIT),其 `preset/` 快照内联于本仓库 `upstream/`,对应上游提交记录见 `REVISION`。
147
- - 任务引导与 Flash 子代理方案参考 [yjh051108/dsh-router-standard](https://github.com/yjh051108/dsh-router-standard)(MIT)。
148
- - 缓存与工具面成本原则参考 [yjh051108/dsh-super-injector](https://github.com/yjh051108/dsh-super-injector)(MIT)。
149
- - 上游 `agent.cordis.yml` 基于 DeepSeek Harness Standard 预设修改;原始 DeepSeek 版权与 MIT 声明保留在 `upstream/dsh-anchored-standard/NOTICE` 与 `LICENSE`。
150
- - 本项目 `LICENSE` 见仓库根目录;随上游快照发布的 `upstream/dsh-anchored-standard/LICENSE` 与 `NOTICE` 继续保留。
135
+ ```text
136
+ /prompt-tool status
137
+ /prompt-tool on|off|toggle <开关>
138
+ /prompt-tool skill <技能目录名> on|off|toggle
139
+ /prompt-tool bootstrapMaxTokens <正整数|0>
140
+ ```
package/lib/client.js CHANGED
@@ -23,7 +23,7 @@ window.__ModuleLoader__.load({
23
23
  }
24
24
  //#endregion
25
25
  //#region \0dsh-css:E:\Documents\GitHub\dsh-plugin-prompt-tool\src\client\PromptEditor.module.css.mjs
26
- const css = ".Ru2OGG_card{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:12px;list-style:none;transition:border-color .16s,background .16s}.Ru2OGG_card:hover{border-color:var(--dsw-alias-label-dimmed)}.Ru2OGG_cardOpen{background:var(--dsw-alias-bg-layer-2);border-color:var(--dsw-alias-label-dimmed)}.Ru2OGG_header{appearance:none;width:100%;font:inherit;color:inherit;text-align:left;cursor:pointer;background:0 0;border:0;border-radius:12px;align-items:center;gap:12px;padding:14px 16px;display:flex}.Ru2OGG_header:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:-2px}.Ru2OGG_headText{flex-direction:column;flex:1;gap:4px;min-width:0;display:flex}.Ru2OGG_name{color:var(--dsw-alias-label-primary);font-size:15px;font-weight:600;line-height:1.4}.Ru2OGG_description{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:1.5}.Ru2OGG_chevron{color:var(--dsw-alias-label-tertiary);flex:none;transition:transform .16s}.Ru2OGG_chevronOpen{transform:rotate(180deg)}.Ru2OGG_pending{white-space:nowrap;background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-secondary);border-radius:999px;flex:none;padding:1px 8px;font-size:11px;font-weight:500;line-height:17px}.Ru2OGG_body{border-top:1px solid var(--dsw-alias-border-l2);flex-direction:column;gap:14px;margin:0 16px;padding:14px 0 16px;display:flex}.Ru2OGG_loading{color:var(--dsw-alias-label-tertiary);font-size:12px;display:inline-block}.Ru2OGG_group{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:12px;flex-direction:column;gap:8px;padding:12px 14px;display:flex}.Ru2OGG_groupTitle{color:var(--dsw-alias-label-secondary);font-size:13px;font-weight:600}.Ru2OGG_subSection{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:12px;flex-direction:column;display:flex;overflow:hidden}.Ru2OGG_subHeader{appearance:none;width:100%;font:inherit;color:inherit;text-align:left;cursor:pointer;background:0 0;border:0;align-items:center;gap:12px;padding:12px 14px;display:flex}.Ru2OGG_subHeader:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:-2px}.Ru2OGG_subTitle{color:var(--dsw-alias-label-primary);flex:1;font-size:13px;font-weight:600;line-height:1.5}.Ru2OGG_subCount{color:var(--dsw-alias-label-tertiary);flex:none;font-size:12px}.Ru2OGG_subBody{flex-direction:column;gap:8px;padding:2px 14px 12px;display:flex}.Ru2OGG_subEmpty{color:var(--dsw-alias-label-tertiary);padding:4px 0 8px;font-size:12px}.Ru2OGG_row{cursor:pointer;align-items:flex-start;gap:10px;padding:6px 0;display:flex}.Ru2OGG_rowStack{flex-direction:column;align-items:stretch;gap:8px;padding:6px 0;display:flex}.Ru2OGG_row input{flex:none;margin-top:3px}.Ru2OGG_rowText{flex-direction:column;gap:2px;display:flex}.Ru2OGG_rowName{color:var(--dsw-alias-label-primary);font-size:13px;line-height:1.5}.Ru2OGG_rowDesc{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:1.5}.Ru2OGG_rowDisabled{opacity:.55}.Ru2OGG_anchorInput{box-sizing:border-box;width:100%;min-height:64px;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);resize:vertical;border-radius:8px;padding:8px 10px;font-family:ui-monospace,monospace;font-size:12px;line-height:1.6}.Ru2OGG_textarea{box-sizing:border-box;width:100%;min-height:420px;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);resize:vertical;border-radius:8px;padding:10px;font-family:ui-monospace,monospace;font-size:13px;line-height:1.6}.Ru2OGG_actions{flex-wrap:wrap;align-items:center;gap:8px;display:flex}.Ru2OGG_primary,.Ru2OGG_secondary{appearance:none;font:inherit;cursor:pointer;border:1px solid #0000;border-radius:8px;padding:5px 14px;font-size:13px;line-height:1.5}.Ru2OGG_primary{background:var(--dsw-alias-label-primary);color:var(--dsw-alias-bg-layer-3)}.Ru2OGG_secondary{border-color:var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);background:0 0}.Ru2OGG_primary:disabled,.Ru2OGG_secondary:disabled{opacity:.55;cursor:default}.Ru2OGG_primary:focus-visible,.Ru2OGG_secondary:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px}.Ru2OGG_notice{color:var(--dsw-alias-state-success-primary);font-size:12px}.Ru2OGG_noticeError{color:var(--dsw-alias-state-error-primary,#d5484d)}";
26
+ const css = ".Ru2OGG_card{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:12px;list-style:none;transition:border-color .16s,background .16s}.Ru2OGG_card:hover{border-color:var(--dsw-alias-label-dimmed)}.Ru2OGG_cardOpen{background:var(--dsw-alias-bg-layer-2);border-color:var(--dsw-alias-label-dimmed)}.Ru2OGG_header{appearance:none;width:100%;font:inherit;color:inherit;text-align:left;cursor:pointer;background:0 0;border:0;border-radius:12px;align-items:center;gap:12px;padding:14px 16px;display:flex}.Ru2OGG_header:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:-2px}.Ru2OGG_headText{flex-direction:column;flex:1;gap:4px;min-width:0;display:flex}.Ru2OGG_name{color:var(--dsw-alias-label-primary);font-size:15px;font-weight:600;line-height:1.4}.Ru2OGG_description{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:1.5}.Ru2OGG_chevron{color:var(--dsw-alias-label-tertiary);flex:none;transition:transform .16s}.Ru2OGG_chevronOpen{transform:rotate(180deg)}.Ru2OGG_pending{white-space:nowrap;background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-secondary);border-radius:999px;flex:none;padding:1px 8px;font-size:11px;font-weight:500;line-height:17px}.Ru2OGG_body{border-top:1px solid var(--dsw-alias-border-l2);flex-direction:column;gap:14px;margin:0 16px;padding:14px 0 16px;display:flex}.Ru2OGG_loading{color:var(--dsw-alias-label-tertiary);font-size:12px;display:inline-block}.Ru2OGG_group{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:12px;flex-direction:column;gap:8px;padding:12px 14px;display:flex}.Ru2OGG_groupTitle{color:var(--dsw-alias-label-secondary);font-size:13px;font-weight:600}.Ru2OGG_subSection{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:12px;flex-direction:column;display:flex;overflow:hidden}.Ru2OGG_subHeader{appearance:none;width:100%;font:inherit;color:inherit;text-align:left;cursor:pointer;background:0 0;border:0;align-items:center;gap:12px;padding:12px 14px;display:flex}.Ru2OGG_subHeader:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:-2px}.Ru2OGG_subTitle{color:var(--dsw-alias-label-primary);flex:1;font-size:13px;font-weight:600;line-height:1.5}.Ru2OGG_subCount{color:var(--dsw-alias-label-tertiary);flex:none;font-size:12px}.Ru2OGG_subBody{flex-direction:column;gap:8px;padding:2px 14px 12px;display:flex}.Ru2OGG_subEmpty{color:var(--dsw-alias-label-tertiary);padding:4px 0 8px;font-size:12px}.Ru2OGG_row{cursor:pointer;align-items:flex-start;gap:10px;padding:6px 0;display:flex}.Ru2OGG_rowStack{flex-direction:column;align-items:stretch;gap:8px;padding:6px 0;display:flex}.Ru2OGG_row input{flex:none;margin-top:3px}.Ru2OGG_rowText{flex-direction:column;gap:2px;display:flex}.Ru2OGG_rowName{color:var(--dsw-alias-label-primary);font-size:13px;line-height:1.5}.Ru2OGG_rowDesc{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:1.5}.Ru2OGG_rowDisabled{opacity:.55}.Ru2OGG_anchorInput{box-sizing:border-box;width:100%;min-height:64px;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);resize:vertical;border-radius:8px;padding:8px 10px;font-family:ui-monospace,monospace;font-size:12px;line-height:1.6}.Ru2OGG_textarea{box-sizing:border-box;width:100%;min-height:420px;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);resize:vertical;border-radius:8px;padding:10px;font-family:ui-monospace,monospace;font-size:13px;line-height:1.6}.Ru2OGG_actions{flex-wrap:wrap;align-items:center;gap:8px;display:flex}.Ru2OGG_primary,.Ru2OGG_secondary{appearance:none;font:inherit;cursor:pointer;border:1px solid #0000;border-radius:8px;padding:5px 14px;font-size:13px;line-height:1.5}.Ru2OGG_primary{background:var(--dsw-alias-label-primary);color:var(--dsw-alias-bg-layer-3)}.Ru2OGG_secondary{border-color:var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);background:0 0}.Ru2OGG_primary:disabled,.Ru2OGG_secondary:disabled{opacity:.55;cursor:default}.Ru2OGG_primary:focus-visible,.Ru2OGG_secondary:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px}.Ru2OGG_notice{color:var(--dsw-alias-state-success-primary);font-size:12px}.Ru2OGG_noticeError{color:var(--dsw-alias-state-error-primary,#d5484d)}.Ru2OGG_bootstrapTokensInput{box-sizing:border-box;width:96px;min-height:36px;max-height:48px;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);text-align:right;border-radius:8px;flex:none;align-self:stretch;margin:0 0 0 auto;padding:4px 8px;font-size:12px;line-height:1.4}.Ru2OGG_bootstrapTokensInput:disabled{opacity:.55;cursor:default}";
27
27
  const tagId = "dsh-plugin-prompt-tool/PromptEditor.module.css";
28
28
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
29
29
  const tag = document.createElement("style");
@@ -36,6 +36,7 @@ window.__ModuleLoader__.load({
36
36
  "actions": "Ru2OGG_actions",
37
37
  "anchorInput": "Ru2OGG_anchorInput",
38
38
  "body": "Ru2OGG_body",
39
+ "bootstrapTokensInput": "Ru2OGG_bootstrapTokensInput",
39
40
  "card": "Ru2OGG_card",
40
41
  "cardOpen": "Ru2OGG_cardOpen",
41
42
  "chevron": "Ru2OGG_chevron",
@@ -100,6 +101,7 @@ window.__ModuleLoader__.load({
100
101
  guideText: "",
101
102
  guideCustom: false,
102
103
  subagentFlash: false,
104
+ bootstrapMaxTokens: 0,
103
105
  injectPrompt: true,
104
106
  skillSwitches: {},
105
107
  skillCatalog: [],
@@ -114,11 +116,14 @@ window.__ModuleLoader__.load({
114
116
  guideText: "",
115
117
  guideCustom: false,
116
118
  subagentFlash: false,
119
+ bootstrapMaxTokens: 0,
117
120
  injectPrompt: true,
118
121
  skillSwitches: {},
119
122
  writeAgents: true,
120
123
  writePreset: true
121
124
  };
125
+ /** 本项目默认不设上限时的显示值(adapter 默认 maxTokens)。 */
126
+ const DEFAULT_BOOTSTRAP_DISPLAY = "256000";
122
127
  const snapshotSwitches = (fields) => ({
123
128
  injectAgentsPrompt: fields.injectAgentsPrompt,
124
129
  anchorFirstTurn: fields.anchorFirstTurn,
@@ -127,12 +132,13 @@ window.__ModuleLoader__.load({
127
132
  guideText: fields.guideText,
128
133
  guideCustom: fields.guideCustom,
129
134
  subagentFlash: fields.subagentFlash,
135
+ bootstrapMaxTokens: fields.bootstrapMaxTokens,
130
136
  injectPrompt: fields.injectPrompt,
131
137
  skillSwitches: { ...fields.skillSwitches },
132
138
  writeAgents: fields.writeAgents,
133
139
  writePreset: fields.writePreset
134
140
  });
135
- const switchesEqual = (a, b) => a.injectAgentsPrompt === b.injectAgentsPrompt && a.anchorFirstTurn === b.anchorFirstTurn && a.anchorText === b.anchorText && a.anchorCustom === b.anchorCustom && a.guideText === b.guideText && a.guideCustom === b.guideCustom && a.subagentFlash === b.subagentFlash && a.injectPrompt === b.injectPrompt && JSON.stringify(a.skillSwitches) === JSON.stringify(b.skillSwitches) && a.writeAgents === b.writeAgents && a.writePreset === b.writePreset;
141
+ const switchesEqual = (a, b) => a.injectAgentsPrompt === b.injectAgentsPrompt && a.anchorFirstTurn === b.anchorFirstTurn && a.anchorText === b.anchorText && a.anchorCustom === b.anchorCustom && a.guideText === b.guideText && a.guideCustom === b.guideCustom && a.subagentFlash === b.subagentFlash && a.bootstrapMaxTokens === b.bootstrapMaxTokens && a.injectPrompt === b.injectPrompt && JSON.stringify(a.skillSwitches) === JSON.stringify(b.skillSwitches) && a.writeAgents === b.writeAgents && a.writePreset === b.writePreset;
136
142
  const asRecord = (value) => value !== null && typeof value === "object" ? value : {};
137
143
  const readString = (source, key) => {
138
144
  const value = source[key];
@@ -142,6 +148,10 @@ window.__ModuleLoader__.load({
142
148
  const value = source[key];
143
149
  return typeof value === "boolean" ? value : fallback;
144
150
  };
151
+ const readNumber = (source, key, fallback) => {
152
+ const value = source[key];
153
+ return typeof value === "number" && Number.isSafeInteger(value) && value >= 0 ? value : fallback;
154
+ };
145
155
  const readSkillSwitches = (source, key) => {
146
156
  const value = source[key];
147
157
  if (value === null || typeof value !== "object") return {};
@@ -204,6 +214,7 @@ window.__ModuleLoader__.load({
204
214
  const [deepseekError, setDeepseekError] = (0, react.useState)("");
205
215
  const [loaded, setLoaded] = (0, react.useState)(false);
206
216
  const [fields, setFields] = (0, react.useState)(EMPTY);
217
+ const [bootstrapTokensDraft, setBootstrapTokensDraft] = (0, react.useState)(DEFAULT_BOOTSTRAP_DISPLAY);
207
218
  const [savedPromptText, setSavedPromptText] = (0, react.useState)("");
208
219
  const [savedAgentsText, setSavedAgentsText] = (0, react.useState)("");
209
220
  const [savedSwitches, setSavedSwitches] = (0, react.useState)(EMPTY_SWITCHES);
@@ -247,6 +258,7 @@ window.__ModuleLoader__.load({
247
258
  guideText: readString(value, "guideText") ?? readString(base, "guideText") ?? "",
248
259
  guideCustom: readBoolean(value, "guideCustom", readBoolean(base, "guideCustom", false)),
249
260
  subagentFlash: readBoolean(value, "subagentFlash", readBoolean(base, "subagentFlash", false)),
261
+ bootstrapMaxTokens: readNumber(value, "bootstrapMaxTokens", readNumber(base, "bootstrapMaxTokens", 0)),
250
262
  injectPrompt: readBoolean(value, "injectPrompt", readBoolean(base, "injectPrompt", true)),
251
263
  skillSwitches: value.skillSwitches !== void 0 || base.skillSwitches !== void 0 ? {
252
264
  ...readSkillSwitches(base, "skillSwitches"),
@@ -258,6 +270,7 @@ window.__ModuleLoader__.load({
258
270
  };
259
271
  fieldsRef.current = next;
260
272
  setFields(next);
273
+ setBootstrapTokensDraft(next.bootstrapMaxTokens > 0 ? String(next.bootstrapMaxTokens) : DEFAULT_BOOTSTRAP_DISPLAY);
261
274
  setSavedPromptText(next.promptText);
262
275
  setSavedAgentsText(next.agentsText);
263
276
  setSavedSwitches(snapshotSwitches(next));
@@ -416,6 +429,11 @@ window.__ModuleLoader__.load({
416
429
  path: ["subagentFlash"],
417
430
  value: fieldsRef.current.subagentFlash
418
431
  },
432
+ {
433
+ op: "set",
434
+ path: ["bootstrapMaxTokens"],
435
+ value: fieldsRef.current.bootstrapMaxTokens
436
+ },
419
437
  {
420
438
  op: "set",
421
439
  path: ["injectPrompt"],
@@ -445,6 +463,20 @@ window.__ModuleLoader__.load({
445
463
  patch({ [key]: !fieldsRef.current[key] });
446
464
  persistSwitches();
447
465
  };
466
+ const toggleBootstrapMaxTokens = () => {
467
+ const next = fieldsRef.current.bootstrapMaxTokens > 0 ? 0 : 256e3;
468
+ patch({ bootstrapMaxTokens: next });
469
+ setBootstrapTokensDraft(DEFAULT_BOOTSTRAP_DISPLAY);
470
+ persistSwitches();
471
+ };
472
+ const commitBootstrapTokensDraft = () => {
473
+ const parsed = Number(bootstrapTokensDraft);
474
+ if (!Number.isSafeInteger(parsed) || parsed <= 0) {
475
+ setBootstrapTokensDraft(DEFAULT_BOOTSTRAP_DISPLAY);
476
+ patch({ bootstrapMaxTokens: 256e3 });
477
+ } else patch({ bootstrapMaxTokens: parsed });
478
+ persistSwitches();
479
+ };
448
480
  const skillEnabled = (folder) => fields.skillSwitches[folder] !== false;
449
481
  const toggleSkill = (folder) => {
450
482
  const enabled = fieldsRef.current.skillSwitches[folder] !== false;
@@ -462,7 +494,7 @@ window.__ModuleLoader__.load({
462
494
  const promptSwitchDirty = fields.injectPrompt !== savedSwitches.injectPrompt;
463
495
  const agentsSwitchDirty = fields.injectAgentsPrompt !== savedSwitches.injectAgentsPrompt || fields.writeAgents !== savedSwitches.writeAgents;
464
496
  const skillsSwitchDirty = JSON.stringify(fields.skillSwitches) !== JSON.stringify(savedSwitches.skillSwitches);
465
- const presetSwitchDirty = fields.writePreset !== savedSwitches.writePreset || fields.anchorFirstTurn !== savedSwitches.anchorFirstTurn || fields.anchorText !== savedSwitches.anchorText;
497
+ const presetSwitchDirty = fields.writePreset !== savedSwitches.writePreset || fields.anchorFirstTurn !== savedSwitches.anchorFirstTurn || fields.anchorText !== savedSwitches.anchorText || fields.bootstrapMaxTokens !== savedSwitches.bootstrapMaxTokens;
466
498
  const discardPrompt = () => patch({ promptText: savedPromptText });
467
499
  const discardAgents = () => patch({ agentsText: savedAgentsText });
468
500
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
@@ -696,6 +728,39 @@ window.__ModuleLoader__.load({
696
728
  })]
697
729
  })]
698
730
  }),
731
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
732
+ className: PromptEditor_module_css_default.row,
733
+ children: [
734
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
735
+ id: "prompt-tool-bootstrap-max-tokens",
736
+ type: "checkbox",
737
+ checked: fields.bootstrapMaxTokens > 0,
738
+ disabled: !fields.writePreset,
739
+ onChange: toggleBootstrapMaxTokens
740
+ }),
741
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
742
+ htmlFor: "prompt-tool-bootstrap-max-tokens",
743
+ className: PromptEditor_module_css_default.rowText,
744
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
745
+ className: PromptEditor_module_css_default.rowName,
746
+ children: "首轮输出封顶"
747
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
748
+ className: PromptEditor_module_css_default.rowDesc,
749
+ children: "关闭时显示项目默认 256000(不设上限);开启后右侧数值生效,晋升后自动剥离。清空或输入 0 会恢复 256000,不关闭开关"
750
+ })]
751
+ }),
752
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
753
+ className: PromptEditor_module_css_default.bootstrapTokensInput,
754
+ type: "number",
755
+ min: 1,
756
+ step: 1,
757
+ value: bootstrapTokensDraft,
758
+ disabled: !fields.writePreset || fields.bootstrapMaxTokens === 0,
759
+ onChange: (e) => setBootstrapTokensDraft(e.target.value),
760
+ onBlur: commitBootstrapTokensDraft
761
+ })
762
+ ]
763
+ }),
699
764
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
700
765
  className: PromptEditor_module_css_default.row,
701
766
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
package/lib/client.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","names":["styles","IconChevronDownOutline14","useState","useRef","useCallback"],"sources":["../node_modules/.pnpm/clsx@2.1.1/node_modules/clsx/dist/clsx.mjs","../src/client/PromptEditor.tsx","../src/client/index.ts"],"sourcesContent":["function r(e){var t,f,n=\"\";if(\"string\"==typeof e||\"number\"==typeof e)n+=e;else if(\"object\"==typeof e)if(Array.isArray(e)){var o=e.length;for(t=0;t<o;t++)e[t]&&(f=r(e[t]))&&(n&&(n+=\" \"),n+=f)}else for(f in e)e[f]&&(n&&(n+=\" \"),n+=f);return n}export function clsx(){for(var e,t,f=0,n=\"\",o=arguments.length;f<o;f++)(e=arguments[f])&&(t=r(e))&&(n&&(n+=\" \"),n+=t);return n}export default clsx;","import { useCallback, useRef, useState } from 'react'\r\nimport type { ReactNode } from 'react'\r\nimport clsx from 'clsx'\r\nimport { IconChevronDownOutline14 } from '@deepseek-ai/dsh-client-ui-primitives'\r\nimport type { IApiClient } from '@deepseek-ai/dsh-client-connection/client'\r\nimport type { InjectFace, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots'\r\nimport type {} from '@deepseek-ai/dsh-client-ui-settings-plugins/client'\r\nimport styles from './PromptEditor.module.css'\r\n\r\nconst SETTINGS_BRIDGE_PREFIX = '/api/prompt-tool/settings'\r\n\r\ntype BridgePathOp = { op: 'set' | 'unset'; path: string[]; value?: unknown }\r\ninterface BridgeSettingsView { ns: string; value: unknown; base?: unknown; revision: number }\r\ntype BridgeResult<T> = { ok: true; value: T; deepseekAvailable?: boolean; deepseekProviders?: string[]; deepseekError?: string } | { ok: false; code?: string; message?: string }\r\n\r\nasync function bridgePost<T>(path: string, body: unknown): Promise<BridgeResult<T>> {\r\n try {\r\n const response = await fetch(SETTINGS_BRIDGE_PREFIX + path, {\r\n method: 'POST',\r\n headers: { 'content-type': 'application/json' },\r\n body: JSON.stringify(body),\r\n })\r\n const payload = await response.json() as unknown\r\n if (payload !== null && typeof payload === 'object') return payload as BridgeResult<T>\r\n return { ok: false, message: 'settings bridge unavailable' }\r\n } catch (error) {\r\n return { ok: false, message: errorMessage(error) }\r\n }\r\n}\r\n\r\nexport interface PromptEditorInjected {\r\n api: IApiClient\r\n}\r\n\r\nexport type PromptEditorProps = PropsRuntime<'settings.plugin.item'> & InjectFace<PromptEditorInjected>\r\n\r\ninterface SkillCatalogEntry {\r\n folder: string\r\n name: string\r\n description: string\r\n}\r\n\r\ninterface Fields {\r\n promptText: string\r\n promptPath: string\r\n agentsText: string\r\n agentsPath: string\r\n injectAgentsPrompt: boolean\r\n anchorFirstTurn: boolean\r\n anchorText: string\r\n anchorCustom: boolean\r\n guideText: string\r\n guideCustom: boolean\r\n subagentFlash: boolean\r\n injectPrompt: boolean\r\n skillSwitches: Record<string, boolean>\r\n skillCatalog: SkillCatalogEntry[]\r\n writeAgents: boolean\r\n writePreset: boolean\r\n}\r\n\r\nconst EMPTY: Fields = {\r\n promptText: '',\r\n promptPath: '',\r\n agentsText: '',\r\n agentsPath: '',\r\n injectAgentsPrompt: false,\r\n anchorFirstTurn: false,\r\n anchorText: '',\r\n anchorCustom: false,\r\n guideText: '',\r\n guideCustom: false,\r\n subagentFlash: false,\r\n injectPrompt: true,\r\n skillSwitches: {},\r\n skillCatalog: [],\r\n writeAgents: true,\r\n writePreset: true,\r\n}\r\n\r\ninterface SwitchSnapshot {\r\n injectAgentsPrompt: boolean\r\n anchorFirstTurn: boolean\r\n anchorText: string\r\n anchorCustom: boolean\r\n guideText: string\r\n guideCustom: boolean\r\n subagentFlash: boolean\r\n injectPrompt: boolean\r\n skillSwitches: Record<string, boolean>\r\n writeAgents: boolean\r\n writePreset: boolean\r\n}\r\n\r\nconst EMPTY_SWITCHES: SwitchSnapshot = {\r\n injectAgentsPrompt: false,\r\n anchorFirstTurn: false,\r\n anchorText: '',\r\n anchorCustom: false,\r\n guideText: '',\r\n guideCustom: false,\r\n subagentFlash: false,\r\n injectPrompt: true,\r\n skillSwitches: {},\r\n writeAgents: true,\r\n writePreset: true,\r\n}\r\n\r\nconst snapshotSwitches = (fields: Fields): SwitchSnapshot => ({\r\n injectAgentsPrompt: fields.injectAgentsPrompt,\r\n anchorFirstTurn: fields.anchorFirstTurn,\r\n anchorText: fields.anchorText,\r\n anchorCustom: fields.anchorCustom,\r\n guideText: fields.guideText,\r\n guideCustom: fields.guideCustom,\r\n subagentFlash: fields.subagentFlash,\r\n injectPrompt: fields.injectPrompt,\r\n skillSwitches: { ...fields.skillSwitches },\r\n writeAgents: fields.writeAgents,\r\n writePreset: fields.writePreset,\r\n})\r\n\r\nconst switchesEqual = (a: SwitchSnapshot, b: SwitchSnapshot): boolean =>\r\n a.injectAgentsPrompt === b.injectAgentsPrompt\r\n && a.anchorFirstTurn === b.anchorFirstTurn\r\n && a.anchorText === b.anchorText\r\n && a.anchorCustom === b.anchorCustom\r\n && a.guideText === b.guideText\r\n && a.guideCustom === b.guideCustom\r\n && a.subagentFlash === b.subagentFlash\r\n && a.injectPrompt === b.injectPrompt\r\n && JSON.stringify(a.skillSwitches) === JSON.stringify(b.skillSwitches)\r\n && a.writeAgents === b.writeAgents\r\n && a.writePreset === b.writePreset\r\n\r\nconst asRecord = (value: unknown): Record<string, unknown> =>\r\n value !== null && typeof value === 'object' ? value as Record<string, unknown> : {}\r\n\r\nconst readString = (source: Record<string, unknown>, key: string): string | undefined => {\r\n const value = source[key]\r\n return typeof value === 'string' ? value : undefined\r\n}\r\n\r\nconst readBoolean = (source: Record<string, unknown>, key: string, fallback: boolean): boolean => {\r\n const value = source[key]\r\n return typeof value === 'boolean' ? value : fallback\r\n}\r\n\r\nconst readSkillSwitches = (source: Record<string, unknown>, key: string): Record<string, boolean> => {\r\n const value = source[key]\r\n if (value === null || typeof value !== 'object') return {}\r\n const entries = Object.entries(value as Record<string, unknown>)\r\n const result: Record<string, boolean> = {}\r\n for (const [name, enabled] of entries) {\r\n if (typeof enabled === 'boolean') result[name] = enabled\r\n }\r\n return result\r\n}\r\n\r\nconst readSkillCatalog = (source: Record<string, unknown>, key: string): SkillCatalogEntry[] => {\r\n const value = source[key]\r\n if (!Array.isArray(value)) return []\r\n return value.flatMap((entry) => {\r\n if (entry === null || typeof entry !== 'object') return []\r\n const record = entry as Record<string, unknown>\r\n const folder = readString(record, 'folder')\r\n const name = readString(record, 'name')\r\n if (folder === undefined || name === undefined) return []\r\n return [{ folder, name, description: readString(record, 'description') ?? '' }]\r\n })\r\n}\r\n\r\nconst errorMessage = (error: unknown): string => error instanceof Error ? error.message : String(error)\r\n\r\nfunction CollapsibleSection(props: { title: string; hint?: string; open: boolean; onToggle: () => void; children: ReactNode }): ReactNode {\r\n return (\r\n <div className={styles.subSection}>\r\n <button type=\"button\" className={styles.subHeader} aria-expanded={props.open} onClick={props.onToggle}>\r\n <span className={styles.subTitle}>{props.title}</span>\r\n {props.hint && <span className={styles.subCount}>{props.hint}</span>}\r\n <IconChevronDownOutline14 className={clsx(styles.chevron, props.open && styles.chevronOpen)} />\r\n </button>\r\n {props.open && <div className={styles.subBody}>{props.children}</div>}\r\n </div>\r\n )\r\n}\r\n\r\nexport function PromptEditor(props: PromptEditorProps): ReactNode {\r\n const { api } = props\r\n const [open, setOpen] = useState(false)\r\n const [promptOpen, setPromptOpen] = useState(false)\r\n const [agentsOpen, setAgentsOpen] = useState(false)\r\n const [skillsOpen, setSkillsOpen] = useState(false)\r\n const [presetOpen, setPresetOpen] = useState(false)\r\n const [deepseekAvailable, setDeepseekAvailable] = useState(false)\r\n const [deepseekProviders, setDeepseekProviders] = useState<string[]>([])\r\n const [deepseekError, setDeepseekError] = useState('')\r\n const [loaded, setLoaded] = useState(false)\r\n const [fields, setFields] = useState<Fields>(EMPTY)\r\n const [savedPromptText, setSavedPromptText] = useState('')\r\n const [savedAgentsText, setSavedAgentsText] = useState('')\r\n const [savedSwitches, setSavedSwitches] = useState<SwitchSnapshot>(EMPTY_SWITCHES)\r\n const [loading, setLoading] = useState(false)\r\n const [savingPrompt, setSavingPrompt] = useState(false)\r\n const [savingAgents, setSavingAgents] = useState(false)\r\n const [restoringPrompt, setRestoringPrompt] = useState(false)\r\n const [restoringAgents, setRestoringAgents] = useState(false)\r\n const [notice, setNotice] = useState('')\r\n const [noticeKind, setNoticeKind] = useState<'ok' | 'error'>('ok')\r\n const fieldsRef = useRef<Fields>(EMPTY)\r\n const revisionRef = useRef<number | undefined>(undefined)\r\n const saveQueueRef = useRef<Promise<void>>(Promise.resolve())\r\n\r\n const showNotice = useCallback((kind: 'ok' | 'error', message: string) => {\r\n setNotice(message)\r\n setNoticeKind(kind)\r\n }, [])\r\n\r\n const load = useCallback(async () => {\r\n setLoading(true)\r\n try {\r\n const res = await bridgePost<BridgeSettingsView>('/describe', {})\r\n if (!res.ok) { showNotice('error', '读取配置失败:' + (res.message ?? '')); return }\r\n setDeepseekAvailable(res.deepseekAvailable === true)\r\n setDeepseekProviders(res.deepseekProviders ?? [])\r\n setDeepseekError(res.deepseekError ?? '')\r\n const ns = res.value\r\n const value = asRecord(ns.value)\r\n const base = asRecord(ns.base)\r\n const next: Fields = {\r\n promptText: readString(value, 'promptText') ?? readString(base, 'promptText') ?? '',\r\n promptPath: readString(value, 'promptPath') ?? readString(base, 'promptPath') ?? '',\r\n agentsText: readString(value, 'agentsText') ?? readString(base, 'agentsText') ?? '',\r\n agentsPath: readString(value, 'agentsPath') ?? readString(base, 'agentsPath') ?? '',\r\n injectAgentsPrompt: readBoolean(value, 'injectAgentsPrompt', readBoolean(base, 'injectAgentsPrompt', false)),\r\n anchorFirstTurn: readBoolean(value, 'anchorFirstTurn', readBoolean(base, 'anchorFirstTurn', false)),\r\n anchorText: readString(value, 'anchorText') ?? readString(base, 'anchorText') ?? '',\r\n anchorCustom: readBoolean(value, 'anchorCustom', readBoolean(base, 'anchorCustom', false)),\r\n guideText: readString(value, 'guideText') ?? readString(base, 'guideText') ?? '',\r\n guideCustom: readBoolean(value, 'guideCustom', readBoolean(base, 'guideCustom', false)),\r\n subagentFlash: readBoolean(value, 'subagentFlash', readBoolean(base, 'subagentFlash', false)),\r\n injectPrompt: readBoolean(value, 'injectPrompt', readBoolean(base, 'injectPrompt', true)),\r\n skillSwitches: value.skillSwitches !== undefined || base.skillSwitches !== undefined\r\n ? { ...readSkillSwitches(base, 'skillSwitches'), ...readSkillSwitches(value, 'skillSwitches') }\r\n : {},\r\n skillCatalog: readSkillCatalog(value, 'skillCatalog').length > 0\r\n ? readSkillCatalog(value, 'skillCatalog')\r\n : readSkillCatalog(base, 'skillCatalog'),\r\n writeAgents: readBoolean(value, 'writeAgents', readBoolean(base, 'writeAgents', true)),\r\n writePreset: readBoolean(value, 'writePreset', readBoolean(base, 'writePreset', true)),\r\n }\r\n fieldsRef.current = next\r\n setFields(next)\r\n setSavedPromptText(next.promptText)\r\n setSavedAgentsText(next.agentsText)\r\n setSavedSwitches(snapshotSwitches(next))\r\n revisionRef.current = ns.revision\r\n setNotice('')\r\n } catch (error) {\r\n showNotice('error', '读取失败:' + errorMessage(error))\r\n } finally {\r\n setLoading(false)\r\n }\r\n }, [showNotice])\r\n\r\n const refreshRevision = useCallback(async () => {\r\n try {\r\n const res = await bridgePost<BridgeSettingsView>('/describe', {})\r\n if (!res.ok) return\r\n revisionRef.current = res.value.revision\r\n } catch {\r\n // 刷新失败保持原 revision,用户可重试。\r\n }\r\n }, [])\r\n\r\n const restoreOriginal = useCallback(async (scope: 'preset' | 'agents') => {\r\n const setBusy = scope === 'preset' ? setRestoringPrompt : setRestoringAgents\r\n setBusy(true)\r\n try {\r\n const res = await bridgePost<BridgeSettingsView>('/restore-originals', {\r\n scope,\r\n expectedRevision: revisionRef.current,\r\n })\r\n if (!res.ok) {\r\n await refreshRevision()\r\n showNotice('error', '从项目还原失败:' + (res.message ?? '') + '(已刷新配置版本,可重试)')\r\n return\r\n }\r\n revisionRef.current = res.value.revision\r\n await load()\r\n showNotice('ok', scope === 'preset' ? 'preset.md 已从项目原文还原到设置' : 'AGENTS.md 已从项目原文还原到设置')\r\n } catch (error) {\r\n await refreshRevision()\r\n showNotice('error', '从项目还原失败:' + errorMessage(error) + '(已刷新配置版本,可重试)')\r\n } finally {\r\n setBusy(false)\r\n }\r\n }, [load, refreshRevision, showNotice])\r\n\r\n const patch = (partial: Partial<Fields>) => {\r\n const next = { ...fieldsRef.current, ...partial }\r\n fieldsRef.current = next\r\n setFields(next)\r\n }\r\n\r\n const toggleOpen = () => {\r\n const next = !open\r\n setOpen(next)\r\n if (!next) return\r\n if (!loaded) {\r\n setLoaded(true)\r\n void load()\r\n } else if (!dirty) {\r\n // 无未保存草稿时,每次展开同步最新 settings(其他客户端可能已修改)。\r\n void load()\r\n }\r\n }\r\n\r\n const openEdit = async () => {\r\n if (!fields.promptPath) { showNotice('error', '路径未知,请先保存一次或在下方直接编辑'); return }\r\n try {\r\n const res = await api.host.openPath({ path: fields.promptPath })\r\n if (res.result.ok) showNotice('ok', '已用系统编辑器打开 preset.md')\r\n else showNotice('error', '打开失败:' + (res.result.error?.message ?? '') + ';可直接在下方编辑框保存')\r\n } catch (error) {\r\n showNotice('error', '打开失败:' + errorMessage(error) + ';可直接在下方编辑框保存')\r\n }\r\n }\r\n\r\n const openAgents = async () => {\r\n if (!fields.agentsPath) { showNotice('error', '路径未知,请先保存一次或在下方直接编辑'); return }\r\n try {\r\n const res = await api.host.openPath({ path: fields.agentsPath })\r\n if (res.result.ok) showNotice('ok', '已用系统编辑器打开 AGENTS.md')\r\n else showNotice('error', '打开失败:' + (res.result.error?.message ?? '') + ';可直接在下方编辑框保存')\r\n } catch (error) {\r\n showNotice('error', '打开失败:' + errorMessage(error) + ';可直接在下方编辑框保存')\r\n }\r\n }\r\n\r\n const enqueueSave = useCallback((ops: BridgePathOp[], okMessage: string | undefined, onSaved: () => void, setBusy?: (busy: boolean) => void) => {\r\n setBusy?.(true)\r\n saveQueueRef.current = saveQueueRef.current.then(async () => {\r\n try {\r\n const res = await bridgePost<BridgeSettingsView>('/mutate', { ops, expectedRevision: revisionRef.current })\r\n if (res.ok) {\r\n revisionRef.current = res.value.revision\r\n onSaved()\r\n if (okMessage) showNotice('ok', okMessage)\r\n } else {\r\n await refreshRevision()\r\n showNotice('error', '保存失败:' + (res.message ?? '') + '(已刷新配置版本,可重试)')\r\n }\r\n } catch (error) {\r\n await refreshRevision()\r\n showNotice('error', '保存失败:' + errorMessage(error) + '(已刷新配置版本,可重试)')\r\n } finally {\r\n setBusy?.(false)\r\n }\r\n }).catch(() => {})\r\n }, [refreshRevision, showNotice])\r\n\r\n const savePrompt = () => enqueueSave(\r\n [{ op: 'set', path: ['promptText'], value: fieldsRef.current.promptText }],\r\n 'preset.md 已保存并生效',\r\n () => setSavedPromptText(fieldsRef.current.promptText),\r\n setSavingPrompt,\r\n )\r\n\r\n const saveAgents = () => enqueueSave(\r\n [{ op: 'set', path: ['agentsText'], value: fieldsRef.current.agentsText }],\r\n 'AGENTS.md 已保存并生效',\r\n () => setSavedAgentsText(fieldsRef.current.agentsText),\r\n setSavingAgents,\r\n )\r\n\r\n const persistSwitches = () => enqueueSave(\r\n [\r\n { op: 'set', path: ['injectAgentsPrompt'], value: fieldsRef.current.injectAgentsPrompt },\r\n { op: 'set', path: ['anchorFirstTurn'], value: fieldsRef.current.anchorFirstTurn },\r\n { op: 'set', path: ['anchorText'], value: fieldsRef.current.anchorText },\r\n { op: 'set', path: ['anchorCustom'], value: fieldsRef.current.anchorCustom },\r\n { op: 'set', path: ['guideText'], value: fieldsRef.current.guideText },\r\n { op: 'set', path: ['guideCustom'], value: fieldsRef.current.guideCustom },\r\n { op: 'set', path: ['subagentFlash'], value: fieldsRef.current.subagentFlash },\r\n { op: 'set', path: ['injectPrompt'], value: fieldsRef.current.injectPrompt },\r\n { op: 'set', path: ['skillSwitches'], value: fieldsRef.current.skillSwitches },\r\n { op: 'set', path: ['writeAgents'], value: fieldsRef.current.writeAgents },\r\n { op: 'set', path: ['writePreset'], value: fieldsRef.current.writePreset },\r\n ],\r\n undefined,\r\n () => setSavedSwitches(snapshotSwitches(fieldsRef.current)),\r\n )\r\n\r\n const toggle = (key: 'injectAgentsPrompt' | 'anchorFirstTurn' | 'anchorCustom' | 'guideCustom' | 'subagentFlash' | 'injectPrompt' | 'writeAgents' | 'writePreset') => {\r\n if (key === 'subagentFlash' && !deepseekAvailable) {\r\n showNotice('error', '未检测到 DeepSeek 模型配置,子代理 Flash 开关不可用')\r\n return\r\n }\r\n patch({ [key]: !fieldsRef.current[key] })\r\n persistSwitches()\r\n }\r\n\r\n const skillEnabled = (folder: string) => fields.skillSwitches[folder] !== false\r\n\r\n const toggleSkill = (folder: string) => {\r\n const enabled = fieldsRef.current.skillSwitches[folder] !== false\r\n patch({ skillSwitches: { ...fieldsRef.current.skillSwitches, [folder]: !enabled } })\r\n persistSwitches()\r\n }\r\n\r\n const currentSwitches = snapshotSwitches(fields)\r\n const dirtyPrompt = fields.promptText !== savedPromptText\r\n const dirtyAgents = fields.agentsText !== savedAgentsText\r\n const dirtySwitches = !switchesEqual(currentSwitches, savedSwitches)\r\n const dirty = dirtyPrompt || dirtyAgents || dirtySwitches\r\n const promptSwitchDirty = fields.injectPrompt !== savedSwitches.injectPrompt\r\n const agentsSwitchDirty = fields.injectAgentsPrompt !== savedSwitches.injectAgentsPrompt\r\n || fields.writeAgents !== savedSwitches.writeAgents\r\n const skillsSwitchDirty = JSON.stringify(fields.skillSwitches) !== JSON.stringify(savedSwitches.skillSwitches)\r\n const presetSwitchDirty = fields.writePreset !== savedSwitches.writePreset\r\n || fields.anchorFirstTurn !== savedSwitches.anchorFirstTurn\r\n || fields.anchorText !== savedSwitches.anchorText\r\n\r\n const discardPrompt = () => patch({ promptText: savedPromptText })\r\n const discardAgents = () => patch({ agentsText: savedAgentsText })\r\n\r\n return (\r\n <li className={clsx(styles.card, open && styles.cardOpen)}>\r\n <button type=\"button\" className={styles.header} aria-expanded={open} onClick={toggleOpen}>\r\n <span className={styles.headText}>\r\n <span className={styles.name}>提示词工具</span>\r\n <span className={styles.description}>编辑 preset.md / AGENTS.md 与各层注入开关,保存后自动生效</span>\r\n </span>\r\n {dirty && <span className={styles.pending}>未保存</span>}\r\n <IconChevronDownOutline14 className={clsx(styles.chevron, open && styles.chevronOpen)} />\r\n </button>\r\n {open && (\r\n <div className={styles.body}>\r\n {loading && <span className={styles.loading}>正在读取配置…</span>}\r\n\r\n <CollapsibleSection\r\n title=\"Preset预设\"\r\n hint={dirtyPrompt || promptSwitchDirty ? '未保存' : undefined}\r\n open={promptOpen}\r\n onToggle={() => setPromptOpen(!promptOpen)}\r\n >\r\n <label className={styles.row}>\r\n <input\r\n type=\"checkbox\"\r\n checked={fields.injectPrompt}\r\n disabled={!fields.writePreset}\r\n onChange={() => toggle('injectPrompt')}\r\n />\r\n <span className={styles.rowText}>\r\n <span className={styles.rowName}>注入 preset.md(锚定层)</span>\r\n <span className={styles.rowDesc}>注入会话晋升后的 agent/pre-step,消息插入决策消息开头,每会话一次</span>\r\n </span>\r\n </label>\r\n <textarea\r\n className={styles.textarea}\r\n aria-label=\"preset.md 内容\"\r\n value={fields.promptText}\r\n onChange={(e) => patch({ promptText: e.target.value })}\r\n spellCheck={false}\r\n />\r\n <div className={styles.actions}>\r\n <button type=\"button\" className={styles.primary} disabled={savingPrompt || savingAgents || !dirtyPrompt} onClick={savePrompt}>{savingPrompt ? '保存中…' : '保存'}</button>\r\n <button type=\"button\" className={styles.secondary} disabled={!dirtyPrompt} onClick={discardPrompt}>还原草稿</button>\r\n <button type=\"button\" className={styles.secondary} disabled={savingPrompt || savingAgents || restoringPrompt || restoringAgents} onClick={() => void restoreOriginal('preset')}>{restoringPrompt ? '还原中…' : '项目还原'}</button>\r\n <button type=\"button\" className={styles.secondary} onClick={openEdit}>打开</button>\r\n </div>\r\n </CollapsibleSection>\r\n\r\n <CollapsibleSection\r\n title=\"AGENTS设置\"\r\n hint={dirtyAgents || agentsSwitchDirty ? '未保存' : undefined}\r\n open={agentsOpen}\r\n onToggle={() => setAgentsOpen(!agentsOpen)}\r\n >\r\n <label className={styles.row}>\r\n <input\r\n type=\"checkbox\"\r\n checked={fields.injectAgentsPrompt}\r\n disabled={!fields.writePreset}\r\n onChange={() => toggle('injectAgentsPrompt')}\r\n />\r\n <span className={styles.rowText}>\r\n <span className={styles.rowName}>注入 AGENTS.md</span>\r\n <span className={styles.rowDesc}>注入会话晋升后的第一个 agent/pre-step,消息追加在决策消息末尾,每会话一次</span>\r\n </span>\r\n </label>\r\n <label className={styles.row}>\r\n <input\r\n type=\"checkbox\"\r\n checked={fields.writeAgents}\r\n onChange={() => toggle('writeAgents')}\r\n />\r\n <span className={styles.rowText}>\r\n <span className={styles.rowName}>写入 ~/.dsh/AGENTS.md</span>\r\n <span className={styles.rowDesc}>保持 AGENTS.md 的全局常驻注入;关闭后不再写入,已有文件保持原样</span>\r\n </span>\r\n </label>\r\n <textarea\r\n className={styles.textarea}\r\n aria-label=\"AGENTS.md 内容\"\r\n value={fields.agentsText}\r\n onChange={(e) => patch({ agentsText: e.target.value })}\r\n spellCheck={false}\r\n />\r\n <div className={styles.actions}>\r\n <button type=\"button\" className={styles.primary} disabled={savingPrompt || savingAgents || !dirtyAgents} onClick={saveAgents}>{savingAgents ? '保存中…' : '保存'}</button>\r\n <button type=\"button\" className={styles.secondary} disabled={!dirtyAgents} onClick={discardAgents}>还原草稿</button>\r\n <button type=\"button\" className={styles.secondary} disabled={savingPrompt || savingAgents || restoringPrompt || restoringAgents} onClick={() => void restoreOriginal('agents')}>{restoringAgents ? '还原中…' : '项目还原'}</button>\r\n <button type=\"button\" className={styles.secondary} onClick={openAgents}>打开</button>\r\n </div>\r\n </CollapsibleSection>\r\n\r\n <CollapsibleSection\r\n title=\"Skills设置\"\r\n hint={`${fields.skillCatalog.length} 个${skillsSwitchDirty ? ' · 未保存' : ''}`}\r\n open={skillsOpen}\r\n onToggle={() => setSkillsOpen(!skillsOpen)}\r\n >\r\n {fields.skillCatalog.length === 0 ? (\r\n <span className={styles.subEmpty}>skills 目录下没有技能</span>\r\n ) : fields.skillCatalog.map((skill) => (\r\n <label key={skill.folder} className={styles.row}>\r\n <input\r\n type=\"checkbox\"\r\n checked={skillEnabled(skill.folder)}\r\n onChange={() => toggleSkill(skill.folder)}\r\n />\r\n <span className={styles.rowText}>\r\n <span className={styles.rowName}>{skill.name || skill.folder}</span>\r\n <span className={styles.rowDesc}>skills/{skill.folder}{skill.description ? ` · ${skill.description}` : ''}</span>\r\n </span>\r\n </label>\r\n ))}\r\n </CollapsibleSection>\r\n\r\n <CollapsibleSection\r\n title=\"锚定与 preset\"\r\n hint={presetSwitchDirty ? '未保存' : undefined}\r\n open={presetOpen}\r\n onToggle={() => setPresetOpen(!presetOpen)}\r\n >\r\n\r\n <label className={styles.row}>\r\n <input\r\n type=\"checkbox\"\r\n checked={fields.writePreset}\r\n onChange={() => toggle('writePreset')}\r\n />\r\n <span className={styles.rowText}>\r\n <span className={styles.rowName}>启用锚定预设</span>\r\n <span className={styles.rowDesc}>总开关:开启时生成并刷新 ~/.dsh/.agent-presets/prompt-tool/,整套锚定预设才有载体;关闭时移除已生成的目录,下面锚定开关随之失效</span>\r\n </span>\r\n </label>\r\n <label className={styles.row}>\r\n <input\r\n type=\"checkbox\"\r\n checked={fields.subagentFlash}\r\n disabled={!fields.writePreset || !deepseekAvailable}\r\n onChange={() => toggle('subagentFlash')}\r\n />\r\n <span className={styles.rowText}>\r\n <span className={styles.rowName}>子代理固定 Flash 模型</span>\r\n <span className={styles.rowDesc}>{deepseekAvailable ? '开启时采用 dsh-router-standard 的 Flash 子代理方案:固定 Flash 路由 + 任务分类人设 + 三锚;宿主直派子代理(含 dsh-mnemon)也会自动补 Flash 路由。关闭时继承主会话模型,工具目录全量放行' : `未检测到 DeepSeek 模型配置,此开关不可用。providers=[${deepseekProviders.join(', ') || '空'}]${deepseekError ? ' error=' + deepseekError : ''}`}</span>\r\n </span>\r\n </label>\r\n <label className={styles.row}>\r\n <input\r\n type=\"checkbox\"\r\n checked={fields.anchorFirstTurn}\r\n disabled={!fields.writePreset}\r\n onChange={() => toggle('anchorFirstTurn')}\r\n />\r\n <span className={styles.rowText}>\r\n <span className={styles.rowName}>追加任务引导</span>\r\n <span className={styles.rowDesc}>总开关:开启后在首条真实用户消息之后追加任务引导;关闭时下方首句与每轮的自定义开关、编辑框全部禁用</span>\r\n </span>\r\n </label>\r\n <label className={styles.row}>\r\n <input\r\n type=\"checkbox\"\r\n checked={fields.anchorCustom}\r\n disabled={!fields.writePreset || !fields.anchorFirstTurn}\r\n onChange={() => toggle('anchorCustom')}\r\n />\r\n <span className={styles.rowText}>\r\n <span className={styles.rowName}>使用自定义引导(首句)</span>\r\n <span className={styles.rowDesc}>开启时使用下方文本作为引导;关闭时忽略下方文本,按任务自动选择 we/let 引导</span>\r\n </span>\r\n </label>\r\n <label className={clsx(styles.rowStack, (!fields.anchorFirstTurn || !fields.anchorCustom) && styles.rowDisabled)}>\r\n <span className={styles.rowText}>\r\n <span className={styles.rowName}>自定义引导文本(首句)</span>\r\n <span className={styles.rowDesc}>仅在上方“使用自定义引导(首句)”开启时生效;关闭时自动文本优先</span>\r\n </span>\r\n <textarea\r\n className={styles.anchorInput}\r\n value={fields.anchorText}\r\n disabled={!fields.writePreset || !fields.anchorFirstTurn || !fields.anchorCustom}\r\n onChange={(e) => patch({ anchorText: e.target.value })}\r\n onBlur={() => persistSwitches()}\r\n spellCheck={false}\r\n />\r\n </label>\r\n <label className={styles.row}>\r\n <input\r\n type=\"checkbox\"\r\n checked={fields.guideCustom}\r\n disabled={!fields.writePreset || !fields.anchorFirstTurn}\r\n onChange={() => toggle('guideCustom')}\r\n />\r\n <span className={styles.rowText}>\r\n <span className={styles.rowName}>使用自定义引导(每轮)</span>\r\n <span className={styles.rowDesc}>开启时每轮固定使用下方文本作为引导;关闭时忽略下方文本,按任务自动选择</span>\r\n </span>\r\n </label>\r\n <label className={clsx(styles.rowStack, (!fields.anchorFirstTurn || !fields.guideCustom) && styles.rowDisabled)}>\r\n <span className={styles.rowText}>\r\n <span className={styles.rowName}>自定义引导文本(每轮)</span>\r\n <span className={styles.rowDesc}>仅在上方“使用自定义引导(每轮)”开启时生效;留空则不注入</span>\r\n </span>\r\n <textarea\r\n className={styles.anchorInput}\r\n value={fields.guideText}\r\n disabled={!fields.writePreset || !fields.anchorFirstTurn || !fields.guideCustom}\r\n onChange={(e) => patch({ guideText: e.target.value })}\r\n onBlur={() => persistSwitches()}\r\n spellCheck={false}\r\n />\r\n </label>\r\n </CollapsibleSection>\r\n\r\n {notice && <span className={clsx(styles.notice, noticeKind === 'error' && styles.noticeError)}>{notice}</span>}\r\n </div>\r\n )}\r\n </li>\r\n )\r\n}\r\n","import type { ClientContext } from '@deepseek-ai/dsh-client-runtime/client'\nimport type { ConnectionHandle } from '@deepseek-ai/dsh-api-remotes/client'\nimport type {} from '@deepseek-ai/dsh-client-ui-settings/client'\nimport type {} from '@deepseek-ai/dsh-client-ui-settings-plugins/client'\nimport { PromptEditor, type PromptEditorInjected } from './PromptEditor.tsx'\n\nexport const inject = ['slots', 'connection']\n\nexport function apply(ctx: ClientContext): void {\n const connection = ctx.get('connection') as ConnectionHandle\n\n const injected = (): PromptEditorInjected => ({\n api: connection.api,\n })\n\n ctx.slots.inject('settings.plugin.item', () => ctx.slots.register({\n name: 'settings.plugin.item',\n id: 'prompt-tool',\n order: 30,\n inject: injected,\n }, PromptEditor))\n}\r\n"],"x_google_ignoreList":[0],"mappings":"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"}
1
+ {"version":3,"file":"client.js","names":["styles","IconChevronDownOutline14","useState","useRef","useCallback"],"sources":["../node_modules/.pnpm/clsx@2.1.1/node_modules/clsx/dist/clsx.mjs","../src/client/PromptEditor.tsx","../src/client/index.ts"],"sourcesContent":["function r(e){var t,f,n=\"\";if(\"string\"==typeof e||\"number\"==typeof e)n+=e;else if(\"object\"==typeof e)if(Array.isArray(e)){var o=e.length;for(t=0;t<o;t++)e[t]&&(f=r(e[t]))&&(n&&(n+=\" \"),n+=f)}else for(f in e)e[f]&&(n&&(n+=\" \"),n+=f);return n}export function clsx(){for(var e,t,f=0,n=\"\",o=arguments.length;f<o;f++)(e=arguments[f])&&(t=r(e))&&(n&&(n+=\" \"),n+=t);return n}export default clsx;","import { useCallback, useRef, useState } from 'react'\r\nimport type { ReactNode } from 'react'\r\nimport clsx from 'clsx'\r\nimport { IconChevronDownOutline14 } from '@deepseek-ai/dsh-client-ui-primitives'\r\nimport type { IApiClient } from '@deepseek-ai/dsh-client-connection/client'\r\nimport type { InjectFace, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots'\r\nimport type {} from '@deepseek-ai/dsh-client-ui-settings-plugins/client'\r\nimport styles from './PromptEditor.module.css'\r\n\r\nconst SETTINGS_BRIDGE_PREFIX = '/api/prompt-tool/settings'\r\n\r\ntype BridgePathOp = { op: 'set' | 'unset'; path: string[]; value?: unknown }\r\ninterface BridgeSettingsView { ns: string; value: unknown; base?: unknown; revision: number }\r\ntype BridgeResult<T> = { ok: true; value: T; deepseekAvailable?: boolean; deepseekProviders?: string[]; deepseekError?: string } | { ok: false; code?: string; message?: string }\r\n\r\nasync function bridgePost<T>(path: string, body: unknown): Promise<BridgeResult<T>> {\r\n try {\r\n const response = await fetch(SETTINGS_BRIDGE_PREFIX + path, {\r\n method: 'POST',\r\n headers: { 'content-type': 'application/json' },\r\n body: JSON.stringify(body),\r\n })\r\n const payload = await response.json() as unknown\r\n if (payload !== null && typeof payload === 'object') return payload as BridgeResult<T>\r\n return { ok: false, message: 'settings bridge unavailable' }\r\n } catch (error) {\r\n return { ok: false, message: errorMessage(error) }\r\n }\r\n}\r\n\r\nexport interface PromptEditorInjected {\r\n api: IApiClient\r\n}\r\n\r\nexport type PromptEditorProps = PropsRuntime<'settings.plugin.item'> & InjectFace<PromptEditorInjected>\r\n\r\ninterface SkillCatalogEntry {\r\n folder: string\r\n name: string\r\n description: string\r\n}\r\n\r\ninterface Fields {\r\n promptText: string\r\n promptPath: string\r\n agentsText: string\r\n agentsPath: string\r\n injectAgentsPrompt: boolean\r\n anchorFirstTurn: boolean\r\n anchorText: string\r\n anchorCustom: boolean\r\n guideText: string\r\n guideCustom: boolean\r\n subagentFlash: boolean\r\n bootstrapMaxTokens: number\r\n injectPrompt: boolean\r\n skillSwitches: Record<string, boolean>\r\n skillCatalog: SkillCatalogEntry[]\r\n writeAgents: boolean\r\n writePreset: boolean\r\n}\r\n\r\nconst EMPTY: Fields = {\r\n promptText: '',\r\n promptPath: '',\r\n agentsText: '',\r\n agentsPath: '',\r\n injectAgentsPrompt: false,\r\n anchorFirstTurn: false,\r\n anchorText: '',\r\n anchorCustom: false,\r\n guideText: '',\r\n guideCustom: false,\r\n subagentFlash: false,\r\n bootstrapMaxTokens: 0,\r\n injectPrompt: true,\r\n skillSwitches: {},\r\n skillCatalog: [],\r\n writeAgents: true,\r\n writePreset: true,\r\n}\r\n\r\ninterface SwitchSnapshot {\r\n injectAgentsPrompt: boolean\r\n anchorFirstTurn: boolean\r\n anchorText: string\r\n anchorCustom: boolean\r\n guideText: string\r\n guideCustom: boolean\r\n subagentFlash: boolean\r\n bootstrapMaxTokens: number\r\n injectPrompt: boolean\r\n skillSwitches: Record<string, boolean>\r\n writeAgents: boolean\r\n writePreset: boolean\r\n}\r\n\r\nconst EMPTY_SWITCHES: SwitchSnapshot = {\r\n injectAgentsPrompt: false,\r\n anchorFirstTurn: false,\r\n anchorText: '',\r\n anchorCustom: false,\r\n guideText: '',\r\n guideCustom: false,\r\n subagentFlash: false,\r\n bootstrapMaxTokens: 0,\r\n injectPrompt: true,\r\n skillSwitches: {},\r\n writeAgents: true,\r\n writePreset: true,\r\n}\r\n\r\n/** 本项目默认不设上限时的显示值(adapter 默认 maxTokens)。 */\r\nconst DEFAULT_BOOTSTRAP_DISPLAY = '256000'\r\n\r\nconst snapshotSwitches = (fields: Fields): SwitchSnapshot => ({\r\n injectAgentsPrompt: fields.injectAgentsPrompt,\r\n anchorFirstTurn: fields.anchorFirstTurn,\r\n anchorText: fields.anchorText,\r\n anchorCustom: fields.anchorCustom,\r\n guideText: fields.guideText,\r\n guideCustom: fields.guideCustom,\r\n subagentFlash: fields.subagentFlash,\r\n bootstrapMaxTokens: fields.bootstrapMaxTokens,\r\n injectPrompt: fields.injectPrompt,\r\n skillSwitches: { ...fields.skillSwitches },\r\n writeAgents: fields.writeAgents,\r\n writePreset: fields.writePreset,\r\n})\r\n\r\nconst switchesEqual = (a: SwitchSnapshot, b: SwitchSnapshot): boolean =>\r\n a.injectAgentsPrompt === b.injectAgentsPrompt\r\n && a.anchorFirstTurn === b.anchorFirstTurn\r\n && a.anchorText === b.anchorText\r\n && a.anchorCustom === b.anchorCustom\r\n && a.guideText === b.guideText\r\n && a.guideCustom === b.guideCustom\r\n && a.subagentFlash === b.subagentFlash\r\n && a.bootstrapMaxTokens === b.bootstrapMaxTokens\r\n && a.injectPrompt === b.injectPrompt\r\n && JSON.stringify(a.skillSwitches) === JSON.stringify(b.skillSwitches)\r\n && a.writeAgents === b.writeAgents\r\n && a.writePreset === b.writePreset\r\n\r\nconst asRecord = (value: unknown): Record<string, unknown> =>\r\n value !== null && typeof value === 'object' ? value as Record<string, unknown> : {}\r\n\r\nconst readString = (source: Record<string, unknown>, key: string): string | undefined => {\r\n const value = source[key]\r\n return typeof value === 'string' ? value : undefined\r\n}\r\n\r\nconst readBoolean = (source: Record<string, unknown>, key: string, fallback: boolean): boolean => {\r\n const value = source[key]\r\n return typeof value === 'boolean' ? value : fallback\r\n}\r\n\r\nconst readNumber = (source: Record<string, unknown>, key: string, fallback: number): number => {\r\n const value = source[key]\r\n return typeof value === 'number' && Number.isSafeInteger(value) && value >= 0 ? value : fallback\r\n}\r\n\r\nconst readSkillSwitches = (source: Record<string, unknown>, key: string): Record<string, boolean> => {\r\n const value = source[key]\r\n if (value === null || typeof value !== 'object') return {}\r\n const entries = Object.entries(value as Record<string, unknown>)\r\n const result: Record<string, boolean> = {}\r\n for (const [name, enabled] of entries) {\r\n if (typeof enabled === 'boolean') result[name] = enabled\r\n }\r\n return result\r\n}\r\n\r\nconst readSkillCatalog = (source: Record<string, unknown>, key: string): SkillCatalogEntry[] => {\r\n const value = source[key]\r\n if (!Array.isArray(value)) return []\r\n return value.flatMap((entry) => {\r\n if (entry === null || typeof entry !== 'object') return []\r\n const record = entry as Record<string, unknown>\r\n const folder = readString(record, 'folder')\r\n const name = readString(record, 'name')\r\n if (folder === undefined || name === undefined) return []\r\n return [{ folder, name, description: readString(record, 'description') ?? '' }]\r\n })\r\n}\r\n\r\nconst errorMessage = (error: unknown): string => error instanceof Error ? error.message : String(error)\r\n\r\nfunction CollapsibleSection(props: { title: string; hint?: string; open: boolean; onToggle: () => void; children: ReactNode }): ReactNode {\r\n return (\r\n <div className={styles.subSection}>\r\n <button type=\"button\" className={styles.subHeader} aria-expanded={props.open} onClick={props.onToggle}>\r\n <span className={styles.subTitle}>{props.title}</span>\r\n {props.hint && <span className={styles.subCount}>{props.hint}</span>}\r\n <IconChevronDownOutline14 className={clsx(styles.chevron, props.open && styles.chevronOpen)} />\r\n </button>\r\n {props.open && <div className={styles.subBody}>{props.children}</div>}\r\n </div>\r\n )\r\n}\r\n\r\nexport function PromptEditor(props: PromptEditorProps): ReactNode {\r\n const { api } = props\r\n const [open, setOpen] = useState(false)\r\n const [promptOpen, setPromptOpen] = useState(false)\r\n const [agentsOpen, setAgentsOpen] = useState(false)\r\n const [skillsOpen, setSkillsOpen] = useState(false)\r\n const [presetOpen, setPresetOpen] = useState(false)\r\n const [deepseekAvailable, setDeepseekAvailable] = useState(false)\r\n const [deepseekProviders, setDeepseekProviders] = useState<string[]>([])\r\n const [deepseekError, setDeepseekError] = useState('')\r\n const [loaded, setLoaded] = useState(false)\r\n const [fields, setFields] = useState<Fields>(EMPTY)\r\n const [bootstrapTokensDraft, setBootstrapTokensDraft] = useState(DEFAULT_BOOTSTRAP_DISPLAY)\r\n const [savedPromptText, setSavedPromptText] = useState('')\r\n const [savedAgentsText, setSavedAgentsText] = useState('')\r\n const [savedSwitches, setSavedSwitches] = useState<SwitchSnapshot>(EMPTY_SWITCHES)\r\n const [loading, setLoading] = useState(false)\r\n const [savingPrompt, setSavingPrompt] = useState(false)\r\n const [savingAgents, setSavingAgents] = useState(false)\r\n const [restoringPrompt, setRestoringPrompt] = useState(false)\r\n const [restoringAgents, setRestoringAgents] = useState(false)\r\n const [notice, setNotice] = useState('')\r\n const [noticeKind, setNoticeKind] = useState<'ok' | 'error'>('ok')\r\n const fieldsRef = useRef<Fields>(EMPTY)\r\n const revisionRef = useRef<number | undefined>(undefined)\r\n const saveQueueRef = useRef<Promise<void>>(Promise.resolve())\r\n\r\n const showNotice = useCallback((kind: 'ok' | 'error', message: string) => {\r\n setNotice(message)\r\n setNoticeKind(kind)\r\n }, [])\r\n\r\n const load = useCallback(async () => {\r\n setLoading(true)\r\n try {\r\n const res = await bridgePost<BridgeSettingsView>('/describe', {})\r\n if (!res.ok) { showNotice('error', '读取配置失败:' + (res.message ?? '')); return }\r\n setDeepseekAvailable(res.deepseekAvailable === true)\r\n setDeepseekProviders(res.deepseekProviders ?? [])\r\n setDeepseekError(res.deepseekError ?? '')\r\n const ns = res.value\r\n const value = asRecord(ns.value)\r\n const base = asRecord(ns.base)\r\n const next: Fields = {\r\n promptText: readString(value, 'promptText') ?? readString(base, 'promptText') ?? '',\r\n promptPath: readString(value, 'promptPath') ?? readString(base, 'promptPath') ?? '',\r\n agentsText: readString(value, 'agentsText') ?? readString(base, 'agentsText') ?? '',\r\n agentsPath: readString(value, 'agentsPath') ?? readString(base, 'agentsPath') ?? '',\r\n injectAgentsPrompt: readBoolean(value, 'injectAgentsPrompt', readBoolean(base, 'injectAgentsPrompt', false)),\r\n anchorFirstTurn: readBoolean(value, 'anchorFirstTurn', readBoolean(base, 'anchorFirstTurn', false)),\r\n anchorText: readString(value, 'anchorText') ?? readString(base, 'anchorText') ?? '',\r\n anchorCustom: readBoolean(value, 'anchorCustom', readBoolean(base, 'anchorCustom', false)),\r\n guideText: readString(value, 'guideText') ?? readString(base, 'guideText') ?? '',\r\n guideCustom: readBoolean(value, 'guideCustom', readBoolean(base, 'guideCustom', false)),\r\n subagentFlash: readBoolean(value, 'subagentFlash', readBoolean(base, 'subagentFlash', false)),\r\n bootstrapMaxTokens: readNumber(value, 'bootstrapMaxTokens', readNumber(base, 'bootstrapMaxTokens', 0)),\r\n injectPrompt: readBoolean(value, 'injectPrompt', readBoolean(base, 'injectPrompt', true)),\r\n skillSwitches: value.skillSwitches !== undefined || base.skillSwitches !== undefined\r\n ? { ...readSkillSwitches(base, 'skillSwitches'), ...readSkillSwitches(value, 'skillSwitches') }\r\n : {},\r\n skillCatalog: readSkillCatalog(value, 'skillCatalog').length > 0\r\n ? readSkillCatalog(value, 'skillCatalog')\r\n : readSkillCatalog(base, 'skillCatalog'),\r\n writeAgents: readBoolean(value, 'writeAgents', readBoolean(base, 'writeAgents', true)),\r\n writePreset: readBoolean(value, 'writePreset', readBoolean(base, 'writePreset', true)),\r\n }\r\n fieldsRef.current = next\r\n setFields(next)\r\n setBootstrapTokensDraft(next.bootstrapMaxTokens > 0 ? String(next.bootstrapMaxTokens) : DEFAULT_BOOTSTRAP_DISPLAY)\r\n setSavedPromptText(next.promptText)\r\n setSavedAgentsText(next.agentsText)\r\n setSavedSwitches(snapshotSwitches(next))\r\n revisionRef.current = ns.revision\r\n setNotice('')\r\n } catch (error) {\r\n showNotice('error', '读取失败:' + errorMessage(error))\r\n } finally {\r\n setLoading(false)\r\n }\r\n }, [showNotice])\r\n\r\n const refreshRevision = useCallback(async () => {\r\n try {\r\n const res = await bridgePost<BridgeSettingsView>('/describe', {})\r\n if (!res.ok) return\r\n revisionRef.current = res.value.revision\r\n } catch {\r\n // 刷新失败保持原 revision,用户可重试。\r\n }\r\n }, [])\r\n\r\n const restoreOriginal = useCallback(async (scope: 'preset' | 'agents') => {\r\n const setBusy = scope === 'preset' ? setRestoringPrompt : setRestoringAgents\r\n setBusy(true)\r\n try {\r\n const res = await bridgePost<BridgeSettingsView>('/restore-originals', {\r\n scope,\r\n expectedRevision: revisionRef.current,\r\n })\r\n if (!res.ok) {\r\n await refreshRevision()\r\n showNotice('error', '从项目还原失败:' + (res.message ?? '') + '(已刷新配置版本,可重试)')\r\n return\r\n }\r\n revisionRef.current = res.value.revision\r\n await load()\r\n showNotice('ok', scope === 'preset' ? 'preset.md 已从项目原文还原到设置' : 'AGENTS.md 已从项目原文还原到设置')\r\n } catch (error) {\r\n await refreshRevision()\r\n showNotice('error', '从项目还原失败:' + errorMessage(error) + '(已刷新配置版本,可重试)')\r\n } finally {\r\n setBusy(false)\r\n }\r\n }, [load, refreshRevision, showNotice])\r\n\r\n const patch = (partial: Partial<Fields>) => {\r\n const next = { ...fieldsRef.current, ...partial }\r\n fieldsRef.current = next\r\n setFields(next)\r\n }\r\n\r\n const toggleOpen = () => {\r\n const next = !open\r\n setOpen(next)\r\n if (!next) return\r\n if (!loaded) {\r\n setLoaded(true)\r\n void load()\r\n } else if (!dirty) {\r\n // 无未保存草稿时,每次展开同步最新 settings(其他客户端可能已修改)。\r\n void load()\r\n }\r\n }\r\n\r\n const openEdit = async () => {\r\n if (!fields.promptPath) { showNotice('error', '路径未知,请先保存一次或在下方直接编辑'); return }\r\n try {\r\n const res = await api.host.openPath({ path: fields.promptPath })\r\n if (res.result.ok) showNotice('ok', '已用系统编辑器打开 preset.md')\r\n else showNotice('error', '打开失败:' + (res.result.error?.message ?? '') + ';可直接在下方编辑框保存')\r\n } catch (error) {\r\n showNotice('error', '打开失败:' + errorMessage(error) + ';可直接在下方编辑框保存')\r\n }\r\n }\r\n\r\n const openAgents = async () => {\r\n if (!fields.agentsPath) { showNotice('error', '路径未知,请先保存一次或在下方直接编辑'); return }\r\n try {\r\n const res = await api.host.openPath({ path: fields.agentsPath })\r\n if (res.result.ok) showNotice('ok', '已用系统编辑器打开 AGENTS.md')\r\n else showNotice('error', '打开失败:' + (res.result.error?.message ?? '') + ';可直接在下方编辑框保存')\r\n } catch (error) {\r\n showNotice('error', '打开失败:' + errorMessage(error) + ';可直接在下方编辑框保存')\r\n }\r\n }\r\n\r\n const enqueueSave = useCallback((ops: BridgePathOp[], okMessage: string | undefined, onSaved: () => void, setBusy?: (busy: boolean) => void) => {\r\n setBusy?.(true)\r\n saveQueueRef.current = saveQueueRef.current.then(async () => {\r\n try {\r\n const res = await bridgePost<BridgeSettingsView>('/mutate', { ops, expectedRevision: revisionRef.current })\r\n if (res.ok) {\r\n revisionRef.current = res.value.revision\r\n onSaved()\r\n if (okMessage) showNotice('ok', okMessage)\r\n } else {\r\n await refreshRevision()\r\n showNotice('error', '保存失败:' + (res.message ?? '') + '(已刷新配置版本,可重试)')\r\n }\r\n } catch (error) {\r\n await refreshRevision()\r\n showNotice('error', '保存失败:' + errorMessage(error) + '(已刷新配置版本,可重试)')\r\n } finally {\r\n setBusy?.(false)\r\n }\r\n }).catch(() => {})\r\n }, [refreshRevision, showNotice])\r\n\r\n const savePrompt = () => enqueueSave(\r\n [{ op: 'set', path: ['promptText'], value: fieldsRef.current.promptText }],\r\n 'preset.md 已保存并生效',\r\n () => setSavedPromptText(fieldsRef.current.promptText),\r\n setSavingPrompt,\r\n )\r\n\r\n const saveAgents = () => enqueueSave(\r\n [{ op: 'set', path: ['agentsText'], value: fieldsRef.current.agentsText }],\r\n 'AGENTS.md 已保存并生效',\r\n () => setSavedAgentsText(fieldsRef.current.agentsText),\r\n setSavingAgents,\r\n )\r\n\r\n const persistSwitches = () => enqueueSave(\r\n [\r\n { op: 'set', path: ['injectAgentsPrompt'], value: fieldsRef.current.injectAgentsPrompt },\r\n { op: 'set', path: ['anchorFirstTurn'], value: fieldsRef.current.anchorFirstTurn },\r\n { op: 'set', path: ['anchorText'], value: fieldsRef.current.anchorText },\r\n { op: 'set', path: ['anchorCustom'], value: fieldsRef.current.anchorCustom },\r\n { op: 'set', path: ['guideText'], value: fieldsRef.current.guideText },\r\n { op: 'set', path: ['guideCustom'], value: fieldsRef.current.guideCustom },\r\n { op: 'set', path: ['subagentFlash'], value: fieldsRef.current.subagentFlash },\r\n { op: 'set', path: ['bootstrapMaxTokens'], value: fieldsRef.current.bootstrapMaxTokens },\r\n { op: 'set', path: ['injectPrompt'], value: fieldsRef.current.injectPrompt },\r\n { op: 'set', path: ['skillSwitches'], value: fieldsRef.current.skillSwitches },\r\n { op: 'set', path: ['writeAgents'], value: fieldsRef.current.writeAgents },\r\n { op: 'set', path: ['writePreset'], value: fieldsRef.current.writePreset },\r\n ],\r\n undefined,\r\n () => setSavedSwitches(snapshotSwitches(fieldsRef.current)),\r\n )\r\n\r\n const toggle = (key: 'injectAgentsPrompt' | 'anchorFirstTurn' | 'anchorCustom' | 'guideCustom' | 'subagentFlash' | 'injectPrompt' | 'writeAgents' | 'writePreset') => {\r\n if (key === 'subagentFlash' && !deepseekAvailable) {\r\n showNotice('error', '未检测到 DeepSeek 模型配置,子代理 Flash 开关不可用')\r\n return\r\n }\r\n patch({ [key]: !fieldsRef.current[key] })\r\n persistSwitches()\r\n }\r\n\r\n const toggleBootstrapMaxTokens = () => {\r\n const next = fieldsRef.current.bootstrapMaxTokens > 0 ? 0 : 256000\r\n patch({ bootstrapMaxTokens: next })\r\n setBootstrapTokensDraft(DEFAULT_BOOTSTRAP_DISPLAY)\r\n persistSwitches()\r\n }\r\n\r\n const commitBootstrapTokensDraft = () => {\r\n const parsed = Number(bootstrapTokensDraft)\r\n if (!Number.isSafeInteger(parsed) || parsed <= 0) {\r\n // 清空或非法输入不关闭开关:恢复为项目默认 256000(不设上限)。\r\n setBootstrapTokensDraft(DEFAULT_BOOTSTRAP_DISPLAY)\r\n patch({ bootstrapMaxTokens: 256000 })\r\n } else {\r\n patch({ bootstrapMaxTokens: parsed })\r\n }\r\n persistSwitches()\r\n }\r\n\r\n const skillEnabled = (folder: string) => fields.skillSwitches[folder] !== false\r\n\r\n const toggleSkill = (folder: string) => {\r\n const enabled = fieldsRef.current.skillSwitches[folder] !== false\r\n patch({ skillSwitches: { ...fieldsRef.current.skillSwitches, [folder]: !enabled } })\r\n persistSwitches()\r\n }\r\n\r\n const currentSwitches = snapshotSwitches(fields)\r\n const dirtyPrompt = fields.promptText !== savedPromptText\r\n const dirtyAgents = fields.agentsText !== savedAgentsText\r\n const dirtySwitches = !switchesEqual(currentSwitches, savedSwitches)\r\n const dirty = dirtyPrompt || dirtyAgents || dirtySwitches\r\n const promptSwitchDirty = fields.injectPrompt !== savedSwitches.injectPrompt\r\n const agentsSwitchDirty = fields.injectAgentsPrompt !== savedSwitches.injectAgentsPrompt\r\n || fields.writeAgents !== savedSwitches.writeAgents\r\n const skillsSwitchDirty = JSON.stringify(fields.skillSwitches) !== JSON.stringify(savedSwitches.skillSwitches)\r\n const presetSwitchDirty = fields.writePreset !== savedSwitches.writePreset\r\n || fields.anchorFirstTurn !== savedSwitches.anchorFirstTurn\r\n || fields.anchorText !== savedSwitches.anchorText\r\n || fields.bootstrapMaxTokens !== savedSwitches.bootstrapMaxTokens\r\n\r\n const discardPrompt = () => patch({ promptText: savedPromptText })\r\n const discardAgents = () => patch({ agentsText: savedAgentsText })\r\n\r\n return (\r\n <li className={clsx(styles.card, open && styles.cardOpen)}>\r\n <button type=\"button\" className={styles.header} aria-expanded={open} onClick={toggleOpen}>\r\n <span className={styles.headText}>\r\n <span className={styles.name}>提示词工具</span>\r\n <span className={styles.description}>编辑 preset.md / AGENTS.md 与各层注入开关,保存后自动生效</span>\r\n </span>\r\n {dirty && <span className={styles.pending}>未保存</span>}\r\n <IconChevronDownOutline14 className={clsx(styles.chevron, open && styles.chevronOpen)} />\r\n </button>\r\n {open && (\r\n <div className={styles.body}>\r\n {loading && <span className={styles.loading}>正在读取配置…</span>}\r\n\r\n <CollapsibleSection\r\n title=\"Preset预设\"\r\n hint={dirtyPrompt || promptSwitchDirty ? '未保存' : undefined}\r\n open={promptOpen}\r\n onToggle={() => setPromptOpen(!promptOpen)}\r\n >\r\n <label className={styles.row}>\r\n <input\r\n type=\"checkbox\"\r\n checked={fields.injectPrompt}\r\n disabled={!fields.writePreset}\r\n onChange={() => toggle('injectPrompt')}\r\n />\r\n <span className={styles.rowText}>\r\n <span className={styles.rowName}>注入 preset.md(锚定层)</span>\r\n <span className={styles.rowDesc}>注入会话晋升后的 agent/pre-step,消息插入决策消息开头,每会话一次</span>\r\n </span>\r\n </label>\r\n <textarea\r\n className={styles.textarea}\r\n aria-label=\"preset.md 内容\"\r\n value={fields.promptText}\r\n onChange={(e) => patch({ promptText: e.target.value })}\r\n spellCheck={false}\r\n />\r\n <div className={styles.actions}>\r\n <button type=\"button\" className={styles.primary} disabled={savingPrompt || savingAgents || !dirtyPrompt} onClick={savePrompt}>{savingPrompt ? '保存中…' : '保存'}</button>\r\n <button type=\"button\" className={styles.secondary} disabled={!dirtyPrompt} onClick={discardPrompt}>还原草稿</button>\r\n <button type=\"button\" className={styles.secondary} disabled={savingPrompt || savingAgents || restoringPrompt || restoringAgents} onClick={() => void restoreOriginal('preset')}>{restoringPrompt ? '还原中…' : '项目还原'}</button>\r\n <button type=\"button\" className={styles.secondary} onClick={openEdit}>打开</button>\r\n </div>\r\n </CollapsibleSection>\r\n\r\n <CollapsibleSection\r\n title=\"AGENTS设置\"\r\n hint={dirtyAgents || agentsSwitchDirty ? '未保存' : undefined}\r\n open={agentsOpen}\r\n onToggle={() => setAgentsOpen(!agentsOpen)}\r\n >\r\n <label className={styles.row}>\r\n <input\r\n type=\"checkbox\"\r\n checked={fields.injectAgentsPrompt}\r\n disabled={!fields.writePreset}\r\n onChange={() => toggle('injectAgentsPrompt')}\r\n />\r\n <span className={styles.rowText}>\r\n <span className={styles.rowName}>注入 AGENTS.md</span>\r\n <span className={styles.rowDesc}>注入会话晋升后的第一个 agent/pre-step,消息追加在决策消息末尾,每会话一次</span>\r\n </span>\r\n </label>\r\n <label className={styles.row}>\r\n <input\r\n type=\"checkbox\"\r\n checked={fields.writeAgents}\r\n onChange={() => toggle('writeAgents')}\r\n />\r\n <span className={styles.rowText}>\r\n <span className={styles.rowName}>写入 ~/.dsh/AGENTS.md</span>\r\n <span className={styles.rowDesc}>保持 AGENTS.md 的全局常驻注入;关闭后不再写入,已有文件保持原样</span>\r\n </span>\r\n </label>\r\n <textarea\r\n className={styles.textarea}\r\n aria-label=\"AGENTS.md 内容\"\r\n value={fields.agentsText}\r\n onChange={(e) => patch({ agentsText: e.target.value })}\r\n spellCheck={false}\r\n />\r\n <div className={styles.actions}>\r\n <button type=\"button\" className={styles.primary} disabled={savingPrompt || savingAgents || !dirtyAgents} onClick={saveAgents}>{savingAgents ? '保存中…' : '保存'}</button>\r\n <button type=\"button\" className={styles.secondary} disabled={!dirtyAgents} onClick={discardAgents}>还原草稿</button>\r\n <button type=\"button\" className={styles.secondary} disabled={savingPrompt || savingAgents || restoringPrompt || restoringAgents} onClick={() => void restoreOriginal('agents')}>{restoringAgents ? '还原中…' : '项目还原'}</button>\r\n <button type=\"button\" className={styles.secondary} onClick={openAgents}>打开</button>\r\n </div>\r\n </CollapsibleSection>\r\n\r\n <CollapsibleSection\r\n title=\"Skills设置\"\r\n hint={`${fields.skillCatalog.length} 个${skillsSwitchDirty ? ' · 未保存' : ''}`}\r\n open={skillsOpen}\r\n onToggle={() => setSkillsOpen(!skillsOpen)}\r\n >\r\n {fields.skillCatalog.length === 0 ? (\r\n <span className={styles.subEmpty}>skills 目录下没有技能</span>\r\n ) : fields.skillCatalog.map((skill) => (\r\n <label key={skill.folder} className={styles.row}>\r\n <input\r\n type=\"checkbox\"\r\n checked={skillEnabled(skill.folder)}\r\n onChange={() => toggleSkill(skill.folder)}\r\n />\r\n <span className={styles.rowText}>\r\n <span className={styles.rowName}>{skill.name || skill.folder}</span>\r\n <span className={styles.rowDesc}>skills/{skill.folder}{skill.description ? ` · ${skill.description}` : ''}</span>\r\n </span>\r\n </label>\r\n ))}\r\n </CollapsibleSection>\r\n\r\n <CollapsibleSection\r\n title=\"锚定与 preset\"\r\n hint={presetSwitchDirty ? '未保存' : undefined}\r\n open={presetOpen}\r\n onToggle={() => setPresetOpen(!presetOpen)}\r\n >\r\n\r\n <label className={styles.row}>\r\n <input\r\n type=\"checkbox\"\r\n checked={fields.writePreset}\r\n onChange={() => toggle('writePreset')}\r\n />\r\n <span className={styles.rowText}>\r\n <span className={styles.rowName}>启用锚定预设</span>\r\n <span className={styles.rowDesc}>总开关:开启时生成并刷新 ~/.dsh/.agent-presets/prompt-tool/,整套锚定预设才有载体;关闭时移除已生成的目录,下面锚定开关随之失效</span>\r\n </span>\r\n </label>\r\n\r\n <div className={styles.row}>\r\n <input\r\n id=\"prompt-tool-bootstrap-max-tokens\"\r\n type=\"checkbox\"\r\n checked={fields.bootstrapMaxTokens > 0}\r\n disabled={!fields.writePreset}\r\n onChange={toggleBootstrapMaxTokens}\r\n />\r\n <label htmlFor=\"prompt-tool-bootstrap-max-tokens\" className={styles.rowText}>\r\n <span className={styles.rowName}>首轮输出封顶</span>\r\n <span className={styles.rowDesc}>关闭时显示项目默认 256000(不设上限);开启后右侧数值生效,晋升后自动剥离。清空或输入 0 会恢复 256000,不关闭开关</span>\r\n </label>\r\n <input\r\n className={styles.bootstrapTokensInput}\r\n type=\"number\"\r\n min={1}\r\n step={1}\r\n value={bootstrapTokensDraft}\r\n disabled={!fields.writePreset || fields.bootstrapMaxTokens === 0}\r\n onChange={(e) => setBootstrapTokensDraft(e.target.value)}\r\n onBlur={commitBootstrapTokensDraft}\r\n />\r\n </div>\r\n <label className={styles.row}>\r\n <input\r\n type=\"checkbox\"\r\n checked={fields.subagentFlash}\r\n disabled={!fields.writePreset || !deepseekAvailable}\r\n onChange={() => toggle('subagentFlash')}\r\n />\r\n <span className={styles.rowText}>\r\n <span className={styles.rowName}>子代理固定 Flash 模型</span>\r\n <span className={styles.rowDesc}>{deepseekAvailable ? '开启时采用 dsh-router-standard 的 Flash 子代理方案:固定 Flash 路由 + 任务分类人设 + 三锚;宿主直派子代理(含 dsh-mnemon)也会自动补 Flash 路由。关闭时继承主会话模型,工具目录全量放行' : `未检测到 DeepSeek 模型配置,此开关不可用。providers=[${deepseekProviders.join(', ') || '空'}]${deepseekError ? ' error=' + deepseekError : ''}`}</span>\r\n </span>\r\n </label>\r\n <label className={styles.row}>\r\n <input\r\n type=\"checkbox\"\r\n checked={fields.anchorFirstTurn}\r\n disabled={!fields.writePreset}\r\n onChange={() => toggle('anchorFirstTurn')}\r\n />\r\n <span className={styles.rowText}>\r\n <span className={styles.rowName}>追加任务引导</span>\r\n <span className={styles.rowDesc}>总开关:开启后在首条真实用户消息之后追加任务引导;关闭时下方首句与每轮的自定义开关、编辑框全部禁用</span>\r\n </span>\r\n </label>\r\n <label className={styles.row}>\r\n <input\r\n type=\"checkbox\"\r\n checked={fields.anchorCustom}\r\n disabled={!fields.writePreset || !fields.anchorFirstTurn}\r\n onChange={() => toggle('anchorCustom')}\r\n />\r\n <span className={styles.rowText}>\r\n <span className={styles.rowName}>使用自定义引导(首句)</span>\r\n <span className={styles.rowDesc}>开启时使用下方文本作为引导;关闭时忽略下方文本,按任务自动选择 we/let 引导</span>\r\n </span>\r\n </label>\r\n <label className={clsx(styles.rowStack, (!fields.anchorFirstTurn || !fields.anchorCustom) && styles.rowDisabled)}>\r\n <span className={styles.rowText}>\r\n <span className={styles.rowName}>自定义引导文本(首句)</span>\r\n <span className={styles.rowDesc}>仅在上方“使用自定义引导(首句)”开启时生效;关闭时自动文本优先</span>\r\n </span>\r\n <textarea\r\n className={styles.anchorInput}\r\n value={fields.anchorText}\r\n disabled={!fields.writePreset || !fields.anchorFirstTurn || !fields.anchorCustom}\r\n onChange={(e) => patch({ anchorText: e.target.value })}\r\n onBlur={() => persistSwitches()}\r\n spellCheck={false}\r\n />\r\n </label>\r\n <label className={styles.row}>\r\n <input\r\n type=\"checkbox\"\r\n checked={fields.guideCustom}\r\n disabled={!fields.writePreset || !fields.anchorFirstTurn}\r\n onChange={() => toggle('guideCustom')}\r\n />\r\n <span className={styles.rowText}>\r\n <span className={styles.rowName}>使用自定义引导(每轮)</span>\r\n <span className={styles.rowDesc}>开启时每轮固定使用下方文本作为引导;关闭时忽略下方文本,按任务自动选择</span>\r\n </span>\r\n </label>\r\n <label className={clsx(styles.rowStack, (!fields.anchorFirstTurn || !fields.guideCustom) && styles.rowDisabled)}>\r\n <span className={styles.rowText}>\r\n <span className={styles.rowName}>自定义引导文本(每轮)</span>\r\n <span className={styles.rowDesc}>仅在上方“使用自定义引导(每轮)”开启时生效;留空则不注入</span>\r\n </span>\r\n <textarea\r\n className={styles.anchorInput}\r\n value={fields.guideText}\r\n disabled={!fields.writePreset || !fields.anchorFirstTurn || !fields.guideCustom}\r\n onChange={(e) => patch({ guideText: e.target.value })}\r\n onBlur={() => persistSwitches()}\r\n spellCheck={false}\r\n />\r\n </label>\r\n </CollapsibleSection>\r\n\r\n {notice && <span className={clsx(styles.notice, noticeKind === 'error' && styles.noticeError)}>{notice}</span>}\r\n </div>\r\n )}\r\n </li>\r\n )\r\n}\r\n","import type { ClientContext } from '@deepseek-ai/dsh-client-runtime/client'\nimport type { ConnectionHandle } from '@deepseek-ai/dsh-api-remotes/client'\nimport type {} from '@deepseek-ai/dsh-client-ui-settings/client'\nimport type {} from '@deepseek-ai/dsh-client-ui-settings-plugins/client'\nimport { PromptEditor, type PromptEditorInjected } from './PromptEditor.tsx'\n\nexport const inject = ['slots', 'connection']\n\nexport function apply(ctx: ClientContext): void {\n const connection = ctx.get('connection') as ConnectionHandle\n\n const injected = (): PromptEditorInjected => ({\n api: connection.api,\n })\n\n ctx.slots.inject('settings.plugin.item', () => ctx.slots.register({\n name: 'settings.plugin.item',\n id: 'prompt-tool',\n order: 30,\n inject: injected,\n }, PromptEditor))\n}\r\n"],"x_google_ignoreList":[0],"mappings":";;;;;;;;;;EAAA,SAAS,EAAE,GAAE;GAAC,IAAI,GAAE,GAAE,IAAE;GAAG,IAAG,YAAU,OAAO,KAAG,YAAU,OAAO,GAAE,KAAG;QAAO,IAAG,YAAU,OAAO,GAAE,IAAG,MAAM,QAAQ,CAAC,GAAE;IAAC,IAAI,IAAE,EAAE;IAAO,KAAI,IAAE,GAAE,IAAE,GAAE,KAAI,EAAE,OAAK,IAAE,EAAE,EAAE,EAAE,OAAK,MAAI,KAAG,MAAK,KAAG;GAAE,OAAM,KAAI,KAAK,GAAE,EAAE,OAAK,MAAI,KAAG,MAAK,KAAG;GAAG,OAAO;EAAC;EAAC,SAAgB,OAAM;GAAC,KAAI,IAAI,GAAE,GAAE,IAAE,GAAE,IAAE,IAAG,IAAE,UAAU,QAAO,IAAE,GAAE,KAAI,CAAC,IAAE,UAAU,QAAM,IAAE,EAAE,CAAC,OAAK,MAAI,KAAG,MAAK,KAAG;GAAG,OAAO;EAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ECS/W,MAAM,yBAAyB;EAM/B,eAAe,WAAc,MAAc,MAAyC;GAClF,IAAI;IAMF,MAAM,UAAU,OAAM,MALC,MAAM,yBAAyB,MAAM;KAC1D,QAAQ;KACR,SAAS,EAAE,gBAAgB,mBAAmB;KAC9C,MAAM,KAAK,UAAU,IAAI;IAC3B,CAAC,EAAA,CAC8B,KAAK;IACpC,IAAI,YAAY,QAAQ,OAAO,YAAY,UAAU,OAAO;IAC5D,OAAO;KAAE,IAAI;KAAO,SAAS;IAA8B;GAC7D,SAAS,OAAO;IACd,OAAO;KAAE,IAAI;KAAO,SAAS,aAAa,KAAK;IAAE;GACnD;EACF;EAkCA,MAAM,QAAgB;GACpB,YAAY;GACZ,YAAY;GACZ,YAAY;GACZ,YAAY;GACZ,oBAAoB;GACpB,iBAAiB;GACjB,YAAY;GACZ,cAAc;GACd,WAAW;GACX,aAAa;GACb,eAAe;GACf,oBAAoB;GACpB,cAAc;GACd,eAAe,CAAC;GAChB,cAAc,CAAC;GACf,aAAa;GACb,aAAa;EACf;EAiBA,MAAM,iBAAiC;GACrC,oBAAoB;GACpB,iBAAiB;GACjB,YAAY;GACZ,cAAc;GACd,WAAW;GACX,aAAa;GACb,eAAe;GACf,oBAAoB;GACpB,cAAc;GACd,eAAe,CAAC;GAChB,aAAa;GACb,aAAa;EACf;;EAGA,MAAM,4BAA4B;EAElC,MAAM,oBAAoB,YAAoC;GAC5D,oBAAoB,OAAO;GAC3B,iBAAiB,OAAO;GACxB,YAAY,OAAO;GACnB,cAAc,OAAO;GACrB,WAAW,OAAO;GAClB,aAAa,OAAO;GACpB,eAAe,OAAO;GACtB,oBAAoB,OAAO;GAC3B,cAAc,OAAO;GACrB,eAAe,EAAE,GAAG,OAAO,cAAc;GACzC,aAAa,OAAO;GACpB,aAAa,OAAO;EACtB;EAEA,MAAM,iBAAiB,GAAmB,MACxC,EAAE,uBAAuB,EAAE,sBACxB,EAAE,oBAAoB,EAAE,mBACxB,EAAE,eAAe,EAAE,cACnB,EAAE,iBAAiB,EAAE,gBACrB,EAAE,cAAc,EAAE,aAClB,EAAE,gBAAgB,EAAE,eACpB,EAAE,kBAAkB,EAAE,iBACtB,EAAE,uBAAuB,EAAE,sBAC3B,EAAE,iBAAiB,EAAE,gBACrB,KAAK,UAAU,EAAE,aAAa,MAAM,KAAK,UAAU,EAAE,aAAa,KAClE,EAAE,gBAAgB,EAAE,eACpB,EAAE,gBAAgB,EAAE;EAEzB,MAAM,YAAY,UAChB,UAAU,QAAQ,OAAO,UAAU,WAAW,QAAmC,CAAC;EAEpF,MAAM,cAAc,QAAiC,QAAoC;GACvF,MAAM,QAAQ,OAAO;GACrB,OAAO,OAAO,UAAU,WAAW,QAAQ,KAAA;EAC7C;EAEA,MAAM,eAAe,QAAiC,KAAa,aAA+B;GAChG,MAAM,QAAQ,OAAO;GACrB,OAAO,OAAO,UAAU,YAAY,QAAQ;EAC9C;EAEA,MAAM,cAAc,QAAiC,KAAa,aAA6B;GAC7F,MAAM,QAAQ,OAAO;GACrB,OAAO,OAAO,UAAU,YAAY,OAAO,cAAc,KAAK,KAAK,SAAS,IAAI,QAAQ;EAC1F;EAEA,MAAM,qBAAqB,QAAiC,QAAyC;GACnG,MAAM,QAAQ,OAAO;GACrB,IAAI,UAAU,QAAQ,OAAO,UAAU,UAAU,OAAO,CAAC;GACzD,MAAM,UAAU,OAAO,QAAQ,KAAgC;GAC/D,MAAM,SAAkC,CAAC;GACzC,KAAK,MAAM,CAAC,MAAM,YAAY,SAC5B,IAAI,OAAO,YAAY,WAAW,OAAO,QAAQ;GAEnD,OAAO;EACT;EAEA,MAAM,oBAAoB,QAAiC,QAAqC;GAC9F,MAAM,QAAQ,OAAO;GACrB,IAAI,CAAC,MAAM,QAAQ,KAAK,GAAG,OAAO,CAAC;GACnC,OAAO,MAAM,SAAS,UAAU;IAC9B,IAAI,UAAU,QAAQ,OAAO,UAAU,UAAU,OAAO,CAAC;IACzD,MAAM,SAAS;IACf,MAAM,SAAS,WAAW,QAAQ,QAAQ;IAC1C,MAAM,OAAO,WAAW,QAAQ,MAAM;IACtC,IAAI,WAAW,KAAA,KAAa,SAAS,KAAA,GAAW,OAAO,CAAC;IACxD,OAAO,CAAC;KAAE;KAAQ;KAAM,aAAa,WAAW,QAAQ,aAAa,KAAK;IAAG,CAAC;GAChF,CAAC;EACH;EAEA,MAAM,gBAAgB,UAA2B,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;EAEtG,SAAS,mBAAmB,OAA8G;GACxI,OACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;IAAK,WAAWA,gCAAO;IAAvB,UAAA,CACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,UAAD;KAAQ,MAAK;KAAS,WAAWA,gCAAO;KAAW,iBAAe,MAAM;KAAM,SAAS,MAAM;KAA7F,UAAA;MACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;OAAM,WAAWA,gCAAO;OAAW,UAAA,MAAM;MAAY,CAAA;MACpD,MAAM,QAAQ,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;OAAM,WAAWA,gCAAO;OAAW,UAAA,MAAM;MAAW,CAAA;MACnE,iBAAA,GAAA,kBAAA,IAAA,CAACC,sCAAAA,0BAAD,EAA0B,WAAW,KAAKD,gCAAO,SAAS,MAAM,QAAQA,gCAAO,WAAW,EAAI,CAAA;KACxF;IACP,CAAA,GAAA,MAAM,QAAQ,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;KAAK,WAAWA,gCAAO;KAAU,UAAA,MAAM;IAAc,CAAA,CACjE;;EAET;EAEA,SAAgB,aAAa,OAAqC;GAChE,MAAM,EAAE,QAAQ;GAChB,MAAM,CAAC,MAAM,YAAA,GAAWE,MAAAA,SAAAA,CAAS,KAAK;GACtC,MAAM,CAAC,YAAY,kBAAA,GAAiBA,MAAAA,SAAAA,CAAS,KAAK;GAClD,MAAM,CAAC,YAAY,kBAAA,GAAiBA,MAAAA,SAAAA,CAAS,KAAK;GAClD,MAAM,CAAC,YAAY,kBAAA,GAAiBA,MAAAA,SAAAA,CAAS,KAAK;GAClD,MAAM,CAAC,YAAY,kBAAA,GAAiBA,MAAAA,SAAAA,CAAS,KAAK;GAClD,MAAM,CAAC,mBAAmB,yBAAA,GAAwBA,MAAAA,SAAAA,CAAS,KAAK;GAChE,MAAM,CAAC,mBAAmB,yBAAA,GAAwBA,MAAAA,SAAAA,CAAmB,CAAC,CAAC;GACvE,MAAM,CAAC,eAAe,qBAAA,GAAoBA,MAAAA,SAAAA,CAAS,EAAE;GACrD,MAAM,CAAC,QAAQ,cAAA,GAAaA,MAAAA,SAAAA,CAAS,KAAK;GAC1C,MAAM,CAAC,QAAQ,cAAA,GAAaA,MAAAA,SAAAA,CAAiB,KAAK;GAClD,MAAM,CAAC,sBAAsB,4BAAA,GAA2BA,MAAAA,SAAAA,CAAS,yBAAyB;GAC1F,MAAM,CAAC,iBAAiB,uBAAA,GAAsBA,MAAAA,SAAAA,CAAS,EAAE;GACzD,MAAM,CAAC,iBAAiB,uBAAA,GAAsBA,MAAAA,SAAAA,CAAS,EAAE;GACzD,MAAM,CAAC,eAAe,qBAAA,GAAoBA,MAAAA,SAAAA,CAAyB,cAAc;GACjF,MAAM,CAAC,SAAS,eAAA,GAAcA,MAAAA,SAAAA,CAAS,KAAK;GAC5C,MAAM,CAAC,cAAc,oBAAA,GAAmBA,MAAAA,SAAAA,CAAS,KAAK;GACtD,MAAM,CAAC,cAAc,oBAAA,GAAmBA,MAAAA,SAAAA,CAAS,KAAK;GACtD,MAAM,CAAC,iBAAiB,uBAAA,GAAsBA,MAAAA,SAAAA,CAAS,KAAK;GAC5D,MAAM,CAAC,iBAAiB,uBAAA,GAAsBA,MAAAA,SAAAA,CAAS,KAAK;GAC5D,MAAM,CAAC,QAAQ,cAAA,GAAaA,MAAAA,SAAAA,CAAS,EAAE;GACvC,MAAM,CAAC,YAAY,kBAAA,GAAiBA,MAAAA,SAAAA,CAAyB,IAAI;GACjE,MAAM,aAAA,GAAYC,MAAAA,OAAAA,CAAe,KAAK;GACtC,MAAM,eAAA,GAAcA,MAAAA,OAAAA,CAA2B,KAAA,CAAS;GACxD,MAAM,gBAAA,GAAeA,MAAAA,OAAAA,CAAsB,QAAQ,QAAQ,CAAC;GAE5D,MAAM,cAAA,GAAaC,MAAAA,YAAAA,EAAa,MAAsB,YAAoB;IACxE,UAAU,OAAO;IACjB,cAAc,IAAI;GACpB,GAAG,CAAC,CAAC;GAEL,MAAM,QAAA,GAAOA,MAAAA,YAAAA,CAAY,YAAY;IACnC,WAAW,IAAI;IACf,IAAI;KACF,MAAM,MAAM,MAAM,WAA+B,aAAa,CAAC,CAAC;KAChE,IAAI,CAAC,IAAI,IAAI;MAAE,WAAW,SAAS,aAAa,IAAI,WAAW,GAAG;MAAG;KAAO;KAC5E,qBAAqB,IAAI,sBAAsB,IAAI;KACnD,qBAAqB,IAAI,qBAAqB,CAAC,CAAC;KAChD,iBAAiB,IAAI,iBAAiB,EAAE;KACxC,MAAM,KAAK,IAAI;KACf,MAAM,QAAQ,SAAS,GAAG,KAAK;KAC/B,MAAM,OAAO,SAAS,GAAG,IAAI;KAC7B,MAAM,OAAe;MACnB,YAAY,WAAW,OAAO,YAAY,KAAK,WAAW,MAAM,YAAY,KAAK;MACjF,YAAY,WAAW,OAAO,YAAY,KAAK,WAAW,MAAM,YAAY,KAAK;MACjF,YAAY,WAAW,OAAO,YAAY,KAAK,WAAW,MAAM,YAAY,KAAK;MACjF,YAAY,WAAW,OAAO,YAAY,KAAK,WAAW,MAAM,YAAY,KAAK;MACjF,oBAAoB,YAAY,OAAO,sBAAsB,YAAY,MAAM,sBAAsB,KAAK,CAAC;MAC3G,iBAAiB,YAAY,OAAO,mBAAmB,YAAY,MAAM,mBAAmB,KAAK,CAAC;MAClG,YAAY,WAAW,OAAO,YAAY,KAAK,WAAW,MAAM,YAAY,KAAK;MACjF,cAAc,YAAY,OAAO,gBAAgB,YAAY,MAAM,gBAAgB,KAAK,CAAC;MACzF,WAAW,WAAW,OAAO,WAAW,KAAK,WAAW,MAAM,WAAW,KAAK;MAC9E,aAAa,YAAY,OAAO,eAAe,YAAY,MAAM,eAAe,KAAK,CAAC;MACtF,eAAe,YAAY,OAAO,iBAAiB,YAAY,MAAM,iBAAiB,KAAK,CAAC;MAC5F,oBAAoB,WAAW,OAAO,sBAAsB,WAAW,MAAM,sBAAsB,CAAC,CAAC;MACrG,cAAc,YAAY,OAAO,gBAAgB,YAAY,MAAM,gBAAgB,IAAI,CAAC;MACxF,eAAe,MAAM,kBAAkB,KAAA,KAAa,KAAK,kBAAkB,KAAA,IACvE;OAAE,GAAG,kBAAkB,MAAM,eAAe;OAAG,GAAG,kBAAkB,OAAO,eAAe;MAAE,IAC5F,CAAC;MACL,cAAc,iBAAiB,OAAO,cAAc,CAAC,CAAC,SAAS,IAC3D,iBAAiB,OAAO,cAAc,IACtC,iBAAiB,MAAM,cAAc;MACzC,aAAa,YAAY,OAAO,eAAe,YAAY,MAAM,eAAe,IAAI,CAAC;MACrF,aAAa,YAAY,OAAO,eAAe,YAAY,MAAM,eAAe,IAAI,CAAC;KACvF;KACA,UAAU,UAAU;KACpB,UAAU,IAAI;KACd,wBAAwB,KAAK,qBAAqB,IAAI,OAAO,KAAK,kBAAkB,IAAI,yBAAyB;KACjH,mBAAmB,KAAK,UAAU;KAClC,mBAAmB,KAAK,UAAU;KAClC,iBAAiB,iBAAiB,IAAI,CAAC;KACvC,YAAY,UAAU,GAAG;KACzB,UAAU,EAAE;IACd,SAAS,OAAO;KACd,WAAW,SAAS,UAAU,aAAa,KAAK,CAAC;IACnD,UAAU;KACR,WAAW,KAAK;IAClB;GACF,GAAG,CAAC,UAAU,CAAC;GAEf,MAAM,mBAAA,GAAkBA,MAAAA,YAAAA,CAAY,YAAY;IAC9C,IAAI;KACF,MAAM,MAAM,MAAM,WAA+B,aAAa,CAAC,CAAC;KAChE,IAAI,CAAC,IAAI,IAAI;KACb,YAAY,UAAU,IAAI,MAAM;IAClC,QAAQ,CAER;GACF,GAAG,CAAC,CAAC;GAEL,MAAM,mBAAA,GAAkBA,MAAAA,YAAAA,CAAY,OAAO,UAA+B;IACxE,MAAM,UAAU,UAAU,WAAW,qBAAqB;IAC1D,QAAQ,IAAI;IACZ,IAAI;KACF,MAAM,MAAM,MAAM,WAA+B,sBAAsB;MACrE;MACA,kBAAkB,YAAY;KAChC,CAAC;KACD,IAAI,CAAC,IAAI,IAAI;MACX,MAAM,gBAAgB;MACtB,WAAW,SAAS,cAAc,IAAI,WAAW,MAAM,eAAe;MACtE;KACF;KACA,YAAY,UAAU,IAAI,MAAM;KAChC,MAAM,KAAK;KACX,WAAW,MAAM,UAAU,WAAW,0BAA0B,uBAAuB;IACzF,SAAS,OAAO;KACd,MAAM,gBAAgB;KACtB,WAAW,SAAS,aAAa,aAAa,KAAK,IAAI,eAAe;IACxE,UAAU;KACR,QAAQ,KAAK;IACf;GACF,GAAG;IAAC;IAAM;IAAiB;GAAU,CAAC;GAEtC,MAAM,SAAS,YAA6B;IAC1C,MAAM,OAAO;KAAE,GAAG,UAAU;KAAS,GAAG;IAAQ;IAChD,UAAU,UAAU;IACpB,UAAU,IAAI;GAChB;GAEA,MAAM,mBAAmB;IACvB,MAAM,OAAO,CAAC;IACd,QAAQ,IAAI;IACZ,IAAI,CAAC,MAAM;IACX,IAAI,CAAC,QAAQ;KACX,UAAU,IAAI;KACd,KAAU;IACZ,OAAO,IAAI,CAAC,OAEV,KAAU;GAEd;GAEA,MAAM,WAAW,YAAY;IAC3B,IAAI,CAAC,OAAO,YAAY;KAAE,WAAW,SAAS,qBAAqB;KAAG;IAAO;IAC7E,IAAI;KACF,MAAM,MAAM,MAAM,IAAI,KAAK,SAAS,EAAE,MAAM,OAAO,WAAW,CAAC;KAC/D,IAAI,IAAI,OAAO,IAAI,WAAW,MAAM,qBAAqB;UACpD,WAAW,SAAS,WAAW,IAAI,OAAO,OAAO,WAAW,MAAM,cAAc;IACvF,SAAS,OAAO;KACd,WAAW,SAAS,UAAU,aAAa,KAAK,IAAI,cAAc;IACpE;GACF;GAEA,MAAM,aAAa,YAAY;IAC7B,IAAI,CAAC,OAAO,YAAY;KAAE,WAAW,SAAS,qBAAqB;KAAG;IAAO;IAC7E,IAAI;KACF,MAAM,MAAM,MAAM,IAAI,KAAK,SAAS,EAAE,MAAM,OAAO,WAAW,CAAC;KAC/D,IAAI,IAAI,OAAO,IAAI,WAAW,MAAM,qBAAqB;UACpD,WAAW,SAAS,WAAW,IAAI,OAAO,OAAO,WAAW,MAAM,cAAc;IACvF,SAAS,OAAO;KACd,WAAW,SAAS,UAAU,aAAa,KAAK,IAAI,cAAc;IACpE;GACF;GAEA,MAAM,eAAA,GAAcA,MAAAA,YAAAA,EAAa,KAAqB,WAA+B,SAAqB,YAAsC;IAC9I,UAAU,IAAI;IACd,aAAa,UAAU,aAAa,QAAQ,KAAK,YAAY;KAC3D,IAAI;MACF,MAAM,MAAM,MAAM,WAA+B,WAAW;OAAE;OAAK,kBAAkB,YAAY;MAAQ,CAAC;MAC1G,IAAI,IAAI,IAAI;OACV,YAAY,UAAU,IAAI,MAAM;OAChC,QAAQ;OACR,IAAI,WAAW,WAAW,MAAM,SAAS;MAC3C,OAAO;OACL,MAAM,gBAAgB;OACtB,WAAW,SAAS,WAAW,IAAI,WAAW,MAAM,eAAe;MACrE;KACF,SAAS,OAAO;MACd,MAAM,gBAAgB;MACtB,WAAW,SAAS,UAAU,aAAa,KAAK,IAAI,eAAe;KACrE,UAAU;MACR,UAAU,KAAK;KACjB;IACF,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC;GACnB,GAAG,CAAC,iBAAiB,UAAU,CAAC;GAEhC,MAAM,mBAAmB,YACvB,CAAC;IAAE,IAAI;IAAO,MAAM,CAAC,YAAY;IAAG,OAAO,UAAU,QAAQ;GAAW,CAAC,GACzE,0BACM,mBAAmB,UAAU,QAAQ,UAAU,GACrD,eACF;GAEA,MAAM,mBAAmB,YACvB,CAAC;IAAE,IAAI;IAAO,MAAM,CAAC,YAAY;IAAG,OAAO,UAAU,QAAQ;GAAW,CAAC,GACzE,0BACM,mBAAmB,UAAU,QAAQ,UAAU,GACrD,eACF;GAEA,MAAM,wBAAwB,YAC5B;IACE;KAAE,IAAI;KAAO,MAAM,CAAC,oBAAoB;KAAG,OAAO,UAAU,QAAQ;IAAmB;IACvF;KAAE,IAAI;KAAO,MAAM,CAAC,iBAAiB;KAAG,OAAO,UAAU,QAAQ;IAAgB;IACjF;KAAE,IAAI;KAAO,MAAM,CAAC,YAAY;KAAG,OAAO,UAAU,QAAQ;IAAW;IACvE;KAAE,IAAI;KAAO,MAAM,CAAC,cAAc;KAAG,OAAO,UAAU,QAAQ;IAAa;IAC3E;KAAE,IAAI;KAAO,MAAM,CAAC,WAAW;KAAG,OAAO,UAAU,QAAQ;IAAU;IACrE;KAAE,IAAI;KAAO,MAAM,CAAC,aAAa;KAAG,OAAO,UAAU,QAAQ;IAAY;IACzE;KAAE,IAAI;KAAO,MAAM,CAAC,eAAe;KAAG,OAAO,UAAU,QAAQ;IAAc;IAC7E;KAAE,IAAI;KAAO,MAAM,CAAC,oBAAoB;KAAG,OAAO,UAAU,QAAQ;IAAmB;IACvF;KAAE,IAAI;KAAO,MAAM,CAAC,cAAc;KAAG,OAAO,UAAU,QAAQ;IAAa;IAC3E;KAAE,IAAI;KAAO,MAAM,CAAC,eAAe;KAAG,OAAO,UAAU,QAAQ;IAAc;IAC7E;KAAE,IAAI;KAAO,MAAM,CAAC,aAAa;KAAG,OAAO,UAAU,QAAQ;IAAY;IACzE;KAAE,IAAI;KAAO,MAAM,CAAC,aAAa;KAAG,OAAO,UAAU,QAAQ;IAAY;GAC3E,GACA,KAAA,SACM,iBAAiB,iBAAiB,UAAU,OAAO,CAAC,CAC5D;GAEA,MAAM,UAAU,QAAsJ;IACpK,IAAI,QAAQ,mBAAmB,CAAC,mBAAmB;KACjD,WAAW,SAAS,oCAAoC;KACxD;IACF;IACA,MAAM,GAAG,MAAM,CAAC,UAAU,QAAQ,KAAK,CAAC;IACxC,gBAAgB;GAClB;GAEA,MAAM,iCAAiC;IACrC,MAAM,OAAO,UAAU,QAAQ,qBAAqB,IAAI,IAAI;IAC5D,MAAM,EAAE,oBAAoB,KAAK,CAAC;IAClC,wBAAwB,yBAAyB;IACjD,gBAAgB;GAClB;GAEA,MAAM,mCAAmC;IACvC,MAAM,SAAS,OAAO,oBAAoB;IAC1C,IAAI,CAAC,OAAO,cAAc,MAAM,KAAK,UAAU,GAAG;KAEhD,wBAAwB,yBAAyB;KACjD,MAAM,EAAE,oBAAoB,MAAO,CAAC;IACtC,OACE,MAAM,EAAE,oBAAoB,OAAO,CAAC;IAEtC,gBAAgB;GAClB;GAEA,MAAM,gBAAgB,WAAmB,OAAO,cAAc,YAAY;GAE1E,MAAM,eAAe,WAAmB;IACtC,MAAM,UAAU,UAAU,QAAQ,cAAc,YAAY;IAC5D,MAAM,EAAE,eAAe;KAAE,GAAG,UAAU,QAAQ;MAAgB,SAAS,CAAC;IAAQ,EAAE,CAAC;IACnF,gBAAgB;GAClB;GAEA,MAAM,kBAAkB,iBAAiB,MAAM;GAC/C,MAAM,cAAc,OAAO,eAAe;GAC1C,MAAM,cAAc,OAAO,eAAe;GAC1C,MAAM,gBAAgB,CAAC,cAAc,iBAAiB,aAAa;GACnE,MAAM,QAAQ,eAAe,eAAe;GAC5C,MAAM,oBAAoB,OAAO,iBAAiB,cAAc;GAChE,MAAM,oBAAoB,OAAO,uBAAuB,cAAc,sBACjE,OAAO,gBAAgB,cAAc;GAC1C,MAAM,oBAAoB,KAAK,UAAU,OAAO,aAAa,MAAM,KAAK,UAAU,cAAc,aAAa;GAC7G,MAAM,oBAAoB,OAAO,gBAAgB,cAAc,eAC1D,OAAO,oBAAoB,cAAc,mBACzC,OAAO,eAAe,cAAc,cACpC,OAAO,uBAAuB,cAAc;GAEjD,MAAM,sBAAsB,MAAM,EAAE,YAAY,gBAAgB,CAAC;GACjE,MAAM,sBAAsB,MAAM,EAAE,YAAY,gBAAgB,CAAC;GAEjE,OACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,MAAD;IAAI,WAAW,KAAKJ,gCAAO,MAAM,QAAQA,gCAAO,QAAQ;IAAxD,UAAA,CACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,UAAD;KAAQ,MAAK;KAAS,WAAWA,gCAAO;KAAQ,iBAAe;KAAM,SAAS;KAA9E,UAAA;MACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,QAAD;OAAM,WAAWA,gCAAO;OAAxB,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;QAAM,WAAWA,gCAAO;QAAM,UAAA;OAAW,CAAA,GACzC,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;QAAM,WAAWA,gCAAO;QAAa,UAAA;OAA8C,CAAA,CAC/E;;MACL,SAAS,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;OAAM,WAAWA,gCAAO;OAAS,UAAA;MAAS,CAAA;MACpD,iBAAA,GAAA,kBAAA,IAAA,CAACC,sCAAAA,0BAAD,EAA0B,WAAW,KAAKD,gCAAO,SAAS,QAAQA,gCAAO,WAAW,EAAI,CAAA;KAClF;IACP,CAAA,GAAA,QACG,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;KAAK,WAAWA,gCAAO;KAAvB,UAAA;MACG,WAAW,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;OAAM,WAAWA,gCAAO;OAAS,UAAA;MAAa,CAAA;MAE1D,iBAAA,GAAA,kBAAA,KAAA,CAAC,oBAAD;OACE,OAAM;OACN,MAAM,eAAe,oBAAoB,QAAQ,KAAA;OACjD,MAAM;OACN,gBAAgB,cAAc,CAAC,UAAU;OAJ3C,UAAA;QAME,iBAAA,GAAA,kBAAA,KAAA,CAAC,SAAD;SAAO,WAAWA,gCAAO;SAAzB,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,SAAD;UACE,MAAK;UACL,SAAS,OAAO;UACd,UAAU,CAAC,OAAO;UACpB,gBAAgB,OAAO,cAAc;SACtC,CAAA,GACD,iBAAA,GAAA,kBAAA,KAAA,CAAC,QAAD;UAAM,WAAWA,gCAAO;UAAxB,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;WAAM,WAAWA,gCAAO;WAAS,UAAA;UAAuB,CAAA,GACxD,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;WAAM,WAAWA,gCAAO;WAAS,UAAA;UAA8C,CAAA,CAC3E;SACD,CAAA,CAAA;;QACP,iBAAA,GAAA,kBAAA,IAAA,CAAC,YAAD;SACE,WAAWA,gCAAO;SAClB,cAAW;SACX,OAAO,OAAO;SACd,WAAW,MAAM,MAAM,EAAE,YAAY,EAAE,OAAO,MAAM,CAAC;SACrD,YAAY;QACb,CAAA;QACD,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;SAAK,WAAWA,gCAAO;SAAvB,UAAA;UACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;WAAQ,MAAK;WAAS,WAAWA,gCAAO;WAAS,UAAU,gBAAgB,gBAAgB,CAAC;WAAa,SAAS;WAAa,UAAA,eAAe,SAAS;UAAa,CAAA;UACpK,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;WAAQ,MAAK;WAAS,WAAWA,gCAAO;WAAW,UAAU,CAAC;WAAa,SAAS;WAAe,UAAA;UAAY,CAAA;UAC/G,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;WAAQ,MAAK;WAAS,WAAWA,gCAAO;WAAW,UAAU,gBAAgB,gBAAgB,mBAAmB;WAAiB,eAAe,KAAK,gBAAgB,QAAQ;WAAI,UAAA,kBAAkB,SAAS;UAAe,CAAA;UAC3N,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;WAAQ,MAAK;WAAS,WAAWA,gCAAO;WAAW,SAAS;WAAU,UAAA;UAAU,CAAA;SAC7E;;OACa;;MAEpB,iBAAA,GAAA,kBAAA,KAAA,CAAC,oBAAD;OACE,OAAM;OACN,MAAM,eAAe,oBAAoB,QAAQ,KAAA;OACjD,MAAM;OACN,gBAAgB,cAAc,CAAC,UAAU;OAJ3C,UAAA;QAME,iBAAA,GAAA,kBAAA,KAAA,CAAC,SAAD;SAAO,WAAWA,gCAAO;SAAzB,UAAA,CACI,iBAAA,GAAA,kBAAA,IAAA,CAAC,SAAD;UACE,MAAK;UACL,SAAS,OAAO;UAChB,UAAU,CAAC,OAAO;UAClB,gBAAgB,OAAO,oBAAoB;SAC5C,CAAA,GACD,iBAAA,GAAA,kBAAA,KAAA,CAAC,QAAD;UAAM,WAAWA,gCAAO;UAAxB,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;WAAM,WAAWA,gCAAO;WAAS,UAAA;UAAkB,CAAA,GACnD,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;WAAM,WAAWA,gCAAO;WAAS,UAAA;UAAkD,CAAA,CAC/E;SACD,CAAA,CAAA;;QACP,iBAAA,GAAA,kBAAA,KAAA,CAAC,SAAD;SAAO,WAAWA,gCAAO;SAAzB,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,SAAD;UACE,MAAK;UACL,SAAS,OAAO;UAChB,gBAAgB,OAAO,aAAa;SACrC,CAAA,GACD,iBAAA,GAAA,kBAAA,KAAA,CAAC,QAAD;UAAM,WAAWA,gCAAO;UAAxB,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;WAAM,WAAWA,gCAAO;WAAS,UAAA;UAAyB,CAAA,GAC1D,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;WAAM,WAAWA,gCAAO;WAAS,UAAA;UAA2C,CAAA,CACxE;SACD,CAAA,CAAA;;QACT,iBAAA,GAAA,kBAAA,IAAA,CAAC,YAAD;SACE,WAAWA,gCAAO;SAClB,cAAW;SACX,OAAO,OAAO;SACd,WAAW,MAAM,MAAM,EAAE,YAAY,EAAE,OAAO,MAAM,CAAC;SACrD,YAAY;QACb,CAAA;QACD,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;SAAK,WAAWA,gCAAO;SAAvB,UAAA;UACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;WAAQ,MAAK;WAAS,WAAWA,gCAAO;WAAS,UAAU,gBAAgB,gBAAgB,CAAC;WAAa,SAAS;WAAa,UAAA,eAAe,SAAS;UAAa,CAAA;UACpK,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;WAAQ,MAAK;WAAS,WAAWA,gCAAO;WAAW,UAAU,CAAC;WAAa,SAAS;WAAe,UAAA;UAAY,CAAA;UAC/G,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;WAAQ,MAAK;WAAS,WAAWA,gCAAO;WAAW,UAAU,gBAAgB,gBAAgB,mBAAmB;WAAiB,eAAe,KAAK,gBAAgB,QAAQ;WAAI,UAAA,kBAAkB,SAAS;UAAe,CAAA;UAC3N,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;WAAQ,MAAK;WAAS,WAAWA,gCAAO;WAAW,SAAS;WAAY,UAAA;UAAU,CAAA;SAC/E;;OACa;;MAEpB,iBAAA,GAAA,kBAAA,IAAA,CAAC,oBAAD;OACE,OAAM;OACN,MAAM,GAAG,OAAO,aAAa,OAAO,IAAI,oBAAoB,WAAW;OACvE,MAAM;OACN,gBAAgB,cAAc,CAAC,UAAU;OAExC,UAAA,OAAO,aAAa,WAAW,IAC9B,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;QAAM,WAAWA,gCAAO;QAAU,UAAA;OAAoB,CAAA,IACpD,OAAO,aAAa,KAAK,UAC3B,iBAAA,GAAA,kBAAA,KAAA,CAAC,SAAD;QAA0B,WAAWA,gCAAO;QAA5C,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,SAAD;SACE,MAAK;SACL,SAAS,aAAa,MAAM,MAAM;SAClC,gBAAgB,YAAY,MAAM,MAAM;QACzC,CAAA,GACD,iBAAA,GAAA,kBAAA,KAAA,CAAC,QAAD;SAAM,WAAWA,gCAAO;SAAxB,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;UAAM,WAAWA,gCAAO;UAAU,UAAA,MAAM,QAAQ,MAAM;SAAa,CAAA,GACnE,iBAAA,GAAA,kBAAA,KAAA,CAAC,QAAD;UAAM,WAAWA,gCAAO;UAAxB,UAAA;WAAiC;WAAQ,MAAM;WAAQ,MAAM,cAAc,MAAM,MAAM,gBAAgB;UAAS;SAC5G,CAAA,CAAA;QACD,CAAA,CAAA;OAVK,GAAA,MAAM,MAUX,CACR;MACiB,CAAA;MAEpB,iBAAA,GAAA,kBAAA,KAAA,CAAC,oBAAD;OACE,OAAM;OACN,MAAM,oBAAoB,QAAQ,KAAA;OAClC,MAAM;OACN,gBAAgB,cAAc,CAAC,UAAU;OAJ3C,UAAA;QAOE,iBAAA,GAAA,kBAAA,KAAA,CAAC,SAAD;SAAO,WAAWA,gCAAO;SAAzB,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,SAAD;UACE,MAAK;UACL,SAAS,OAAO;UAChB,gBAAgB,OAAO,aAAa;SACrC,CAAA,GACD,iBAAA,GAAA,kBAAA,KAAA,CAAC,QAAD;UAAM,WAAWA,gCAAO;UAAxB,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;WAAM,WAAWA,gCAAO;WAAS,UAAA;UAAY,CAAA,GAC7C,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;WAAM,WAAWA,gCAAO;WAAS,UAAA;UAAuF,CAAA,CACpH;SACD,CAAA,CAAA;;QAEP,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;SAAK,WAAWA,gCAAO;SAAvB,UAAA;UACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,SAAD;WACE,IAAG;WACH,MAAK;WACL,SAAS,OAAO,qBAAqB;WACrC,UAAU,CAAC,OAAO;WAClB,UAAU;UACX,CAAA;UACD,iBAAA,GAAA,kBAAA,KAAA,CAAC,SAAD;WAAO,SAAQ;WAAmC,WAAWA,gCAAO;WAApE,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;YAAM,WAAWA,gCAAO;YAAS,UAAA;WAAY,CAAA,GAC7C,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;YAAM,WAAWA,gCAAO;YAAS,UAAA;WAAuE,CAAA,CACnG;;UACP,iBAAA,GAAA,kBAAA,IAAA,CAAC,SAAD;WACE,WAAWA,gCAAO;WAClB,MAAK;WACL,KAAK;WACL,MAAM;WACN,OAAO;WACP,UAAU,CAAC,OAAO,eAAe,OAAO,uBAAuB;WAC/D,WAAW,MAAM,wBAAwB,EAAE,OAAO,KAAK;WACvD,QAAQ;UACT,CAAA;SACE;;QACL,iBAAA,GAAA,kBAAA,KAAA,CAAC,SAAD;SAAO,WAAWA,gCAAO;SAAzB,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,SAAD;UACE,MAAK;UACL,SAAS,OAAO;UAChB,UAAU,CAAC,OAAO,eAAe,CAAC;UAClC,gBAAgB,OAAO,eAAe;SACvC,CAAA,GACD,iBAAA,GAAA,kBAAA,KAAA,CAAC,QAAD;UAAM,WAAWA,gCAAO;UAAxB,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;WAAM,WAAWA,gCAAO;WAAS,UAAA;UAAoB,CAAA,GACrD,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;WAAM,WAAWA,gCAAO;WAAU,UAAA,oBAAoB,8HAA8H,wCAAwC,kBAAkB,KAAK,IAAI,KAAK,IAAI,GAAG,gBAAgB,YAAY,gBAAgB;UAAW,CAAA,CACtT;SACD,CAAA,CAAA;;QACP,iBAAA,GAAA,kBAAA,KAAA,CAAC,SAAD;SAAO,WAAWA,gCAAO;SAAzB,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,SAAD;UACE,MAAK;UACL,SAAS,OAAO;UAChB,UAAU,CAAC,OAAO;UAClB,gBAAgB,OAAO,iBAAiB;SACzC,CAAA,GACD,iBAAA,GAAA,kBAAA,KAAA,CAAC,QAAD;UAAM,WAAWA,gCAAO;UAAxB,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;WAAM,WAAWA,gCAAO;WAAS,UAAA;UAAY,CAAA,GAC7C,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;WAAM,WAAWA,gCAAO;WAAS,UAAA;UAAuD,CAAA,CACpF;SACD,CAAA,CAAA;;QACP,iBAAA,GAAA,kBAAA,KAAA,CAAC,SAAD;SAAO,WAAWA,gCAAO;SAAzB,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,SAAD;UACE,MAAK;UACL,SAAS,OAAO;UAChB,UAAU,CAAC,OAAO,eAAe,CAAC,OAAO;UACzC,gBAAgB,OAAO,cAAc;SACtC,CAAA,GACD,iBAAA,GAAA,kBAAA,KAAA,CAAC,QAAD;UAAM,WAAWA,gCAAO;UAAxB,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;WAAM,WAAWA,gCAAO;WAAS,UAAA;UAAiB,CAAA,GAClD,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;WAAM,WAAWA,gCAAO;WAAS,UAAA;UAA+C,CAAA,CAC5E;SACD,CAAA,CAAA;;QACP,iBAAA,GAAA,kBAAA,KAAA,CAAC,SAAD;SAAO,WAAW,KAAKA,gCAAO,WAAW,CAAC,OAAO,mBAAmB,CAAC,OAAO,iBAAiBA,gCAAO,WAAW;SAA/G,UAAA,CACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,QAAD;UAAM,WAAWA,gCAAO;UAAxB,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;WAAM,WAAWA,gCAAO;WAAS,UAAA;UAAiB,CAAA,GAClD,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;WAAM,WAAWA,gCAAO;WAAS,UAAA;UAAsC,CAAA,CACnE;SACN,CAAA,GAAA,iBAAA,GAAA,kBAAA,IAAA,CAAC,YAAD;UACE,WAAWA,gCAAO;UAClB,OAAO,OAAO;UACd,UAAU,CAAC,OAAO,eAAe,CAAC,OAAO,mBAAmB,CAAC,OAAO;UACpE,WAAW,MAAM,MAAM,EAAE,YAAY,EAAE,OAAO,MAAM,CAAC;UACrD,cAAc,gBAAgB;UAC9B,YAAY;SACb,CAAA,CACI;;QACP,iBAAA,GAAA,kBAAA,KAAA,CAAC,SAAD;SAAO,WAAWA,gCAAO;SAAzB,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,SAAD;UACE,MAAK;UACL,SAAS,OAAO;UAChB,UAAU,CAAC,OAAO,eAAe,CAAC,OAAO;UACzC,gBAAgB,OAAO,aAAa;SACrC,CAAA,GACD,iBAAA,GAAA,kBAAA,KAAA,CAAC,QAAD;UAAM,WAAWA,gCAAO;UAAxB,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;WAAM,WAAWA,gCAAO;WAAS,UAAA;UAAiB,CAAA,GAClD,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;WAAM,WAAWA,gCAAO;WAAS,UAAA;UAAyC,CAAA,CACtE;SACD,CAAA,CAAA;;QACP,iBAAA,GAAA,kBAAA,KAAA,CAAC,SAAD;SAAO,WAAW,KAAKA,gCAAO,WAAW,CAAC,OAAO,mBAAmB,CAAC,OAAO,gBAAgBA,gCAAO,WAAW;SAA9G,UAAA,CACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,QAAD;UAAM,WAAWA,gCAAO;UAAxB,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;WAAM,WAAWA,gCAAO;WAAS,UAAA;UAAiB,CAAA,GAClD,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;WAAM,WAAWA,gCAAO;WAAS,UAAA;UAAmC,CAAA,CAChE;SACN,CAAA,GAAA,iBAAA,GAAA,kBAAA,IAAA,CAAC,YAAD;UACE,WAAWA,gCAAO;UAClB,OAAO,OAAO;UACd,UAAU,CAAC,OAAO,eAAe,CAAC,OAAO,mBAAmB,CAAC,OAAO;UACpE,WAAW,MAAM,MAAM,EAAE,WAAW,EAAE,OAAO,MAAM,CAAC;UACpD,cAAc,gBAAgB;UAC9B,YAAY;SACb,CAAA,CACI;;OACW;;MAEnB,UAAU,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;OAAM,WAAW,KAAKA,gCAAO,QAAQ,eAAe,WAAWA,gCAAO,WAAW;OAAI,UAAA;MAAa,CAAA;KAC5G;IAEL,CAAA,CAAA;;EAER;;;EC1rBA,MAAa,SAAS,CAAC,SAAS,YAAY;EAE5C,SAAgB,MAAM,KAA0B;GAC9C,MAAM,aAAa,IAAI,IAAI,YAAY;GAEvC,MAAM,kBAAwC,EAC5C,KAAK,WAAW,IAClB;GAEA,IAAI,MAAM,OAAO,8BAA8B,IAAI,MAAM,SAAS;IAChE,MAAM;IACN,IAAI;IACJ,OAAO;IACP,QAAQ;GACV,GAAG,YAAY,CAAC;EAClB"}
package/lib/index.d.mts CHANGED
@@ -34,8 +34,8 @@ interface Config {
34
34
  subagentFlashProvider: string;
35
35
  /** 子代理 Flash 模型名(默认 deepseek-v4-flash)。 */
36
36
  subagentFlashModel: string;
37
- /** Windows custom-bash 可执行名或路径;默认 bash.exe(PATH 查找)。 */
38
- customBashPath: string;
37
+ /** 首轮输出封顶;0 或未设置 = 不设封顶(本项目默认),正整数 = 请求 #1 的 maxTokens。 */
38
+ bootstrapMaxTokens: number;
39
39
  /** 技能目录(默认包内 skills/,可指向本地测试目录)。 */
40
40
  skillsDir: string;
41
41
  /** 技能候选排序基数,技能目录内按下标递增。 */
@@ -67,6 +67,7 @@ interface PromptSettings {
67
67
  guideText: string;
68
68
  guideCustom: boolean;
69
69
  subagentFlash: boolean;
70
+ bootstrapMaxTokens: number;
70
71
  /** 运行时检测:是否注册了 DeepSeek 模型路由(不写入 settings)。 */
71
72
  deepseekAvailable: boolean;
72
73
  injectPrompt: boolean;
package/lib/index.mjs CHANGED
@@ -50,7 +50,7 @@ const Config = z.object({
50
50
  subagentFlash: z.boolean().default(false),
51
51
  subagentFlashProvider: z.string().default("deepseek-official"),
52
52
  subagentFlashModel: z.string().default("deepseek-v4-flash"),
53
- customBashPath: z.string().default("bash.exe"),
53
+ bootstrapMaxTokens: z.natural().default(0),
54
54
  skillsDir: z.string().default(DEFAULT_SKILLS_DIR),
55
55
  skillRankBase: z.natural().default(DEFAULT_SKILL_RANK_BASE),
56
56
  residentAgentsPath: z.string().default(DEFAULT_RESIDENT_AGENTS_PATH),
@@ -70,6 +70,7 @@ const PromptSettingsSchema = z.object({
70
70
  guideText: z.string().default(DEFAULT_GUIDE_TEXT),
71
71
  guideCustom: z.boolean().default(false),
72
72
  subagentFlash: z.boolean().default(false),
73
+ bootstrapMaxTokens: z.natural().default(0),
73
74
  deepseekAvailable: z.boolean().default(true),
74
75
  injectPrompt: z.boolean().default(true),
75
76
  skillSwitches: z.dict(z.boolean()).default({}),
@@ -309,6 +310,7 @@ function renderTuiStatus(source) {
309
310
  "锚点文本:",
310
311
  ` anchorText ${source.anchorText.length > 0 ? source.anchorText : "(空 = 按任务自动选择)"}`,
311
312
  ` deepseekAvailable ${source.deepseekAvailable ? "是" : "否(未检测到 DeepSeek 模型,subagentFlash 不可用)"}`,
313
+ ` bootstrapMaxTokens ${source.bootstrapMaxTokens > 0 ? source.bootstrapMaxTokens : "0(关闭,不设封顶)"}`,
312
314
  "技能开关:"
313
315
  ];
314
316
  for (const skill of source.skillCatalog) {
@@ -333,7 +335,7 @@ function registerTuiCommand(ctx, getSource, getDeepseekAvailable, getDeepseekSta
333
335
  handler: async (invocation) => {
334
336
  const usage = () => ({
335
337
  kind: "error",
336
- text: "用法:/prompt-tool status\n /prompt-tool on|off|toggle <writeAgents|writePreset|injectPrompt|injectAgentsPrompt|anchorFirstTurn|anchorCustom|guideCustom|subagentFlash>\n /prompt-tool skill <技能目录名> on|off|toggle"
338
+ text: "用法:/prompt-tool status\n /prompt-tool on|off|toggle <writeAgents|writePreset|injectPrompt|injectAgentsPrompt|anchorFirstTurn|anchorCustom|guideCustom|subagentFlash>\n /prompt-tool skill <技能目录名> on|off|toggle\n /prompt-tool bootstrapMaxTokens <正整数|0(关闭)>"
337
339
  });
338
340
  const tokens = invocation.rawInput.trim().split(/\s+/).filter((token) => token.length > 0);
339
341
  const source = getSource();
@@ -360,6 +362,25 @@ function registerTuiCommand(ctx, getSource, getDeepseekAvailable, getDeepseekSta
360
362
  kind: "success",
361
363
  text: `已把技能 ${folder} 设为 ${next ? "开" : "关"}
362
364
 
365
+ ${renderTuiStatus(getSource())}`
366
+ };
367
+ }
368
+ if (tokens[0] === "bootstrapMaxTokens") {
369
+ const raw = tokens[1];
370
+ const value = raw === void 0 ? NaN : Number(raw);
371
+ if (!Number.isSafeInteger(value) || value < 0) return {
372
+ kind: "error",
373
+ text: "bootstrapMaxTokens 需要非负整数:0 关闭封顶,正整数设置首轮 maxTokens。"
374
+ };
375
+ await sctx.settings.mutate(NS, [{
376
+ op: "set",
377
+ path: ["bootstrapMaxTokens"],
378
+ value
379
+ }]);
380
+ return {
381
+ kind: "success",
382
+ text: `已把 bootstrapMaxTokens 设为 ${value === 0 ? "关闭(不设封顶)" : String(value)}
383
+
363
384
  ${renderTuiStatus(getSource())}`
364
385
  };
365
386
  }
@@ -668,7 +689,7 @@ function writePreset(prompt, options) {
668
689
  subagentFlash: options.subagentFlash,
669
690
  subagentFlashProvider: options.subagentFlashProvider,
670
691
  subagentFlashModel: options.subagentFlashModel,
671
- bashPath: options.customBashPath
692
+ bootstrapMaxTokens: options.bootstrapMaxTokens
672
693
  }), "utf8");
673
694
  const meta = readFileSync(PRESET_TEMPLATE_META, "utf8").replace(/^order:.*$/m, `order: ${options.presetOrder}`);
674
695
  writeFileSync(join(presetDir, "preset.yml"), meta, "utf8");
@@ -763,7 +784,8 @@ function apply(ctx, config) {
763
784
  anchorCustom: config.anchorCustom,
764
785
  guideText: config.guideText,
765
786
  guideCustom: config.guideCustom,
766
- subagentFlash: config.subagentFlash && getDeepseekAvailable()
787
+ subagentFlash: config.subagentFlash && getDeepseekAvailable(),
788
+ bootstrapMaxTokens: config.bootstrapMaxTokens
767
789
  };
768
790
  installSubagentFlashRoute(ctx, () => runtime.subagentFlash, config.subagentFlashProvider, config.subagentFlashModel);
769
791
  let currentSource = () => ({
@@ -778,6 +800,7 @@ function apply(ctx, config) {
778
800
  guideText: runtime.guideText,
779
801
  guideCustom: runtime.guideCustom,
780
802
  subagentFlash: runtime.subagentFlash,
803
+ bootstrapMaxTokens: runtime.bootstrapMaxTokens,
781
804
  deepseekAvailable: getDeepseekAvailable(),
782
805
  injectPrompt: runtime.injectPrompt,
783
806
  skillSwitches: runtime.skillSwitches,
@@ -800,12 +823,13 @@ function apply(ctx, config) {
800
823
  anchorCustom: typeof next.anchorCustom === "boolean" ? next.anchorCustom : config.anchorCustom,
801
824
  guideText: typeof next.guideText === "string" ? next.guideText : config.guideText,
802
825
  guideCustom: typeof next.guideCustom === "boolean" ? next.guideCustom : config.guideCustom,
803
- subagentFlash: (typeof next.subagentFlash === "boolean" ? next.subagentFlash : config.subagentFlash) && getDeepseekAvailable()
826
+ subagentFlash: (typeof next.subagentFlash === "boolean" ? next.subagentFlash : config.subagentFlash) && getDeepseekAvailable(),
827
+ bootstrapMaxTokens: Number.isSafeInteger(next.bootstrapMaxTokens) && next.bootstrapMaxTokens >= 0 ? next.bootstrapMaxTokens : config.bootstrapMaxTokens
804
828
  };
805
829
  const promptChanged = next.promptText !== current;
806
830
  const agentsChanged = next.agentsText !== currentAgents;
807
831
  const skillSwitchesChanged = JSON.stringify(runtime.skillSwitches) !== JSON.stringify(nextRuntime.skillSwitches);
808
- const settingsChanged = runtime.writeAgents !== nextRuntime.writeAgents || runtime.writePreset !== nextRuntime.writePreset || runtime.injectAgentsPrompt !== nextRuntime.injectAgentsPrompt || runtime.injectPrompt !== nextRuntime.injectPrompt || skillSwitchesChanged || runtime.anchorFirstTurn !== nextRuntime.anchorFirstTurn || runtime.anchorText !== nextRuntime.anchorText || runtime.anchorCustom !== nextRuntime.anchorCustom || runtime.guideText !== nextRuntime.guideText || runtime.guideCustom !== nextRuntime.guideCustom || runtime.subagentFlash !== nextRuntime.subagentFlash;
832
+ const settingsChanged = runtime.writeAgents !== nextRuntime.writeAgents || runtime.writePreset !== nextRuntime.writePreset || runtime.injectAgentsPrompt !== nextRuntime.injectAgentsPrompt || runtime.injectPrompt !== nextRuntime.injectPrompt || skillSwitchesChanged || runtime.anchorFirstTurn !== nextRuntime.anchorFirstTurn || runtime.anchorText !== nextRuntime.anchorText || runtime.anchorCustom !== nextRuntime.anchorCustom || runtime.guideText !== nextRuntime.guideText || runtime.guideCustom !== nextRuntime.guideCustom || runtime.subagentFlash !== nextRuntime.subagentFlash || runtime.bootstrapMaxTokens !== nextRuntime.bootstrapMaxTokens;
809
833
  if (!needsInitialApply && !promptChanged && !agentsChanged && !settingsChanged) return;
810
834
  needsInitialApply = false;
811
835
  if (promptChanged) current = next.promptText;
@@ -821,6 +845,7 @@ function apply(ctx, config) {
821
845
  runtime.guideText = nextRuntime.guideText;
822
846
  runtime.guideCustom = nextRuntime.guideCustom;
823
847
  runtime.subagentFlash = nextRuntime.subagentFlash;
848
+ runtime.bootstrapMaxTokens = nextRuntime.bootstrapMaxTokens;
824
849
  skillSwitches = runtime.skillSwitches;
825
850
  if (skillSwitchesChanged) invalidateSkills?.();
826
851
  let residentAgentsWritten = false;
@@ -841,7 +866,7 @@ function apply(ctx, config) {
841
866
  subagentFlash: runtime.subagentFlash,
842
867
  subagentFlashProvider: config.subagentFlashProvider,
843
868
  subagentFlashModel: config.subagentFlashModel,
844
- customBashPath: config.customBashPath,
869
+ bootstrapMaxTokens: runtime.bootstrapMaxTokens,
845
870
  agentsInstructionText: runtime.injectAgentsPrompt && currentAgents.length > 0 ? currentAgents : void 0,
846
871
  presetDir: config.presetDir,
847
872
  presetOrder: config.presetOrder
@@ -867,6 +892,7 @@ function apply(ctx, config) {
867
892
  guideText: config.guideText,
868
893
  guideCustom: config.guideCustom,
869
894
  subagentFlash: config.subagentFlash && getDeepseekAvailable(),
895
+ bootstrapMaxTokens: config.bootstrapMaxTokens,
870
896
  deepseekAvailable: getDeepseekAvailable(),
871
897
  injectPrompt: config.injectPrompt,
872
898
  skillSwitches: { ...config.skillSwitches },
@@ -28,8 +28,8 @@ interface BuildCordisOptions {
28
28
  subagentFlashProvider?: string;
29
29
  /** 子代理 Flash 模型名(默认 deepseek-v4-flash)。 */
30
30
  subagentFlashModel?: string;
31
- /** Windows custom-bash 可执行名或路径;默认 bash.exe(PATH 查找)。 */
32
- bashPath?: string;
31
+ /** 首轮输出封顶(bootstrapMaxTokens);0 或未设置 = 本项目默认无封顶。 */
32
+ bootstrapMaxTokens?: number;
33
33
  }
34
34
  declare function buildCordis(prompt: string, options?: BuildCordisOptions): string;
35
35
  //#endregion
@@ -86,11 +86,16 @@ function normalizeContextGate(source) {
86
86
  if (!source.includes(block)) throw new Error("vendor agent.cordis.yml missing context-gate row");
87
87
  return source.replace(block, replaced);
88
88
  }
89
- /** 上游可能携带作者机器固定路径;生成时统一改写为可配置 bashPath。 */
90
- function normalizeBashPath(source, bashPath) {
91
- const marker = /bashPath:\s*'[^']*'/.exec(source);
92
- if (marker === null) throw new Error("vendor agent.cordis.yml missing bashPath row");
93
- return source.replace(marker[0], `bashPath: '${bashPath}'`);
89
+ /** 按开关向上游 tool-bootstrap 行注入 bootstrapMaxTokens;0/未设置保持无封顶。 */
90
+ function applyBootstrapMaxTokens(source, value) {
91
+ if (value === void 0 || !Number.isSafeInteger(value) || value <= 0) return source;
92
+ const rowStart = source.indexOf("- id: tool-bootstrap\n name: ./tool-bootstrap.mjs\n config:\n");
93
+ if (rowStart < 0) throw new Error("vendor agent.cordis.yml missing tool-bootstrap config block");
94
+ const include = source.indexOf(" includeSubagents: true\n", rowStart);
95
+ if (include < 0) throw new Error("vendor agent.cordis.yml tool-bootstrap row missing includeSubagents");
96
+ const at = include + 26 + 1;
97
+ return source.slice(0, at) + ` bootstrapMaxTokens: ${value}
98
+ ` + source.slice(at);
94
99
  }
95
100
  /** 给 subagent/subagent_fork 行加 dsh-router-standard 的 Flash 子代理方案;关闭时保持继承主会话路由。 */
96
101
  function applySubagentFlash(source, enabled, provider, model) {
@@ -133,7 +138,7 @@ function buildCordis(prompt, options = {}) {
133
138
  const injectPrompt = options.injectPrompt !== false;
134
139
  const anchorText = typeof options.anchorText === "string" && options.anchorText.length > 0 ? options.anchorText : "";
135
140
  const indent = (s) => s.split(/\r?\n/).map((l) => l.length === 0 ? "" : " " + l).join("\n");
136
- const up = normalizeContextGate(normalizeBashPath(applySubagentFlash(readFileSync(VENDOR_CORDIS, "utf8").replace(/\r\n/g, "\n"), options.subagentFlash === true, typeof options.subagentFlashProvider === "string" && options.subagentFlashProvider.length > 0 ? options.subagentFlashProvider : "deepseek-official", typeof options.subagentFlashModel === "string" && options.subagentFlashModel.length > 0 ? options.subagentFlashModel : "deepseek-v4-flash"), typeof options.bashPath === "string" && options.bashPath.length > 0 ? options.bashPath : "bash.exe"));
141
+ const up = applyBootstrapMaxTokens(normalizeContextGate(applySubagentFlash(readFileSync(VENDOR_CORDIS, "utf8").replace(/\r\n/g, "\n"), options.subagentFlash === true, typeof options.subagentFlashProvider === "string" && options.subagentFlashProvider.length > 0 ? options.subagentFlashProvider : "deepseek-official", typeof options.subagentFlashModel === "string" && options.subagentFlashModel.length > 0 ? options.subagentFlashModel : "deepseek-v4-flash")), options.bootstrapMaxTokens);
137
142
  const bootstrap = /^-\s+id:\s+tool-bootstrap\s*$/m.exec(up);
138
143
  if (!bootstrap) throw new Error("vendor agent.cordis.yml missing tool-bootstrap row");
139
144
  let cursor = up.indexOf("\n", bootstrap.index);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-plugin-prompt-tool",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "DSH 插件:提示词工具,规范模型的思维链与回答内容,提供 Web UI 编辑 preset.md 与 AGENTS.md,并按 skills 目录注册可开关技能。",
5
5
  "license": "MIT",
6
6
  "repository": {
package/plan.md CHANGED
@@ -1,4 +1,8 @@
1
1
  # dsh-plugin-prompt-tool 重新设计计划(v2)
2
+ > **v2.15 上游同步(当前实现)**:同步上游到 `25f21ae`;移除
3
+ > `customBashPath` 生成时改写,直接沿用上游 custom-bash 运行时探测。
4
+ > 新增 `bootstrapMaxTokens` 开关(0=关闭;正整数写入上游 tool-bootstrap)。
5
+
2
6
  > **v2.14 上游同步与路径校对(当前实现)**:已从上游 main 同步到
3
7
  > `ba7a27c`;新增 context-gate.mjs;custom-bash 的固定路径在生成时归一化为
4
8
  > `customBashPath`(默认 bash.exe,PATH 查找)。
@@ -1 +1 @@
1
- ba7a27cf92f80ef3a73a16a05b9df46a20ebbb2a
1
+ 25f21aefaf8ddc414da54d2e581e43740d977c6e
@@ -149,12 +149,19 @@
149
149
  name: '@deepseek-ai/dsh-tool-pwsh'
150
150
  disabled: !!js process.platform !== 'win32'
151
151
 
152
- # The Minimal preset's shell: a PTY-backed persistent bash, byte-identical in
153
- # configuration to the official `minimal` preset's `persistent-shell` group so
154
- # the first request exposes exactly Minimal's real `bash` schema. The PTY
155
- # registry is an agent-owned service, so it lives in an entry-local realm; the
156
- # backend still consumes the host sandbox policy and subprocess implementation,
157
- # while the tool registers into this agent's scoped catalog.
152
+ # The Minimal preset's shell: a PTY-backed persistent bash, schema-identical
153
+ # to the official `minimal` preset's `persistent-shell` group so the first
154
+ # request exposes exactly Minimal's real `bash` schema. ONE deliberate config
155
+ # difference: an adaptive `shellPath` the terminal-bash plugin default
156
+ # `/bin/bash` where that absolute path exists (every host that ships it keeps
157
+ # the previous behavior), otherwise a bare `bash` resolved through the same
158
+ # scrubbed PATH the other harness tools use. The fallback exists because
159
+ # `/bin/bash` does not exist on NixOS and other hosts that keep bash
160
+ # elsewhere; there `execvp("/bin/bash")` exits during PTY startup ("PTY shell
161
+ # exited during startup") and every bash call fails. The PTY registry is an
162
+ # agent-owned service, so it lives in an entry-local realm; the backend still
163
+ # consumes the host sandbox policy and subprocess implementation, while the
164
+ # tool registers into this agent's scoped catalog.
158
165
  #
159
166
  # DISABLED ON WINDOWS: DSH's PTY backend is linux/darwin-only, so the
160
167
  # persistent shell cannot serve win32. The `custom-bash` row below registers
@@ -172,6 +179,7 @@
172
179
  - id: terminal-bash
173
180
  name: '@deepseek-ai/dsh-terminal-bash'
174
181
  config:
182
+ shellPath: !!js "process.getBuiltinModule?.('node:fs')?.existsSync('/bin/bash') ? '/bin/bash' : 'bash'"
175
183
  timeoutMs: 300000
176
184
 
177
185
  - id: persistent-bash
@@ -191,15 +199,18 @@
191
199
  # Windows-only `bash` tool (see custom-bash.mjs): registers the SAME
192
200
  # tool name as the persistent shell with a Minimal-compatible description, but
193
201
  # executes through the ordinary cross-platform subprocess seam (`bash -c`)
194
- # instead of a PTY. `bashPath` defaults to `bash` on PATH; point it at Git
195
- # Bash explicitly when the WSL shim would otherwise be picked up. No OS
202
+ # instead of a PTY. The shell is resolved WITHOUT a hardcoded install path
203
+ # (issue #24): `bashPath` unset probes the `git` executable's install root,
204
+ # then the well-known Git-for-Windows roots (Program Files(/x86), per-user
205
+ # LOCALAPPDATA, scoop's `current` junction), then plain `bash` on PATH — set
206
+ # `bashPath` explicitly only to pin a shell that probing cannot find. No OS
196
207
  # sandbox confinement on Windows (landlock is linux-only); the tool
197
- # description says so.
208
+ # description says so. When no bash exists at all the tool errors with
209
+ # guidance instead of switching shells — pwsh stays its own tool in the
210
+ # promoted catalog.
198
211
  - id: custom-bash
199
212
  name: ./custom-bash.mjs
200
213
  disabled: !!js process.platform !== 'win32'
201
- config:
202
- bashPath: 'C:\Program Files\Git\bin\bash.exe'
203
214
 
204
215
  # ── filesystem ──────────────────────────────────────────────────────────────
205
216
 
@@ -13,9 +13,27 @@
13
13
  * the ordinary (cross-platform) subprocess seam keeps the schema anchor
14
14
  * without the PTY dependency.
15
15
  *
16
- * Executable resolution (config `bashPath`):
17
- * - explicit absolute path (e.g. `C:\Program Files\Git\bin\bash.exe`), or
18
- * - `bash` resolved through `ctx.subprocess.resolveExecutable` (PATH lookup).
16
+ * Executable resolution (config `bashPath`, issue #24 — no hardcoded install
17
+ * path): an explicit non-empty `bashPath` wins unconditionally. Unset, the
18
+ * Git Bash executable is INFERRED, in probe order:
19
+ * 1. the `git` executable on PATH — its install root carries `bin\bash.exe`
20
+ * one level up from `cmd\`, beside `bin\`, or two levels up from
21
+ * `mingw64\bin\` (the standard installer, choco, and winget all resolve
22
+ * here; a scoop SHIM does not — its directory is the shims root, not the
23
+ * app — which is what step 2 covers);
24
+ * 2. the well-known Git-for-Windows roots derived from environment variables
25
+ * (`ProgramFiles`, `ProgramFiles(x86)`, per-user `LOCALAPPDATA\Programs
26
+ * \Git`, scoop's `~\scoop\apps\git\current` junction);
27
+ * 3. plain `bash` through `ctx.subprocess.resolveExecutable` (PATH lookup —
28
+ * last resort, since on Windows that may pick the WSL shim; WSL bash is
29
+ * still true bash, only the filesystem paths shift to /mnt/…).
30
+ *
31
+ * If NOTHING resolves, the tool fails with an actionable error naming the
32
+ * remedies — it does NOT silently execute under a different shell: the
33
+ * schema above promises `bash -c` semantics, and pwsh/cmd are different
34
+ * command languages. PowerShell stays available as its OWN tool (`pwsh`,
35
+ * present in the promoted catalog on Windows, unlockable via
36
+ * dev_tool_search).
19
37
  *
20
38
  * Semantics mirror the official bash tool: `bash -c <command>` in a fresh
21
39
  * process, bounded output, non-zero exit reported not thrown. No sandbox
@@ -24,6 +42,9 @@
24
42
  * `str_replace_editor` (Minimal's two tools).
25
43
  */
26
44
 
45
+ import { access } from 'node:fs/promises'
46
+ import { dirname, join } from 'node:path'
47
+
27
48
  /** Cordis plugin name used by loader diagnostics. */
28
49
  export const name = 'custom-bash'
29
50
 
@@ -33,6 +54,34 @@ export const inject = ['subprocess', 'tools']
33
54
  const DEFAULT_TIMEOUT_MS = 120000
34
55
  const DEFAULT_MAX_OUTPUT_BYTES = 64000
35
56
 
57
+ /**
58
+ * Git Bash candidate paths, in probe order (see the header): the `git`
59
+ * executable's install root first, then the well-known env-derived roots.
60
+ * Exported for tests; pure — existence probing happens at the call site.
61
+ */
62
+ export function bashCandidates(env, gitExe) {
63
+ const candidates = []
64
+ // git at <root>\cmd\git.exe (installer/scoop) or <root>\bin\git.exe →
65
+ // <root>\bin\bash.exe; <root>\mingw64\bin\git.exe (portable) → two up.
66
+ // A bare relative name means `git` did not actually resolve to a path.
67
+ if (typeof gitExe === 'string' && /[/\\]/.test(gitExe)) {
68
+ const dir = dirname(gitExe)
69
+ const root = dirname(dir)
70
+ candidates.push(
71
+ join(root, 'bin', 'bash.exe'),
72
+ join(dir, 'bash.exe'),
73
+ join(dirname(root), 'bin', 'bash.exe'),
74
+ )
75
+ }
76
+ if (env.ProgramFiles) candidates.push(join(env.ProgramFiles, 'Git', 'bin', 'bash.exe'))
77
+ if (env['ProgramFiles(x86)']) candidates.push(join(env['ProgramFiles(x86)'], 'Git', 'bin', 'bash.exe'))
78
+ if (env.LOCALAPPDATA) candidates.push(join(env.LOCALAPPDATA, 'Programs', 'Git', 'bin', 'bash.exe'))
79
+ if (env.USERPROFILE) candidates.push(join(env.USERPROFILE, 'scoop', 'apps', 'git', 'current', 'bin', 'bash.exe'))
80
+ // Layouts overlap (a `bin` git.exe derives the same bash twice) — probe
81
+ // order survives the dedupe, insertion order is preserved.
82
+ return [...new Set(candidates)]
83
+ }
84
+
36
85
  /** Tool parameter schema for the model-facing command. */
37
86
  const commandSchema = {
38
87
  type: 'object',
@@ -52,10 +101,54 @@ const commandSchema = {
52
101
 
53
102
  /** Register the model-facing `bash` tool. */
54
103
  export function apply(ctx, config) {
55
- const bashPath = typeof config?.bashPath === 'string' && config.bashPath.length > 0 ? config.bashPath : 'bash'
104
+ const explicitBashPath = typeof config?.bashPath === 'string' && config.bashPath.length > 0 ? config.bashPath : undefined
56
105
  const timeoutMs = Number.isSafeInteger(config?.timeoutMs) && config.timeoutMs > 0 ? config.timeoutMs : DEFAULT_TIMEOUT_MS
57
106
  const maxOutputBytes = Number.isSafeInteger(config?.maxOutputBytes) && config.maxOutputBytes > 0 ? config.maxOutputBytes : DEFAULT_MAX_OUTPUT_BYTES
58
107
 
108
+ // The inferred executable is memoized per plugin instance: candidate probing
109
+ // walks the filesystem, and the answer cannot change within a mount. A
110
+ // failed inference is NOT memoized — the plain `bash` fallback resolves
111
+ // fresh on every execute until some probe succeeds.
112
+ let inferredShell
113
+ const exists = (path) => access(path).then(() => true, () => false)
114
+ const resolveShell = async (signal) => {
115
+ if (explicitBashPath !== undefined) {
116
+ // A misconfigured explicit path must fail as itself, not as a
117
+ // discovery miss — the raw resolution error says which path failed.
118
+ return ctx.subprocess.resolveExecutable(explicitBashPath, undefined, signal)
119
+ }
120
+ if (inferredShell !== undefined) {
121
+ return ctx.subprocess.resolveExecutable(inferredShell, undefined, signal)
122
+ }
123
+ let gitExe
124
+ try {
125
+ gitExe = await ctx.subprocess.resolveExecutable('git', undefined, signal)
126
+ } catch {
127
+ // git unresolvable → the env-derived candidates below still apply
128
+ }
129
+ for (const candidate of bashCandidates(process.env, gitExe)) {
130
+ if (!(await exists(candidate))) continue
131
+ try {
132
+ inferredShell = await ctx.subprocess.resolveExecutable(candidate, undefined, signal)
133
+ return inferredShell
134
+ } catch {
135
+ // Exists but unresolvable (EPERM, a broken scoop junction): keep
136
+ // probing — one bad root must not block the rest of the chain, and
137
+ // nothing is memoized so later executes can still find a good one.
138
+ continue
139
+ }
140
+ }
141
+ try {
142
+ return await ctx.subprocess.resolveExecutable('bash', undefined, signal)
143
+ } catch (error) {
144
+ // Total discovery failure (no Git Bash root, no env root, no bash on
145
+ // PATH): name the remedies instead of leaking a raw ENOENT. Never
146
+ // fall back to pwsh/cmd here — the schema promises `bash -c`
147
+ // semantics; a different shell would silently break every command.
148
+ throw new Error(`bash executable not found — install Git for Windows, expose a bash on PATH, or set the custom-bash \`bashPath\` config (${String((error && error.message) || error)})`)
149
+ }
150
+ }
151
+
59
152
  ctx.tools.register({
60
153
  name: 'bash',
61
154
  description: [
@@ -81,7 +174,7 @@ export function apply(ctx, config) {
81
174
  render: (_args, value) => [{ type: 'text', text: value.text }],
82
175
  },
83
176
  async execute(args, exec) {
84
- const shell = await ctx.subprocess.resolveExecutable(bashPath, undefined, exec?.signal)
177
+ const shell = await resolveShell(exec?.signal)
85
178
  const workdir = typeof args.workdir === 'string' && args.workdir.length > 0
86
179
  ? args.workdir
87
180
  : exec?.agent?.session?.header?.cwd