dsh-mnemon-source-skills 0.5.8 → 0.5.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -61,3 +61,5 @@ The optional `./client` entry is an ordinary DSH Client plugin. This package own
61
61
  From a source checkout, install the declared dependencies and run `pnpm verify` to check the Source's behavior, the market's parsers and archive reader, and Source-backed page interactions.
62
62
 
63
63
  [Plugin development](https://github.com/omdsh-dev/dsh-mnemon/blob/main/docs/en/development/extensions.md) · [中文指南](https://github.com/omdsh-dev/dsh-mnemon/blob/main/docs/zh-CN/development/extensions.md)
64
+
65
+ The optional Host `skills.autoAdopt` setting is off by default. It queues only new idle-review suggestions, requires complete paired text backtests without per-case or per-check regression, retries at most two rewrites, and uses Source-owned adoption under the storage lock. Source checks the exact draft/cases/results and current file again; cancellation is checked inside queued writes. Automatic activity and manual-review reasons are visible on each suggestion. See the [Skills guide](../../docs/en/guides/skills.md#optional-automatic-adoption) for limits and the distinction from real external-action validation.
package/lib/client.js CHANGED
@@ -35,7 +35,7 @@ window.__ModuleLoader__.load({
35
35
  function json(value) {
36
36
  return JSON.parse(JSON.stringify(value ?? null));
37
37
  }
38
- const CONFLICT = /revision conflict|changed since it was read; reload/u;
38
+ const CONFLICT = /revision conflict|Skills Source changed since it was read; reload/u;
39
39
  /**
40
40
  * The Source's revision moves with every background review and run, so each
41
41
  * write fetches it first and tries once more if a background write slipped in
@@ -127,6 +127,7 @@ window.__ModuleLoader__.load({
127
127
  id,
128
128
  content
129
129
  }),
130
+ cancelRun: (id) => mutate("run-cancel", { id }),
130
131
  adoptDraft: (id, root) => mutate("draft-adopt", root === void 0 ? { id } : {
131
132
  id,
132
133
  root
@@ -317,7 +318,9 @@ window.__ModuleLoader__.load({
317
318
  "evolution.noAgent": "当前没有可用的任务 Agent",
318
319
  "evolution.drafting": "正在生成草案",
319
320
  "evolution.draftFailed": "草案生成失败:{error}",
320
- "evolution.skillGone": "这个技能已不存在。",
321
+ "evolution.skillGone": "原技能文件已删除、移动或无法读取。请恢复文件,或在新位置打开技能并重新生成草案。",
322
+ "evolution.originUnavailable": "旧记录未可靠保存原工作区。请切换到原工作区,在技能页打开该技能并重新生成草案;现有草案和证据会保留。",
323
+ "evolution.currentSkill": "当前技能文件",
321
324
  "evolution.skillChanged": "技能在建议之后被改过,生成草案前会以当前版本为准。",
322
325
  "evolution.adopted": "已采纳",
323
326
  "evolution.openSkill": "查看技能",
@@ -533,7 +536,34 @@ window.__ModuleLoader__.load({
533
536
  "evolution.loadFailedBody": "请重试,或在左侧选择其他建议。",
534
537
  "tests.model": "全局模型",
535
538
  "tests.modelCard": "所选回测模型",
536
- "tests.modelAuto": "跟随默认"
539
+ "tests.modelAuto": "跟随默认",
540
+ "draft.baseChanged": "原技能已变更,此草案已过期。请基于当前文件重新生成并回测。",
541
+ "draft.targetExists": "同名技能已存在。请重新生成为改进草案并回测,再确认采纳。",
542
+ "draft.refreshGuidance": "基于当前技能文件重新生成,保留最近的修改,并重新运行回测。",
543
+ "draft.regenerate": "重新生成并回测",
544
+ "draft.cancelBacktest": "取消回测",
545
+ "draft.interrupted": "回测已中断。可以重新回测,或在检查草案后手动采纳。",
546
+ "evolution.auto.title": "自动采纳",
547
+ "evolution.auto.history": "自动采纳活动记录",
548
+ "evolution.auto.attempt": "第 {count} 轮",
549
+ "evolution.auto.scores": "基线 {baseline} / 草案 {draft}",
550
+ "evolution.auto.stage.queued": "等待处理",
551
+ "evolution.auto.stage.drafting": "正在起草",
552
+ "evolution.auto.stage.backtesting": "正在对比回测",
553
+ "evolution.auto.stage.rewriting": "依据退步结果重写",
554
+ "evolution.auto.stage.adopted": "已自动采纳",
555
+ "evolution.auto.stage.paused": "已停止自动处理",
556
+ "evolution.auto.stage.review": "需要人工审查",
557
+ "evolution.auto.reason.disabled": "自动采纳或所需权限已关闭",
558
+ "evolution.auto.reason.no-cases": "没有启用的回测用例",
559
+ "evolution.auto.reason.case-limit": "启用的用例超过每轮上限;调整用例或上限后手动处理本建议",
560
+ "evolution.auto.reason.unmeasured": "回测未完整测出所有检查项,或存在图片依赖、错误、中断",
561
+ "evolution.auto.reason.regressed": "至少一个用例或原先通过的检查项出现退步",
562
+ "evolution.auto.reason.changed": "建议、草案或用例已变动",
563
+ "evolution.auto.reason.failed": "自动处理失败,保留人工处理入口",
564
+ "evolution.auto.reason.queue-full": "自动处理队列已满",
565
+ "evolution.auto.reason.model-unavailable": "无法确定起草或回测模型",
566
+ "evolution.auto.reason.interrupted": "Host 已停止,不自动重启此建议"
537
567
  };
538
568
  var en = {
539
569
  "page.title": "Skills",
@@ -671,7 +701,9 @@ window.__ModuleLoader__.load({
671
701
  "evolution.noAgent": "No task Agent is available",
672
702
  "evolution.drafting": "Drafting",
673
703
  "evolution.draftFailed": "Drafting failed: {error}",
674
- "evolution.skillGone": "This skill no longer exists.",
704
+ "evolution.skillGone": "The original skill file was deleted, moved, or cannot be read. Restore it or open the skill at its new location and generate a new draft.",
705
+ "evolution.originUnavailable": "The original workspace was not recorded reliably. Select its original workspace, open the skill on the Skills page and generate a new draft; the existing draft and evidence are kept.",
706
+ "evolution.currentSkill": "Current skill file",
675
707
  "evolution.skillChanged": "The skill changed after this suggestion; the draft starts from its current version.",
676
708
  "evolution.adopted": "Adopted",
677
709
  "evolution.openSkill": "Open skill",
@@ -887,7 +919,34 @@ window.__ModuleLoader__.load({
887
919
  "evolution.loadFailedBody": "Retry, or select another suggestion on the left.",
888
920
  "tests.model": "Global model",
889
921
  "tests.modelCard": "Selected model",
890
- "tests.modelAuto": "Follow default"
922
+ "tests.modelAuto": "Follow default",
923
+ "draft.baseChanged": "The original skill changed. This draft is out of date; regenerate it against the current file and backtest it.",
924
+ "draft.targetExists": "A skill with this name now exists. Regenerate an update and backtest it before adopting.",
925
+ "draft.refreshGuidance": "Regenerate against the current skill file, preserving its latest changes, then run backtests.",
926
+ "draft.regenerate": "Regenerate and backtest",
927
+ "draft.cancelBacktest": "Cancel backtests",
928
+ "draft.interrupted": "Backtests were interrupted. Run them again, or review the draft before adopting manually.",
929
+ "evolution.auto.title": "Automatic adoption",
930
+ "evolution.auto.history": "Automatic adoption activity",
931
+ "evolution.auto.attempt": "Attempt {count}",
932
+ "evolution.auto.scores": "Baseline {baseline} / draft {draft}",
933
+ "evolution.auto.stage.queued": "Queued",
934
+ "evolution.auto.stage.drafting": "Drafting",
935
+ "evolution.auto.stage.backtesting": "Backtesting a pair",
936
+ "evolution.auto.stage.rewriting": "Rewriting after regression",
937
+ "evolution.auto.stage.adopted": "Automatically adopted",
938
+ "evolution.auto.stage.paused": "Automatic work stopped",
939
+ "evolution.auto.stage.review": "Manual review required",
940
+ "evolution.auto.reason.disabled": "Automatic adoption or its required permissions were disabled",
941
+ "evolution.auto.reason.no-cases": "No enabled evaluation cases",
942
+ "evolution.auto.reason.case-limit": "Enabled cases exceed the per-run limit; adjust cases or the limit and review this suggestion manually",
943
+ "evolution.auto.reason.unmeasured": "Some checks were not fully measured, or a case needs images, failed or was interrupted",
944
+ "evolution.auto.reason.regressed": "At least one case or previously passing check regressed",
945
+ "evolution.auto.reason.changed": "The suggestion, draft or evaluation cases changed",
946
+ "evolution.auto.reason.failed": "Automatic work failed; manual review remains available",
947
+ "evolution.auto.reason.queue-full": "Automatic queue is full",
948
+ "evolution.auto.reason.model-unavailable": "No resolved draft or backtest model",
949
+ "evolution.auto.reason.interrupted": "Host stopped; this suggestion will not restart automatically"
891
950
  };
892
951
  //#endregion
893
952
  //#region \0source-css:/home/runner/work/dsh-mnemon/dsh-mnemon/plugins/dsh-mnemon-source-skills/presentation/page.module.css.mjs
@@ -3726,6 +3785,7 @@ window.__ModuleLoader__.load({
3726
3785
  context.refresh();
3727
3786
  } catch (error) {
3728
3787
  context.fail(error);
3788
+ context.refresh();
3729
3789
  } finally {
3730
3790
  setBusy(false);
3731
3791
  }
@@ -3748,7 +3808,7 @@ window.__ModuleLoader__.load({
3748
3808
  className: css.skKicker,
3749
3809
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ActionTag, { action: suggestion.action }), suggestion.status !== "pending" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatusTag, { status: suggestion.status })]
3750
3810
  }),
3751
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: view.skill === void 0 ? suggestion.skillName : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3811
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: view.skill === void 0 || view.skillInCatalog === false ? suggestion.skillName : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3752
3812
  type: "button",
3753
3813
  className: css.skTitleLink,
3754
3814
  onClick: () => context.navigate({
@@ -3811,12 +3871,13 @@ window.__ModuleLoader__.load({
3811
3871
  suggestion.rationale !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Clamp, { text: suggestion.rationale }),
3812
3872
  suggestion.action === "update" && view.skill === void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
3813
3873
  className: css.skWarn,
3814
- children: t("evolution.skillGone")
3874
+ children: t(view.resolution === "origin-unavailable" ? "evolution.originUnavailable" : "evolution.skillGone")
3815
3875
  }),
3816
3876
  suggestion.action === "update" && view.skill !== void 0 && !view.hashCurrent && suggestion.status === "pending" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
3817
3877
  className: css.skWarn,
3818
3878
  children: t("evolution.skillChanged")
3819
3879
  }),
3880
+ suggestion.autoAdoption !== void 0 && suggestion.autoAdoption.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AutoAdoptionHistory, { events: suggestion.autoAdoption }),
3820
3881
  suggestion.status === "drafting" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Busy, { label: t("evolution.drafting") }),
3821
3882
  suggestion.lastError !== void 0 && suggestion.status === "pending" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
3822
3883
  className: css.inlineError,
@@ -3832,7 +3893,7 @@ window.__ModuleLoader__.load({
3832
3893
  children: [
3833
3894
  t("evolution.adopted"),
3834
3895
  " ",
3835
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
3896
+ view.skillInCatalog !== false && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
3836
3897
  type: "button",
3837
3898
  className: css.skLink,
3838
3899
  onClick: () => context.navigate({
@@ -3843,9 +3904,16 @@ window.__ModuleLoader__.load({
3843
3904
  })
3844
3905
  ]
3845
3906
  }),
3846
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3907
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3847
3908
  className: css.skFolds,
3848
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Fold, {
3909
+ children: [view.skill !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Fold, {
3910
+ title: t("evolution.currentSkill"),
3911
+ meta: t(`root.${view.skill.root}`),
3912
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
3913
+ className: css.skMuted,
3914
+ children: view.skill.path
3915
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SkillMarkdown, { body: view.skill.body })]
3916
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Fold, {
3849
3917
  title: t("evolution.evidence"),
3850
3918
  meta: t("evolution.evidenceMeta", {
3851
3919
  conversations: references.length,
@@ -3884,7 +3952,7 @@ window.__ModuleLoader__.load({
3884
3952
  })]
3885
3953
  }, index);
3886
3954
  })
3887
- })
3955
+ })]
3888
3956
  })
3889
3957
  ]
3890
3958
  });
@@ -3968,6 +4036,7 @@ window.__ModuleLoader__.load({
3968
4036
  context.refresh();
3969
4037
  } catch (error) {
3970
4038
  context.fail(error);
4039
+ context.refresh();
3971
4040
  } finally {
3972
4041
  setBusy(false);
3973
4042
  }
@@ -3986,16 +4055,22 @@ window.__ModuleLoader__.load({
3986
4055
  });
3987
4056
  const started = await context.client.backtest({
3988
4057
  skillName: draft.skillName,
3989
- target: { kind: draft.action === "update" ? "current" : "none" }
4058
+ target: {
4059
+ kind: draft.action === "update" ? "current" : "none",
4060
+ ...draft.action === "update" ? { draftId: draft.id } : {}
4061
+ }
3990
4062
  });
3991
4063
  await context.client.linkRun(draft.id, started.runId);
3992
4064
  });
3993
- const redraft = (note) => act(() => context.client.generateDraft({
3994
- ...draft.suggestionId === void 0 ? { skill: draft.skillName } : { suggestionId: draft.suggestionId },
4065
+ const redraft = (note, backtest = false) => act(() => context.client.generateDraft({
4066
+ ...backtest ? { backtest: true } : {},
4067
+ draftId: draft.id,
4068
+ ...draft.suggestionId === void 0 ? {} : { suggestionId: draft.suggestionId },
3995
4069
  guidance: note,
3996
4070
  ...route
3997
4071
  }), () => setRedrafting(null));
3998
4072
  const ready = draft.status === "ready";
4073
+ const originalAvailable = draft.action !== "update" || view.base !== void 0;
3999
4074
  const progress = (run) => run === void 0 ? 0 : run.progress.done;
4000
4075
  const total = (candidate?.progress.total ?? 0) + (baseline?.progress.total ?? 0);
4001
4076
  const results = /* @__PURE__ */ new Map();
@@ -4029,7 +4104,7 @@ window.__ModuleLoader__.load({
4029
4104
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
4030
4105
  variant: "primary",
4031
4106
  size: "sm",
4032
- disabled: !context.writable || busy || editing !== null || running,
4107
+ disabled: !context.writable || busy || editing !== null || running || view.conflict !== void 0 || !originalAvailable,
4033
4108
  onClick: () => regressed ? setConfirmAdopt(true) : void adopt(),
4034
4109
  children: t("draft.adopt")
4035
4110
  }),
@@ -4047,13 +4122,27 @@ window.__ModuleLoader__.load({
4047
4122
  items: [{
4048
4123
  id: "edit",
4049
4124
  label: t("draft.edit")
4050
- }, ...context.client.canAssist("skill-draft") ? [{
4125
+ }, ...originalAvailable && context.client.canAssist("skill-draft") ? [{
4051
4126
  id: "redraft",
4052
4127
  label: t("draft.redraft")
4053
4128
  }] : []]
4054
4129
  })
4055
4130
  ] })]
4056
4131
  }),
4132
+ !originalAvailable && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
4133
+ className: css.skWarn,
4134
+ children: t(view.resolution === "origin-unavailable" ? "evolution.originUnavailable" : "evolution.skillGone")
4135
+ }),
4136
+ view.conflict !== void 0 && ready && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4137
+ className: css.skWarn,
4138
+ role: "status",
4139
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: t(view.conflict === "target-exists" ? "draft.targetExists" : "draft.baseChanged") }), originalAvailable && context.client.canAssist("skill-draft") && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
4140
+ size: "sm",
4141
+ disabled: !context.writable || busy || running,
4142
+ onClick: () => void redraft(t("draft.refreshGuidance"), true),
4143
+ children: t("draft.regenerate")
4144
+ })]
4145
+ }),
4057
4146
  draft.notes !== "" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Clamp, { text: draft.notes }),
4058
4147
  draft.changes.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MoreList, {
4059
4148
  items: draft.changes,
@@ -4062,37 +4151,53 @@ window.__ModuleLoader__.load({
4062
4151
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4063
4152
  className: css.skBacktest,
4064
4153
  "data-tone": regressed ? "bad" : candidateScore !== void 0 && baselineScore !== void 0 && candidateScore > baselineScore ? "good" : void 0,
4065
- children: [running ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Busy, { label: t("draft.backtesting", {
4066
- done: progress(candidate) + progress(baseline),
4067
- total
4068
- }) }) : candidateScore !== void 0 && baselineScore !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
4069
- type: "button",
4070
- className: css.skBacktestLine,
4071
- "aria-expanded": details,
4072
- onClick: () => setDetails((value) => !value),
4073
- children: [
4074
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("draft.backtest") }),
4075
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("strong", { children: [
4076
- baseline?.target.kind === "none" ? t("draft.withoutSkill") : t("draft.current"),
4077
- " ",
4078
- points(baselineScore),
4079
- " → ",
4080
- t("draft.candidate"),
4081
- " ",
4082
- points(candidateScore)
4083
- ] }),
4084
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("em", { children: candidateScore === baselineScore ? t("draft.same") : `${candidateScore > baselineScore ? "+" : "−"}${Math.round(Math.abs(candidateScore - baselineScore) * 100)}` })
4085
- ]
4086
- }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4087
- className: css.skMuted,
4088
- children: cases === 0 ? t("draft.noTests") : view.runs.length === 0 ? t("draft.notTested") : t("draft.notComparable")
4089
- }), !running && (cases ?? 0) > 0 && context.client.canAssist("skill-backtest") && ready && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
4090
- variant: "ghost",
4091
- size: "sm",
4092
- disabled: !context.writable || busy,
4093
- onClick: () => void backtest(),
4094
- children: view.runs.length === 0 ? t("draft.runBacktest") : t("draft.rerun")
4095
- })]
4154
+ children: [
4155
+ running ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Busy, { label: t("draft.backtesting", {
4156
+ done: progress(candidate) + progress(baseline),
4157
+ total
4158
+ }) }) : candidateScore !== void 0 && baselineScore !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
4159
+ type: "button",
4160
+ className: css.skBacktestLine,
4161
+ "aria-expanded": details,
4162
+ onClick: () => setDetails((value) => !value),
4163
+ children: [
4164
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("draft.backtest") }),
4165
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("strong", { children: [
4166
+ baseline?.target.kind === "none" ? t("draft.withoutSkill") : t("draft.current"),
4167
+ " ",
4168
+ points(baselineScore),
4169
+ " → ",
4170
+ t("draft.candidate"),
4171
+ " ",
4172
+ points(candidateScore)
4173
+ ] }),
4174
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("em", { children: candidateScore === baselineScore ? t("draft.same") : `${candidateScore > baselineScore ? "+" : "−"}${Math.round(Math.abs(candidateScore - baselineScore) * 100)}` })
4175
+ ]
4176
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4177
+ className: css.skMuted,
4178
+ children: cases === 0 ? t("draft.noTests") : view.runs.length === 0 ? t("draft.notTested") : t("draft.notComparable")
4179
+ }),
4180
+ running && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
4181
+ variant: "ghost",
4182
+ size: "sm",
4183
+ disabled: !context.writable || busy,
4184
+ onClick: () => void act(async () => {
4185
+ for (const run of view.runs.filter((item) => item.status === "running")) await context.client.cancelRun(run.id);
4186
+ }),
4187
+ children: t("draft.cancelBacktest")
4188
+ }),
4189
+ !running && (candidate?.status === "interrupted" || baseline?.status === "interrupted") && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
4190
+ role: "status",
4191
+ children: t("draft.interrupted")
4192
+ }),
4193
+ !running && (cases ?? 0) > 0 && context.client.canAssist("skill-backtest") && ready && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
4194
+ variant: "ghost",
4195
+ size: "sm",
4196
+ disabled: !context.writable || busy || !originalAvailable,
4197
+ onClick: () => void backtest(),
4198
+ children: view.runs.length === 0 ? t("draft.runBacktest") : t("draft.rerun")
4199
+ })
4200
+ ]
4096
4201
  }),
4097
4202
  excluded > 0 && !running && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
4098
4203
  className: css.skMuted,
@@ -4188,6 +4293,35 @@ window.__ModuleLoader__.load({
4188
4293
  ]
4189
4294
  });
4190
4295
  }
4296
+ function AutoAdoptionHistory(props) {
4297
+ const t = useT();
4298
+ const latest = props.events[props.events.length - 1];
4299
+ const describe = (event) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
4300
+ t(`evolution.auto.stage.${event.stage}`),
4301
+ event.attempt > 0 ? ` · ${t("evolution.auto.attempt", { count: event.attempt })}` : "",
4302
+ event.reason === void 0 ? "" : ` · ${t(`evolution.auto.reason.${event.reason}`)}`,
4303
+ event.baselineScore === void 0 || event.draftScore === void 0 ? "" : ` · ${t("evolution.auto.scores", {
4304
+ baseline: points(event.baselineScore),
4305
+ draft: points(event.draftScore)
4306
+ })}`,
4307
+ event.detail === void 0 ? "" : ` · ${event.detail}`
4308
+ ] });
4309
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
4310
+ "aria-label": t("evolution.auto.title"),
4311
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", { children: [
4312
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: t("evolution.auto.title") }),
4313
+ " · ",
4314
+ describe(latest)
4315
+ ] }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Fold, {
4316
+ title: t("evolution.auto.history"),
4317
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ol", { children: props.events.map((event, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", { children: [
4318
+ stamp(event.at),
4319
+ " · ",
4320
+ describe(event)
4321
+ ] }, index)) })
4322
+ })]
4323
+ });
4324
+ }
4191
4325
  //#endregion
4192
4326
  //#region src/contracts.ts
4193
4327
  const SKILL_ROOT_KINDS = [
@@ -0,0 +1,119 @@
1
+ //#region src/auto-adoption.ts
2
+ /** Stable evaluation inputs, excluding presentation timestamps and provenance. Hash at the server boundary. */
3
+ function skillCaseSignature(cases) {
4
+ return JSON.stringify([...cases].sort((a, b) => a.id < b.id ? -1 : a.id > b.id ? 1 : 0).map((entry) => [
5
+ entry.id,
6
+ entry.skillName,
7
+ entry.title,
8
+ entry.prompt,
9
+ entry.context ?? "",
10
+ entry.enabled,
11
+ entry.requiresVision === true,
12
+ entry.checks.map((check) => [
13
+ check.id,
14
+ check.text,
15
+ check.must
16
+ ])
17
+ ]));
18
+ }
19
+ /** Automatic decisions require full matching coverage, not the UI's scored intersection. */
20
+ function compareAutomaticSkillRuns(cases, baseline, draft, casesHash) {
21
+ const unmeasured = { outcome: "unmeasured" };
22
+ if (cases.length === 0 || cases.some((entry) => !entry.enabled || entry.requiresVision === true || entry.checks.length === 0)) return unmeasured;
23
+ const ids = new Set(cases.map((entry) => entry.id));
24
+ if (ids.size !== cases.length || baseline === void 0 || draft === void 0 || baseline.model === void 0 || draft.model === void 0 || baseline.model.provider !== draft.model.provider || baseline.model.model !== draft.model.model || baseline.model.reasoningEffort !== draft.model.reasoningEffort) return unmeasured;
25
+ const complete = (run) => {
26
+ if (run.status !== "completed" || run.casesHash !== casesHash || run.errors || run.caseIds.length !== ids.size || new Set(run.caseIds).size !== ids.size || run.caseIds.some((id) => !ids.has(id)) || run.results.length !== ids.size || run.progress.done !== ids.size || run.progress.total !== ids.size) return;
27
+ const results = new Map(run.results.map((result) => [result.caseId, result]));
28
+ if (results.size !== ids.size) return;
29
+ for (const entry of cases) {
30
+ const result = results.get(entry.id);
31
+ if (result === void 0 || result.fullyGraded !== true || result.verdict === "error" || result.unmeasuredReason !== void 0 || result.error !== void 0 || !Number.isFinite(result.score)) return;
32
+ const checks = new Map(result.checks.map((check) => [check.checkId, check.pass]));
33
+ if (checks.size !== entry.checks.length || result.checks.length !== checks.size || entry.checks.some((check) => typeof checks.get(check.id) !== "boolean")) return;
34
+ const weight = entry.checks.reduce((sum, check) => sum + (check.must ? 2 : 1), 0);
35
+ const score = entry.checks.reduce((sum, check) => sum + (checks.get(check.id) ? check.must ? 2 : 1 : 0), 0) / weight;
36
+ const verdict = entry.checks.every((check) => !check.must || checks.get(check.id)) ? "pass" : "fail";
37
+ if (Math.abs(result.score - score) > 1e-8 || result.verdict !== verdict) return;
38
+ }
39
+ return results;
40
+ };
41
+ const before = complete(baseline);
42
+ const after = complete(draft);
43
+ if (before === void 0 || after === void 0) return unmeasured;
44
+ const baselineScore = cases.reduce((sum, entry) => sum + before.get(entry.id).score, 0) / cases.length;
45
+ const draftScore = cases.reduce((sum, entry) => sum + after.get(entry.id).score, 0) / cases.length;
46
+ return {
47
+ outcome: cases.some((entry) => {
48
+ const left = before.get(entry.id);
49
+ const right = after.get(entry.id);
50
+ const checks = new Map(right.checks.map((check) => [check.checkId, check.pass]));
51
+ return right.score + 1e-8 < left.score || left.checks.some((check) => check.pass && !checks.get(check.checkId));
52
+ }) ? "regressed" : "not-worse",
53
+ baselineScore,
54
+ draftScore
55
+ };
56
+ }
57
+ //#endregion
58
+ //#region src/contracts.ts
59
+ const SKILL_ROOT_KINDS = [
60
+ "project-dsh",
61
+ "project-agents",
62
+ "user-dsh",
63
+ "user-agents"
64
+ ];
65
+ const SKILL_ROOT_RANK = {
66
+ "project-dsh": 100,
67
+ "project-agents": 200,
68
+ "user-dsh": 400,
69
+ "user-agents": 500
70
+ };
71
+ const EVOLUTION_SIGNALS = [
72
+ "correction",
73
+ "directive",
74
+ "tool-failure",
75
+ "approach-change",
76
+ "procedure",
77
+ "skill-gap",
78
+ "skill-misleading",
79
+ "success"
80
+ ];
81
+ const SUGGESTION_STATUSES = [
82
+ "pending",
83
+ "drafting",
84
+ "review",
85
+ "adopted",
86
+ "dismissed",
87
+ "expired",
88
+ "invalidated"
89
+ ];
90
+ const DEFAULT_SKILLS_POLICY = Object.freeze({
91
+ allowCreate: true,
92
+ allowUpdate: true,
93
+ minConfidence: .6,
94
+ expiryDays: 7,
95
+ maxPendingPerSession: 5
96
+ });
97
+ const AUTO_ADOPTION_STAGES = [
98
+ "queued",
99
+ "drafting",
100
+ "backtesting",
101
+ "rewriting",
102
+ "adopted",
103
+ "paused",
104
+ "review"
105
+ ];
106
+ const AUTO_ADOPTION_REASONS = [
107
+ "disabled",
108
+ "no-cases",
109
+ "case-limit",
110
+ "unmeasured",
111
+ "regressed",
112
+ "changed",
113
+ "failed",
114
+ "queue-full",
115
+ "model-unavailable",
116
+ "interrupted"
117
+ ];
118
+ //#endregion
119
+ export { SKILL_ROOT_KINDS as a, compareAutomaticSkillRuns as c, EVOLUTION_SIGNALS as i, skillCaseSignature as l, AUTO_ADOPTION_STAGES as n, SKILL_ROOT_RANK as o, DEFAULT_SKILLS_POLICY as r, SUGGESTION_STATUSES as s, AUTO_ADOPTION_REASONS as t };
@@ -20,6 +20,16 @@ interface DetectedSource {
20
20
  label: string;
21
21
  }
22
22
  //#endregion
23
+ //#region src/auto-adoption.d.ts
24
+ /** Stable evaluation inputs, excluding presentation timestamps and provenance. Hash at the server boundary. */
25
+ declare function skillCaseSignature(cases: readonly EvalCase[]): string;
26
+ /** Automatic decisions require full matching coverage, not the UI's scored intersection. */
27
+ declare function compareAutomaticSkillRuns(cases: readonly EvalCase[], baseline: BacktestRun | undefined, draft: BacktestRun | undefined, casesHash: string): {
28
+ outcome: 'unmeasured' | 'regressed' | 'not-worse';
29
+ baselineScore?: number;
30
+ draftScore?: number;
31
+ };
32
+ //#endregion
23
33
  //#region src/contracts.d.ts
24
34
  /** The DSH skill roots this Source reads and writes, in DSH's precedence order. */
25
35
  type SkillRootKind = 'project-dsh' | 'project-agents' | 'user-dsh' | 'user-agents';
@@ -163,6 +173,13 @@ interface EvidenceReference {
163
173
  }
164
174
  type SuggestionStatus = 'pending' | 'drafting' | 'review' | 'adopted' | 'dismissed' | 'expired' | 'invalidated';
165
175
  declare const SUGGESTION_STATUSES: readonly SuggestionStatus[];
176
+ /** A file selected by the Source catalog, independent of the page's storage scope. */
177
+ interface SkillOrigin {
178
+ path: string;
179
+ /** The owning project root; absent for user skills. */
180
+ workspace?: string;
181
+ }
182
+ type SkillResolution = 'available' | 'missing' | 'origin-unavailable';
166
183
  interface SkillSuggestion {
167
184
  id: string;
168
185
  skillName: string;
@@ -177,12 +194,15 @@ interface SkillSuggestion {
177
194
  /** For `update`: the SKILL.md the reviewer read, so a later edit invalidates it. */
178
195
  skillKey?: string;
179
196
  skillFileHash?: string;
197
+ /** Absent on legacy records; never supplied by the model. */
198
+ origin?: SkillOrigin;
180
199
  sourceSessionId?: string;
181
200
  sourceSessionTitle?: string;
182
201
  reviewId?: string;
183
202
  status: SuggestionStatus;
184
203
  draftId?: string;
185
204
  lastError?: string;
205
+ autoAdoption?: AutoAdoptionEvent[];
186
206
  createdAt: string;
187
207
  updatedAt: string;
188
208
  expiresAt: string;
@@ -196,6 +216,8 @@ interface SkillDraft {
196
216
  /** For `update`, the SKILL.md hash the draft was written against. */
197
217
  baseKey?: string;
198
218
  baseHash?: string;
219
+ /** The file this update replaces; absent on legacy records. */
220
+ origin?: SkillOrigin;
199
221
  /** For `create`, where adopting writes the new bundle. */
200
222
  targetRoot?: SkillRootKind;
201
223
  content: string;
@@ -281,6 +303,8 @@ interface CaseResult {
281
303
  error?: string;
282
304
  /** Kept under verdict=error so older readers also exclude it from scores. */
283
305
  unmeasuredReason?: 'requires-vision';
306
+ /** Every expected judge check was explicitly returned; absent on older runs. */
307
+ fullyGraded?: boolean;
284
308
  }
285
309
  type RunStatus = 'running' | 'completed' | 'failed' | 'interrupted';
286
310
  interface BacktestRun {
@@ -293,6 +317,11 @@ interface BacktestRun {
293
317
  total: number;
294
318
  };
295
319
  caseIds: string[];
320
+ /** Source-verified case inputs at start; absent on older runs. */
321
+ casesHash?: string;
322
+ model?: ModelRoute & {
323
+ reasoningEffort?: string;
324
+ };
296
325
  results: CaseResult[];
297
326
  /** Over the graded cases; absent when none could be graded. */
298
327
  passRate?: number;
@@ -319,6 +348,10 @@ interface ReviewLogEntry {
319
348
  error?: string;
320
349
  /** The conversation turn the reviewed window ended with. */
321
350
  turn?: number;
351
+ autoAdoptions?: Array<AutoAdoptionEvent & {
352
+ suggestionId: string;
353
+ skillName: string;
354
+ }>;
322
355
  /** The suggestions this review created or strengthened. */
323
356
  suggestionIds?: string[];
324
357
  }
@@ -422,6 +455,8 @@ interface ReviewRecordResult {
422
455
  reviewId: string;
423
456
  trajectoryId?: string;
424
457
  suggestionIds: string[];
458
+ /** Newly inserted ids only; older Sources omit this capability. */
459
+ createdSuggestionIds?: string[];
425
460
  inserted: number;
426
461
  updated: number;
427
462
  casesAdded: number;
@@ -467,14 +502,20 @@ interface SuggestionView {
467
502
  suggestion: SkillSuggestion;
468
503
  /** The loaded file is still the one the reviewer read. */
469
504
  hashCurrent: boolean;
470
- skill?: SkillSummary;
505
+ skill?: SkillDocument;
506
+ resolution?: SkillResolution;
507
+ /** Whether the selected scope's catalog offers this same file for navigation. */
508
+ skillInCatalog?: boolean;
471
509
  draft?: SkillDraft;
472
510
  trajectories: TrajectorySummary[];
473
511
  }
474
512
  interface DraftView {
475
513
  draft: SkillDraft;
514
+ /** File changes need a new draft, never a retry with a newer revision. */
515
+ conflict?: 'base-changed' | 'target-exists';
476
516
  /** The file the draft replaces, as it is now. */
477
517
  base?: SkillDocument;
518
+ resolution?: SkillResolution;
478
519
  runs: BacktestRun[];
479
520
  }
480
521
  /** A string, or the same text per language. */
@@ -622,5 +663,27 @@ interface MarketInstall {
622
663
  files: number;
623
664
  installedAt: string;
624
665
  }
666
+ declare const AUTO_ADOPTION_STAGES: readonly ["queued", "drafting", "backtesting", "rewriting", "adopted", "paused", "review"];
667
+ declare const AUTO_ADOPTION_REASONS: readonly ["disabled", "no-cases", "case-limit", "unmeasured", "regressed", "changed", "failed", "queue-full", "model-unavailable", "interrupted"];
668
+ interface AutoAdoptionEvent {
669
+ at: string;
670
+ stage: typeof AUTO_ADOPTION_STAGES[number];
671
+ attempt: number;
672
+ reason?: typeof AUTO_ADOPTION_REASONS[number];
673
+ detail?: string;
674
+ baselineScore?: number;
675
+ draftScore?: number;
676
+ }
677
+ type DraftExpectation = {
678
+ id: string;
679
+ hash: string;
680
+ } | null;
681
+ interface AutomaticAdoption {
682
+ attempt: number;
683
+ draftHash: string;
684
+ baselineRunId: string;
685
+ draftRunId: string;
686
+ casesHash: string;
687
+ }
625
688
  //#endregion
626
- export { BacktestRun, CaseResult, CatalogView, DEFAULT_SKILLS_POLICY, type DetectedSource, DraftPreferences, DraftPreferencesView, DraftStatus, DraftView, EVOLUTION_SIGNALS, EvalCase, EvalCheck, EvidencePack, EvidenceReference, EvolutionSignal, LocalizedText, MarketAudit, MarketEntry, MarketInstall, MarketInstallState, MarketItem, MarketPreview, MarketRegistry, MarketRegistryFile, MarketSearchResult, MarketSource, type MarketSourceKind, MarketView, ModelRoute, ReviewLogEntry, ReviewRecordInput, ReviewRecordResult, ReviewScore, RunStatus, RunTarget, RunTargetKind, SKILL_ROOT_KINDS, SKILL_ROOT_RANK, SUGGESTION_STATUSES, SkillCatalog, SkillDocument, SkillDraft, SkillInvocation, SkillProblem, SkillRoot, SkillRootKind, SkillSuggestion, SkillSummary, SkillVersion, SkillVersionReason, SkillsOverview, SkillsPolicy, SuggestionStatus, SuggestionView, Trajectory, TrajectoryAnnotation, TrajectoryStats, TrajectoryStep, TrajectoryStepKind, TrajectorySummary, TrashEntry, TrashRestoreInput, TrashRestorePreview };
689
+ export { AUTO_ADOPTION_REASONS, AUTO_ADOPTION_STAGES, AutoAdoptionEvent, AutomaticAdoption, BacktestRun, CaseResult, CatalogView, DEFAULT_SKILLS_POLICY, type DetectedSource, DraftExpectation, DraftPreferences, DraftPreferencesView, DraftStatus, DraftView, EVOLUTION_SIGNALS, EvalCase, EvalCheck, EvidencePack, EvidenceReference, EvolutionSignal, LocalizedText, MarketAudit, MarketEntry, MarketInstall, MarketInstallState, MarketItem, MarketPreview, MarketRegistry, MarketRegistryFile, MarketSearchResult, MarketSource, type MarketSourceKind, MarketView, ModelRoute, ReviewLogEntry, ReviewRecordInput, ReviewRecordResult, ReviewScore, RunStatus, RunTarget, RunTargetKind, SKILL_ROOT_KINDS, SKILL_ROOT_RANK, SUGGESTION_STATUSES, SkillCatalog, SkillDocument, SkillDraft, SkillInvocation, SkillOrigin, SkillProblem, SkillResolution, SkillRoot, SkillRootKind, SkillSuggestion, SkillSummary, SkillVersion, SkillVersionReason, SkillsOverview, SkillsPolicy, SuggestionStatus, SuggestionView, Trajectory, TrajectoryAnnotation, TrajectoryStats, TrajectoryStep, TrajectoryStepKind, TrajectorySummary, TrashEntry, TrashRestoreInput, TrashRestorePreview, compareAutomaticSkillRuns, skillCaseSignature };