dsh-router-core 0.4.7 → 0.4.8

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.
Files changed (53) hide show
  1. package/README.md +9 -69
  2. package/cordis.patch.yml +2 -7
  3. package/docs/ext.md +3 -17
  4. package/lib/client-registry.js +50 -565
  5. package/lib/client.js +50 -565
  6. package/lib/ext-prompt/index.js +20 -15
  7. package/lib/index.js +0 -419
  8. package/lib/types/ext/contract.d.ts +1 -1
  9. package/lib/types/ext-prompt/content.d.ts +11 -0
  10. package/lib/types/ext-prompt/index.d.ts +7 -9
  11. package/lib/types/ext-prompt/mount.d.ts +2 -2
  12. package/lib/types/ext-prompt/plugin.d.ts +3 -5
  13. package/lib/types/suppliers/registry.d.ts +0 -1
  14. package/package.json +1 -10
  15. package/src/client/ExtDetail.tsx +1 -13
  16. package/src/client/group-controls.test.ts +1 -1
  17. package/src/client/group-controls.ts +1 -1
  18. package/src/client/index.tsx +0 -17
  19. package/src/client/router-components.test.ts +1 -1
  20. package/src/client/router.css +3 -354
  21. package/src/ext/contract.ts +1 -1
  22. package/src/ext-prompt/content.ts +17 -12
  23. package/src/ext-prompt/ext-prompt.test.ts +3 -3
  24. package/src/ext-prompt/index.ts +7 -9
  25. package/src/ext-prompt/mount.ts +2 -2
  26. package/src/ext-prompt/plugin.ts +3 -5
  27. package/src/router/index.ts +2 -3
  28. package/src/router/models-cache.test.ts +5 -6
  29. package/src/router/supplier-switch.test.ts +1 -1
  30. package/src/suppliers/registry.ts +0 -71
  31. package/lib/contract-D4BWZ7Xe.js +0 -13
  32. package/lib/ext-test/index.js +0 -36
  33. package/lib/ext-test/locale/en.json +0 -6
  34. package/lib/ext-test/locale/zh.json +0 -6
  35. package/lib/ext-test/package.json +0 -5
  36. package/lib/types/ext-test/index.d.ts +0 -21
  37. package/lib/types/ext-test/plugin.d.ts +0 -23
  38. package/lib/types/suppliers/probe.d.ts +0 -160
  39. package/src/client/ExtTestPanel.tsx +0 -392
  40. package/src/client/comp-button-scope.test.ts +0 -58
  41. package/src/client/comp-panel-layout.test.ts +0 -58
  42. package/src/client/ext-panels.test.ts +0 -44
  43. package/src/client/ext-panels.ts +0 -49
  44. package/src/client/model-choice.test.ts +0 -41
  45. package/src/client/model-choice.ts +0 -37
  46. package/src/ext-test/ext-test.test.ts +0 -60
  47. package/src/ext-test/index.ts +0 -35
  48. package/src/ext-test/locale/en.json +0 -6
  49. package/src/ext-test/locale/zh.json +0 -6
  50. package/src/ext-test/plugin.ts +0 -34
  51. package/src/ext-test/row.json +0 -5
  52. package/src/suppliers/probe.test.ts +0 -266
  53. package/src/suppliers/probe.ts +0 -475
@@ -13,13 +13,11 @@
13
13
  *
14
14
  * 列表页只列**已启用**的扩展(关闭的连卡片都不出现),所以这里也不必再显示
15
15
  * 「已启用/未启用」——那行字只能是常量。布局同供应商详情:返回链接 + 图标 +
16
- * 名称/副行,下面跟扩展说明卡与未就绪横幅。有 `api()` 支持的自定义扩展可在未来
17
- * 按需加独立面板,此处不内置任何具体扩展的专属视图。
16
+ * 名称/副行,下面跟扩展说明卡与未就绪横幅。
18
17
  */
19
18
  import { Fragment, useEffect, useState } from 'react'
20
19
  import { Switch } from '@deepseek-ai/dsh-client-ui-primitives'
21
20
  import { ROUTER_API_BASE, type ExtControlItem, type RouterExtItem, type RouterExtResponse } from '../shared.ts'
22
- import { extPanel } from './ext-panels.ts'
23
21
  import { groupControlRows } from './group-controls.ts'
24
22
  import { Modal } from './Modal.tsx'
25
23
 
@@ -599,10 +597,6 @@ interface ExtDetailProps {
599
597
  }
600
598
 
601
599
  export function ExtDetail({ item, onBack }: ExtDetailProps): JSX.Element {
602
- // 扩展**自带**详情面板时用它(插件自检就是这么做的:选供应商/模型/连接 + 跑测试)。
603
- // 没有就退回下面这张通用只读页 —— 保留返回链接与标题,导航形状与有没有自定义
604
- // 面板无关,用户点进去的预期是稳定的。
605
- const Custom = extPanel(item.id)
606
600
  return (
607
601
  <div className="dshr-tabBody">
608
602
  {/* Header(同供应商详情:返回链接 + 图标 + 名称/副行) */}
@@ -635,13 +629,9 @@ export function ExtDetail({ item, onBack }: ExtDetailProps): JSX.Element {
635
629
  </div>
636
630
  </div>
637
631
 
638
- {/* 扩展自带的面板优先(注册表命中时下面几块都不渲染) */}
639
- {Custom !== undefined ? <Custom /> : (
640
- <>
641
632
  {/* 子开关(扩展自己的行为细节;没有 controls 就不渲染这一块) */}
642
633
  {item.controls !== undefined && item.controls.length > 0 && <ExtControls item={item} />}
643
634
 
644
- {/* 实际生效的提示词(只读预览;回答「关掉的真的没进去吗」) */}
645
635
  {/* 未就绪警示(同供应商详情的出错横幅) */}
646
636
  {item.ready === false && (
647
637
  <div className="dshr-alert">
@@ -649,8 +639,6 @@ export function ExtDetail({ item, onBack }: ExtDetailProps): JSX.Element {
649
639
  <span>{item.detail !== undefined && item.detail !== '' ? item.detail : '当前不可用'}</span>
650
640
  </div>
651
641
  )}
652
- </>
653
- )}
654
642
  </div>
655
643
  )
656
644
  }
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * 详情页 controls 分组的判据(`group-controls.ts`)。
3
3
  *
4
- * 关键是**向后兼容不可退**:没用 `group` 字段的扩展(ext-test 等)必须
4
+ * 关键是**向后兼容不可退**:没用 `group` 字段的扩展必须
5
5
  * 拿到"一个无组节、顺序不变"的结果 —— 分组是新能力的**叠加**,不是重排。
6
6
  */
7
7
  import test from 'node:test'
@@ -5,7 +5,7 @@
5
5
  * 准则分类 + 宿主系统提示词段落),平铺成一长列无从分辨哪条是谁的。
6
6
  *
7
7
  * 两条规则:
8
- * ① **无组节恒排最前** —— 没用 `group` 字段的扩展(ext-test 等)拿到的
8
+ * ① **无组节恒排最前** —— 没用 `group` 字段的扩展拿到的
9
9
  * 分组结果就是原来的平铺列表(一个无名节),面板行为一个像素不变;
10
10
  * ② 组的先后 = 组名在 `rows` 里的**首次出现序**,组内保持原顺序 ——
11
11
  * 不按字母重排:`rows` 的顺序来自服务端(段落按 prompt 序),重排
@@ -11,9 +11,6 @@
11
11
  import { mountRouterWorkspace } from './workspace-mount.tsx'
12
12
  import { RouterSettingsSection } from './settings-section.tsx'
13
13
  import { RouterComponentsSection } from './RouterComponentsSection.tsx'
14
- import { ExtTestPanel } from './ExtTestPanel.tsx'
15
- import { registerExtPanel } from './ext-panels.ts'
16
- import { EXT_TEST_ID } from '../ext-test/plugin.ts'
17
14
  import { registerSettingsNavIcon } from './settings-nav-icon.ts'
18
15
  import { rewriteRouterModelHint } from './model-hint-copy.ts'
19
16
  import { LastHitDock } from './LastHitDock.tsx'
@@ -97,19 +94,6 @@ function mountComponentsSection(ctx: Ctx): (() => void) | undefined {
97
94
  }, RouterComponentsSection))
98
95
  }
99
96
 
100
- /**
101
- * 给「插件自检」扩展登记它的详情面板。
102
- *
103
- * 面板的**唯一入口**是设置 → 路由 → 扩展 → 点开那张卡片(`ExtDetail` 查注册表拿到
104
- * 本组件)。**刻意不给插件页那一行再挂一个详情页**:那一行的宿主行开关已经能开/关
105
- * 它了,再点进去看同一块面板就是同一个东西有两个入口 —— 与「内置扩展不在自绘节里
106
- * 重复列」是同一条理由。
107
- */
108
- function registerExtTestPanel(ctx: Ctx): void {
109
- const dispose = registerExtPanel(EXT_TEST_ID, ExtTestPanel)
110
- ctx.effect(() => dispose, 'dsh-router: ext test panel registry')
111
- }
112
-
113
97
  export function apply(ctx: Ctx): void {
114
98
  // 「最近命中」徽章:走官方 conversation.composer.dock(scope: session → 组件
115
99
  // props 注入 sessionId,按当前会话取命中)。0.2.0 起这个座位无条件可用 ——
@@ -128,7 +112,6 @@ export function apply(ctx: Ctx): void {
128
112
  if (disposeComponents !== undefined) {
129
113
  ctx.effect(() => disposeComponents, 'dsh-router: plugin page components')
130
114
  }
131
- registerExtTestPanel(ctx)
132
115
  // 换掉宿主的默认齿轮(契约没有 icon 字段,只能注册后认领自己的行)
133
116
  ctx.effect(() => registerSettingsNavIcon(SECTION_LABEL), 'dsh-router: settings nav icon')
134
117
  // 设置-模型 里 Router 卡片的那句死路提示 → 正确说法(同样只能认领后就地改写)
@@ -105,7 +105,7 @@ test('内置扩展不进这一节(它已经是原生「包含的组件」里
105
105
  const out = groupRouterComponents({ ok: true }, {
106
106
  ok: true,
107
107
  enhancers: [
108
- { id: 'test', name: '插件自检', source: 'builtin' },
108
+ { id: 'prompt', name: '分层提示词', source: 'builtin' },
109
109
  { id: 'rtk', name: 'RTK' },
110
110
  ],
111
111
  })
@@ -3456,9 +3456,9 @@ html[data-dsh-router-active]:not([data-dsh-taskboard-active]):not(
3456
3456
  }
3457
3457
 
3458
3458
  /* 标题 + 「修改」按钮的**同一行**(`ExtDetail` 的子开关区)。
3459
- ⚠️ 单独一个类而不改 `.dshr-compRowName`:那个类 `RouterComponentsSection` /
3460
- `ExtTestPanel` 也在用,给它加 `display:flex` 会把**那些页面的标题**也变成
3461
- flex 盒子(它们标题后面没有按钮,平白多一层)。
3459
+ ⚠️ 单独一个类而不改 `.dshr-compRowName`:那个类 `RouterComponentsSection`
3460
+ 也在用,给它加 `display:flex` 会把**那一页的标题**也变成
3461
+ flex 盒子(那边标题后面没有按钮,平白多一层)。
3462
3462
  没有这层时按钮会掉到**下一行**:`.dshr-compRowMain` 是 column,而
3463
3463
  `.dshr-compRowName` 是行内元素、标题一长就折行(2026-09-29 用户报)。 */
3464
3464
  /* 子开关区:`.dshr-compGroup` 的 `gap: 12px` 覆盖为 0(2026-09-30 用户要求)。
@@ -3512,354 +3512,3 @@ html[data-dsh-router-active]:not([data-dsh-taskboard-active]):not(
3512
3512
  缩进那种「引用块」的额外装饰(用户要求),层级只靠颜色就够。 */
3513
3513
  white-space: pre-wrap;
3514
3514
  }
3515
-
3516
- /* ============================================================
3517
- dsh-router — 插件自检面板(设置 → 路由 → 扩展 → 插件自检)
3518
- ------------------------------------------------------------
3519
- 这一页渲染在**宿主的插件页**里,DOM 祖先既不是 .dshr-settings 也不是
3520
- .dshr-shell,所以文件顶部那套 `--rs-*` 主题变量在这里**不存在**(未定义的
3521
- var() 会让整条声明失效:边框/文字色退成继承值)。
3522
-
3523
- 所以这里**不用**设置页那套组件样式,直接按宿主页面的视觉语言写,用宿主的
3524
- `--dsw-alias-*` token:几何对齐它(14px 小标题、12px 次要文字、0.5px 描边、
3525
- 12px 圆角),配色也从同一套 token 取 —— 于是这块面板看起来就是原生的一部分。
3526
- 主按钮不用在这里画:交给宿主的 Button 原语(见 env.d.ts),配色由原语自己的
3527
- token 家族决定,不必猜「主色底上配什么字色」。
3528
- ============================================================ */
3529
-
3530
- .dshr-comp {
3531
- gap: 16px;
3532
- }
3533
-
3534
- .dshr-compIntro {
3535
- margin: 0;
3536
- color: var(--dsw-alias-label-secondary);
3537
- font-size: 13px;
3538
- line-height: 20px;
3539
- }
3540
-
3541
- /* 三栏收进同一块面板,读起来是「一组控件」而不是三个飘着的输入框。
3542
- 窄了换行(详情区宽度随窗口变,写死三列会在窄窗口挤成一团)。 */
3543
- .dshr-compForm {
3544
- display: grid;
3545
- grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
3546
- gap: 14px 16px;
3547
- padding: 14px 16px;
3548
- border: 0.5px solid var(--dsw-alias-border-l1);
3549
- border-radius: 12px;
3550
- background: var(--dsw-alias-bg-layer-1);
3551
- }
3552
-
3553
- .dshr-compField {
3554
- display: flex;
3555
- flex-direction: column;
3556
- gap: 6px;
3557
- min-width: 0;
3558
- }
3559
-
3560
- .dshr-compFieldHead {
3561
- display: flex;
3562
- align-items: baseline;
3563
- justify-content: space-between;
3564
- gap: 8px;
3565
- }
3566
-
3567
- .dshr-compFieldLabel {
3568
- color: var(--dsw-alias-label-primary);
3569
- font-size: 12px;
3570
- font-weight: 500;
3571
- line-height: 18px;
3572
- }
3573
-
3574
- /* 标题右侧的用法要点。语义放这里比塞进顶部那段长文字更容易被读到 ——
3575
- 「模型只列可用的」「连接留空 = 池内任选」都是用这一栏时才关心的。 */
3576
- .dshr-compFieldNote {
3577
- color: var(--dsw-alias-label-tertiary);
3578
- font-size: 12px;
3579
- line-height: 18px;
3580
- text-align: right;
3581
- }
3582
-
3583
- /* 36px / 12px 圆角,与宿主 Button 原语的 md 尺寸对齐。 */
3584
- .dshr-compSelect {
3585
- width: 100%;
3586
- height: 36px;
3587
- padding: 0 32px 0 11px;
3588
- appearance: none;
3589
- border: 0.5px solid var(--dsw-alias-border-l2);
3590
- border-radius: 12px;
3591
- background-color: var(--dsw-alias-bg-layer-2);
3592
- background-image:
3593
- linear-gradient(45deg, transparent 50%, var(--dsw-alias-label-tertiary) 50%),
3594
- linear-gradient(135deg, var(--dsw-alias-label-tertiary) 50%, transparent 50%);
3595
- background-position:
3596
- calc(100% - 17px) 16px,
3597
- calc(100% - 12px) 16px;
3598
- background-size: 5px 5px;
3599
- background-repeat: no-repeat;
3600
- color: var(--dsw-alias-label-primary);
3601
- font: inherit;
3602
- font-size: 13px;
3603
- line-height: 18px;
3604
- text-overflow: ellipsis;
3605
- cursor: pointer;
3606
- }
3607
-
3608
- .dshr-compSelect:hover:not(:disabled) {
3609
- border-color: var(--dsw-alias-border-l3);
3610
- }
3611
-
3612
- .dshr-compSelect:focus {
3613
- outline: none;
3614
- border-color: var(--dsw-alias-brand-primary);
3615
- box-shadow: 0 0 0 3px color-mix(in srgb, var(--dsw-alias-brand-primary) 22%, transparent);
3616
- }
3617
-
3618
- .dshr-compSelect:disabled {
3619
- border-color: var(--dsw-alias-border-l1);
3620
- background-color: var(--dsw-alias-bg-layer-1);
3621
- color: var(--dsw-alias-label-tertiary);
3622
- cursor: not-allowed;
3623
- }
3624
-
3625
- /* 动作行:把按钮与它旁边的提示排在一条基线上。两个动作行(访问测试 / 契约体检)
3626
- 共用 `dshr-compBtnRow` —— 那条抬特异性的字色规则就挂在它上面(见下)。 */
3627
- .dshr-compActions {
3628
- display: flex;
3629
- align-items: center;
3630
- gap: 12px;
3631
- }
3632
-
3633
- .dshr-compHint {
3634
- color: var(--dsw-alias-label-tertiary);
3635
- font-size: 12px;
3636
- line-height: 18px;
3637
- }
3638
-
3639
- /* 结果区:成功/失败/请求失败 三种语气。底下垫一层极淡的状态色,比只有一行
3640
- 彩色文字更容易扫到 —— 自检的结果是这一页唯一重要的信息。 */
3641
- .dshr-compNotice {
3642
- display: flex;
3643
- gap: 10px;
3644
- padding: 12px 14px;
3645
- border: 0.5px solid var(--dsw-alias-border-l1);
3646
- border-radius: 12px;
3647
- background: var(--dsw-alias-bg-layer-1);
3648
- }
3649
-
3650
- .dshr-compNotice[data-tone="ok"] {
3651
- border-color: color-mix(in srgb, var(--dsw-alias-state-success-primary) 34%, transparent);
3652
- background: color-mix(in srgb, var(--dsw-alias-state-success-primary) 8%, transparent);
3653
- }
3654
-
3655
- .dshr-compNotice[data-tone="fail"],
3656
- .dshr-compNotice[data-tone="network"] {
3657
- border-color: color-mix(in srgb, var(--dsw-alias-state-error-primary) 34%, transparent);
3658
- background: color-mix(in srgb, var(--dsw-alias-state-error-primary) 8%, transparent);
3659
- }
3660
-
3661
- .dshr-compNoticeIcon {
3662
- flex: none;
3663
- width: 16px;
3664
- height: 16px;
3665
- margin-top: 2px;
3666
- color: var(--dsw-alias-state-success-secondary);
3667
- }
3668
-
3669
- .dshr-compNotice[data-tone="fail"] .dshr-compNoticeIcon,
3670
- .dshr-compNotice[data-tone="network"] .dshr-compNoticeIcon {
3671
- color: var(--dsw-alias-state-error-primary);
3672
- }
3673
-
3674
- .dshr-compNoticeBody {
3675
- display: flex;
3676
- flex-direction: column;
3677
- gap: 2px;
3678
- min-width: 0;
3679
- }
3680
-
3681
- .dshr-compNoticeTitle {
3682
- color: var(--dsw-alias-label-primary);
3683
- font-size: 13px;
3684
- font-weight: 500;
3685
- line-height: 20px;
3686
- }
3687
-
3688
- .dshr-compNoticeText {
3689
- color: var(--dsw-alias-label-secondary);
3690
- font-size: 12.5px;
3691
- line-height: 19px;
3692
- /* 上游可能回一整段 HTML/JSON,限定宽度换行,别把布局撑破 */
3693
- overflow-wrap: anywhere;
3694
- }
3695
-
3696
- /* 回显测的是哪三样。一条「通过」如果不带对象,就是没法归因的一串绿字。 */
3697
- .dshr-compNoticeMeta {
3698
- color: var(--dsw-alias-label-tertiary);
3699
- font-size: 12px;
3700
- line-height: 18px;
3701
- overflow-wrap: anywhere;
3702
- }
3703
-
3704
- /* 宿主 Button 原语的 primary 想把字色设成 --dsw-alias-label-primary-foreground,
3705
- 但上面那条兜底 `.dshr-settings button { color: inherit; font: inherit }`
3706
- 特异性 (1,1) **高于**原语的 `.primary` (1,0) —— 赢的是它。于是按钮变成
3707
- 「#0f1115 的底 + 继承来的 #0f1115 的字」,黑底黑字、按钮看着是空的。
3708
-
3709
- 按本文件既有的抬特异性办法修(见文件里 .dshr-tab-on / .dshr-period 那几处注释:
3710
- 凡是受这条兜底影响的规则,都必须带挂载容器抬到 (2,0) 以上)。这里
3711
- `.dshr-comp .dshr-compBtnRow button` 是 (2,1),不依赖源码顺序,以后有人挪块也不会翻车。
3712
-
3713
- 作用域挂在**动作行的共用类 `dshr-compBtnRow`** 上,不是某个具体容器:上一版这条
3714
- 只写了 `.dshr-compActions`,随后新增的「契约体检」按钮在另一个容器里,于是又变回
3715
- 黑底黑字。现在两个动作行共用这个类,再加按钮只需沿用它。
3716
-
3717
- 修的是**把原语的意图还回去**,不是自己另挑一个颜色:字色仍由原语那个 token 决定。 */
3718
- .dshr-comp .dshr-compBtnRow button {
3719
- color: var(--dsw-alias-label-primary-foreground);
3720
- font-size: 14px;
3721
- line-height: 22px;
3722
- }
3723
-
3724
- /* --- 契约体检区(插件自检面板的第二块) ---
3725
- 状态靠 data-state 上色,和「结果卡」那套语气一致:过了是绿、失败是红、
3726
- 没实现是灰、不执行是中性(它是**故意**不跑的,不是问题)。 */
3727
-
3728
- .dshr-compSection {
3729
- display: flex;
3730
- flex-direction: column;
3731
- gap: 12px;
3732
- margin-top: 4px;
3733
- padding-top: 16px;
3734
- border-top: 0.5px solid var(--dsw-alias-border-l1);
3735
- }
3736
-
3737
- .dshr-compHead {
3738
- display: flex;
3739
- align-items: baseline;
3740
- justify-content: space-between;
3741
- gap: 8px;
3742
- }
3743
-
3744
- .dshr-compSectionTitle {
3745
- margin: 0;
3746
- color: var(--dsw-alias-label-primary);
3747
- font-size: 14px;
3748
- font-weight: 500;
3749
- line-height: 20px;
3750
- }
3751
-
3752
- .dshr-compSectionNote {
3753
- color: var(--dsw-alias-label-tertiary);
3754
- font-size: 12px;
3755
- line-height: 18px;
3756
- }
3757
-
3758
- .dshr-compProbeRow {
3759
- display: flex;
3760
- align-items: flex-end;
3761
- gap: 12px;
3762
- }
3763
-
3764
- .dshr-compProbeRow .dshr-compField {
3765
- flex: 1;
3766
- }
3767
-
3768
- .dshr-compProbeBtn {
3769
- flex: none;
3770
- }
3771
-
3772
- .dshr-compReport {
3773
- display: flex;
3774
- flex-direction: column;
3775
- gap: 8px;
3776
- padding: 12px 14px;
3777
- border: 0.5px solid var(--dsw-alias-border-l1);
3778
- border-radius: 12px;
3779
- background: var(--dsw-alias-bg-layer-1);
3780
- }
3781
-
3782
- .dshr-compReportHead {
3783
- display: flex;
3784
- align-items: baseline;
3785
- justify-content: space-between;
3786
- gap: 10px;
3787
- flex-wrap: wrap;
3788
- }
3789
-
3790
- .dshr-compReportName {
3791
- color: var(--dsw-alias-label-primary);
3792
- font-size: 13px;
3793
- font-weight: 500;
3794
- line-height: 20px;
3795
- }
3796
-
3797
- .dshr-compReportSum {
3798
- color: var(--dsw-alias-label-tertiary);
3799
- font-size: 12px;
3800
- line-height: 18px;
3801
- }
3802
-
3803
- .dshr-compMembers {
3804
- display: flex;
3805
- flex-direction: column;
3806
- margin: 0;
3807
- padding: 0;
3808
- list-style: none;
3809
- }
3810
-
3811
- .dshr-compMember {
3812
- display: grid;
3813
- grid-template-columns: 16px minmax(0, auto) minmax(0, 1fr);
3814
- align-items: baseline;
3815
- gap: 10px;
3816
- padding: 5px 0;
3817
- border-bottom: 0.5px solid var(--dsw-alias-border-l1);
3818
- }
3819
-
3820
- .dshr-compMember:last-child {
3821
- border-bottom: 0;
3822
- }
3823
-
3824
- .dshr-compMemberState {
3825
- flex: none;
3826
- text-align: center;
3827
- font-size: 12px;
3828
- line-height: 18px;
3829
- }
3830
-
3831
- .dshr-compMember[data-state="ok"] .dshr-compMemberState {
3832
- color: var(--dsw-alias-state-success-secondary);
3833
- }
3834
-
3835
- .dshr-compMember[data-state="fail"] .dshr-compMemberState {
3836
- color: var(--dsw-alias-state-error-primary);
3837
- }
3838
-
3839
- .dshr-compMember[data-state="absent"] .dshr-compMemberState,
3840
- .dshr-compMember[data-state="skipped"] .dshr-compMemberState {
3841
- color: var(--dsw-alias-label-tertiary);
3842
- }
3843
-
3844
- .dshr-compMemberName {
3845
- color: var(--dsw-alias-label-primary);
3846
- font-size: 12.5px;
3847
- line-height: 19px;
3848
- white-space: nowrap;
3849
- }
3850
-
3851
- .dshr-compMemberKey {
3852
- margin-left: 6px;
3853
- color: var(--dsw-alias-label-tertiary);
3854
- font-family: var(--dsw-font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
3855
- font-size: 11px;
3856
- }
3857
-
3858
- .dshr-compMemberDetail {
3859
- color: var(--dsw-alias-label-secondary);
3860
- font-size: 12px;
3861
- line-height: 18px;
3862
- overflow-wrap: anywhere;
3863
- }
3864
-
3865
-
@@ -182,7 +182,7 @@ export interface ExtControl {
182
182
  /**
183
183
  * 分组名(可选;缺省 = 无组)—— 面板按它把控制条**分节**渲染,组间插一条
184
184
  * 组标题行,组内顺序照旧。**无组的条目照旧平铺** ⇒ 没用这个字段的扩展
185
- * (ext-test 等)面板行为一个像素都不变。
185
+ * 面板行为一个像素都不变。
186
186
  *
187
187
  * 为什么要有:一条扩展同时管两类东西时(分层提示词 = 准则分类 + 宿主段落),
188
188
  * 平铺成一长列会让"哪条是准则、哪条是宿主的段"无从分辨 —— 分组是**归属
@@ -26,6 +26,17 @@
26
26
  * `categories`/`text`/`order` 里残留的 `hardening` 键无须迁移 ——
27
27
  * `resolveCategories` 规则 2 会忽略不认识的 id。
28
28
  * ② 身份气质:「默认中文」→「回复、思考过程必须使用中文」。
29
+ *
30
+ * ⚠️ 2026-10-27 用户点名的四处改动:
31
+ * ① **删掉「可判定」分类**(整条移除,id `verdifiable`);已存配置里的残留键
32
+ * 由 `resolveCategories` 忽略,无须迁移。
33
+ * ② **删掉「绝不偷懒」分类**(整条移除,id `no-cheap`)——用户判定它与别处重复。
34
+ * ③ 执行顺序重写:去掉「查结构行号 / 声明任务四件套 / 读码取切片 / 循既有骨架 /
35
+ * 以闸门为准」几段,改为「循既有架构/提出更优架构 → 验证以测试/e2e 验收」;
36
+ * 并在首尾补上「读文档」与「更新文档」—— 任务不是从零开始,也不以提交收尾。
37
+ * ④ **新增「文档」分类**(id `doc`):用户口述「文档不写流水账、只写权威内容」
38
+ * + 点名范围「docs/ 下的调研、设计、任务」,本文件只做润色。
39
+ * 排在「全局收口」之后 —— 收口查"文档过期没有",这条管"文档该写什么"。
29
40
  */
30
41
 
31
42
  /** 一个可独立开关的提示词分类。 */
@@ -87,11 +98,12 @@ export const PROMPT_CATEGORIES: readonly PromptCategory[] = [
87
98
  '做完那块区域该比动手前更整或更少。',
88
99
  },
89
100
  {
90
- id: 'verdifiable',
91
- title: '可判定',
101
+ id: 'doc',
102
+ title: '文档',
92
103
  defaultOn: true,
93
104
  body:
94
- '可判定:只有清单和会变红的闸门生效,形容词不生效。新约束一律写成"动作+闸门"。',
105
+ '文档:docs/ 里的调研/设计/任务文档,只写权威内容——结论、约束、怎么用;' +
106
+ '不写流水账(过程、试错、时间线),历史归 git。',
95
107
  },
96
108
  {
97
109
  id: 'root-cause',
@@ -123,13 +135,6 @@ export const PROMPT_CATEGORIES: readonly PromptCategory[] = [
123
135
  '结构:先让结构对再让代码短;命名/边界/单一职责优先于行数;依赖单向无环、公共逻辑收敛一处;' +
124
136
  '单文件单功能,软顶函数 ~40 行/文件 ~300 行。',
125
137
  },
126
- {
127
- id: 'no-cheap',
128
- title: '绝不偷懒',
129
- defaultOn: true,
130
- body:
131
- '绝不偷懒:边界输入校验、防数据丢失、安全、可访问性、真实硬件校准;非平凡逻辑留个能跑的检查。',
132
- },
133
138
  {
134
139
  id: 'delivery',
135
140
  title: '交付',
@@ -143,8 +148,8 @@ export const PROMPT_CATEGORIES: readonly PromptCategory[] = [
143
148
  title: '执行顺序',
144
149
  defaultOn: true,
145
150
  body:
146
- '执行:拉记忆→看全貌→建领地→定位任务→查结构行号→声明「任务/文件/验证/提交」→读码取切片→' +
147
- '循既有骨架→验证以闸门为准(混合状态下的绿灯不作数)→收口复查全貌→提交。',
151
+ '执行:拉记忆→读文档→看全貌→定位任务→建领地→循既有架构/提出更优架构→验证以测试/e2e 验收→' +
152
+ '收口复查全貌→更新文档→提交。',
148
153
  },
149
154
  ] as const
150
155
 
@@ -3,7 +3,7 @@
3
3
  * 1. 登记进 `router.ext` 表,带上可显示的身份(名字/说明/就绪);
4
4
  * 2. **幂等**:同一个 id 已在表里就不顶掉别人的登记;
5
5
  * 3. `source: 'builtin'`(随核心分发 ⇒ 插件页的原生行,自绘节不重复列);
6
- * 4. **准则段落真的挂上了** —— ext-test 没有这一条,本扩展多做执行面,必须验。
6
+ * 4. **准则段落真的挂上了** —— 这是本扩展唯一的执行面,必须单独验。
7
7
  *
8
8
  * 为什么第 4 条不可省:扩展的「卡片出现与否」取决于它在不在这张表里,而**准则
9
9
  * 有没有进 prompt** 取决于段落挂没挂 —— 两者是**不同的失效**,都静默且都不报错。
@@ -75,7 +75,7 @@ test('★ patch 里真的声明了这一行,且 id/name 逐字对得上', () =
75
75
  assert.equal(block![1], 'dsh-router-core/ext-prompt', 'patch 的 name 必须是能解析的子路径说明符')
76
76
  })
77
77
 
78
- test('★ 那一行默认关闭(与 ext-test 同款)', () => {
78
+ test('★ 那一行默认关闭', () => {
79
79
  // `disabled: true` = 行默认关 ⇒ loader 不 import ⇒ 扩展不进 `router.ext` 表 ⇒
80
80
  // 卡片不出现、准则不进 prompt。
81
81
  //
@@ -148,7 +148,7 @@ test('幂等:同一个 id 已在表里时不顶掉别人的登记', async () =
148
148
  assert.equal(table[EXT_PROMPT_ID], first, '重复登记不该换成一个新对象')
149
149
  })
150
150
 
151
- test('★ apply 把准则挂成了 system prompt 段落(ext-test 没这层,必须单独验)', async () => {
151
+ test('★ apply 把准则挂成了 system prompt 段落', async () => {
152
152
  const { ctx, sections } = await realCtx()
153
153
  apply(ctx as unknown as Parameters<typeof apply>[0])
154
154
  assert.equal(sections.length, 1, '准则没挂上 = 卡片亮着但 prompt 里没有准则')
@@ -2,14 +2,12 @@
2
2
  * 分层提示词扩展的 **cordis 行** —— 官方插件页(设置 → 插件 → dsh-router-core)详情页
3
3
  * 原生「包含的组件」里的那一行。
4
4
  *
5
- * 壳的形状与 `ext-test` 同款(cordis 身份 + 登记,实现 = `plugin.ts`),
6
- * 但**多一件执行面的事**:挂 system prompt 段落。ext-test 不做拦截、只登记;
7
- * 本扩展要把准则真注入,所以额外等 `systemPrompt` service 就绪后挂 section。
5
+ * 壳的形状 = cordis 身份 + 登记,实现 = `plugin.ts`;**执行面的事**是挂
6
+ * system prompt 段落 —— 所以额外等 `systemPrompt` service 就绪后挂 section。
8
7
  *
9
- * **默认开启**(patch 里不带 `disabled`,与三行内置供应商一致):
10
- * 协作准则是每次协作都要的基础设施,不是可选增强。与 `ext-test` 的
11
- * `disabled: true` **刻意不同** —— 自检是「想跑才跑」的工具,准则不是;
12
- * 让准则默认缺席,会变成「用户不知道它存在 ⇒ 协作按另一套规则执行」。
8
+ * **默认关闭**(patch 里 `disabled: true`):开不开由用户决定。行关闭时 loader
9
+ * 根本不 import 本模块,扩展就不在 `router.ext` 表里 ——「打开才出现这张卡片」
10
+ * 是天然的,不需要额外状态位。
13
11
  *
14
12
  * 登记时把扩展开关置为开:本扩展**没有独立的运行期开关**,它的开关就是这一行
15
13
  * (宿主管的那个)。不置上的话,用户开了行还要再点一次,两级开关说同一件事。
@@ -52,8 +50,8 @@ function systemPromptOf(sctx: Context): SystemPromptFace | undefined {
52
50
  *
53
51
  * ⚠️⚠️ **必须导出这个数组,且 `apply` 里直接用 `ctx.systemPrompt`**(2026-09-29 修)
54
52
  * ——我原先写成「在 apply 里 `ctx.inject(['systemPrompt'], cb)`」,**准则一直不进
55
- * prompt**:面板上卡片、开关、原文全都正常(`GET /router/api/ext` 实测有 14 条
56
- * controls),但模型看到的 system prompt 里没有它。
53
+ * prompt**:面板上卡片、开关、原文全都正常(`GET /router/api/ext` 里 controls 一条
54
+ * 不少),但模型看到的 system prompt 里没有它。
57
55
  *
58
56
  * 根因(本机 cordis 4.0.4 实测):`ctx.inject(deps, cb)` 的回调**不是同步触发** ——
59
57
  * provide 之后立刻 `inject`,回调要等到下一个异步点才跑。插件的 `apply` 是**同步
@@ -2,9 +2,9 @@
2
2
  * 挂载层 —— 把准则注册成 `ctx.systemPrompt` 的**一个**段落。
3
3
  *
4
4
  * ════════════════════════════════════════════════════════════════════
5
- * 为什么只注册一段,而不是 14 段
5
+ * 为什么只注册一段,而不是一段一个分类
6
6
  * ════════════════════════════════════════════════════════════════════
7
- * 面板开关的粒度是「分类」,但 prompt 读起来应该是一段连续的准则。分成 14 段
7
+ * 面板开关的粒度是「分类」,但 prompt 读起来应该是一段连续的准则。分成多段
8
8
  * 会让 KV cache 每个分类各占一段,且**增删任意一个分类都让它之后的全部分位**。
9
9
  * 一段 + 拼装 ⇒ 分类增删只改这一段内部,段的位置与前缀稳定。
10
10
  *
@@ -1,16 +1,14 @@
1
1
  /**
2
2
  * 分层提示词扩展(`router.ext` 侧的实现)—— 声明与状态都在这里。
3
3
  *
4
- * 与 `ext-test`(插件自检)同款:**不挂任何监听、不改写任何东西**,
5
- * 唯一作用是让用户在官方插件页里看到一张卡片、能开关它,并把协作准则
6
- * 注册成一段 system prompt。
4
+ * **不挂任何监听、不改写任何东西**,唯一作用是让用户在官方插件页里看到一张
5
+ * 卡片、能开关它,并把协作准则注册成一段 system prompt。
7
6
  *
8
7
  * 与 `rtk` / `jev` 那类扩展的区别要写清楚:那两家挂 `tools/execute`
9
8
  * 改写**工具调用**;本扩展只往 **system prompt** 加一段。名字同族是因为
10
9
  * 都在 `router.ext` 这张表里,拦截面不重叠。
11
10
  *
12
- * 复用 `router.ext` 通道只为拿到**发现 + 开关**两件事(见 ext-test 的同款说明),
13
- * 真正的注入在 `mount.ts`。
11
+ * 复用 `router.ext` 通道只为拿到**发现 + 开关**两件事,真正的注入在 `mount.ts`。
14
12
  */
15
13
  import type { ExtStoreService, RouterExt } from '../ext/contract.ts'
16
14
  import { PROMPT_CATEGORIES, PROMPT_TITLE } from './content.ts'
@@ -1043,9 +1043,8 @@ export class Router {
1043
1043
  // 注意这里**刻意不**把「用户禁用过的模型」也补回来:插件的 listModels 只该报
1044
1044
  // 「模型来源」,启用状态由核心合并(见 loader.ts 的 modelsWithEnabled)。
1045
1045
  // 插件若在 listModels 里就把自己禁用的过滤掉,那是它违约 —— 该修的是插件,
1046
- // 核心不该反过来替它兜着。兜着的后果比表面症状更糟:面板看着正常了,
1047
- // 而体检的越权检测(probe.ts 的 hiddenDisabledIds)会因为模型已被补全而
1048
- // 永远报 OK,那个 bug 就再也没人发现了。
1046
+ // 核心不该反过来替它兜着:兜着之后面板看着正常了,插件那个 bug 就再也没人
1047
+ // 发现了(症状消失、原因还在)。少报就让它少报,用户看得见才好去修插件。
1049
1048
  const seen = new Set(models.map((mm) => mm.id))
1050
1049
  for (const id of custom) {
1051
1050
  if (!seen.has(id)) {