@agent-stash/dsh-skill-scout 0.1.0 → 0.2.0

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/dist/client.js CHANGED
@@ -656,16 +656,23 @@ window.__ModuleLoader__.load({
656
656
  "recovery",
657
657
  "complex"
658
658
  ]);
659
- /** 建议在界面上的状态;reviewing 只存在于 Host 内存,表示模型正在复核它。 */
659
+ /**
660
+ * 建议在界面上的状态。summarizing 与 drafting 只存在于 Host 内存,表示发现模型正在写摘要、
661
+ * 生成模型正在写草稿;其余与存储里的状态一致。
662
+ */
660
663
  const ItemStateSchema = /* @__PURE__ */ picklist([
661
664
  "detected",
662
- "reviewing",
663
- "ready",
665
+ "summarizing",
666
+ "proposed",
664
667
  "skipped",
668
+ "drafting",
669
+ "ready",
665
670
  "failed",
666
671
  "dismissed",
667
672
  "written"
668
673
  ]);
674
+ /** 模型调用的两个阶段:摘要与草稿。 */
675
+ const StageViewSchema = /* @__PURE__ */ picklist(["summary", "draft"]);
669
676
  const DecisionSchema = /* @__PURE__ */ picklist([
670
677
  "skip",
671
678
  "suggest_create",
@@ -691,10 +698,24 @@ window.__ModuleLoader__.load({
691
698
  id: /* @__PURE__ */ string(),
692
699
  kind: SignalKindViewSchema,
693
700
  state: ItemStateSchema,
694
- /** 模型给出的标题;未复核时为 null,界面用 goal 代替。 */
701
+ /** 摘要给出的标题;还没有摘要时为 null,界面用 goal 代替。 */
695
702
  title: Nullable(/* @__PURE__ */ string()),
696
703
  goal: /* @__PURE__ */ string(),
697
- /** 模型给出的理由。 */
704
+ /** 规则在这组任务里找到的共有步骤,原来的写法。 */
705
+ steps: /* @__PURE__ */ array(/* @__PURE__ */ string()),
706
+ /** 摘要:在会话里发现了什么。 */
707
+ found: Nullable(/* @__PURE__ */ string()),
708
+ /** 摘要:Skill 做什么;改进时是追加的内容补上了什么。 */
709
+ description: Nullable(/* @__PURE__ */ string()),
710
+ /** 摘要:什么时候用。 */
711
+ trigger: Nullable(/* @__PURE__ */ string()),
712
+ /** 摘要:打算写进 Skill 的步骤或追加的内容。 */
713
+ outline: /* @__PURE__ */ array(/* @__PURE__ */ string()),
714
+ /** 摘要:照着做的风险。 */
715
+ risks: /* @__PURE__ */ array(/* @__PURE__ */ string()),
716
+ /** 摘要:怎么确认照着做成功了。 */
717
+ verification: Nullable(/* @__PURE__ */ string()),
718
+ /** 摘要判为跳过的理由。 */
698
719
  reason: Nullable(/* @__PURE__ */ string()),
699
720
  occurrences: /* @__PURE__ */ number(),
700
721
  sessions: /* @__PURE__ */ array(SessionRefSchema),
@@ -706,6 +727,7 @@ window.__ModuleLoader__.load({
706
727
  skill: Nullable(/* @__PURE__ */ string()),
707
728
  cautions: /* @__PURE__ */ array(/* @__PURE__ */ string()),
708
729
  failure: Nullable(/* @__PURE__ */ object({
730
+ stage: StageViewSchema,
709
731
  kind: FailureKindViewSchema,
710
732
  message: /* @__PURE__ */ string(),
711
733
  at: /* @__PURE__ */ number(),
@@ -726,27 +748,37 @@ window.__ModuleLoader__.load({
726
748
  })),
727
749
  dismissedSimilar: /* @__PURE__ */ boolean()
728
750
  });
751
+ /** 一个阶段此刻使用的模型;没有可用模型时为 null。 */
752
+ const ModelViewSchema = Nullable(/* @__PURE__ */ object({
753
+ provider: /* @__PURE__ */ string(),
754
+ model: /* @__PURE__ */ string(),
755
+ effort: Nullable(/* @__PURE__ */ string()),
756
+ source: /* @__PURE__ */ picklist(["config", "default"])
757
+ }));
729
758
  const InboxViewSchema = /* @__PURE__ */ object({
730
759
  status: /* @__PURE__ */ object({
731
760
  discovery: /* @__PURE__ */ boolean(),
732
- review: /* @__PURE__ */ boolean(),
761
+ /** 后台自动写摘要。 */
762
+ summarize: /* @__PURE__ */ boolean(),
733
763
  notify: /* @__PURE__ */ picklist(["badge", "quiet"]),
734
764
  scope: /* @__PURE__ */ picklist(["project", "user"]),
735
- model: Nullable(/* @__PURE__ */ object({
736
- provider: /* @__PURE__ */ string(),
737
- model: /* @__PURE__ */ string(),
738
- effort: Nullable(/* @__PURE__ */ string()),
739
- source: /* @__PURE__ */ picklist(["config", "default"])
740
- })),
741
- /** 本进程持有工作锁,负责分析与复核。 */
765
+ models: /* @__PURE__ */ object({
766
+ summary: ModelViewSchema,
767
+ draft: ModelViewSchema
768
+ }),
769
+ /** 本进程持有工作锁,负责分析与后台摘要。 */
742
770
  lead: /* @__PURE__ */ boolean(),
743
771
  scanning: /* @__PURE__ */ boolean(),
744
772
  /** 回合已结束、等静默期过去后再分析的会话数。 */
745
773
  waiting: /* @__PURE__ */ number(),
746
- reviewing: Nullable(/* @__PURE__ */ string()),
774
+ /** 正在进行的摘要与草稿请求数。 */
775
+ working: /* @__PURE__ */ number(),
747
776
  usage: /* @__PURE__ */ object({
748
- reviews: /* @__PURE__ */ number(),
777
+ /** 今天的后台摘要次数与上限。 */
778
+ summaries: /* @__PURE__ */ number(),
749
779
  limit: /* @__PURE__ */ number(),
780
+ /** 今天用户发起的摘要与草稿次数。 */
781
+ requested: /* @__PURE__ */ number(),
750
782
  inputTokens: /* @__PURE__ */ number(),
751
783
  outputTokens: /* @__PURE__ */ number()
752
784
  }),
@@ -783,7 +815,7 @@ window.__ModuleLoader__.load({
783
815
  "fix",
784
816
  "recovered"
785
817
  ])),
786
- /** 模型的结论引用了它。 */
818
+ /** 摘要或草稿引用了它。 */
787
819
  cited: /* @__PURE__ */ boolean()
788
820
  });
789
821
  const EvidenceEpisodeSchema = /* @__PURE__ */ object({
@@ -797,6 +829,7 @@ window.__ModuleLoader__.load({
797
829
  reads: /* @__PURE__ */ number(),
798
830
  ops: /* @__PURE__ */ array(EvidenceOpSchema),
799
831
  reply: Nullable(/* @__PURE__ */ string()),
832
+ steering: /* @__PURE__ */ array(/* @__PURE__ */ string()),
800
833
  followup: Nullable(/* @__PURE__ */ string())
801
834
  });
802
835
  /** 可编辑的 Skill 字段。 */
@@ -806,17 +839,20 @@ window.__ModuleLoader__.load({
806
839
  whenToUse: /* @__PURE__ */ pipe(/* @__PURE__ */ string(), /* @__PURE__ */ trim(), /* @__PURE__ */ maxLength(500)),
807
840
  body: /* @__PURE__ */ pipe(/* @__PURE__ */ string(), /* @__PURE__ */ trim(), /* @__PURE__ */ minLength(1), /* @__PURE__ */ maxLength(2e4))
808
841
  });
809
- /** 一条建议的完整信息:证据、模型结论与草稿。 */
842
+ /** 一次模型调用的来源与用量。 */
843
+ const CallViewSchema = Nullable(/* @__PURE__ */ object({
844
+ provider: /* @__PURE__ */ string(),
845
+ model: /* @__PURE__ */ string(),
846
+ at: /* @__PURE__ */ number(),
847
+ inputTokens: /* @__PURE__ */ number(),
848
+ outputTokens: /* @__PURE__ */ number()
849
+ }));
850
+ /** 一条建议的完整信息:证据、摘要与草稿。 */
810
851
  const DetailViewSchema = /* @__PURE__ */ object({
811
852
  item: ItemViewSchema,
812
853
  episodes: /* @__PURE__ */ array(EvidenceEpisodeSchema),
813
- review: Nullable(/* @__PURE__ */ object({
814
- provider: /* @__PURE__ */ string(),
815
- model: /* @__PURE__ */ string(),
816
- at: /* @__PURE__ */ number(),
817
- inputTokens: /* @__PURE__ */ number(),
818
- outputTokens: /* @__PURE__ */ number()
819
- })),
854
+ summary: CallViewSchema,
855
+ draftCall: CallViewSchema,
820
856
  /** suggest_create 的草稿字段。 */
821
857
  draft: Nullable(SkillFieldsSchema),
822
858
  /** suggest_improve 的追加内容。 */
@@ -1003,7 +1039,7 @@ window.__ModuleLoader__.load({
1003
1039
  }
1004
1040
  //#endregion
1005
1041
  //#region \0dsh-css:src/client/detail.module.css.mjs
1006
- const css$2 = ".FSsC8q_tabs{z-index:1;background:var(--dsw-alias-bg-layer-2);padding:0 0 12px;position:sticky;top:0}.FSsC8q_panel{min-width:0}.FSsC8q_stack{flex-direction:column;gap:14px;min-width:0;display:flex}.FSsC8q_heading{color:var(--dsw-alias-label-secondary);margin:0;font-size:12px;font-weight:600;line-height:18px}.FSsC8q_why p{color:var(--dsw-alias-label-primary);margin:4px 0 0;font-size:13px;line-height:20px}.FSsC8q_callout{border-radius:var(--dsw-radius-lg);background:var(--dsw-alias-interactive-bg-hover);flex-direction:column;gap:8px;margin:0;padding:12px 14px;font-size:13px;line-height:20px;display:flex}.FSsC8q_callout p{margin:0}.FSsC8q_note{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:18px}.FSsC8q_caution{border-radius:var(--dsw-radius-md);background:var(--dsw-alias-state-warn-tertiary);color:var(--dsw-alias-state-warn-label);margin:0;padding:8px 12px;font-size:12px;line-height:18px}.FSsC8q_failure{color:var(--dsw-alias-state-error-label,var(--dsw-alias-state-warn-label));flex-direction:column;gap:2px;margin:0;font-size:13px;line-height:20px;display:flex}.FSsC8q_failureDetail{color:var(--dsw-alias-label-tertiary);overflow-wrap:anywhere;font-size:12px;line-height:18px}.FSsC8q_episode{border-top:.5px solid var(--dsw-alias-border-l2);flex-direction:column;gap:8px;padding:12px 0 0;display:flex}.FSsC8q_episodeHead{align-items:flex-start;gap:10px;display:flex}.FSsC8q_ref{border-radius:var(--dsw-radius-xs);background:var(--dsw-alias-interactive-bg-hover);min-width:24px;color:var(--dsw-alias-label-secondary);text-align:center;font-variant-numeric:tabular-nums;flex:none;padding:1px 6px;font-size:11px;line-height:18px}.FSsC8q_episodeText{flex-direction:column;flex:1;gap:2px;min-width:0;display:flex}.FSsC8q_goal{-webkit-line-clamp:2;color:var(--dsw-alias-label-primary);overflow-wrap:anywhere;-webkit-box-orient:vertical;margin:0;font-size:13px;line-height:20px;display:-webkit-box;overflow:hidden}.FSsC8q_meta{color:var(--dsw-alias-label-tertiary);flex-wrap:wrap;gap:4px 10px;margin:0;font-size:12px;line-height:18px;display:flex}.FSsC8q_path{font-family:var(--dsw-font-family-mono,ui-monospace, monospace);font-size:11px}.FSsC8q_ops{flex-direction:column;margin:0;padding:0 0 0 34px;list-style:none;display:flex}.FSsC8q_op{border-radius:var(--dsw-radius-md);align-items:flex-start;gap:8px;padding:4px 8px;font-size:12px;line-height:18px;display:flex}.FSsC8q_op[data-cited]{background:var(--dsw-alias-interactive-bg-hover)}.FSsC8q_opGlyph{width:14px;height:18px;color:var(--dsw-alias-label-tertiary);flex:none;justify-content:center;align-items:center;display:inline-flex}.FSsC8q_op[data-ok=false] .FSsC8q_opGlyph{color:var(--dsw-alias-state-warn-label)}.FSsC8q_opBody{flex-wrap:wrap;flex:1;align-items:baseline;gap:2px 8px;min-width:0;display:flex}.FSsC8q_opLabel{min-width:0;font-family:var(--dsw-font-family-mono,ui-monospace, monospace);color:var(--dsw-alias-label-primary);overflow-wrap:anywhere;font-size:12px}.FSsC8q_opLabel[data-kind=edit]{color:var(--dsw-alias-label-secondary)}.FSsC8q_opTags{flex-wrap:wrap;gap:4px;display:inline-flex}.FSsC8q_tag{border-radius:var(--dsw-radius-xs);background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-secondary);padding:0 6px;font-size:11px;line-height:16px}.FSsC8q_tag[data-cited]{color:var(--dsw-alias-state-business-primary)}.FSsC8q_opError{color:var(--dsw-alias-label-tertiary);font-family:var(--dsw-font-family-mono,ui-monospace, monospace);overflow-wrap:anywhere;flex-basis:100%;font-size:11px}.FSsC8q_opRef{color:var(--dsw-alias-label-caption,var(--dsw-alias-label-tertiary));font-variant-numeric:tabular-nums;flex:none;font-size:11px}.FSsC8q_toolbar{justify-content:space-between;align-items:center;gap:8px;display:flex}.FSsC8q_facts{grid-template-columns:max-content 1fr;gap:4px 16px;margin:0;font-size:13px;line-height:20px;display:grid}.FSsC8q_facts dt{color:var(--dsw-alias-label-tertiary)}.FSsC8q_facts dd{overflow-wrap:anywhere;min-width:0;margin:0}.FSsC8q_markdown{border-radius:var(--dsw-radius-lg);background:var(--dsw-alias-bg-layer-1,var(--dsw-alias-bg-base));box-shadow:0 0 0 .5px var(--dsw-alias-border-l2);padding:4px 16px}.FSsC8q_editor{flex-direction:column;gap:12px;display:flex}.FSsC8q_field{flex-direction:column;gap:4px;display:flex}.FSsC8q_fieldLabel{color:var(--dsw-alias-label-secondary);font-size:12px;font-weight:500;line-height:18px}.FSsC8q_input{box-sizing:border-box;border-radius:var(--dsw-radius-md);background:var(--dsw-alias-bg-layer-1,var(--dsw-alias-bg-base));width:100%;box-shadow:0 0 0 .5px var(--dsw-alias-border-l2);color:var(--dsw-alias-label-primary);font:inherit;resize:vertical;border:0;padding:7px 10px;font-size:13px;line-height:20px}.FSsC8q_input:focus-visible{box-shadow:0 0 0 1.5px var(--dsw-alias-state-business-primary);outline:none}.FSsC8q_mono{font-family:var(--dsw-font-family-mono,ui-monospace, monospace);font-size:12px;line-height:18px}.FSsC8q_spacer{flex:1}.FSsC8q_actionError{color:var(--dsw-alias-state-error-label,var(--dsw-alias-state-warn-label));flex-basis:100%;margin:0;font-size:12px;line-height:18px}.FSsC8q_written{min-width:0;color:var(--dsw-alias-label-secondary);flex-direction:column;margin:0;font-size:12px;line-height:18px;display:flex}.FSsC8q_writtenPath{color:var(--dsw-alias-label-primary);font-family:var(--dsw-font-family-mono,ui-monospace, monospace);overflow-wrap:anywhere}";
1042
+ const css$2 = ".FSsC8q_tabs{z-index:1;background:var(--dsw-alias-bg-layer-2);padding:0 0 12px;position:sticky;top:0}.FSsC8q_panel{min-width:0}.FSsC8q_stack{flex-direction:column;gap:14px;min-width:0;display:flex}.FSsC8q_heading{color:var(--dsw-alias-label-secondary);margin:0;font-size:12px;font-weight:600;line-height:18px}.FSsC8q_block{flex-direction:column;gap:6px;display:flex}.FSsC8q_steps{color:var(--dsw-alias-label-tertiary);flex-direction:column;gap:2px;margin:0;padding:0 0 0 20px;font-size:12px;line-height:18px;display:flex}.FSsC8q_steps code{font-family:var(--dsw-font-family-mono,ui-monospace, monospace);color:var(--dsw-alias-label-primary);overflow-wrap:anywhere}.FSsC8q_summary{grid-template-columns:max-content 1fr;gap:10px 16px;margin:0;font-size:13px;line-height:20px;display:grid}.FSsC8q_summary dt{color:var(--dsw-alias-label-tertiary)}.FSsC8q_summary dd{min-width:0;color:var(--dsw-alias-label-primary);overflow-wrap:anywhere;margin:0}.FSsC8q_planTarget{flex-wrap:wrap;align-items:baseline;gap:6px;margin:0;display:flex}.FSsC8q_planTarget code{font-family:var(--dsw-font-family-mono,ui-monospace, monospace);font-size:12px}.FSsC8q_outline{color:var(--dsw-alias-label-secondary);margin:4px 0 0;padding:0 0 0 20px}.FSsC8q_risks{margin:0;padding:0 0 0 16px}.FSsC8q_working{flex-direction:column;gap:10px;display:flex}.FSsC8q_workingLabel{color:var(--dsw-alias-label-secondary);margin:0;font-size:12px;line-height:18px}.FSsC8q_skeleton{border-radius:var(--dsw-radius-lg);box-shadow:0 0 0 .5px var(--dsw-alias-border-l2);flex-direction:column;gap:10px;padding:16px;display:flex}.FSsC8q_skeletonBar{border-radius:var(--dsw-radius-xs);background:var(--dsw-alias-bg-skeleton);height:10px;animation:1.1s cubic-bezier(.36,0,.64,1) infinite both FSsC8q_scout-skeleton}.FSsC8q_skeletonGap{height:4px}@keyframes FSsC8q_scout-skeleton{0%,to{opacity:1}50%{opacity:.5}}@media (prefers-reduced-motion:reduce){.FSsC8q_skeletonBar{animation:none}}.FSsC8q_callout{border-radius:var(--dsw-radius-lg);background:var(--dsw-alias-interactive-bg-hover);flex-direction:column;gap:8px;margin:0;padding:12px 14px;font-size:13px;line-height:20px;display:flex}.FSsC8q_callout p{margin:0}.FSsC8q_note{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:18px}.FSsC8q_caution{border-radius:var(--dsw-radius-md);background:var(--dsw-alias-state-warn-tertiary);color:var(--dsw-alias-state-warn-label);margin:0;padding:8px 12px;font-size:12px;line-height:18px}.FSsC8q_failure{color:var(--dsw-alias-state-error-label,var(--dsw-alias-state-warn-label));flex-direction:column;gap:2px;margin:0;font-size:13px;line-height:20px;display:flex}.FSsC8q_failureDetail{color:var(--dsw-alias-label-tertiary);overflow-wrap:anywhere;font-size:12px;line-height:18px}.FSsC8q_episode{border-top:.5px solid var(--dsw-alias-border-l2);flex-direction:column;gap:8px;padding:12px 0 0;display:flex}.FSsC8q_episodeHead{align-items:flex-start;gap:10px;display:flex}.FSsC8q_ref{border-radius:var(--dsw-radius-xs);background:var(--dsw-alias-interactive-bg-hover);min-width:24px;color:var(--dsw-alias-label-secondary);text-align:center;font-variant-numeric:tabular-nums;flex:none;padding:1px 6px;font-size:11px;line-height:18px}.FSsC8q_episodeText{flex-direction:column;flex:1;gap:2px;min-width:0;display:flex}.FSsC8q_goal{-webkit-line-clamp:2;color:var(--dsw-alias-label-primary);overflow-wrap:anywhere;-webkit-box-orient:vertical;margin:0;font-size:13px;line-height:20px;display:-webkit-box;overflow:hidden}.FSsC8q_meta{color:var(--dsw-alias-label-tertiary);flex-wrap:wrap;gap:4px 10px;margin:0;font-size:12px;line-height:18px;display:flex}.FSsC8q_path{font-family:var(--dsw-font-family-mono,ui-monospace, monospace);font-size:11px}.FSsC8q_ops{flex-direction:column;margin:0;padding:0 0 0 34px;list-style:none;display:flex}.FSsC8q_op{border-radius:var(--dsw-radius-md);align-items:flex-start;gap:8px;padding:4px 8px;font-size:12px;line-height:18px;display:flex}.FSsC8q_op[data-cited]{background:var(--dsw-alias-interactive-bg-hover)}.FSsC8q_opGlyph{width:14px;height:18px;color:var(--dsw-alias-label-tertiary);flex:none;justify-content:center;align-items:center;display:inline-flex}.FSsC8q_op[data-ok=false] .FSsC8q_opGlyph{color:var(--dsw-alias-state-warn-label)}.FSsC8q_opBody{flex-wrap:wrap;flex:1;align-items:baseline;gap:2px 8px;min-width:0;display:flex}.FSsC8q_opLabel{min-width:0;font-family:var(--dsw-font-family-mono,ui-monospace, monospace);color:var(--dsw-alias-label-primary);overflow-wrap:anywhere;font-size:12px}.FSsC8q_opLabel[data-kind=edit]{color:var(--dsw-alias-label-secondary)}.FSsC8q_opTags{flex-wrap:wrap;gap:4px;display:inline-flex}.FSsC8q_tag{border-radius:var(--dsw-radius-xs);background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-secondary);padding:0 6px;font-size:11px;line-height:16px}.FSsC8q_tag[data-cited]{color:var(--dsw-alias-state-business-primary)}.FSsC8q_opError{color:var(--dsw-alias-label-tertiary);font-family:var(--dsw-font-family-mono,ui-monospace, monospace);overflow-wrap:anywhere;flex-basis:100%;font-size:11px}.FSsC8q_opRef{color:var(--dsw-alias-label-caption,var(--dsw-alias-label-tertiary));font-variant-numeric:tabular-nums;flex:none;font-size:11px}.FSsC8q_toolbar{justify-content:space-between;align-items:center;gap:8px;display:flex}.FSsC8q_toolbarActions{align-items:center;gap:4px;display:flex}.FSsC8q_facts{grid-template-columns:max-content 1fr;gap:4px 16px;margin:0;font-size:13px;line-height:20px;display:grid}.FSsC8q_facts dt{color:var(--dsw-alias-label-tertiary)}.FSsC8q_facts dd{overflow-wrap:anywhere;min-width:0;margin:0}.FSsC8q_markdown{border-radius:var(--dsw-radius-lg);background:var(--dsw-alias-bg-layer-1,var(--dsw-alias-bg-base));box-shadow:0 0 0 .5px var(--dsw-alias-border-l2);padding:4px 16px}.FSsC8q_editor{flex-direction:column;gap:12px;display:flex}.FSsC8q_field{flex-direction:column;gap:4px;display:flex}.FSsC8q_fieldLabel{color:var(--dsw-alias-label-secondary);font-size:12px;font-weight:500;line-height:18px}.FSsC8q_input{box-sizing:border-box;border-radius:var(--dsw-radius-md);background:var(--dsw-alias-bg-layer-1,var(--dsw-alias-bg-base));width:100%;box-shadow:0 0 0 .5px var(--dsw-alias-border-l2);color:var(--dsw-alias-label-primary);font:inherit;resize:vertical;border:0;padding:7px 10px;font-size:13px;line-height:20px}.FSsC8q_input:focus-visible{box-shadow:0 0 0 1.5px var(--dsw-alias-state-business-primary);outline:none}.FSsC8q_mono{font-family:var(--dsw-font-family-mono,ui-monospace, monospace);font-size:12px;line-height:18px}.FSsC8q_spacer{flex:1}.FSsC8q_actionError{color:var(--dsw-alias-state-error-label,var(--dsw-alias-state-warn-label));flex-basis:100%;margin:0;font-size:12px;line-height:18px}.FSsC8q_written{min-width:0;color:var(--dsw-alias-label-secondary);flex-direction:column;margin:0;font-size:12px;line-height:18px;display:flex}.FSsC8q_writtenPath{color:var(--dsw-alias-label-primary);font-family:var(--dsw-font-family-mono,ui-monospace, monospace);overflow-wrap:anywhere}";
1007
1043
  const tag$2 = "@agent-stash/dsh-skill-scout/detail.module.css";
1008
1044
  if (document.querySelector(`style[data-plugin-css=${JSON.stringify(tag$2)}]`) === null) {
1009
1045
  const style = document.createElement("style");
@@ -1014,6 +1050,7 @@ window.__ModuleLoader__.load({
1014
1050
  }
1015
1051
  var detail_module_css_default = {
1016
1052
  "actionError": "FSsC8q_actionError",
1053
+ "block": "FSsC8q_block",
1017
1054
  "callout": "FSsC8q_callout",
1018
1055
  "caution": "FSsC8q_caution",
1019
1056
  "editor": "FSsC8q_editor",
@@ -1040,15 +1077,26 @@ window.__ModuleLoader__.load({
1040
1077
  "opRef": "FSsC8q_opRef",
1041
1078
  "ops": "FSsC8q_ops",
1042
1079
  "opTags": "FSsC8q_opTags",
1080
+ "outline": "FSsC8q_outline",
1043
1081
  "panel": "FSsC8q_panel",
1044
1082
  "path": "FSsC8q_path",
1083
+ "planTarget": "FSsC8q_planTarget",
1045
1084
  "ref": "FSsC8q_ref",
1085
+ "risks": "FSsC8q_risks",
1086
+ "scout-skeleton": "FSsC8q_scout-skeleton",
1087
+ "skeleton": "FSsC8q_skeleton",
1088
+ "skeletonBar": "FSsC8q_skeletonBar",
1089
+ "skeletonGap": "FSsC8q_skeletonGap",
1046
1090
  "spacer": "FSsC8q_spacer",
1047
1091
  "stack": "FSsC8q_stack",
1092
+ "steps": "FSsC8q_steps",
1093
+ "summary": "FSsC8q_summary",
1048
1094
  "tabs": "FSsC8q_tabs",
1049
1095
  "tag": "FSsC8q_tag",
1050
1096
  "toolbar": "FSsC8q_toolbar",
1051
- "why": "FSsC8q_why",
1097
+ "toolbarActions": "FSsC8q_toolbarActions",
1098
+ "working": "FSsC8q_working",
1099
+ "workingLabel": "FSsC8q_workingLabel",
1052
1100
  "written": "FSsC8q_written",
1053
1101
  "writtenPath": "FSsC8q_writtenPath"
1054
1102
  };
@@ -1069,10 +1117,18 @@ window.__ModuleLoader__.load({
1069
1117
  function sessionCount(t, count) {
1070
1118
  return t(count === 1 ? "meta.sessions.one" : "meta.sessions.other", { count });
1071
1119
  }
1072
- /** 卡片标题:模型标题,未复核时用用户请求。 */
1120
+ /** 卡片标题:摘要里的标题,还没有摘要时用用户请求。 */
1073
1121
  function titleOf(item) {
1074
1122
  return item.title ?? item.goal;
1075
1123
  }
1124
+ /** 状态文案;失败按阶段区分。 */
1125
+ function stateLabel(t, item) {
1126
+ return item.state === "failed" && item.failure?.stage === "draft" ? t("state.draftFailed") : t(`state.${item.state}`);
1127
+ }
1128
+ /** 模型正在处理这条建议。 */
1129
+ function inFlight(item) {
1130
+ return item.state === "summarizing" || item.state === "drafting";
1131
+ }
1076
1132
  /** 一行元信息:信号类型 · 会话数 · 时间。 */
1077
1133
  function metaOf(t, item, now) {
1078
1134
  return [
@@ -1081,11 +1137,19 @@ window.__ModuleLoader__.load({
1081
1137
  ago(t, item.updatedAt, now)
1082
1138
  ].join(" · ");
1083
1139
  }
1084
- /** 按状态分组:可保存、本地发现(含复核中与失败)、历史。 */
1140
+ /** 失败在草稿阶段的建议仍有摘要,留在待决定的分组里。 */
1141
+ function draftFailed(item) {
1142
+ return item.state === "failed" && item.failure?.stage === "draft";
1143
+ }
1144
+ /** 等用户决定的建议:标题栏的数字只数这些。 */
1145
+ function needsReview(item) {
1146
+ return item.state === "proposed" || item.state === "drafting" || item.state === "ready" || draftFailed(item);
1147
+ }
1148
+ /** 按状态分组:待决定、本地发现、历史。 */
1085
1149
  function groupItems(items) {
1086
1150
  return {
1087
- ready: items.filter((item) => item.state === "ready"),
1088
- found: items.filter((item) => item.state === "detected" || item.state === "reviewing" || item.state === "failed"),
1151
+ review: items.filter(needsReview),
1152
+ found: items.filter((item) => item.state === "detected" || item.state === "summarizing" || item.state === "failed" && !draftFailed(item)),
1089
1153
  history: items.filter((item) => item.state === "written" || item.state === "skipped" || item.state === "dismissed")
1090
1154
  };
1091
1155
  }
@@ -1097,8 +1161,8 @@ window.__ModuleLoader__.load({
1097
1161
  //#endregion
1098
1162
  //#region src/client/detail.tsx
1099
1163
  /**
1100
- * 一条建议的详情:证据、Skill 预览与编辑、文件改动,以及保存、忽略、撤销。
1101
- * 证据与草稿从 Host 读取;建议的状态跟随收件箱刷新,状态或更新时间变化时重新读取详情。
1164
+ * 一条建议的详情:摘要、证据、Skill 预览与编辑、文件改动,以及生成摘要、同意生成草稿、保存、忽略、撤销。
1165
+ * 证据、摘要与草稿从 Host 读取;建议的状态跟随收件箱刷新,状态或更新时间变化时重新读取详情。
1102
1166
  */
1103
1167
  function message(error) {
1104
1168
  return error instanceof Error ? error.message : String(error);
@@ -1235,6 +1299,14 @@ window.__ModuleLoader__.load({
1235
1299
  className: detail_module_css_default.note,
1236
1300
  children: t(episode.reads === 1 ? "evidence.reads.one" : "evidence.reads.other", { count: episode.reads })
1237
1301
  }),
1302
+ keyed(episode.steering).map(({ key, text }) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
1303
+ className: detail_module_css_default.note,
1304
+ children: [
1305
+ t("evidence.steering"),
1306
+ ": ",
1307
+ text
1308
+ ]
1309
+ }, key)),
1238
1310
  episode.followup !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
1239
1311
  className: detail_module_css_default.note,
1240
1312
  children: [
@@ -1295,12 +1367,73 @@ window.__ModuleLoader__.load({
1295
1367
  ]
1296
1368
  });
1297
1369
  }
1370
+ /** 给可能重复的文本行配上稳定的 key:文本加上它第几次出现。 */
1371
+ function keyed(lines) {
1372
+ const seen = /* @__PURE__ */ new Map();
1373
+ return lines.map((text) => {
1374
+ const count = (seen.get(text) ?? 0) + 1;
1375
+ seen.set(text, count);
1376
+ return {
1377
+ key: `${text}#${String(count)}`,
1378
+ text
1379
+ };
1380
+ });
1381
+ }
1382
+ /** 摘要占位条的宽度:几行短文本。 */
1383
+ const SUMMARY_BARS = [
1384
+ "88%",
1385
+ "72%",
1386
+ "46%"
1387
+ ];
1388
+ /** 草稿占位条的宽度,按 SKILL.md 的排版:标题、描述,空一行,一节标题与几行步骤;空串是段间空行。 */
1389
+ const DRAFT_BARS = [
1390
+ "36%",
1391
+ "94%",
1392
+ "82%",
1393
+ "",
1394
+ "22%",
1395
+ "90%",
1396
+ "74%",
1397
+ "84%",
1398
+ "58%"
1399
+ ];
1400
+ /** 相邻占位条的起伏错开的时间,连起来是一道自上而下的波。 */
1401
+ const BAR_STAGGER = 70;
1402
+ /** 模型写作时的占位:按最终排版画出的几条横条,依次快速明暗起伏;减少动态效果时静止。 */
1403
+ function Skeleton({ bars }) {
1404
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1405
+ className: detail_module_css_default.skeleton,
1406
+ "aria-hidden": "true",
1407
+ children: keyed(bars).map(({ key, text: width }, index) => width === "" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: detail_module_css_default.skeletonGap }, key) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1408
+ className: detail_module_css_default.skeletonBar,
1409
+ style: {
1410
+ width,
1411
+ animationDelay: `${String(index * BAR_STAGGER)}ms`
1412
+ }
1413
+ }, key))
1414
+ });
1415
+ }
1416
+ /** 模型正在处理时的一行状态与占位。 */
1417
+ function Working({ label, bars }) {
1418
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1419
+ className: detail_module_css_default.working,
1420
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1421
+ className: detail_module_css_default.workingLabel,
1422
+ role: "status",
1423
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.TextShimmer, {
1424
+ active: true,
1425
+ children: label
1426
+ })
1427
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Skeleton, { bars })]
1428
+ });
1429
+ }
1298
1430
  /** 一条建议的详情对话框。 */
1299
- function Detail({ id, item, inbox, loadDetail, modelName, defaultScope, openSession, t, onClose }) {
1431
+ function Detail({ id, item, inbox, loadDetail, models, defaultScope, openSession, t, onClose }) {
1300
1432
  const tabsId = (0, react.useId)();
1301
1433
  const labels = useLabels(t);
1302
1434
  const [detail, setDetail] = (0, react.useState)({ status: "loading" });
1303
- const [tab, setTab] = (0, react.useState)("evidence");
1435
+ /** 用户选中的页签;为 null 时跟随状态:草稿生成中与可以保存时看 Skill,其余看摘要。 */
1436
+ const [chosenTab, setChosenTab] = (0, react.useState)(null);
1304
1437
  const [scope, setScope] = (0, react.useState)(defaultScope);
1305
1438
  const [editing, setEditing] = (0, react.useState)(false);
1306
1439
  const [fields, setFields] = (0, react.useState)(null);
@@ -1334,12 +1467,16 @@ window.__ModuleLoader__.load({
1334
1467
  const value = detail.status === "ready" ? detail.value : null;
1335
1468
  const create = item.decision === "suggest_create";
1336
1469
  const improve = item.decision === "suggest_improve";
1337
- const reviewed = create || improve;
1470
+ const proposal = create || improve;
1471
+ const drafted = value !== null && (value.draft !== null || value.section !== null);
1338
1472
  const draft = fields ?? value?.draft ?? null;
1339
1473
  const shownSection = section ?? value?.section ?? null;
1340
1474
  const parsed = draft === null ? null : /* @__PURE__ */ safeParse(SkillFieldsSchema, draft);
1341
1475
  const invalid = create && parsed !== null && !parsed.success;
1342
- const wantPreview = reviewed && item.state === "ready" && value !== null;
1476
+ const draftFailed = item.state === "failed" && item.failure?.stage === "draft";
1477
+ /** 摘要已经给出计划,等用户同意生成草稿。 */
1478
+ const awaitingConsent = proposal && (item.state === "proposed" || draftFailed);
1479
+ const wantPreview = drafted && item.state === "ready";
1343
1480
  (0, react.useEffect)(() => {
1344
1481
  if (!wantPreview || invalid) return void 0;
1345
1482
  let stale = false;
@@ -1399,6 +1536,24 @@ window.__ModuleLoader__.load({
1399
1536
  }
1400
1537
  };
1401
1538
  const skillName = item.skill ?? "";
1539
+ const summarize = () => {
1540
+ run("summarize", {
1541
+ action: "summarize",
1542
+ id
1543
+ });
1544
+ };
1545
+ /** 同意摘要,生成草稿;之前的编辑与预览作废,页签回到跟随状态。 */
1546
+ const generate = () => {
1547
+ setFields(null);
1548
+ setSection(null);
1549
+ setEditing(false);
1550
+ setPreview(null);
1551
+ setChosenTab(null);
1552
+ run("draft", {
1553
+ action: "draft",
1554
+ id
1555
+ });
1556
+ };
1402
1557
  const dismiss = async (similar) => {
1403
1558
  if (await run("dismiss", {
1404
1559
  action: "dismiss",
@@ -1433,134 +1588,202 @@ window.__ModuleLoader__.load({
1433
1588
  setPreview(null);
1434
1589
  }
1435
1590
  };
1436
- const tabs = [{
1437
- value: "evidence",
1438
- label: t("detail.tab.evidence")
1439
- }, ...reviewed ? [{
1440
- value: "skill",
1441
- label: t("detail.tab.skill")
1442
- }, ...item.state === "ready" ? [{
1443
- value: "changes",
1444
- label: t("detail.tab.changes")
1445
- }] : []] : []];
1446
- const activeTab = tabs.some((entry) => entry.value === tab) ? tab : "evidence";
1591
+ const tabs = [
1592
+ {
1593
+ value: "summary",
1594
+ label: t("detail.tab.summary")
1595
+ },
1596
+ ...item.state === "drafting" || drafted ? [{
1597
+ value: "skill",
1598
+ label: t("detail.tab.skill")
1599
+ }] : [],
1600
+ ...item.state === "ready" && drafted ? [{
1601
+ value: "changes",
1602
+ label: t("detail.tab.changes")
1603
+ }] : [],
1604
+ {
1605
+ value: "evidence",
1606
+ label: t("detail.tab.evidence")
1607
+ }
1608
+ ];
1609
+ const autoTab = item.state === "drafting" || item.state === "ready" ? "skill" : "summary";
1610
+ const wanted = chosenTab ?? autoTab;
1611
+ const activeTab = tabs.some((entry) => entry.value === wanted) ? wanted : "summary";
1447
1612
  const subtitle = [
1448
1613
  t(`kind.${item.kind}`),
1449
1614
  sessionCount(t, item.occurrences),
1450
- value?.review === null || value === null ? t(`state.${item.state}`) : t("detail.reviewedBy", { model: value.review.model })
1615
+ value?.draftCall != null ? t("detail.draftedBy", { model: value.draftCall.model }) : value?.summary != null ? t("detail.summarizedBy", { model: value.summary.model }) : stateLabel(t, item)
1451
1616
  ].join(" · ");
1452
- const reviewPanel = () => {
1453
- if (reviewed || item.state === "dismissed" || item.state === "written") return null;
1454
- if (item.state === "skipped") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1455
- className: detail_module_css_default.callout,
1456
- children: t("detail.skipped")
1617
+ const failure = (stage) => item.state === "failed" && item.failure?.stage === stage ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
1618
+ className: detail_module_css_default.failure,
1619
+ role: "alert",
1620
+ children: [t(stage === "summary" ? "detail.failed.summary" : "detail.failed.draft", { kind: t(`failure.${item.failure.kind}`) }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1621
+ className: detail_module_css_default.failureDetail,
1622
+ children: item.failure.message
1623
+ })]
1624
+ }) : null;
1625
+ /** 规则匹配到的步骤,原来的写法。 */
1626
+ const steps = () => item.steps.length === 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
1627
+ className: detail_module_css_default.block,
1628
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
1629
+ className: detail_module_css_default.heading,
1630
+ children: t("detail.steps")
1631
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ol", {
1632
+ className: detail_module_css_default.steps,
1633
+ children: keyed(item.steps).map(({ key, text }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", { children: text }) }, key))
1634
+ })]
1635
+ });
1636
+ /** 摘要里的方案:发现了什么、Skill 做什么、何时使用、将要新建或改进哪个 Skill 与提纲、风险、验证方式。 */
1637
+ const plan = () => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("dl", {
1638
+ className: detail_module_css_default.summary,
1639
+ children: [
1640
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("dt", { children: t("detail.found") }),
1641
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("dd", { children: item.found }),
1642
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("dt", { children: t("detail.description") }),
1643
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("dd", { children: item.description }),
1644
+ item.trigger !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("dt", { children: t("detail.trigger") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("dd", { children: item.trigger })] }),
1645
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("dt", { children: t("detail.plan") }),
1646
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("dd", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
1647
+ className: detail_module_css_default.planTarget,
1648
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t(create ? "detail.plan.create" : "detail.plan.improve") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", { children: skillName })]
1649
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ol", {
1650
+ className: detail_module_css_default.outline,
1651
+ children: keyed(item.outline).map(({ key, text }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: text }, key))
1652
+ })] }),
1653
+ item.risks.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("dt", { children: t("detail.risks") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("dd", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
1654
+ className: detail_module_css_default.risks,
1655
+ children: keyed(item.risks).map(({ key, text }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: text }, key))
1656
+ }) })] }),
1657
+ item.verification !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("dt", { children: t("detail.verification") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("dd", { children: item.verification })] })
1658
+ ]
1659
+ });
1660
+ const summaryTab = () => {
1661
+ if (item.state === "summarizing") return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1662
+ className: detail_module_css_default.stack,
1663
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Working, {
1664
+ label: t("detail.summarizing", { model: models.summary ?? "" }),
1665
+ bars: SUMMARY_BARS
1666
+ }), steps()]
1457
1667
  });
1458
- if (item.state === "reviewing") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1459
- className: detail_module_css_default.callout,
1460
- role: "status",
1461
- children: t("detail.reviewing", { model: modelName ?? "" })
1668
+ if (proposal) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1669
+ className: detail_module_css_default.stack,
1670
+ children: [plan(), awaitingConsent && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1671
+ className: detail_module_css_default.callout,
1672
+ children: [failure("draft"), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1673
+ className: detail_module_css_default.note,
1674
+ children: models.draft === null ? t("detail.noDraftModel") : t("detail.consent", { model: models.draft })
1675
+ })]
1676
+ })]
1462
1677
  });
1463
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1464
- className: detail_module_css_default.callout,
1465
- children: [
1466
- item.failure !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
1467
- className: detail_module_css_default.failure,
1468
- role: "alert",
1469
- children: [t("detail.failed", { kind: t(`failure.${item.failure.kind}`) }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1470
- className: detail_module_css_default.failureDetail,
1471
- children: item.failure.message
1472
- })]
1473
- }),
1474
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: t("detail.local") }),
1475
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1678
+ if (item.decision === "skip") return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1679
+ className: detail_module_css_default.stack,
1680
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1681
+ className: detail_module_css_default.callout,
1682
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: t("detail.skipped") }), item.reason !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1476
1683
  className: detail_module_css_default.note,
1477
- children: modelName === null ? t("detail.noModel") : t("detail.reviewNote", {
1478
- count: item.sessions.length,
1479
- model: modelName
1684
+ children: item.reason
1685
+ })]
1686
+ }), steps()]
1687
+ });
1688
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1689
+ className: detail_module_css_default.stack,
1690
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1691
+ className: detail_module_css_default.callout,
1692
+ children: [
1693
+ failure("summary"),
1694
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: t("detail.local") }),
1695
+ item.state !== "written" && item.state !== "dismissed" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1696
+ className: detail_module_css_default.note,
1697
+ children: models.summary === null ? t("detail.noSummaryModel") : t("detail.summaryNote", {
1698
+ count: item.sessions.length,
1699
+ model: models.summary
1700
+ })
1480
1701
  })
1481
- }),
1482
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
1483
- size: "sm",
1484
- variant: "outline",
1485
- "data-autofocus": "",
1486
- disabled: modelName === null || busy !== null,
1487
- onClick: () => void run("review", {
1488
- action: "analyze",
1489
- id
1490
- }),
1491
- children: item.failure === null ? t("detail.review") : t("detail.retry")
1492
- }) })
1493
- ]
1702
+ ]
1703
+ }), steps()]
1494
1704
  });
1495
1705
  };
1496
- const evidence = () => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1706
+ const evidence = () => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1497
1707
  className: detail_module_css_default.stack,
1498
- children: [
1499
- reviewPanel(),
1500
- item.reason !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
1501
- className: detail_module_css_default.why,
1502
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
1503
- className: detail_module_css_default.heading,
1504
- children: t("detail.why")
1505
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: item.reason })]
1506
- }),
1507
- item.cautions.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1508
- className: detail_module_css_default.caution,
1509
- role: "note",
1510
- children: t("detail.cautions", { list: item.cautions.map((caution) => {
1511
- const key = `caution.${caution}`;
1512
- return isCaution(key) ? t(key) : caution;
1513
- }).join(", ") })
1514
- }),
1515
- value?.episodes.map((episode) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Episode, {
1516
- episode,
1517
- now,
1518
- openSession,
1519
- t
1520
- }, episode.episodeId))
1521
- ]
1708
+ children: value?.episodes.map((episode) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Episode, {
1709
+ episode,
1710
+ now,
1711
+ openSession,
1712
+ t
1713
+ }, episode.episodeId))
1522
1714
  });
1523
- const skill = () => {
1524
- if (create && draft !== null) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1525
- className: detail_module_css_default.stack,
1526
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1527
- className: detail_module_css_default.toolbar,
1528
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
1715
+ const cautions = () => item.cautions.length === 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1716
+ className: detail_module_css_default.caution,
1717
+ role: "note",
1718
+ children: t("detail.cautions", { list: item.cautions.map((caution) => {
1719
+ const key = `caution.${caution}`;
1720
+ return isCaution(key) ? t(key) : caution;
1721
+ }).join(", ") })
1722
+ });
1723
+ /** 草稿页签的工具栏:编辑、还原,以及按同一份摘要重新生成。 */
1724
+ const toolbar = (lead) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1725
+ className: detail_module_css_default.toolbar,
1726
+ children: [lead, /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
1727
+ className: detail_module_css_default.toolbarActions,
1728
+ children: [
1729
+ item.state === "ready" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
1529
1730
  size: "sm",
1530
1731
  variant: "ghost",
1531
- onClick: () => setEditing(!editing),
1532
- disabled: item.state !== "ready",
1533
- children: editing ? t("skill.preview") : t("skill.edit")
1534
- }), fields !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
1732
+ disabled: busy !== null,
1733
+ onClick: generate,
1734
+ children: t("detail.redraft")
1735
+ }),
1736
+ (create ? fields : section) !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
1535
1737
  size: "sm",
1536
1738
  variant: "ghost",
1537
- onClick: () => setFields(null),
1739
+ onClick: () => create ? setFields(null) : setSection(null),
1538
1740
  children: t("skill.reset")
1539
- })]
1540
- }), editing ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(FieldsEditor, {
1541
- fields: draft,
1542
- onChange: setFields,
1543
- t
1544
- }), invalid && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1545
- className: detail_module_css_default.failure,
1546
- role: "alert",
1547
- children: t("skill.invalid")
1548
- })] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("dl", {
1549
- className: detail_module_css_default.facts,
1550
- children: [
1551
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("dt", { children: t("skill.name") }),
1552
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("dd", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", { children: draft.name }) }),
1553
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("dt", { children: t("skill.description") }),
1554
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("dd", { children: draft.description }),
1555
- draft.whenToUse !== "" && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("dt", { children: t("skill.whenToUse") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("dd", { children: draft.whenToUse })] })
1556
- ]
1557
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1558
- className: detail_module_css_default.markdown,
1559
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.MarkdownText, {
1560
- text: draft.body,
1561
- labels: labels.markdown
1741
+ }),
1742
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
1743
+ size: "sm",
1744
+ variant: "ghost",
1745
+ onClick: () => setEditing(!editing),
1746
+ disabled: item.state !== "ready",
1747
+ children: editing ? t("skill.preview") : t("skill.edit")
1562
1748
  })
1563
- })] })]
1749
+ ]
1750
+ })]
1751
+ });
1752
+ const skill = () => {
1753
+ if (item.state === "drafting") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Working, {
1754
+ label: t("detail.drafting", { model: models.draft ?? "" }),
1755
+ bars: DRAFT_BARS
1756
+ });
1757
+ if (create && draft !== null) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1758
+ className: detail_module_css_default.stack,
1759
+ children: [
1760
+ cautions(),
1761
+ toolbar(/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {})),
1762
+ editing ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(FieldsEditor, {
1763
+ fields: draft,
1764
+ onChange: setFields,
1765
+ t
1766
+ }), invalid && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1767
+ className: detail_module_css_default.failure,
1768
+ role: "alert",
1769
+ children: t("skill.invalid")
1770
+ })] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("dl", {
1771
+ className: detail_module_css_default.facts,
1772
+ children: [
1773
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("dt", { children: t("skill.name") }),
1774
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("dd", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", { children: draft.name }) }),
1775
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("dt", { children: t("skill.description") }),
1776
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("dd", { children: draft.description }),
1777
+ draft.whenToUse !== "" && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("dt", { children: t("skill.whenToUse") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("dd", { children: draft.whenToUse })] })
1778
+ ]
1779
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1780
+ className: detail_module_css_default.markdown,
1781
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.MarkdownText, {
1782
+ text: draft.body,
1783
+ labels: labels.markdown
1784
+ })
1785
+ })] })
1786
+ ]
1564
1787
  });
1565
1788
  if (improve && shownSection !== null) {
1566
1789
  const notWritable = preview?.status === "ready" && preview.value.kind === "improve" && !preview.value.writable;
@@ -1571,19 +1794,11 @@ window.__ModuleLoader__.load({
1571
1794
  className: detail_module_css_default.caution,
1572
1795
  children: t("skill.notWritable", { skill: skillName })
1573
1796
  }),
1574
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1575
- className: detail_module_css_default.toolbar,
1576
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1577
- className: detail_module_css_default.heading,
1578
- children: t("skill.section", { skill: skillName })
1579
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
1580
- size: "sm",
1581
- variant: "ghost",
1582
- onClick: () => setEditing(!editing),
1583
- disabled: item.state !== "ready",
1584
- children: editing ? t("skill.preview") : t("skill.edit")
1585
- })]
1586
- }),
1797
+ cautions(),
1798
+ toolbar(/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1799
+ className: detail_module_css_default.heading,
1800
+ children: t("skill.section", { skill: skillName })
1801
+ })),
1587
1802
  editing ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
1588
1803
  className: `${detail_module_css_default.input} ${detail_module_css_default.mono}`,
1589
1804
  "aria-label": t("skill.section", { skill: skillName }),
@@ -1634,6 +1849,56 @@ window.__ModuleLoader__.load({
1634
1849
  })]
1635
1850
  });
1636
1851
  };
1852
+ /** 右侧的主操作:随阶段从生成摘要、同意生成草稿到保存。 */
1853
+ const primary = () => {
1854
+ if (item.state === "summarizing" || item.state === "drafting") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
1855
+ size: "sm",
1856
+ variant: "primary",
1857
+ disabled: true,
1858
+ children: stateLabel(t, item)
1859
+ });
1860
+ if (awaitingConsent) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
1861
+ size: "sm",
1862
+ variant: "primary",
1863
+ "data-autofocus": "",
1864
+ disabled: models.draft === null || busy !== null,
1865
+ onClick: generate,
1866
+ children: draftFailed ? t("detail.redraft") : create ? t("detail.draft.create") : t("detail.draft.improve")
1867
+ });
1868
+ if (item.state === "ready") {
1869
+ const writable = !(preview?.status === "ready" && preview.value.kind === "improve" && !preview.value.writable);
1870
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [create && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.SegmentedControl, {
1871
+ id: `${tabsId}-scope`,
1872
+ label: t("scope.label"),
1873
+ value: scope,
1874
+ disabled: busy !== null,
1875
+ options: [{
1876
+ value: "project",
1877
+ label: t("scope.project")
1878
+ }, {
1879
+ value: "user",
1880
+ label: t("scope.user")
1881
+ }],
1882
+ onChange: setScope
1883
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
1884
+ size: "sm",
1885
+ variant: "primary",
1886
+ "data-autofocus": "",
1887
+ disabled: busy !== null || invalid || !writable,
1888
+ onClick: () => void save(),
1889
+ children: busy === "save" ? t("action.saving") : create ? t("action.save") : t("action.improve", { skill: skillName })
1890
+ })] });
1891
+ }
1892
+ const again = item.decision === "skip" || item.failure !== null;
1893
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
1894
+ size: "sm",
1895
+ variant: item.decision === "skip" ? "outline" : "primary",
1896
+ "data-autofocus": "",
1897
+ disabled: models.summary === null || busy !== null,
1898
+ onClick: summarize,
1899
+ children: again ? t("detail.resummarize") : t("detail.summarize")
1900
+ });
1901
+ };
1637
1902
  const footer = () => {
1638
1903
  if (item.state === "written" && item.write !== null) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
1639
1904
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
@@ -1681,7 +1946,6 @@ window.__ModuleLoader__.load({
1681
1946
  }),
1682
1947
  children: t("action.restore")
1683
1948
  })] });
1684
- const writable = !(preview?.status === "ready" && preview.value.kind === "improve" && !preview.value.writable);
1685
1949
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
1686
1950
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
1687
1951
  size: "sm",
@@ -1698,28 +1962,7 @@ window.__ModuleLoader__.load({
1698
1962
  children: t("action.dismissSimilar")
1699
1963
  }),
1700
1964
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: detail_module_css_default.spacer }),
1701
- item.state === "ready" && create && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.SegmentedControl, {
1702
- id: `${tabsId}-scope`,
1703
- label: t("scope.label"),
1704
- value: scope,
1705
- disabled: busy !== null,
1706
- options: [{
1707
- value: "project",
1708
- label: t("scope.project")
1709
- }, {
1710
- value: "user",
1711
- label: t("scope.user")
1712
- }],
1713
- onChange: setScope
1714
- }),
1715
- item.state === "ready" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
1716
- size: "sm",
1717
- variant: "primary",
1718
- "data-autofocus": "",
1719
- disabled: busy !== null || invalid || !writable,
1720
- onClick: () => void save(),
1721
- children: busy === "save" ? t("action.saving") : create ? t("action.save") : t("action.improve", { skill: skillName })
1722
- })
1965
+ primary()
1723
1966
  ] });
1724
1967
  };
1725
1968
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Dialog, {
@@ -1751,20 +1994,21 @@ window.__ModuleLoader__.load({
1751
1994
  })
1752
1995
  ]
1753
1996
  }),
1754
- value !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [tabs.length > 1 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1997
+ value !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1755
1998
  className: detail_module_css_default.tabs,
1756
1999
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.SegmentedControl, {
1757
2000
  id: tabsId,
1758
2001
  label: t("detail.tabs"),
1759
2002
  value: activeTab,
1760
2003
  options: tabs,
1761
- onChange: setTab
2004
+ onChange: setChosenTab
1762
2005
  })
1763
2006
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1764
2007
  className: detail_module_css_default.panel,
1765
- role: tabs.length > 1 ? "tabpanel" : void 0,
1766
- "aria-labelledby": tabs.length > 1 ? `${tabsId}-${activeTab}` : void 0,
2008
+ role: "tabpanel",
2009
+ "aria-labelledby": `${tabsId}-${activeTab}`,
1767
2010
  children: [
2011
+ activeTab === "summary" && summaryTab(),
1768
2012
  activeTab === "evidence" && evidence(),
1769
2013
  activeTab === "skill" && skill(),
1770
2014
  activeTab === "changes" && changes()
@@ -1791,7 +2035,7 @@ window.__ModuleLoader__.load({
1791
2035
  }
1792
2036
  //#endregion
1793
2037
  //#region \0dsh-css:src/client/inbox.module.css.mjs
1794
- const css$1 = ".Wfa-jG_root{margin-left:4px;position:relative}.Wfa-jG_trigger{width:28px;height:28px;color:var(--dsw-alias-label-tertiary);cursor:pointer;background:0 0;border:0;border-radius:28px;flex:none;justify-content:center;align-items:center;padding:0;display:inline-flex;position:relative}.Wfa-jG_trigger:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:1px;color:var(--dsw-alias-label-secondary)}@media (hover:hover){.Wfa-jG_trigger:hover{color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-interactive-bg-hover)}}.Wfa-jG_trigger[aria-expanded=true]{color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-interactive-bg-hover)}.Wfa-jG_trigger svg{flex:none}.Wfa-jG_badge{box-sizing:border-box;background:var(--dsw-alias-label-secondary);min-width:14px;height:14px;color:var(--dsw-alias-bg-layer-1,var(--dsw-alias-bg-base));text-align:center;font-variant-numeric:tabular-nums;pointer-events:none;border-radius:7px;padding:0 3px;font-size:9px;font-weight:600;line-height:14px;position:absolute;top:1px;right:0}.Wfa-jG_panel{z-index:100;box-sizing:border-box;background:var(--dsw-specific-menu);width:360px;max-width:calc(100vw - 32px);max-height:min(480px,100vh - 140px);backdrop-filter:var(--dsw-menu-backdrop-filter);--dsw-elevation-stroke-color:var(--dsw-alias-border-l1);box-shadow:var(--dsw-elevation-prominent);color:var(--dsw-alias-label-primary);animation:Wfa-jG_panel-in .14s var(--ds-ease-in-out,ease);border:0;border-radius:16px;flex-direction:column;padding:4px;display:flex;position:fixed}.Wfa-jG_panel:focus{outline:none}@keyframes Wfa-jG_panel-in{0%{opacity:0;transform:translateY(-2px)}to{opacity:1;transform:none}}.Wfa-jG_head{flex:none;justify-content:space-between;align-items:center;gap:8px;padding:8px 10px 6px 12px;display:flex}.Wfa-jG_title{color:var(--dsw-alias-label-secondary);margin:0;font-size:12px;font-weight:600;line-height:18px}.Wfa-jG_working{color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:16px}.Wfa-jG_scroll{--dsh-scrollbar-thumb:var(--dsw-alias-scrollbar-bg-l2);--dsh-scrollbar-thumb-hover:var(--dsw-alias-scrollbar-hover-l2);flex-direction:column;flex:auto;gap:6px;min-height:0;display:flex;overflow-y:auto}.Wfa-jG_section{flex-direction:column;display:flex}.Wfa-jG_sectionTitle{color:var(--dsw-alias-label-tertiary);margin:0;padding:5px 12px 2px;font-size:11px;font-weight:500;line-height:16px}.Wfa-jG_list{flex-direction:column;gap:1px;margin:0;padding:0;list-style:none;display:flex}.Wfa-jG_card{border-radius:10px;align-items:flex-start;gap:4px;display:flex;position:relative}@media (hover:hover){.Wfa-jG_card:hover{background:var(--dsw-alias-interactive-bg-hover)}}.Wfa-jG_open{min-width:0;color:inherit;font:inherit;text-align:left;cursor:pointer;background:0 0;border:0;border-radius:10px;flex-direction:column;flex:1;gap:2px;padding:8px 4px 8px 12px;display:flex}.Wfa-jG_open:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:-2px}.Wfa-jG_cardTitle{-webkit-line-clamp:2;overflow-wrap:anywhere;-webkit-box-orient:vertical;font-size:13px;line-height:18px;display:-webkit-box;overflow:hidden}.Wfa-jG_card[data-state=detected] .Wfa-jG_cardTitle,.Wfa-jG_card[data-state=failed] .Wfa-jG_cardTitle,.Wfa-jG_card[data-state=reviewing] .Wfa-jG_cardTitle{color:var(--dsw-alias-label-secondary)}.Wfa-jG_cardMeta{color:var(--dsw-alias-label-tertiary);flex-wrap:wrap;gap:2px 6px;font-size:11px;line-height:16px;display:flex}.Wfa-jG_state{color:var(--dsw-alias-label-secondary)}.Wfa-jG_card[data-state=failed] .Wfa-jG_state{color:var(--dsw-alias-state-warn-label)}.Wfa-jG_dismiss{width:22px;height:22px;color:var(--dsw-alias-label-tertiary);cursor:pointer;opacity:0;background:0 0;border:0;border-radius:6px;flex:none;justify-content:center;align-items:center;margin:7px 6px 0 0;padding:0;display:inline-flex}.Wfa-jG_card:hover .Wfa-jG_dismiss,.Wfa-jG_card:focus-within .Wfa-jG_dismiss{opacity:1}.Wfa-jG_dismiss:focus-visible{opacity:1;outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:-2px}@media (hover:hover){.Wfa-jG_dismiss:hover{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-interactive-bg-hover)}}@media (hover:none){.Wfa-jG_dismiss{opacity:1}}.Wfa-jG_historyToggle{color:var(--dsw-alias-label-tertiary);font:inherit;cursor:pointer;background:0 0;border:0;border-radius:6px;align-self:flex-start;margin:2px 0 2px 8px;padding:2px 4px;font-size:11px;line-height:16px}.Wfa-jG_historyToggle:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary)}@media (hover:hover){.Wfa-jG_historyToggle:hover{color:var(--dsw-alias-label-secondary)}}.Wfa-jG_banner{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-secondary);border-radius:10px;margin:2px 4px 4px;padding:8px 10px;font-size:12px;line-height:18px}.Wfa-jG_empty{flex-direction:column;gap:4px;padding:10px 12px 14px;display:flex}.Wfa-jG_emptyTitle{color:var(--dsw-alias-label-primary);margin:0;font-size:13px;line-height:20px}.Wfa-jG_emptyBody{color:var(--dsw-alias-label-secondary);margin:0;font-size:12px;line-height:18px}.Wfa-jG_caption{color:var(--dsw-alias-label-tertiary);margin:4px 0 0;padding:0 12px;font-size:11px;line-height:16px}.Wfa-jG_empty .Wfa-jG_caption{padding:0}.Wfa-jG_errorRow{color:var(--dsw-alias-state-warn-label);justify-content:space-between;align-items:center;gap:8px;padding:8px 12px;font-size:12px;line-height:18px;display:flex}.Wfa-jG_link{color:var(--dsw-alias-state-business-primary);font:inherit;cursor:pointer;background:0 0;border:0;padding:0;font-size:12px}.Wfa-jG_link:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:2px;border-radius:4px}.Wfa-jG_notice{border-top:.5px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);flex:none;justify-content:space-between;align-items:center;gap:8px;margin-top:4px;padding:8px 12px;font-size:12px;line-height:18px;display:flex}.Wfa-jG_noticeText{overflow-wrap:anywhere;min-width:0}@media (prefers-reduced-motion:reduce){.Wfa-jG_panel{animation:none}}";
2038
+ const css$1 = ".Wfa-jG_root{margin-left:4px;position:relative}.Wfa-jG_trigger{width:28px;height:28px;color:var(--dsw-alias-label-tertiary);cursor:pointer;background:0 0;border:0;border-radius:28px;flex:none;justify-content:center;align-items:center;padding:0;display:inline-flex;position:relative}.Wfa-jG_trigger:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:1px;color:var(--dsw-alias-label-secondary)}@media (hover:hover){.Wfa-jG_trigger:hover{color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-interactive-bg-hover)}}.Wfa-jG_trigger[aria-expanded=true]{color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-interactive-bg-hover)}.Wfa-jG_trigger svg{flex:none}.Wfa-jG_badge{box-sizing:border-box;background:var(--dsw-alias-label-secondary);min-width:14px;height:14px;color:var(--dsw-alias-bg-layer-1,var(--dsw-alias-bg-base));text-align:center;font-variant-numeric:tabular-nums;pointer-events:none;border-radius:7px;padding:0 3px;font-size:9px;font-weight:600;line-height:14px;position:absolute;top:1px;right:0}.Wfa-jG_panel{z-index:100;box-sizing:border-box;background:var(--dsw-specific-menu);width:360px;max-width:calc(100vw - 32px);max-height:min(480px,100vh - 140px);backdrop-filter:var(--dsw-menu-backdrop-filter);--dsw-elevation-stroke-color:var(--dsw-alias-border-l1);box-shadow:var(--dsw-elevation-prominent);color:var(--dsw-alias-label-primary);animation:Wfa-jG_panel-in .14s var(--ds-ease-in-out,ease);border:0;border-radius:16px;flex-direction:column;padding:4px;display:flex;position:fixed}.Wfa-jG_panel:focus{outline:none}@keyframes Wfa-jG_panel-in{0%{opacity:0;transform:translateY(-2px)}to{opacity:1;transform:none}}.Wfa-jG_head{flex:none;justify-content:space-between;align-items:center;gap:8px;padding:8px 10px 6px 12px;display:flex}.Wfa-jG_title{color:var(--dsw-alias-label-secondary);margin:0;font-size:12px;font-weight:600;line-height:18px}.Wfa-jG_working{color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:16px}.Wfa-jG_scroll{--dsh-scrollbar-thumb:var(--dsw-alias-scrollbar-bg-l2);--dsh-scrollbar-thumb-hover:var(--dsw-alias-scrollbar-hover-l2);flex-direction:column;flex:auto;gap:6px;min-height:0;display:flex;overflow-y:auto}.Wfa-jG_section{flex-direction:column;display:flex}.Wfa-jG_sectionTitle{color:var(--dsw-alias-label-tertiary);margin:0;padding:5px 12px 2px;font-size:11px;font-weight:500;line-height:16px}.Wfa-jG_list{flex-direction:column;gap:1px;margin:0;padding:0;list-style:none;display:flex}.Wfa-jG_card{border-radius:10px;align-items:flex-start;gap:4px;display:flex;position:relative}@media (hover:hover){.Wfa-jG_card:hover{background:var(--dsw-alias-interactive-bg-hover)}}.Wfa-jG_open{min-width:0;color:inherit;font:inherit;text-align:left;cursor:pointer;background:0 0;border:0;border-radius:10px;flex-direction:column;flex:1;gap:2px;padding:8px 4px 8px 12px;display:flex}.Wfa-jG_open:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:-2px}.Wfa-jG_cardTitle{-webkit-line-clamp:2;overflow-wrap:anywhere;-webkit-box-orient:vertical;font-size:13px;line-height:18px;display:-webkit-box;overflow:hidden}.Wfa-jG_card[data-state=detected] .Wfa-jG_cardTitle,.Wfa-jG_card[data-state=summarizing] .Wfa-jG_cardTitle,.Wfa-jG_card[data-state=failed]:not([data-stage=draft]) .Wfa-jG_cardTitle{color:var(--dsw-alias-label-secondary)}.Wfa-jG_cardSummary{-webkit-line-clamp:2;color:var(--dsw-alias-label-secondary);overflow-wrap:anywhere;-webkit-box-orient:vertical;font-size:12px;line-height:17px;display:-webkit-box;overflow:hidden}.Wfa-jG_cardMeta{color:var(--dsw-alias-label-tertiary);flex-wrap:wrap;gap:2px 6px;font-size:11px;line-height:16px;display:flex}.Wfa-jG_state{color:var(--dsw-alias-label-secondary)}.Wfa-jG_card[data-state=failed] .Wfa-jG_state{color:var(--dsw-alias-state-warn-label)}.Wfa-jG_dismiss{width:22px;height:22px;color:var(--dsw-alias-label-tertiary);cursor:pointer;opacity:0;background:0 0;border:0;border-radius:6px;flex:none;justify-content:center;align-items:center;margin:7px 6px 0 0;padding:0;display:inline-flex}.Wfa-jG_card:hover .Wfa-jG_dismiss,.Wfa-jG_card:focus-within .Wfa-jG_dismiss{opacity:1}.Wfa-jG_dismiss:focus-visible{opacity:1;outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:-2px}@media (hover:hover){.Wfa-jG_dismiss:hover{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-interactive-bg-hover)}}@media (hover:none){.Wfa-jG_dismiss{opacity:1}}.Wfa-jG_historyToggle{color:var(--dsw-alias-label-tertiary);font:inherit;cursor:pointer;background:0 0;border:0;border-radius:6px;align-self:flex-start;margin:2px 0 2px 8px;padding:2px 4px;font-size:11px;line-height:16px}.Wfa-jG_historyToggle:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary)}@media (hover:hover){.Wfa-jG_historyToggle:hover{color:var(--dsw-alias-label-secondary)}}.Wfa-jG_banner{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-secondary);border-radius:10px;margin:2px 4px 4px;padding:8px 10px;font-size:12px;line-height:18px}.Wfa-jG_empty{flex-direction:column;gap:4px;padding:10px 12px 14px;display:flex}.Wfa-jG_emptyTitle{color:var(--dsw-alias-label-primary);margin:0;font-size:13px;line-height:20px}.Wfa-jG_emptyBody{color:var(--dsw-alias-label-secondary);margin:0;font-size:12px;line-height:18px}.Wfa-jG_caption{color:var(--dsw-alias-label-tertiary);margin:4px 0 0;padding:0 12px;font-size:11px;line-height:16px}.Wfa-jG_empty .Wfa-jG_caption{padding:0}.Wfa-jG_errorRow{color:var(--dsw-alias-state-warn-label);justify-content:space-between;align-items:center;gap:8px;padding:8px 12px;font-size:12px;line-height:18px;display:flex}.Wfa-jG_link{color:var(--dsw-alias-state-business-primary);font:inherit;cursor:pointer;background:0 0;border:0;padding:0;font-size:12px}.Wfa-jG_link:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:2px;border-radius:4px}.Wfa-jG_notice{border-top:.5px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);flex:none;justify-content:space-between;align-items:center;gap:8px;margin-top:4px;padding:8px 12px;font-size:12px;line-height:18px;display:flex}.Wfa-jG_noticeText{overflow-wrap:anywhere;min-width:0}@media (prefers-reduced-motion:reduce){.Wfa-jG_panel{animation:none}}";
1795
2039
  const tag$1 = "@agent-stash/dsh-skill-scout/inbox.module.css";
1796
2040
  if (document.querySelector(`style[data-plugin-css=${JSON.stringify(tag$1)}]`) === null) {
1797
2041
  const style = document.createElement("style");
@@ -1806,6 +2050,7 @@ window.__ModuleLoader__.load({
1806
2050
  "caption": "Wfa-jG_caption",
1807
2051
  "card": "Wfa-jG_card",
1808
2052
  "cardMeta": "Wfa-jG_cardMeta",
2053
+ "cardSummary": "Wfa-jG_cardSummary",
1809
2054
  "cardTitle": "Wfa-jG_cardTitle",
1810
2055
  "dismiss": "Wfa-jG_dismiss",
1811
2056
  "empty": "Wfa-jG_empty",
@@ -1835,7 +2080,7 @@ window.__ModuleLoader__.load({
1835
2080
  /**
1836
2081
  * 会话标题栏里的经验提炼入口:一个图标按钮,有新建议时带一个小计数;点开是收件箱浮层,
1837
2082
  * 点卡片打开详情对话框。入口在第一次读取完成前不渲染,避免标题栏闪动。
1838
- * 计数只算打开收件箱之后出现的可保存建议,看过即清零。
2083
+ * 计数只算打开收件箱之后出现的、等用户决定的摘要与草稿,看过即清零;详情里正打开的那条在眼前,不计入。
1839
2084
  */
1840
2085
  const SEEN_KEY = "agent-stash-skill-scout:seen";
1841
2086
  const SeenSchema = /* @__PURE__ */ pipe(/* @__PURE__ */ string(), /* @__PURE__ */ parseJson(), /* @__PURE__ */ array(/* @__PURE__ */ string()));
@@ -1869,32 +2114,45 @@ window.__ModuleLoader__.load({
1869
2114
  left: 0,
1870
2115
  top: 0
1871
2116
  };
1872
- /** 新建议数:可保存且还没看过。 */
1873
- function unseen(inbox, seen) {
1874
- if (inbox === null) return 0;
1875
- return inbox.items.filter((item) => item.state === "ready" && !seen.has(item.id)).length;
2117
+ /** 等用户决定的摘要与草稿。 */
2118
+ function awaiting(inbox) {
2119
+ return (inbox?.items ?? []).filter((item) => item.state === "proposed" || item.state === "ready").map((item) => item.id);
2120
+ }
2121
+ /** 新建议数:等用户决定、还没看过,也不是详情里正打开的那条。 */
2122
+ function unseen(inbox, seen, viewing) {
2123
+ return awaiting(inbox).filter((id) => id !== viewing && !seen.has(id)).length;
1876
2124
  }
1877
2125
  function Card({ item, now, t, onOpen, onDismiss }) {
1878
2126
  const title = titleOf(item);
1879
- const showState = item.state !== "ready";
1880
2127
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
1881
2128
  className: inbox_module_css_default.card,
1882
2129
  "data-state": item.state,
2130
+ "data-stage": item.failure?.stage,
1883
2131
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
1884
2132
  type: "button",
1885
2133
  className: inbox_module_css_default.open,
1886
2134
  "data-item": item.id,
1887
2135
  onClick: () => onOpen(item.id),
1888
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1889
- className: inbox_module_css_default.cardTitle,
1890
- children: title
1891
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
1892
- className: inbox_module_css_default.cardMeta,
1893
- children: [showState && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1894
- className: inbox_module_css_default.state,
1895
- children: t(`state.${item.state}`)
1896
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: metaOf(t, item, now) })]
1897
- })]
2136
+ children: [
2137
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2138
+ className: inbox_module_css_default.cardTitle,
2139
+ children: title
2140
+ }),
2141
+ item.description !== null && item.state !== "written" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2142
+ className: inbox_module_css_default.cardSummary,
2143
+ children: item.description
2144
+ }),
2145
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2146
+ className: inbox_module_css_default.cardMeta,
2147
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2148
+ className: inbox_module_css_default.state,
2149
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.TextShimmer, {
2150
+ active: inFlight(item),
2151
+ children: stateLabel(t, item)
2152
+ })
2153
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: metaOf(t, item, now) })]
2154
+ })
2155
+ ]
1898
2156
  }), onDismiss !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1899
2157
  type: "button",
1900
2158
  className: inbox_module_css_default.dismiss,
@@ -1910,7 +2168,7 @@ window.__ModuleLoader__.load({
1910
2168
  const pending = inbox.items.some((item) => item.state === "detected" || item.state === "failed");
1911
2169
  let key = null;
1912
2170
  if (!status.discovery) key = "banner.discoveryOff";
1913
- else if (status.model === null && pending) key = "banner.noModel";
2171
+ else if (status.models.summary === null && pending) key = "banner.noModel";
1914
2172
  else if (!status.lead) key = "banner.secondary";
1915
2173
  if (key === null) return null;
1916
2174
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
@@ -1921,7 +2179,7 @@ window.__ModuleLoader__.load({
1921
2179
  function PanelBody({ inbox, now, t, onOpen, onDismiss }) {
1922
2180
  const [history, setHistory] = (0, react.useState)(false);
1923
2181
  const groups = groupItems(inbox.items);
1924
- if (groups.ready.length === 0 && groups.found.length === 0 && groups.history.length === 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2182
+ if (groups.review.length === 0 && groups.found.length === 0 && groups.history.length === 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1925
2183
  className: inbox_module_css_default.empty,
1926
2184
  children: [
1927
2185
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Banner, {
@@ -1955,7 +2213,7 @@ window.__ModuleLoader__.load({
1955
2213
  now,
1956
2214
  t,
1957
2215
  onOpen,
1958
- onDismiss: item.state === "reviewing" ? void 0 : onDismiss
2216
+ onDismiss: inFlight(item) ? void 0 : onDismiss
1959
2217
  }, item.id))
1960
2218
  })]
1961
2219
  });
@@ -1964,7 +2222,7 @@ window.__ModuleLoader__.load({
1964
2222
  inbox,
1965
2223
  t
1966
2224
  }),
1967
- section("section.ready", groups.ready),
2225
+ section("section.review", groups.review),
1968
2226
  section("section.found", groups.found),
1969
2227
  groups.history.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
1970
2228
  className: inbox_module_css_default.section,
@@ -2017,23 +2275,24 @@ window.__ModuleLoader__.load({
2017
2275
  position
2018
2276
  ]);
2019
2277
  (0, react.useEffect)(() => {
2020
- if (!open || state.inbox === null) return;
2021
- const ready = state.inbox.items.filter((item) => item.state === "ready").map((item) => item.id);
2022
- if (ready.some((id) => !seen.has(id))) seen.add(ready);
2278
+ if (state.inbox === null) return;
2279
+ const ids = awaiting(state.inbox).filter((id) => open || id === detailId);
2280
+ if (ids.some((id) => !seen.has(id))) seen.add(ids);
2023
2281
  }, [
2024
2282
  open,
2283
+ detailId,
2025
2284
  state.inbox,
2026
2285
  seen
2027
2286
  ]);
2028
2287
  if (state.inbox === null && state.status !== "error") return null;
2029
- const fresh = unseen(state.inbox, seen);
2288
+ const fresh = unseen(state.inbox, seen, detailId);
2030
2289
  const badge = state.inbox?.status.notify === "badge" && fresh > 0;
2031
2290
  const label = fresh === 0 ? t("entry.label") : t(fresh === 1 ? "entry.count.one" : "entry.count.other", { count: fresh });
2032
- const working = state.inbox !== null && (state.inbox.status.scanning || state.inbox.status.reviewing !== null);
2033
- /** 打开收件箱:刷新状态,并把当前的可保存建议记为看过。 */
2291
+ const asking = (state.inbox?.status.working ?? 0) > 0;
2292
+ const working = asking || state.inbox?.status.scanning === true;
2293
+ /** 打开收件箱:刷新状态,并把当前的待决定建议记为看过。 */
2034
2294
  const show = () => {
2035
- const ready = state.inbox?.items.filter((item) => item.state === "ready").map((item) => item.id) ?? [];
2036
- seen.add(ready);
2295
+ seen.add(awaiting(state.inbox));
2037
2296
  setSeenVersion((version) => version + 1);
2038
2297
  setNow(Date.now());
2039
2298
  setOpen(true);
@@ -2092,7 +2351,10 @@ window.__ModuleLoader__.load({
2092
2351
  }), working && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2093
2352
  className: inbox_module_css_default.working,
2094
2353
  role: "status",
2095
- children: state.inbox?.status.reviewing === null ? t("panel.analyzing") : t("panel.reviewing")
2354
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.TextShimmer, {
2355
+ active: true,
2356
+ children: asking ? t("panel.working") : t("panel.analyzing")
2357
+ })
2096
2358
  })]
2097
2359
  }),
2098
2360
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
@@ -2167,7 +2429,10 @@ window.__ModuleLoader__.load({
2167
2429
  item: detailItem,
2168
2430
  inbox,
2169
2431
  loadDetail,
2170
- modelName: state.inbox?.status.model?.model ?? null,
2432
+ models: {
2433
+ summary: state.inbox?.status.models.summary?.model ?? null,
2434
+ draft: state.inbox?.status.models.draft?.model ?? null
2435
+ },
2171
2436
  defaultScope: state.inbox?.status.scope ?? "project",
2172
2437
  openSession,
2173
2438
  t,
@@ -2187,19 +2452,18 @@ window.__ModuleLoader__.load({
2187
2452
  "entry.count.other": "Skill suggestions, {count} new",
2188
2453
  "panel.title": "Skill suggestions",
2189
2454
  "panel.analyzing": "Analyzing sessions",
2190
- "panel.reviewing": "Reviewing",
2455
+ "panel.working": "Asking the model",
2191
2456
  "panel.loading": "Loading",
2192
2457
  "panel.error": "Could not load suggestions.",
2193
2458
  "panel.retry": "Retry",
2194
2459
  "panel.empty.title": "No suggestions",
2195
2460
  "panel.empty.body": "A suggestion appears when a workflow repeats across sessions or a fix passes a check.",
2196
2461
  "panel.analyzed": "{sessions} sessions analyzed",
2197
- "section.ready": "Ready to save",
2462
+ "section.review": "Needs your decision",
2198
2463
  "section.found": "Found locally",
2199
2464
  "history.show": "History ({count})",
2200
2465
  "history.hide": "Hide history",
2201
- "banner.noModel": "No review model is available. Choose one in Skill Scout settings.",
2202
- "banner.reviewOff": "Background review is off. Open a finding to review it.",
2466
+ "banner.noModel": "No discovery model is available. Choose one in Skill Scout settings.",
2203
2467
  "banner.discoveryOff": "Session analysis is off.",
2204
2468
  "banner.secondary": "Another DSH window is analyzing sessions.",
2205
2469
  "notice.dismissed": "Dismissed",
@@ -2213,11 +2477,14 @@ window.__ModuleLoader__.load({
2213
2477
  "kind.complex": "Long verified task",
2214
2478
  "meta.sessions.one": "{count} session",
2215
2479
  "meta.sessions.other": "{count} sessions",
2216
- "state.detected": "Not reviewed",
2217
- "state.reviewing": "Reviewing",
2218
- "state.ready": "Ready",
2480
+ "state.detected": "No summary yet",
2481
+ "state.summarizing": "Summarizing",
2482
+ "state.proposed": "Summary ready",
2219
2483
  "state.skipped": "Not worth saving",
2220
- "state.failed": "Review failed",
2484
+ "state.drafting": "Drafting",
2485
+ "state.ready": "Draft ready",
2486
+ "state.failed": "Summary failed",
2487
+ "state.draftFailed": "Draft failed",
2221
2488
  "state.dismissed": "Dismissed",
2222
2489
  "state.written": "Saved",
2223
2490
  "time.now": "just now",
@@ -2225,6 +2492,7 @@ window.__ModuleLoader__.load({
2225
2492
  "time.hours": "{count} h ago",
2226
2493
  "time.days": "{count} d ago",
2227
2494
  "detail.close": "Close",
2495
+ "detail.tab.summary": "Summary",
2228
2496
  "detail.tab.evidence": "Evidence",
2229
2497
  "detail.tab.skill": "Skill",
2230
2498
  "detail.tab.changes": "Changes",
@@ -2232,15 +2500,32 @@ window.__ModuleLoader__.load({
2232
2500
  "detail.why": "Why",
2233
2501
  "detail.loading": "Loading the evidence",
2234
2502
  "detail.error": "Could not load this suggestion.",
2235
- "detail.reviewedBy": "Reviewed by {model}",
2236
- "detail.local": "Found by local rules from tool results. Not reviewed yet.",
2237
- "detail.reviewNote": "Review sends a redacted summary of {count} tasks to {model}: requests, commands, exit codes, and one error line per failed command.",
2238
- "detail.review": "Review with model",
2239
- "detail.reviewing": "Reviewing with {model}",
2240
- "detail.noModel": "No review model is available.",
2241
- "detail.skipped": "The review found nothing worth saving.",
2242
- "detail.failed": "Review failed: {kind}",
2243
- "detail.retry": "Review again",
2503
+ "detail.summarizedBy": "Summary by {model}",
2504
+ "detail.draftedBy": "Draft by {model}",
2505
+ "detail.local": "Local rules found this in tool results. No model has read it yet.",
2506
+ "detail.steps": "Steps the rules matched",
2507
+ "detail.summaryNote": "A summary sends redacted evidence from {count} tasks to {model}: requests, messages during the task, commands, exit codes, and one error line per failed command. It returns a few lines, not a Skill.",
2508
+ "detail.summarize": "Summarize",
2509
+ "detail.resummarize": "Summarize again",
2510
+ "detail.summarizing": "{model} is writing the summary",
2511
+ "detail.noSummaryModel": "No discovery model is available.",
2512
+ "detail.found": "Found",
2513
+ "detail.description": "Purpose",
2514
+ "detail.trigger": "When to use",
2515
+ "detail.plan": "Plan",
2516
+ "detail.risks": "Risks",
2517
+ "detail.verification": "Verify",
2518
+ "detail.plan.create": "Create",
2519
+ "detail.plan.improve": "Add to",
2520
+ "detail.consent": "Drafting sends the same evidence and this plan to {model}, which writes the Skill. You review and edit it before anything is saved.",
2521
+ "detail.noDraftModel": "No drafting model is available.",
2522
+ "detail.draft.create": "Draft Skill",
2523
+ "detail.draft.improve": "Draft addition",
2524
+ "detail.redraft": "Draft again",
2525
+ "detail.drafting": "{model} is drafting the Skill",
2526
+ "detail.skipped": "The summary found nothing worth saving.",
2527
+ "detail.failed.summary": "Summary failed: {kind}",
2528
+ "detail.failed.draft": "Drafting failed: {kind}",
2244
2529
  "detail.openSession": "Open session",
2245
2530
  "detail.cautions": "Check before saving: {list}",
2246
2531
  "failure.timeout": "timed out",
@@ -2262,6 +2547,7 @@ window.__ModuleLoader__.load({
2262
2547
  "evidence.reads.one": "{count} read not shown",
2263
2548
  "evidence.reads.other": "{count} reads not shown",
2264
2549
  "evidence.exit": "exit {code}",
2550
+ "evidence.steering": "During the task",
2265
2551
  "evidence.followup": "Next message",
2266
2552
  "evidence.list": "Operations in task {ref}",
2267
2553
  "caution.sudo": "uses sudo",
@@ -2299,8 +2585,8 @@ window.__ModuleLoader__.load({
2299
2585
  "written.missing": "DSH does not list the Skill yet. Check the file.",
2300
2586
  "error.invalid": "This action does not apply to the suggestion in its current state.",
2301
2587
  "error.missing": "The suggestion no longer exists.",
2302
- "error.busy": "Another review is running. Try again when it finishes.",
2303
- "error.no-model": "No review model is available.",
2588
+ "error.busy": "This suggestion is already with the model.",
2589
+ "error.no-model": "No model is available for this step.",
2304
2590
  "error.exists": "A Skill file already exists at that path.",
2305
2591
  "error.changed": "The file changed after the preview. Nothing was written.",
2306
2592
  "error.outside": "The target is outside the project and user Skill folders.",
@@ -2322,16 +2608,19 @@ window.__ModuleLoader__.load({
2322
2608
  "settings.save": "Save",
2323
2609
  "settings.saving": "Saving",
2324
2610
  "settings.section.discovery": "Discovery",
2325
- "settings.section.review": "Model review",
2611
+ "settings.section.summary": "Summaries",
2612
+ "settings.section.draft": "Drafting",
2326
2613
  "settings.section.limits": "Limits",
2327
2614
  "settings.section.privacy": "Privacy",
2328
2615
  "settings.section.inbox": "Inbox",
2329
2616
  "settings.discovery": "Analyze finished sessions",
2330
2617
  "settings.discoveryHint": "Runs on this machine and reads only the session logs DSH already keeps.",
2331
- "settings.review": "Review in the background",
2332
- "settings.reviewHint": "Sends redacted evidence of strong findings to the review model. When off, a review runs only when you ask for one.",
2333
- "settings.model": "Review model",
2334
- "settings.modelHint": "The default follows the Agent's default model.",
2618
+ "settings.summarize": "Summarize in the background",
2619
+ "settings.summarizeHint": "Sends redacted evidence of strong findings to the discovery model for a short summary. A Skill is drafted only after you approve its summary.",
2620
+ "settings.summaryModel": "Discovery model",
2621
+ "settings.summaryModelHint": "Writes the summaries; a small, fast model is enough. The default follows the Agent's default model.",
2622
+ "settings.draftModel": "Drafting model",
2623
+ "settings.draftModelHint": "Writes the Skill after you approve a summary. The default follows the Agent's default model.",
2335
2624
  "settings.modelDefault": "Default ({model})",
2336
2625
  "settings.modelDefaultUnknown": "Default model",
2337
2626
  "settings.modelLoading": "Loading models",
@@ -2340,12 +2629,14 @@ window.__ModuleLoader__.load({
2340
2629
  "settings.effort": "Reasoning effort",
2341
2630
  "settings.effortHint": "Lower effort is cheaper and faster.",
2342
2631
  "settings.effortDefault": "Model default",
2343
- "settings.timeout": "Timeout",
2344
- "settings.timeoutHint": "Seconds before a review is abandoned.",
2345
- "settings.dailyReviews": "Reviews per day",
2346
- "settings.dailyReviewsHint": "Background reviews allowed each day.",
2347
- "settings.reviewInterval": "Review interval",
2348
- "settings.reviewIntervalHint": "Minutes between background reviews.",
2632
+ "settings.summaryTimeout": "Timeout",
2633
+ "settings.summaryTimeoutHint": "Seconds before a summary is abandoned.",
2634
+ "settings.draftTimeout": "Timeout",
2635
+ "settings.draftTimeoutHint": "Seconds before a draft is abandoned.",
2636
+ "settings.dailySummaries": "Summaries per day",
2637
+ "settings.dailySummariesHint": "Background summaries allowed each day. Your own requests do not count.",
2638
+ "settings.summaryInterval": "Summary interval",
2639
+ "settings.summaryIntervalHint": "Minutes between background summaries.",
2349
2640
  "settings.quietPeriod": "Quiet period",
2350
2641
  "settings.quietPeriodHint": "Minutes a session stays idle before it is analyzed.",
2351
2642
  "settings.historyDays": "History",
@@ -2355,7 +2646,7 @@ window.__ModuleLoader__.load({
2355
2646
  "settings.minOccurrences": "Repeats needed",
2356
2647
  "settings.minOccurrencesHint": "Independent sessions before a repeated workflow counts.",
2357
2648
  "settings.evidenceBudget": "Evidence budget",
2358
- "settings.evidenceBudgetHint": "Characters of evidence sent with one review.",
2649
+ "settings.evidenceBudgetHint": "Characters of evidence sent with one request.",
2359
2650
  "settings.readOutput": "Include error lines",
2360
2651
  "settings.readOutputHint": "Keep one redacted line from each failed command as evidence.",
2361
2652
  "settings.redactPatterns": "Extra redaction",
@@ -2367,13 +2658,14 @@ window.__ModuleLoader__.load({
2367
2658
  "settings.notify.quiet": "Icon only",
2368
2659
  "settings.scope": "New Skills go to",
2369
2660
  "settings.scopeHint": "Project: .agents/skills in the repository. User: ~/.agents/skills.",
2370
- "settings.prompt": "Review guidance",
2371
- "settings.promptHint": "Replaces the built-in guidance. The reply format is always added.",
2661
+ "settings.prompt": "Summary guidance",
2662
+ "settings.promptHint": "Replaces the built-in guidance for deciding what is worth saving. The reply format is always added.",
2372
2663
  "settings.promptPlaceholder": "Built-in guidance",
2373
2664
  "unit.seconds": "s",
2374
2665
  "unit.minutes": "min",
2375
2666
  "unit.days": "days",
2376
2667
  "unit.sessions": "sessions",
2668
+ "unit.requests": "requests",
2377
2669
  "unit.characters": "chars"
2378
2670
  },
2379
2671
  zh: {
@@ -2382,19 +2674,18 @@ window.__ModuleLoader__.load({
2382
2674
  "entry.count.other": "Skill 建议,{count} 条新建议",
2383
2675
  "panel.title": "Skill 建议",
2384
2676
  "panel.analyzing": "正在分析会话",
2385
- "panel.reviewing": "正在复核",
2677
+ "panel.working": "正在请求模型",
2386
2678
  "panel.loading": "正在加载",
2387
2679
  "panel.error": "无法加载建议。",
2388
2680
  "panel.retry": "重试",
2389
2681
  "panel.empty.title": "暂无建议",
2390
2682
  "panel.empty.body": "同一流程在多个会话里重复出现,或某次修复经检查命令验证后,这里会出现建议。",
2391
2683
  "panel.analyzed": "已分析 {sessions} 个会话",
2392
- "section.ready": "可以保存",
2684
+ "section.review": "待你决定",
2393
2685
  "section.found": "本地发现",
2394
2686
  "history.show": "历史({count})",
2395
2687
  "history.hide": "收起历史",
2396
- "banner.noModel": "没有可用的复核模型,请在经验提炼的设置里选择。",
2397
- "banner.reviewOff": "后台复核已关闭,打开一条发现即可手动复核。",
2688
+ "banner.noModel": "没有可用的发现模型,请在经验提炼的设置里选择。",
2398
2689
  "banner.discoveryOff": "会话分析已关闭。",
2399
2690
  "banner.secondary": "另一个 DSH 窗口正在分析会话。",
2400
2691
  "notice.dismissed": "已忽略",
@@ -2408,11 +2699,14 @@ window.__ModuleLoader__.load({
2408
2699
  "kind.complex": "经验证的长任务",
2409
2700
  "meta.sessions.one": "{count} 个会话",
2410
2701
  "meta.sessions.other": "{count} 个会话",
2411
- "state.detected": "未复核",
2412
- "state.reviewing": "复核中",
2413
- "state.ready": "可以保存",
2702
+ "state.detected": "尚未摘要",
2703
+ "state.summarizing": "正在摘要",
2704
+ "state.proposed": "摘要待确认",
2414
2705
  "state.skipped": "不值得保存",
2415
- "state.failed": "复核失败",
2706
+ "state.drafting": "正在生成",
2707
+ "state.ready": "草稿待保存",
2708
+ "state.failed": "摘要失败",
2709
+ "state.draftFailed": "生成失败",
2416
2710
  "state.dismissed": "已忽略",
2417
2711
  "state.written": "已保存",
2418
2712
  "time.now": "刚刚",
@@ -2420,6 +2714,7 @@ window.__ModuleLoader__.load({
2420
2714
  "time.hours": "{count} 小时前",
2421
2715
  "time.days": "{count} 天前",
2422
2716
  "detail.close": "关闭",
2717
+ "detail.tab.summary": "摘要",
2423
2718
  "detail.tab.evidence": "证据",
2424
2719
  "detail.tab.skill": "Skill",
2425
2720
  "detail.tab.changes": "改动",
@@ -2427,15 +2722,32 @@ window.__ModuleLoader__.load({
2427
2722
  "detail.why": "理由",
2428
2723
  "detail.loading": "正在加载证据",
2429
2724
  "detail.error": "无法加载这条建议。",
2430
- "detail.reviewedBy": "由 {model} 复核",
2431
- "detail.local": "由本地规则依据工具结果发现,尚未复核。",
2432
- "detail.reviewNote": "复核会把 {count} 个任务的脱敏摘要发给 {model}:用户请求、命令、退出码,以及每条失败命令的一行错误输出。",
2433
- "detail.review": "用模型复核",
2434
- "detail.reviewing": "{model} 正在复核",
2435
- "detail.noModel": "没有可用的复核模型。",
2436
- "detail.skipped": "复核认为没有值得保存的内容。",
2437
- "detail.failed": "复核失败:{kind}",
2438
- "detail.retry": "重新复核",
2725
+ "detail.summarizedBy": "摘要:{model}",
2726
+ "detail.draftedBy": "草稿:{model}",
2727
+ "detail.local": "由本地规则依据工具结果发现,还没有发给任何模型。",
2728
+ "detail.steps": "规则匹配到的步骤",
2729
+ "detail.summaryNote": "生成摘要会把 {count} 个任务的脱敏证据发给 {model}:用户请求、任务中的插话、命令、退出码,以及每条失败命令的一行错误输出。模型只返回几行摘要,不写 Skill。",
2730
+ "detail.summarize": "生成摘要",
2731
+ "detail.resummarize": "重新摘要",
2732
+ "detail.summarizing": "{model} 正在写摘要",
2733
+ "detail.noSummaryModel": "没有可用的发现模型。",
2734
+ "detail.found": "发现",
2735
+ "detail.description": "用途",
2736
+ "detail.trigger": "何时使用",
2737
+ "detail.plan": "将要做",
2738
+ "detail.risks": "风险",
2739
+ "detail.verification": "验证方式",
2740
+ "detail.plan.create": "新建",
2741
+ "detail.plan.improve": "追加到",
2742
+ "detail.consent": "生成草稿会把同样的证据和这份计划发给 {model},由它写出 Skill。保存之前你可以审阅和修改。",
2743
+ "detail.noDraftModel": "没有可用的生成模型。",
2744
+ "detail.draft.create": "生成 Skill 草稿",
2745
+ "detail.draft.improve": "生成追加内容",
2746
+ "detail.redraft": "重新生成",
2747
+ "detail.drafting": "{model} 正在生成 Skill",
2748
+ "detail.skipped": "摘要认为没有值得保存的内容。",
2749
+ "detail.failed.summary": "摘要失败:{kind}",
2750
+ "detail.failed.draft": "生成草稿失败:{kind}",
2439
2751
  "detail.openSession": "打开会话",
2440
2752
  "detail.cautions": "保存前请确认:{list}",
2441
2753
  "failure.timeout": "超时",
@@ -2457,6 +2769,7 @@ window.__ModuleLoader__.load({
2457
2769
  "evidence.reads.one": "另有 {count} 次读取未列出",
2458
2770
  "evidence.reads.other": "另有 {count} 次读取未列出",
2459
2771
  "evidence.exit": "退出码 {code}",
2772
+ "evidence.steering": "任务中插话",
2460
2773
  "evidence.followup": "下一条消息",
2461
2774
  "evidence.list": "任务 {ref} 的操作",
2462
2775
  "caution.sudo": "使用 sudo",
@@ -2494,8 +2807,8 @@ window.__ModuleLoader__.load({
2494
2807
  "written.missing": "DSH 还没有列出这个 Skill,请检查文件。",
2495
2808
  "error.invalid": "这条建议当前的状态不支持这个操作。",
2496
2809
  "error.missing": "这条建议已不存在。",
2497
- "error.busy": "另一条建议正在复核,完成后再试。",
2498
- "error.no-model": "没有可用的复核模型。",
2810
+ "error.busy": "这条建议正在等模型回复。",
2811
+ "error.no-model": "这一步没有可用的模型。",
2499
2812
  "error.exists": "这个路径已经有 Skill 文件。",
2500
2813
  "error.changed": "预览之后文件被改过,没有写入任何内容。",
2501
2814
  "error.outside": "目标不在项目或用户的 Skill 目录里。",
@@ -2517,16 +2830,19 @@ window.__ModuleLoader__.load({
2517
2830
  "settings.save": "保存",
2518
2831
  "settings.saving": "正在保存",
2519
2832
  "settings.section.discovery": "发现",
2520
- "settings.section.review": "模型复核",
2833
+ "settings.section.summary": "摘要",
2834
+ "settings.section.draft": "生成 Skill",
2521
2835
  "settings.section.limits": "限额",
2522
2836
  "settings.section.privacy": "隐私",
2523
2837
  "settings.section.inbox": "收件箱",
2524
2838
  "settings.discovery": "分析已结束的会话",
2525
2839
  "settings.discoveryHint": "在本机运行,只读取 DSH 已保存的会话日志。",
2526
- "settings.review": "后台复核",
2527
- "settings.reviewHint": "把较强发现的脱敏证据发给复核模型。关闭时只在你手动要求时复核。",
2528
- "settings.model": "复核模型",
2529
- "settings.modelHint": "默认跟随 Agent 的默认模型。",
2840
+ "settings.summarize": "后台生成摘要",
2841
+ "settings.summarizeHint": "把较强发现的脱敏证据发给发现模型,只生成简短摘要。你同意摘要之后才会生成 Skill 草稿。",
2842
+ "settings.summaryModel": "发现模型",
2843
+ "settings.summaryModelHint": "写摘要用,小而快的模型就够。默认跟随 Agent 的默认模型。",
2844
+ "settings.draftModel": "生成模型",
2845
+ "settings.draftModelHint": "你同意摘要后写 Skill 用。默认跟随 Agent 的默认模型。",
2530
2846
  "settings.modelDefault": "默认({model})",
2531
2847
  "settings.modelDefaultUnknown": "默认模型",
2532
2848
  "settings.modelLoading": "正在加载模型",
@@ -2535,12 +2851,14 @@ window.__ModuleLoader__.load({
2535
2851
  "settings.effort": "推理强度",
2536
2852
  "settings.effortHint": "强度越低,越便宜也越快。",
2537
2853
  "settings.effortDefault": "模型默认",
2538
- "settings.timeout": "超时",
2539
- "settings.timeoutHint": "单次复核等待的秒数。",
2540
- "settings.dailyReviews": "每日复核次数",
2541
- "settings.dailyReviewsHint": "每天允许的后台复核次数。",
2542
- "settings.reviewInterval": "复核间隔",
2543
- "settings.reviewIntervalHint": "两次后台复核之间的分钟数。",
2854
+ "settings.summaryTimeout": "超时",
2855
+ "settings.summaryTimeoutHint": "单次摘要等待的秒数。",
2856
+ "settings.draftTimeout": "超时",
2857
+ "settings.draftTimeoutHint": "单次生成等待的秒数。",
2858
+ "settings.dailySummaries": "每日摘要次数",
2859
+ "settings.dailySummariesHint": "每天允许的后台摘要次数,手动发起的不计入。",
2860
+ "settings.summaryInterval": "摘要间隔",
2861
+ "settings.summaryIntervalHint": "两次后台摘要之间的分钟数。",
2544
2862
  "settings.quietPeriod": "静默期",
2545
2863
  "settings.quietPeriodHint": "会话空闲多少分钟后开始分析。",
2546
2864
  "settings.historyDays": "历史范围",
@@ -2550,7 +2868,7 @@ window.__ModuleLoader__.load({
2550
2868
  "settings.minOccurrences": "重复次数",
2551
2869
  "settings.minOccurrencesHint": "重复流程至少要出现在多少个独立会话里。",
2552
2870
  "settings.evidenceBudget": "证据上限",
2553
- "settings.evidenceBudgetHint": "单次复核发送的证据字符数。",
2871
+ "settings.evidenceBudgetHint": "单次请求发送的证据字符数。",
2554
2872
  "settings.readOutput": "包含错误行",
2555
2873
  "settings.readOutputHint": "每条失败命令保留一行脱敏后的输出作为证据。",
2556
2874
  "settings.redactPatterns": "追加脱敏规则",
@@ -2562,13 +2880,14 @@ window.__ModuleLoader__.load({
2562
2880
  "settings.notify.quiet": "只显示图标",
2563
2881
  "settings.scope": "新 Skill 保存到",
2564
2882
  "settings.scopeHint": "项目:仓库里的 .agents/skills。用户:~/.agents/skills。",
2565
- "settings.prompt": "复核要求",
2566
- "settings.promptHint": "替换内置的判断标准,回复格式的约定始终保留。",
2883
+ "settings.prompt": "摘要要求",
2884
+ "settings.promptHint": "替换内置的「是否值得保存」判断标准,回复格式的约定始终保留。",
2567
2885
  "settings.promptPlaceholder": "内置的判断标准",
2568
2886
  "unit.seconds": "秒",
2569
2887
  "unit.minutes": "分钟",
2570
2888
  "unit.days": "天",
2571
2889
  "unit.sessions": "个",
2890
+ "unit.requests": "次",
2572
2891
  "unit.characters": "字符"
2573
2892
  }
2574
2893
  };
@@ -2612,19 +2931,23 @@ window.__ModuleLoader__.load({
2612
2931
  //#region src/client/settings.tsx
2613
2932
  /**
2614
2933
  * 插件页里经验提炼条目的配置页。改动先暂存,点保存才按读到的版本号写回 profile;
2615
- * 写入被拒绝或传输失败时保留暂存值并提示。复核模型从 Host 的模型目录里选,
2616
- * 留空时跟随 Agent 的默认模型。
2934
+ * 写入被拒绝或传输失败时保留暂存值并提示。发现模型(写摘要)与生成模型(写 Skill 草稿)
2935
+ * 分别从 Host 的模型目录里选,留空时跟随 Agent 的默认模型。
2617
2936
  */
2618
2937
  /** 与 Host `Config` 相同的默认值。 */
2619
2938
  const DEFAULT_SETTINGS = {
2620
2939
  discovery: true,
2621
- review: false,
2622
- provider: "",
2623
- model: "",
2624
- effort: "",
2625
- timeout: 90,
2626
- dailyReviews: 6,
2627
- reviewInterval: 10,
2940
+ summarize: false,
2941
+ summaryProvider: "",
2942
+ summaryModel: "",
2943
+ summaryEffort: "",
2944
+ summaryTimeout: 60,
2945
+ draftProvider: "",
2946
+ draftModel: "",
2947
+ draftEffort: "",
2948
+ draftTimeout: 180,
2949
+ dailySummaries: 6,
2950
+ summaryInterval: 10,
2628
2951
  quietPeriod: 2,
2629
2952
  historyDays: 30,
2630
2953
  maxSessions: 200,
@@ -2638,17 +2961,22 @@ window.__ModuleLoader__.load({
2638
2961
  };
2639
2962
  /** 数值字段的范围与单位,与 Host `Config` 一致。 */
2640
2963
  const NUMBERS = {
2641
- timeout: {
2964
+ summaryTimeout: {
2642
2965
  min: 10,
2643
2966
  max: 600,
2644
2967
  unit: "unit.seconds"
2645
2968
  },
2646
- dailyReviews: {
2969
+ draftTimeout: {
2970
+ min: 10,
2971
+ max: 900,
2972
+ unit: "unit.seconds"
2973
+ },
2974
+ dailySummaries: {
2647
2975
  min: 1,
2648
2976
  max: 100,
2649
- unit: "unit.sessions"
2977
+ unit: "unit.requests"
2650
2978
  },
2651
- reviewInterval: {
2979
+ summaryInterval: {
2652
2980
  min: 1,
2653
2981
  max: 1440,
2654
2982
  unit: "unit.minutes"
@@ -2682,13 +3010,17 @@ window.__ModuleLoader__.load({
2682
3010
  /** 设置服务快照值的结构;缺失或类型不符的字段取默认值。 */
2683
3011
  const SettingsSchema = /* @__PURE__ */ object({
2684
3012
  discovery: /* @__PURE__ */ fallback(/* @__PURE__ */ boolean(), DEFAULT_SETTINGS.discovery),
2685
- review: /* @__PURE__ */ fallback(/* @__PURE__ */ boolean(), DEFAULT_SETTINGS.review),
2686
- provider: /* @__PURE__ */ fallback(/* @__PURE__ */ string(), DEFAULT_SETTINGS.provider),
2687
- model: /* @__PURE__ */ fallback(/* @__PURE__ */ string(), DEFAULT_SETTINGS.model),
2688
- effort: /* @__PURE__ */ fallback(/* @__PURE__ */ string(), DEFAULT_SETTINGS.effort),
2689
- timeout: /* @__PURE__ */ fallback(/* @__PURE__ */ number(), DEFAULT_SETTINGS.timeout),
2690
- dailyReviews: /* @__PURE__ */ fallback(/* @__PURE__ */ number(), DEFAULT_SETTINGS.dailyReviews),
2691
- reviewInterval: /* @__PURE__ */ fallback(/* @__PURE__ */ number(), DEFAULT_SETTINGS.reviewInterval),
3013
+ summarize: /* @__PURE__ */ fallback(/* @__PURE__ */ boolean(), DEFAULT_SETTINGS.summarize),
3014
+ summaryProvider: /* @__PURE__ */ fallback(/* @__PURE__ */ string(), DEFAULT_SETTINGS.summaryProvider),
3015
+ summaryModel: /* @__PURE__ */ fallback(/* @__PURE__ */ string(), DEFAULT_SETTINGS.summaryModel),
3016
+ summaryEffort: /* @__PURE__ */ fallback(/* @__PURE__ */ string(), DEFAULT_SETTINGS.summaryEffort),
3017
+ summaryTimeout: /* @__PURE__ */ fallback(/* @__PURE__ */ number(), DEFAULT_SETTINGS.summaryTimeout),
3018
+ draftProvider: /* @__PURE__ */ fallback(/* @__PURE__ */ string(), DEFAULT_SETTINGS.draftProvider),
3019
+ draftModel: /* @__PURE__ */ fallback(/* @__PURE__ */ string(), DEFAULT_SETTINGS.draftModel),
3020
+ draftEffort: /* @__PURE__ */ fallback(/* @__PURE__ */ string(), DEFAULT_SETTINGS.draftEffort),
3021
+ draftTimeout: /* @__PURE__ */ fallback(/* @__PURE__ */ number(), DEFAULT_SETTINGS.draftTimeout),
3022
+ dailySummaries: /* @__PURE__ */ fallback(/* @__PURE__ */ number(), DEFAULT_SETTINGS.dailySummaries),
3023
+ summaryInterval: /* @__PURE__ */ fallback(/* @__PURE__ */ number(), DEFAULT_SETTINGS.summaryInterval),
2692
3024
  quietPeriod: /* @__PURE__ */ fallback(/* @__PURE__ */ number(), DEFAULT_SETTINGS.quietPeriod),
2693
3025
  historyDays: /* @__PURE__ */ fallback(/* @__PURE__ */ number(), DEFAULT_SETTINGS.historyDays),
2694
3026
  maxSessions: /* @__PURE__ */ fallback(/* @__PURE__ */ number(), DEFAULT_SETTINGS.maxSessions),
@@ -2706,13 +3038,17 @@ window.__ModuleLoader__.load({
2706
3038
  }
2707
3039
  const SETTING_KEYS = [
2708
3040
  "discovery",
2709
- "review",
2710
- "provider",
2711
- "model",
2712
- "effort",
2713
- "timeout",
2714
- "dailyReviews",
2715
- "reviewInterval",
3041
+ "summarize",
3042
+ "summaryProvider",
3043
+ "summaryModel",
3044
+ "summaryEffort",
3045
+ "summaryTimeout",
3046
+ "draftProvider",
3047
+ "draftModel",
3048
+ "draftEffort",
3049
+ "draftTimeout",
3050
+ "dailySummaries",
3051
+ "summaryInterval",
2716
3052
  "quietPeriod",
2717
3053
  "historyDays",
2718
3054
  "maxSessions",
@@ -2725,9 +3061,10 @@ window.__ModuleLoader__.load({
2725
3061
  "prompt"
2726
3062
  ];
2727
3063
  const NUMBER_KEYS = [
2728
- "timeout",
2729
- "dailyReviews",
2730
- "reviewInterval",
3064
+ "summaryTimeout",
3065
+ "draftTimeout",
3066
+ "dailySummaries",
3067
+ "summaryInterval",
2731
3068
  "quietPeriod",
2732
3069
  "historyDays",
2733
3070
  "maxSessions",
@@ -2785,8 +3122,27 @@ window.__ModuleLoader__.load({
2785
3122
  function modelValue(provider, model) {
2786
3123
  return provider === "" || model === "" ? "" : `${provider}${SEPARATOR}${model}`;
2787
3124
  }
2788
- function ModelFields({ staged, catalog, disabled, onChange, t }) {
3125
+ /** 每个阶段的 provider、模型与推理强度对应的设置字段。 */
3126
+ const MODEL_KEYS = {
3127
+ summary: {
3128
+ provider: "summaryProvider",
3129
+ model: "summaryModel",
3130
+ effort: "summaryEffort"
3131
+ },
3132
+ draft: {
3133
+ provider: "draftProvider",
3134
+ model: "draftModel",
3135
+ effort: "draftEffort"
3136
+ }
3137
+ };
3138
+ function ModelFields({ stage, staged: values, catalog, disabled, onChange, t }) {
2789
3139
  const id = (0, react.useId)();
3140
+ const keys = MODEL_KEYS[stage];
3141
+ const staged = {
3142
+ provider: values[keys.provider],
3143
+ model: values[keys.model],
3144
+ effort: values[keys.effort]
3145
+ };
2790
3146
  const groups = catalog.status === "ready" ? catalog.catalog.groups : [];
2791
3147
  const current = modelValue(staged.provider, staged.model);
2792
3148
  const fallback = catalog.status === "ready" ? catalog.catalog.default : null;
@@ -2797,8 +3153,8 @@ window.__ModuleLoader__.load({
2797
3153
  const listed = groups.some((group) => group.models.some((model) => modelValue(group.id, model.id) === current));
2798
3154
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Field, {
2799
3155
  id: `${id}-model`,
2800
- label: t("settings.model"),
2801
- hint: t("settings.modelHint"),
3156
+ label: t(stage === "summary" ? "settings.summaryModel" : "settings.draftModel"),
3157
+ hint: t(stage === "summary" ? "settings.summaryModelHint" : "settings.draftModelHint"),
2802
3158
  children: [
2803
3159
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
2804
3160
  id: `${id}-model`,
@@ -2808,9 +3164,9 @@ window.__ModuleLoader__.load({
2808
3164
  onChange: (event) => {
2809
3165
  const [provider = "", model = ""] = event.target.value.split(SEPARATOR);
2810
3166
  onChange({
2811
- provider,
2812
- model,
2813
- effort: ""
3167
+ [keys.provider]: provider,
3168
+ [keys.model]: model,
3169
+ [keys.effort]: ""
2814
3170
  });
2815
3171
  },
2816
3172
  children: [
@@ -2850,7 +3206,7 @@ window.__ModuleLoader__.load({
2850
3206
  className: settings_module_css_default.select,
2851
3207
  value: staged.effort,
2852
3208
  disabled,
2853
- onChange: (event) => onChange({ effort: event.target.value }),
3209
+ onChange: (event) => onChange({ [keys.effort]: event.target.value }),
2854
3210
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
2855
3211
  value: "",
2856
3212
  children: t("settings.effortDefault")
@@ -2991,17 +3347,18 @@ window.__ModuleLoader__.load({
2991
3347
  children: [
2992
3348
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
2993
3349
  className: settings_module_css_default.title,
2994
- children: translate("settings.section.review")
3350
+ children: translate("settings.section.summary")
2995
3351
  }),
2996
- toggle("review", "settings.review", "settings.reviewHint"),
3352
+ toggle("summarize", "settings.summarize", "settings.summarizeHint"),
2997
3353
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModelFields, {
3354
+ stage: "summary",
2998
3355
  staged,
2999
3356
  catalog: models,
3000
3357
  disabled: !writable,
3001
3358
  onChange: update,
3002
3359
  t: translate
3003
3360
  }),
3004
- number("timeout"),
3361
+ number("summaryTimeout"),
3005
3362
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
3006
3363
  id: `${id}-prompt`,
3007
3364
  label: translate("settings.prompt"),
@@ -3019,6 +3376,24 @@ window.__ModuleLoader__.load({
3019
3376
  })
3020
3377
  ]
3021
3378
  }),
3379
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
3380
+ className: settings_module_css_default.section,
3381
+ children: [
3382
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
3383
+ className: settings_module_css_default.title,
3384
+ children: translate("settings.section.draft")
3385
+ }),
3386
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModelFields, {
3387
+ stage: "draft",
3388
+ staged,
3389
+ catalog: models,
3390
+ disabled: !writable,
3391
+ onChange: update,
3392
+ t: translate
3393
+ }),
3394
+ number("draftTimeout")
3395
+ ]
3396
+ }),
3022
3397
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
3023
3398
  className: settings_module_css_default.section,
3024
3399
  children: [
@@ -3026,8 +3401,8 @@ window.__ModuleLoader__.load({
3026
3401
  className: settings_module_css_default.title,
3027
3402
  children: translate("settings.section.limits")
3028
3403
  }),
3029
- number("dailyReviews"),
3030
- number("reviewInterval"),
3404
+ number("dailySummaries"),
3405
+ number("summaryInterval"),
3031
3406
  number("evidenceBudget")
3032
3407
  ]
3033
3408
  }),
@@ -3116,7 +3491,9 @@ window.__ModuleLoader__.load({
3116
3491
  }
3117
3492
  //#endregion
3118
3493
  //#region src/client/store.ts
3119
- /** 进行中时的读取间隔。 */
3494
+ /** 模型正在写摘要或草稿时的读取间隔:回复一到,界面在一秒内换掉进行中的占位。 */
3495
+ const MODEL_INTERVAL = 1e3;
3496
+ /** 分析会话或核对写入时的读取间隔。 */
3120
3497
  const BUSY_INTERVAL = 3e3;
3121
3498
  /** 空闲时的读取间隔。 */
3122
3499
  const IDLE_INTERVAL = 45e3;
@@ -3128,9 +3505,15 @@ window.__ModuleLoader__.load({
3128
3505
  error: null,
3129
3506
  notice: null
3130
3507
  };
3131
- /** 有后台工作正在进行:分析、复核,或写入后核对 DSH 能否发现。 */
3132
- function busy(inbox) {
3133
- return inbox !== null && (inbox.status.scanning || inbox.status.reviewing !== null || inbox.items.some((item) => item.write?.checking === true));
3508
+ /**
3509
+ * 下一次读取的间隔:模型调用进行中最短;分析会话或写入后核对 DSH 能否发现时居中;其余时候最长。
3510
+ * @internal 导出供测试使用。
3511
+ */
3512
+ function pollInterval(inbox) {
3513
+ if (inbox === null) return IDLE_INTERVAL;
3514
+ if (inbox.status.working > 0) return MODEL_INTERVAL;
3515
+ if (inbox.status.scanning || inbox.items.some((item) => item.write?.checking === true)) return BUSY_INTERVAL;
3516
+ return IDLE_INTERVAL;
3134
3517
  }
3135
3518
  /**
3136
3519
  * 创建收件箱状态。
@@ -3152,7 +3535,7 @@ window.__ModuleLoader__.load({
3152
3535
  timer = setTimeout(() => {
3153
3536
  if (document.visibilityState === "hidden") schedule();
3154
3537
  else refresh();
3155
- }, busy(state.inbox) ? BUSY_INTERVAL : IDLE_INTERVAL);
3538
+ }, pollInterval(state.inbox));
3156
3539
  };
3157
3540
  async function refresh() {
3158
3541
  const current = ++generation;