dsh-mnemon-source-skills 0.5.7 → 0.5.9

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/lib/client.js CHANGED
@@ -194,6 +194,7 @@ window.__ModuleLoader__.load({
194
194
  "tab.market": "市场",
195
195
  "tab.trash": "回收站",
196
196
  "common.loading": "加载中",
197
+ "common.retry": "重试",
197
198
  "common.cancel": "取消",
198
199
  "common.close": "关闭",
199
200
  "common.delete": "删除",
@@ -316,7 +317,9 @@ window.__ModuleLoader__.load({
316
317
  "evolution.noAgent": "当前没有可用的任务 Agent",
317
318
  "evolution.drafting": "正在生成草案",
318
319
  "evolution.draftFailed": "草案生成失败:{error}",
319
- "evolution.skillGone": "这个技能已不存在。",
320
+ "evolution.skillGone": "原技能文件已删除、移动或无法读取。请恢复文件,或在新位置打开技能并重新生成草案。",
321
+ "evolution.originUnavailable": "旧记录未可靠保存原工作区。请切换到原工作区,在技能页打开该技能并重新生成草案;现有草案和证据会保留。",
322
+ "evolution.currentSkill": "当前技能文件",
320
323
  "evolution.skillChanged": "技能在建议之后被改过,生成草案前会以当前版本为准。",
321
324
  "evolution.adopted": "已采纳",
322
325
  "evolution.openSkill": "查看技能",
@@ -526,6 +529,10 @@ window.__ModuleLoader__.load({
526
529
  "evolution.deleteTitle": "删除这条建议?",
527
530
  "evolution.deleteBody": "它生成的草案和证据引用会一起删掉,无法恢复。",
528
531
  "evolution.deleted": "已删除",
532
+ "evolution.missing": "该建议已删除",
533
+ "evolution.missingBody": "可以在左侧选择其他建议,或返回记录查看原始内容。",
534
+ "evolution.loadFailed": "无法加载这条建议",
535
+ "evolution.loadFailedBody": "请重试,或在左侧选择其他建议。",
529
536
  "tests.model": "全局模型",
530
537
  "tests.modelCard": "所选回测模型",
531
538
  "tests.modelAuto": "跟随默认"
@@ -543,6 +550,7 @@ window.__ModuleLoader__.load({
543
550
  "tab.market": "Market",
544
551
  "tab.trash": "Trash",
545
552
  "common.loading": "Loading",
553
+ "common.retry": "Retry",
546
554
  "common.cancel": "Cancel",
547
555
  "common.close": "Close",
548
556
  "common.delete": "Delete",
@@ -665,7 +673,9 @@ window.__ModuleLoader__.load({
665
673
  "evolution.noAgent": "No task Agent is available",
666
674
  "evolution.drafting": "Drafting",
667
675
  "evolution.draftFailed": "Drafting failed: {error}",
668
- "evolution.skillGone": "This skill no longer exists.",
676
+ "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.",
677
+ "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.",
678
+ "evolution.currentSkill": "Current skill file",
669
679
  "evolution.skillChanged": "The skill changed after this suggestion; the draft starts from its current version.",
670
680
  "evolution.adopted": "Adopted",
671
681
  "evolution.openSkill": "Open skill",
@@ -875,6 +885,10 @@ window.__ModuleLoader__.load({
875
885
  "evolution.deleteTitle": "Delete this suggestion?",
876
886
  "evolution.deleteBody": "Its draft and the evidence references it cites go with it, and this cannot be undone.",
877
887
  "evolution.deleted": "Deleted",
888
+ "evolution.missing": "This suggestion has been deleted",
889
+ "evolution.missingBody": "Select another suggestion on the left, or return to Evidence to read the original record.",
890
+ "evolution.loadFailed": "Could not load this suggestion",
891
+ "evolution.loadFailedBody": "Retry, or select another suggestion on the left.",
878
892
  "tests.model": "Global model",
879
893
  "tests.modelCard": "Selected model",
880
894
  "tests.modelAuto": "Follow default"
@@ -3661,11 +3675,14 @@ window.__ModuleLoader__.load({
3661
3675
  model: props.model.model
3662
3676
  };
3663
3677
  const [view, setView] = (0, react.useState)(null);
3678
+ const [loadError, setLoadError] = (0, react.useState)(null);
3679
+ const [reload, setReload] = (0, react.useState)(0);
3664
3680
  const [trajectories, setTrajectories] = (0, react.useState)(/* @__PURE__ */ new Map());
3665
3681
  const [busy, setBusy] = (0, react.useState)(false);
3666
3682
  const [deleting, setDeleting] = (0, react.useState)(false);
3667
3683
  (0, react.useEffect)(() => {
3668
3684
  let active = true;
3685
+ setLoadError(null);
3669
3686
  context.client.suggestion(props.id).then(async (value) => {
3670
3687
  if (!active) return;
3671
3688
  setView(value);
@@ -3675,15 +3692,34 @@ window.__ModuleLoader__.load({
3675
3692
  loaded.set(reference.trajectoryId, await context.client.trajectory(reference.trajectoryId).catch(() => null));
3676
3693
  }
3677
3694
  if (active) setTrajectories(loaded);
3678
- }).catch(context.fail);
3695
+ }).catch((error) => {
3696
+ if (!active) return;
3697
+ setView(null);
3698
+ if ((error instanceof Error ? error.message : String(error)) === "suggestion not found: " + props.id) setLoadError("missing");
3699
+ else {
3700
+ setLoadError("failed");
3701
+ context.fail(error);
3702
+ }
3703
+ });
3679
3704
  return () => {
3680
3705
  active = false;
3681
3706
  };
3682
3707
  }, [
3683
3708
  context.client,
3684
3709
  props.id,
3685
- context.revision
3710
+ context.revision,
3711
+ reload
3686
3712
  ]);
3713
+ if (loadError !== null) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Empty, {
3714
+ glyph: "?",
3715
+ title: t(loadError === "missing" ? "evolution.missing" : "evolution.loadFailed"),
3716
+ action: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
3717
+ variant: "ghost",
3718
+ onClick: () => setReload((value) => value + 1),
3719
+ children: t("common.retry")
3720
+ }),
3721
+ children: t(loadError === "missing" ? "evolution.missingBody" : "evolution.loadFailedBody")
3722
+ });
3687
3723
  if (view === null) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Busy, { label: t("common.loading") });
3688
3724
  const { suggestion } = view;
3689
3725
  const canDraft = context.client.canAssist("skill-draft") && !(suggestion.action === "update" && view.skill === void 0);
@@ -3716,7 +3752,7 @@ window.__ModuleLoader__.load({
3716
3752
  className: css.skKicker,
3717
3753
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ActionTag, { action: suggestion.action }), suggestion.status !== "pending" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatusTag, { status: suggestion.status })]
3718
3754
  }),
3719
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: view.skill === void 0 ? suggestion.skillName : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3755
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: view.skill === void 0 || view.skillInCatalog === false ? suggestion.skillName : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3720
3756
  type: "button",
3721
3757
  className: css.skTitleLink,
3722
3758
  onClick: () => context.navigate({
@@ -3779,7 +3815,7 @@ window.__ModuleLoader__.load({
3779
3815
  suggestion.rationale !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Clamp, { text: suggestion.rationale }),
3780
3816
  suggestion.action === "update" && view.skill === void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
3781
3817
  className: css.skWarn,
3782
- children: t("evolution.skillGone")
3818
+ children: t(view.resolution === "origin-unavailable" ? "evolution.originUnavailable" : "evolution.skillGone")
3783
3819
  }),
3784
3820
  suggestion.action === "update" && view.skill !== void 0 && !view.hashCurrent && suggestion.status === "pending" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
3785
3821
  className: css.skWarn,
@@ -3800,7 +3836,7 @@ window.__ModuleLoader__.load({
3800
3836
  children: [
3801
3837
  t("evolution.adopted"),
3802
3838
  " ",
3803
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
3839
+ view.skillInCatalog !== false && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
3804
3840
  type: "button",
3805
3841
  className: css.skLink,
3806
3842
  onClick: () => context.navigate({
@@ -3811,9 +3847,16 @@ window.__ModuleLoader__.load({
3811
3847
  })
3812
3848
  ]
3813
3849
  }),
3814
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3850
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3815
3851
  className: css.skFolds,
3816
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Fold, {
3852
+ children: [view.skill !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Fold, {
3853
+ title: t("evolution.currentSkill"),
3854
+ meta: t(`root.${view.skill.root}`),
3855
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
3856
+ className: css.skMuted,
3857
+ children: view.skill.path
3858
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SkillMarkdown, { body: view.skill.body })]
3859
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Fold, {
3817
3860
  title: t("evolution.evidence"),
3818
3861
  meta: t("evolution.evidenceMeta", {
3819
3862
  conversations: references.length,
@@ -3852,7 +3895,7 @@ window.__ModuleLoader__.load({
3852
3895
  })]
3853
3896
  }, index);
3854
3897
  })
3855
- })
3898
+ })]
3856
3899
  })
3857
3900
  ]
3858
3901
  });
@@ -3954,16 +3997,21 @@ window.__ModuleLoader__.load({
3954
3997
  });
3955
3998
  const started = await context.client.backtest({
3956
3999
  skillName: draft.skillName,
3957
- target: { kind: draft.action === "update" ? "current" : "none" }
4000
+ target: {
4001
+ kind: draft.action === "update" ? "current" : "none",
4002
+ ...draft.action === "update" ? { draftId: draft.id } : {}
4003
+ }
3958
4004
  });
3959
4005
  await context.client.linkRun(draft.id, started.runId);
3960
4006
  });
3961
4007
  const redraft = (note) => act(() => context.client.generateDraft({
3962
- ...draft.suggestionId === void 0 ? { skill: draft.skillName } : { suggestionId: draft.suggestionId },
4008
+ draftId: draft.id,
4009
+ ...draft.suggestionId === void 0 ? {} : { suggestionId: draft.suggestionId },
3963
4010
  guidance: note,
3964
4011
  ...route
3965
4012
  }), () => setRedrafting(null));
3966
4013
  const ready = draft.status === "ready";
4014
+ const originalAvailable = draft.action !== "update" || view.base !== void 0;
3967
4015
  const progress = (run) => run === void 0 ? 0 : run.progress.done;
3968
4016
  const total = (candidate?.progress.total ?? 0) + (baseline?.progress.total ?? 0);
3969
4017
  const results = /* @__PURE__ */ new Map();
@@ -3997,7 +4045,7 @@ window.__ModuleLoader__.load({
3997
4045
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
3998
4046
  variant: "primary",
3999
4047
  size: "sm",
4000
- disabled: !context.writable || busy || editing !== null || running,
4048
+ disabled: !context.writable || busy || editing !== null || running || !originalAvailable,
4001
4049
  onClick: () => regressed ? setConfirmAdopt(true) : void adopt(),
4002
4050
  children: t("draft.adopt")
4003
4051
  }),
@@ -4015,13 +4063,17 @@ window.__ModuleLoader__.load({
4015
4063
  items: [{
4016
4064
  id: "edit",
4017
4065
  label: t("draft.edit")
4018
- }, ...context.client.canAssist("skill-draft") ? [{
4066
+ }, ...originalAvailable && context.client.canAssist("skill-draft") ? [{
4019
4067
  id: "redraft",
4020
4068
  label: t("draft.redraft")
4021
4069
  }] : []]
4022
4070
  })
4023
4071
  ] })]
4024
4072
  }),
4073
+ !originalAvailable && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
4074
+ className: css.skWarn,
4075
+ children: t(view.resolution === "origin-unavailable" ? "evolution.originUnavailable" : "evolution.skillGone")
4076
+ }),
4025
4077
  draft.notes !== "" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Clamp, { text: draft.notes }),
4026
4078
  draft.changes.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MoreList, {
4027
4079
  items: draft.changes,
@@ -4057,7 +4109,7 @@ window.__ModuleLoader__.load({
4057
4109
  }), !running && (cases ?? 0) > 0 && context.client.canAssist("skill-backtest") && ready && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
4058
4110
  variant: "ghost",
4059
4111
  size: "sm",
4060
- disabled: !context.writable || busy,
4112
+ disabled: !context.writable || busy || !originalAvailable,
4061
4113
  onClick: () => void backtest(),
4062
4114
  children: view.runs.length === 0 ? t("draft.runBacktest") : t("draft.rerun")
4063
4115
  })]
@@ -163,6 +163,13 @@ interface EvidenceReference {
163
163
  }
164
164
  type SuggestionStatus = 'pending' | 'drafting' | 'review' | 'adopted' | 'dismissed' | 'expired' | 'invalidated';
165
165
  declare const SUGGESTION_STATUSES: readonly SuggestionStatus[];
166
+ /** A file selected by the Source catalog, independent of the page's storage scope. */
167
+ interface SkillOrigin {
168
+ path: string;
169
+ /** The owning project root; absent for user skills. */
170
+ workspace?: string;
171
+ }
172
+ type SkillResolution = 'available' | 'missing' | 'origin-unavailable';
166
173
  interface SkillSuggestion {
167
174
  id: string;
168
175
  skillName: string;
@@ -177,6 +184,8 @@ interface SkillSuggestion {
177
184
  /** For `update`: the SKILL.md the reviewer read, so a later edit invalidates it. */
178
185
  skillKey?: string;
179
186
  skillFileHash?: string;
187
+ /** Absent on legacy records; never supplied by the model. */
188
+ origin?: SkillOrigin;
180
189
  sourceSessionId?: string;
181
190
  sourceSessionTitle?: string;
182
191
  reviewId?: string;
@@ -196,6 +205,8 @@ interface SkillDraft {
196
205
  /** For `update`, the SKILL.md hash the draft was written against. */
197
206
  baseKey?: string;
198
207
  baseHash?: string;
208
+ /** The file this update replaces; absent on legacy records. */
209
+ origin?: SkillOrigin;
199
210
  /** For `create`, where adopting writes the new bundle. */
200
211
  targetRoot?: SkillRootKind;
201
212
  content: string;
@@ -467,7 +478,10 @@ interface SuggestionView {
467
478
  suggestion: SkillSuggestion;
468
479
  /** The loaded file is still the one the reviewer read. */
469
480
  hashCurrent: boolean;
470
- skill?: SkillSummary;
481
+ skill?: SkillDocument;
482
+ resolution?: SkillResolution;
483
+ /** Whether the selected scope's catalog offers this same file for navigation. */
484
+ skillInCatalog?: boolean;
471
485
  draft?: SkillDraft;
472
486
  trajectories: TrajectorySummary[];
473
487
  }
@@ -475,6 +489,7 @@ interface DraftView {
475
489
  draft: SkillDraft;
476
490
  /** The file the draft replaces, as it is now. */
477
491
  base?: SkillDocument;
492
+ resolution?: SkillResolution;
478
493
  runs: BacktestRun[];
479
494
  }
480
495
  /** A string, or the same text per language. */
@@ -623,4 +638,4 @@ interface MarketInstall {
623
638
  installedAt: string;
624
639
  }
625
640
  //#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 };
641
+ 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, SkillOrigin, SkillProblem, SkillResolution, SkillRoot, SkillRootKind, SkillSuggestion, SkillSummary, SkillVersion, SkillVersionReason, SkillsOverview, SkillsPolicy, SuggestionStatus, SuggestionView, Trajectory, TrajectoryAnnotation, TrajectoryStats, TrajectoryStep, TrajectoryStepKind, TrajectorySummary, TrashEntry, TrashRestoreInput, TrashRestorePreview };
package/lib/index.js CHANGED
@@ -368,6 +368,33 @@ function rootPath(catalog, kind) {
368
368
  if (root === void 0) throw new Error(kind.startsWith("project") ? "project skill roots need a selected workspace" : "unknown skill root: " + kind);
369
369
  return root.path;
370
370
  }
371
+ //#endregion
372
+ //#region src/identity.ts
373
+ function skillOrigin(catalog, skill) {
374
+ return {
375
+ path: skill.path,
376
+ ...skill.root.startsWith("project-") ? { workspace: catalog.projectRoot } : {}
377
+ };
378
+ }
379
+ function validOrigin(key, origin) {
380
+ return origin !== null && typeof origin === "object" && typeof origin.path === "string" && isAbsolute(origin.path) && (key.startsWith("project-") ? typeof origin.workspace === "string" && isAbsolute(origin.workspace) : origin.workspace === void 0);
381
+ }
382
+ /** Old reviews kept their cwd in the cited trajectory, even before origins existed. */
383
+ function legacySuggestionWorkspace(ledger, suggestion) {
384
+ if (!suggestion.skillKey?.startsWith("project-")) return void 0;
385
+ const cited = new Set(suggestion.evidence.map((reference) => reference.trajectoryId));
386
+ const trajectories = ledger.trajectories.filter((trajectory) => cited.has(trajectory.id) && (suggestion.reviewId === void 0 || trajectory.reviewId === suggestion.reviewId));
387
+ if (suggestion.reviewId === void 0 && trajectories.length !== cited.size) return void 0;
388
+ if (trajectories.length === 0 || trajectories.some((trajectory) => typeof trajectory.workspace !== "string" || !isAbsolute(trajectory.workspace))) return void 0;
389
+ const roots = new Set(trajectories.map((trajectory) => projectRoot(trajectory.workspace)));
390
+ return roots.size === 1 ? [...roots][0] : void 0;
391
+ }
392
+ /** A name alone, or identical contents in another project, is not an identity. */
393
+ function sameSuggestionSkill(ledger, suggestion, catalog, skill) {
394
+ if (suggestion.skillKey !== skill.key) return false;
395
+ if (suggestion.origin !== void 0) return validOrigin(skill.key, suggestion.origin) && resolve(suggestion.origin.path) === resolve(skill.path) && (!skill.root.startsWith("project-") || resolve(suggestion.origin.workspace) === catalog.projectRoot);
396
+ return !skill.root.startsWith("project-") || legacySuggestionWorkspace(ledger, suggestion) === catalog.projectRoot;
397
+ }
371
398
  function emptyLedger() {
372
399
  return {
373
400
  version: 1,
@@ -423,6 +450,19 @@ function trashStamp(id) {
423
450
  const match = /^(\d{4}-\d{2}-\d{2})T(\d{2})-(\d{2})-(\d{2})-(\d{3})Z-/u.exec(id);
424
451
  return match === null ? void 0 : `${match[1]}T${match[2]}:${match[3]}:${match[4]}.${match[5]}Z`;
425
452
  }
453
+ /** References are navigation links, not evidence: keep the records when a suggestion is deleted. */
454
+ function pruneSuggestionLinks(ledger) {
455
+ const ids = new Set(ledger.suggestions.map((suggestion) => suggestion.id));
456
+ for (const review of ledger.reviews) {
457
+ if (review.suggestionIds === void 0) continue;
458
+ const kept = review.suggestionIds.filter((id) => ids.has(id));
459
+ if (kept.length !== review.suggestionIds.length) review.suggestions = Math.min(review.suggestions, kept.length);
460
+ if (kept.length === 0) delete review.suggestionIds;
461
+ else review.suggestionIds = kept;
462
+ }
463
+ for (const trajectory of ledger.trajectories) trajectory.suggestionIds = trajectory.suggestionIds.filter((id) => ids.has(id));
464
+ return ledger;
465
+ }
426
466
  /**
427
467
  * The Skills Source's own files under `<dataDir>/skills`. Every file is
428
468
  * replaced atomically; callers serialize writes with the storage lock.
@@ -478,7 +518,7 @@ var SkillsStore = class {
478
518
  ledger() {
479
519
  const value = object$1(this.readJson("ledger.json"));
480
520
  if (value === void 0) return emptyLedger();
481
- return {
521
+ return pruneSuggestionLinks({
482
522
  version: 1,
483
523
  generation: typeof value.generation === "number" ? value.generation : 0,
484
524
  suggestions: array(value.suggestions),
@@ -487,7 +527,7 @@ var SkillsStore = class {
487
527
  versions: array(value.versions),
488
528
  trajectories: array(value.trajectories),
489
529
  installs: array(value.installs)
490
- };
530
+ });
491
531
  }
492
532
  /**
493
533
  * What the user picked for draft and backtest runs, kept out of `fingerprint()`
@@ -527,6 +567,7 @@ var SkillsStore = class {
527
567
  this.writeJson("preferences.json", preferences);
528
568
  }
529
569
  saveLedger(ledger) {
570
+ pruneSuggestionLinks(ledger);
530
571
  ledger.generation += 1;
531
572
  ledger.reviews = ledger.reviews.slice(0, 200);
532
573
  const kept = /* @__PURE__ */ new Set();
@@ -558,7 +599,16 @@ var SkillsStore = class {
558
599
  if (!/^[a-z0-9-]{1,80}$/u.test(id)) throw new Error("invalid trajectory id");
559
600
  const value = object$1(this.readJson(join("trajectories", `${id}.json`)));
560
601
  if (value === void 0) throw new Error("trajectory not found: " + id);
561
- return value;
602
+ const trajectory = value;
603
+ const ids = new Set(this.ledger().suggestions.map((suggestion) => suggestion.id));
604
+ trajectory.suggestionIds = trajectory.suggestionIds.filter((id) => ids.has(id));
605
+ for (const annotation of trajectory.annotations) {
606
+ if (annotation.suggestionIds === void 0) continue;
607
+ const kept = annotation.suggestionIds.filter((id) => ids.has(id));
608
+ if (kept.length === 0) delete annotation.suggestionIds;
609
+ else annotation.suggestionIds = kept;
610
+ }
611
+ return trajectory;
562
612
  }
563
613
  saveTrajectory(trajectory) {
564
614
  this.writeJson(join("trajectories", `${trajectory.id}.json`), trajectory);
@@ -930,7 +980,9 @@ function admitReview(context, input) {
930
980
  });
931
981
  continue;
932
982
  }
933
- if (ledger.suggestions.find((suggestion) => suggestion.skillName === proposal.skillName && suggestion.action === proposal.action && suggestion.status === "dismissed" && suggestion.sourceSessionId === input.review.sessionId && now.getTime() - Date.parse(suggestion.updatedAt) < DISMISSED_COOLDOWN_MS) !== void 0) {
983
+ const skill = activeSkill(catalog, proposal.skillName);
984
+ const sameSkill = (suggestion) => suggestion.skillName === proposal.skillName && suggestion.action === proposal.action && (skill === void 0 || sameSuggestionSkill(ledger, suggestion, catalog, skill));
985
+ if (ledger.suggestions.find((suggestion) => sameSkill(suggestion) && suggestion.status === "dismissed" && suggestion.sourceSessionId === input.review.sessionId && now.getTime() - Date.parse(suggestion.updatedAt) < DISMISSED_COOLDOWN_MS) !== void 0) {
934
986
  skipped.push({
935
987
  skillName: proposal.skillName,
936
988
  action: proposal.action,
@@ -938,8 +990,7 @@ function admitReview(context, input) {
938
990
  });
939
991
  continue;
940
992
  }
941
- const skill = activeSkill(catalog, proposal.skillName);
942
- const open = ledger.suggestions.find((suggestion) => suggestion.skillName === proposal.skillName && suggestion.action === proposal.action && OPEN_STATUSES.has(suggestion.status));
993
+ const open = ledger.suggestions.find((suggestion) => sameSkill(suggestion) && OPEN_STATUSES.has(suggestion.status));
943
994
  if (open !== void 0) {
944
995
  open.evidence = [...open.evidence, evidence].slice(-12);
945
996
  open.signals = signals([...open.signals, ...proposal.signals]);
@@ -951,6 +1002,7 @@ function admitReview(context, input) {
951
1002
  if (skill !== void 0) {
952
1003
  open.skillKey = skill.key;
953
1004
  open.skillFileHash = skill.hash;
1005
+ open.origin = skillOrigin(catalog, skill);
954
1006
  }
955
1007
  updated++;
956
1008
  } else skipped.push({
@@ -974,7 +1026,8 @@ function admitReview(context, input) {
974
1026
  evidence: [evidence],
975
1027
  ...skill === void 0 ? {} : {
976
1028
  skillKey: skill.key,
977
- skillFileHash: skill.hash
1029
+ skillFileHash: skill.hash,
1030
+ origin: skillOrigin(catalog, skill)
978
1031
  },
979
1032
  sourceSessionId: input.review.sessionId,
980
1033
  ...input.review.sessionTitle === void 0 ? {} : { sourceSessionTitle: input.review.sessionTitle },
@@ -3050,6 +3103,28 @@ var SkillsService = class {
3050
3103
  catalog(workspace) {
3051
3104
  return readCatalog(this.options(workspace));
3052
3105
  }
3106
+ original(key, origin, legacyWorkspace) {
3107
+ if (key === void 0 || origin !== void 0 && !validOrigin(key, origin) || key.startsWith("project-") && origin === void 0 && legacyWorkspace === void 0) return { resolution: "origin-unavailable" };
3108
+ const catalog = this.catalog(origin?.workspace ?? legacyWorkspace);
3109
+ const skill = catalog.skills.find((candidate) => candidate.key === key && (origin === void 0 || resolve(candidate.path) === resolve(origin.path)));
3110
+ return skill === void 0 ? { resolution: "missing" } : {
3111
+ resolution: "available",
3112
+ skill,
3113
+ origin: skillOrigin(catalog, skill)
3114
+ };
3115
+ }
3116
+ suggestionOriginal(ledger, suggestion) {
3117
+ return this.original(suggestion.skillKey, suggestion.origin, suggestion.origin === void 0 ? legacySuggestionWorkspace(ledger, suggestion) : void 0);
3118
+ }
3119
+ draftOriginal(ledger, draft) {
3120
+ if (draft.origin !== void 0) return this.original(draft.baseKey, draft.origin);
3121
+ const suggestion = ledger.suggestions.find((candidate) => candidate.id === draft.suggestionId && candidate.skillKey === draft.baseKey);
3122
+ return suggestion === void 0 ? this.original(draft.baseKey, void 0) : this.suggestionOriginal(ledger, suggestion);
3123
+ }
3124
+ requireOriginal(original) {
3125
+ if (original.skill !== void 0) return original.skill;
3126
+ throw new Error(original.resolution === "origin-unavailable" ? "The original workspace was not recorded reliably. Open the skill in its original workspace and generate a new draft; the existing draft and evidence are kept." : "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.");
3127
+ }
3053
3128
  catalogView(workspace) {
3054
3129
  const catalog = this.catalog(workspace);
3055
3130
  const ledger = this.store.ledger();
@@ -3147,13 +3222,19 @@ var SkillsService = class {
3147
3222
  expireSuggestions(ledger, this.clock());
3148
3223
  const suggestion = ledger.suggestions.find((candidate) => candidate.id === id);
3149
3224
  if (suggestion === void 0) throw new Error("suggestion not found: " + id);
3150
- const skill = activeSkill(this.catalog(workspace), suggestion.skillName);
3225
+ const original = suggestion.action === "update" ? this.suggestionOriginal(ledger, suggestion) : void 0;
3226
+ const catalog = this.catalog(workspace);
3227
+ const skill = original === void 0 ? activeSkill(catalog, suggestion.skillName) : original.skill;
3151
3228
  const draft = suggestion.draftId === void 0 ? void 0 : ledger.drafts.find((candidate) => candidate.id === suggestion.draftId);
3152
3229
  const cited = new Set(suggestion.evidence.map((reference) => reference.trajectoryId));
3153
3230
  return {
3154
3231
  suggestion,
3155
3232
  hashCurrent: suggestion.action === "create" ? skill === void 0 : skill !== void 0 && skill.hash === suggestion.skillFileHash,
3156
- ...skill === void 0 ? {} : { skill },
3233
+ ...original === void 0 ? {} : { resolution: original.resolution },
3234
+ ...skill === void 0 ? {} : {
3235
+ skill: readSkillDocument(skill),
3236
+ skillInCatalog: catalog.skills.some((candidate) => candidate.key === skill.key && candidate.path === skill.path)
3237
+ },
3157
3238
  ...draft === void 0 ? {} : { draft },
3158
3239
  trajectories: ledger.trajectories.filter((entry) => cited.has(entry.id))
3159
3240
  };
@@ -3182,16 +3263,19 @@ var SkillsService = class {
3182
3263
  return staleRun(run, this.clock().getTime());
3183
3264
  }
3184
3265
  drafts(filter = {}) {
3185
- return this.store.ledger().drafts.filter((draft) => (filter.suggestionId === void 0 || draft.suggestionId === filter.suggestionId) && (filter.skill === void 0 || draft.skillName === filter.skill));
3266
+ const ledger = this.store.ledger();
3267
+ return ledger.drafts.filter((draft) => (filter.suggestionId === void 0 || draft.suggestionId === filter.suggestionId) && (filter.skill === void 0 || draft.skillName === filter.skill) && (filter.path === void 0 || this.draftOriginal(ledger, draft).skill?.path === filter.path));
3186
3268
  }
3187
- draft(workspace, id) {
3188
- const draft = this.store.ledger().drafts.find((candidate) => candidate.id === id);
3269
+ draft(_workspace, id) {
3270
+ const ledger = this.store.ledger();
3271
+ const draft = ledger.drafts.find((candidate) => candidate.id === id);
3189
3272
  if (draft === void 0) throw new Error("draft not found: " + id);
3190
- const catalog = this.catalog(workspace);
3191
- const base = draft.baseKey === void 0 ? void 0 : catalog.skills.find((skill) => skill.key === draft.baseKey);
3273
+ const original = draft.action === "update" ? this.draftOriginal(ledger, draft) : void 0;
3274
+ const base = original?.skill;
3192
3275
  const now = this.clock().getTime();
3193
3276
  return {
3194
3277
  draft,
3278
+ ...original === void 0 ? {} : { resolution: original.resolution },
3195
3279
  ...base === void 0 ? {} : { base: readSkillDocument(base) },
3196
3280
  runs: draft.runIds.flatMap((id) => {
3197
3281
  const run = this.store.evals().runs.find((candidate) => candidate.id === id);
@@ -3208,11 +3292,14 @@ var SkillsService = class {
3208
3292
  const ledger = this.store.ledger();
3209
3293
  const evals = this.store.evals();
3210
3294
  const catalog = this.catalog(workspace);
3295
+ const prior = input.draftId === void 0 ? void 0 : ledger.drafts.find((candidate) => candidate.id === input.draftId);
3296
+ if (input.draftId !== void 0 && prior === void 0) throw new Error("draft not found: " + input.draftId);
3297
+ if (prior !== void 0 && input.suggestionId !== void 0 && prior.suggestionId !== input.suggestionId) throw new Error("draft is for another suggestion");
3211
3298
  const suggestion = input.suggestionId === void 0 ? void 0 : ledger.suggestions.find((candidate) => candidate.id === input.suggestionId);
3212
3299
  if (input.suggestionId !== void 0 && suggestion === void 0) throw new Error("suggestion not found: " + input.suggestionId);
3213
- const skillName = suggestion?.skillName ?? input.skill;
3300
+ const skillName = prior?.skillName ?? suggestion?.skillName ?? input.skill;
3214
3301
  if (skillName === void 0 || !isSkillName(skillName)) throw new Error("a skill name or a suggestion is required");
3215
- const loaded = activeSkill(catalog, skillName);
3302
+ const loaded = prior?.action === "update" ? this.requireOriginal(this.draftOriginal(ledger, prior)) : suggestion?.action === "update" ? this.requireOriginal(this.suggestionOriginal(ledger, suggestion)) : activeSkill(catalog, skillName);
3216
3303
  const action = loaded === void 0 ? "create" : "update";
3217
3304
  const references = suggestion?.evidence ?? ledger.trajectories.filter((entry) => entry.stats.skills.includes(skillName) || entry.suggestionIds.some((id) => ledger.suggestions.find((candidate) => candidate.id === id)?.skillName === skillName)).slice(0, 3).map((entry) => ({
3218
3305
  trajectoryId: entry.id,
@@ -3250,7 +3337,7 @@ var SkillsService = class {
3250
3337
  description: clip(skill.description, 200),
3251
3338
  root: skill.root
3252
3339
  })),
3253
- drafts: ledger.drafts.filter((draft) => draft.skillName === skillName).slice(0, 3)
3340
+ drafts: ledger.drafts.filter((draft) => draft.skillName === skillName && (loaded === void 0 ? draft.action === "create" : this.draftOriginal(ledger, draft).skill?.path === loaded.path)).slice(0, 3)
3254
3341
  };
3255
3342
  }
3256
3343
  /** Bump the ledger generation, so the revision moves with every write. */
@@ -3698,11 +3785,18 @@ var SkillsService = class {
3698
3785
  return this.write(() => {
3699
3786
  validateSkillContent(input.content, input.skillName);
3700
3787
  const ledger = this.store.ledger();
3701
- const loaded = activeSkill(this.catalog(workspace), input.skillName);
3788
+ const catalog = this.catalog(workspace);
3702
3789
  const now = this.clock().toISOString();
3790
+ const prior = input.draftId === void 0 ? void 0 : ledger.drafts.find((candidate) => candidate.id === input.draftId);
3791
+ if (input.draftId !== void 0 && prior === void 0) throw new Error("draft not found: " + input.draftId);
3792
+ if (prior !== void 0 && (prior.skillName !== input.skillName || prior.suggestionId !== input.suggestionId)) throw new Error("draft is for another skill or suggestion");
3703
3793
  const suggestion = input.suggestionId === void 0 ? void 0 : ledger.suggestions.find((candidate) => candidate.id === input.suggestionId);
3704
3794
  if (input.suggestionId !== void 0 && suggestion === void 0) throw new Error("suggestion not found: " + input.suggestionId);
3705
- for (const draft of ledger.drafts) if (draft.status === "ready" && draft.skillName === input.skillName && draft.suggestionId === input.suggestionId) {
3795
+ if (suggestion !== void 0 && suggestion.skillName !== input.skillName) throw new Error("suggestion is for another skill");
3796
+ const original = prior?.action === "update" ? this.draftOriginal(ledger, prior) : suggestion?.action === "update" ? this.suggestionOriginal(ledger, suggestion) : void 0;
3797
+ const loaded = original === void 0 ? activeSkill(catalog, input.skillName) : this.requireOriginal(original);
3798
+ const origin = loaded === void 0 ? void 0 : original?.origin ?? skillOrigin(catalog, loaded);
3799
+ for (const draft of ledger.drafts) if (draft.status === "ready" && draft.skillName === input.skillName && draft.suggestionId === input.suggestionId && (loaded === void 0 ? draft.action === "create" : this.draftOriginal(ledger, draft).skill?.path === loaded.path)) {
3706
3800
  draft.status = "discarded";
3707
3801
  draft.updatedAt = now;
3708
3802
  }
@@ -3713,7 +3807,8 @@ var SkillsService = class {
3713
3807
  action: loaded === void 0 ? "create" : "update",
3714
3808
  ...loaded === void 0 ? { targetRoot: input.targetRoot ?? suggestion?.targetRoot ?? DEFAULT_ROOT } : {
3715
3809
  baseKey: loaded.key,
3716
- baseHash: loaded.hash
3810
+ baseHash: loaded.hash,
3811
+ origin
3717
3812
  },
3718
3813
  content: input.content,
3719
3814
  notes: clip(input.notes, 2e3),
@@ -3757,12 +3852,14 @@ var SkillsService = class {
3757
3852
  const catalog = this.catalog(workspace);
3758
3853
  let key;
3759
3854
  let previous;
3855
+ let updated;
3760
3856
  if (draft.action === "update") {
3761
- const base = catalog.skills.find((skill) => skill.key === draft.baseKey);
3762
- if (base === void 0) throw new Error(`${draft.skillName} is no longer in ${draft.baseKey?.split(":")[0]}; draft it again`);
3857
+ const original = this.draftOriginal(ledger, draft);
3858
+ const base = this.requireOriginal(original);
3763
3859
  previous = this.requireHash(base, draft.baseHash);
3764
3860
  writeFileAtomic(base.path, draft.content);
3765
3861
  key = base.key;
3862
+ updated = findSkill(this.catalog(original.origin?.workspace), key);
3766
3863
  } else {
3767
3864
  const root = input.root ?? draft.targetRoot ?? DEFAULT_ROOT;
3768
3865
  if (activeSkill(catalog, draft.skillName) !== void 0) throw new Error(`${draft.skillName} exists now; draft it again as an update`);
@@ -3791,7 +3888,7 @@ var SkillsService = class {
3791
3888
  }
3792
3889
  this.store.saveLedger(ledger);
3793
3890
  return {
3794
- skill: findSkill(this.catalog(workspace), key),
3891
+ skill: updated ?? findSkill(this.catalog(workspace), key),
3795
3892
  version
3796
3893
  };
3797
3894
  });
@@ -3876,7 +3973,9 @@ var SkillsService = class {
3876
3973
  }
3877
3974
  };
3878
3975
  }
3879
- const loaded = activeSkill(this.catalog(workspace), skillName);
3976
+ const draft = target.draftId === void 0 ? void 0 : this.store.ledger().drafts.find((candidate) => candidate.id === target.draftId && candidate.skillName === skillName);
3977
+ if (target.draftId !== void 0 && draft === void 0) throw new Error("draft not found for this skill");
3978
+ const loaded = draft === void 0 ? activeSkill(this.catalog(workspace), skillName) : this.requireOriginal(this.draftOriginal(this.store.ledger(), draft));
3880
3979
  if (loaded === void 0) throw new Error(`${skillName} has no file to test; test a draft or no skill`);
3881
3980
  const document = readSkillDocument(loaded);
3882
3981
  return {
@@ -3884,7 +3983,8 @@ var SkillsService = class {
3884
3983
  target: {
3885
3984
  kind: "current",
3886
3985
  skillKey: loaded.key,
3887
- hash: document.hash
3986
+ hash: document.hash,
3987
+ ...draft === void 0 ? {} : { draftId: draft.id }
3888
3988
  }
3889
3989
  };
3890
3990
  }
@@ -4731,7 +4831,8 @@ function createSkillsMemorySource(config = {}) {
4731
4831
  case "run": return service.run(memoryInputText(fields.id, "id", 100));
4732
4832
  case "drafts": return service.drafts({
4733
4833
  ...optionalText(fields.suggestionId, "suggestionId", 100) === void 0 ? {} : { suggestionId: optionalText(fields.suggestionId, "suggestionId", 100) },
4734
- ...optionalText(fields.skill, "skill", 64) === void 0 ? {} : { skill: optionalText(fields.skill, "skill", 64) }
4834
+ ...optionalText(fields.skill, "skill", 64) === void 0 ? {} : { skill: optionalText(fields.skill, "skill", 64) },
4835
+ ...optionalText(fields.path, "path", 4096) === void 0 ? {} : { path: optionalText(fields.path, "path", 4096) }
4735
4836
  });
4736
4837
  case "draft": return service.draft(root, memoryInputText(fields.id, "id", 100));
4737
4838
  case "draft-preferences": return service.draftPreferences();
@@ -4741,6 +4842,7 @@ function createSkillsMemorySource(config = {}) {
4741
4842
  });
4742
4843
  case "evidence-pack": return service.evidencePack(root, {
4743
4844
  ...optionalText(fields.suggestionId, "suggestionId", 100) === void 0 ? {} : { suggestionId: optionalText(fields.suggestionId, "suggestionId", 100) },
4845
+ ...optionalText(fields.draftId, "draftId", 100) === void 0 ? {} : { draftId: optionalText(fields.draftId, "draftId", 100) },
4744
4846
  ...optionalText(fields.skill, "skill", 64) === void 0 ? {} : { skill: optionalText(fields.skill, "skill", 64) }
4745
4847
  });
4746
4848
  case "target-content": return service.targetContent(root, memoryInputText(fields.skillName, "skillName", 64), runTarget(fields.target));
@@ -4861,6 +4963,7 @@ function createSkillsMemorySource(config = {}) {
4861
4963
  case "draft-record":
4862
4964
  value = await service.recordDraft(root, {
4863
4965
  ...optionalText(fields.suggestionId, "suggestionId", 100) === void 0 ? {} : { suggestionId: optionalText(fields.suggestionId, "suggestionId", 100) },
4966
+ ...optionalText(fields.draftId, "draftId", 100) === void 0 ? {} : { draftId: optionalText(fields.draftId, "draftId", 100) },
4864
4967
  skillName: memoryInputText(fields.skillName, "skillName", 64),
4865
4968
  content: typeof fields.content === "string" ? fields.content : "",
4866
4969
  notes: typeof fields.notes === "string" ? fields.notes : "",
@@ -42,6 +42,7 @@ export interface SkillsPageClient {
42
42
  }): Promise<ReviewLogEntry[]>;
43
43
  evidencePack(input: {
44
44
  suggestionId?: string;
45
+ draftId?: string;
45
46
  skill?: string;
46
47
  }): Promise<EvidencePack>;
47
48
  createSkill(input: {
@@ -135,6 +136,7 @@ export interface SkillsPageClient {
135
136
  canAssist(operation: 'skill-draft' | 'skill-backtest' | 'skill-derive-cases' | 'skill-review-now'): boolean;
136
137
  generateDraft(input: {
137
138
  suggestionId?: string;
139
+ draftId?: string;
138
140
  skill?: string;
139
141
  guidance?: string;
140
142
  targetRoot?: SkillRootKind;
@@ -145,6 +145,13 @@ export interface EvidenceReference {
145
145
  }
146
146
  export type SuggestionStatus = 'pending' | 'drafting' | 'review' | 'adopted' | 'dismissed' | 'expired' | 'invalidated';
147
147
  export declare const SUGGESTION_STATUSES: readonly SuggestionStatus[];
148
+ /** A file selected by the Source catalog, independent of the page's storage scope. */
149
+ export interface SkillOrigin {
150
+ path: string;
151
+ /** The owning project root; absent for user skills. */
152
+ workspace?: string;
153
+ }
154
+ export type SkillResolution = 'available' | 'missing' | 'origin-unavailable';
148
155
  export interface SkillSuggestion {
149
156
  id: string;
150
157
  skillName: string;
@@ -159,6 +166,8 @@ export interface SkillSuggestion {
159
166
  /** For `update`: the SKILL.md the reviewer read, so a later edit invalidates it. */
160
167
  skillKey?: string;
161
168
  skillFileHash?: string;
169
+ /** Absent on legacy records; never supplied by the model. */
170
+ origin?: SkillOrigin;
162
171
  sourceSessionId?: string;
163
172
  sourceSessionTitle?: string;
164
173
  reviewId?: string;
@@ -178,6 +187,8 @@ export interface SkillDraft {
178
187
  /** For `update`, the SKILL.md hash the draft was written against. */
179
188
  baseKey?: string;
180
189
  baseHash?: string;
190
+ /** The file this update replaces; absent on legacy records. */
191
+ origin?: SkillOrigin;
181
192
  /** For `create`, where adopting writes the new bundle. */
182
193
  targetRoot?: SkillRootKind;
183
194
  content: string;
@@ -449,7 +460,10 @@ export interface SuggestionView {
449
460
  suggestion: SkillSuggestion;
450
461
  /** The loaded file is still the one the reviewer read. */
451
462
  hashCurrent: boolean;
452
- skill?: SkillSummary;
463
+ skill?: SkillDocument;
464
+ resolution?: SkillResolution;
465
+ /** Whether the selected scope's catalog offers this same file for navigation. */
466
+ skillInCatalog?: boolean;
453
467
  draft?: SkillDraft;
454
468
  trajectories: TrajectorySummary[];
455
469
  }
@@ -457,6 +471,7 @@ export interface DraftView {
457
471
  draft: SkillDraft;
458
472
  /** The file the draft replaces, as it is now. */
459
473
  base?: SkillDocument;
474
+ resolution?: SkillResolution;
460
475
  runs: BacktestRun[];
461
476
  }
462
477
  export type { DetectedSource, MarketSourceKind } from './source-kind.ts';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-mnemon-source-skills",
3
- "version": "0.5.7",
3
+ "version": "0.5.9",
4
4
  "description": "Skills Source for Composable View Memory: DSH skill management, evolution suggestions, evidence trajectories and eval sets.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -62,7 +62,7 @@
62
62
  "@types/node": "^22.0.0",
63
63
  "@types/react": "^18.3.0",
64
64
  "@types/react-dom": "^18.3.0",
65
- "dsh-mnemon": "^0.5.40",
65
+ "dsh-mnemon": "^0.5.42",
66
66
  "jsdom": "^26.0.0",
67
67
  "react": "^18.2.0",
68
68
  "react-dom": "^18.2.0",
@@ -12,6 +12,7 @@
12
12
  "tab.market": "市场",
13
13
  "tab.trash": "回收站",
14
14
  "common.loading": "加载中",
15
+ "common.retry": "重试",
15
16
  "common.cancel": "取消",
16
17
  "common.close": "关闭",
17
18
  "common.delete": "删除",
@@ -134,7 +135,9 @@
134
135
  "evolution.noAgent": "当前没有可用的任务 Agent",
135
136
  "evolution.drafting": "正在生成草案",
136
137
  "evolution.draftFailed": "草案生成失败:{error}",
137
- "evolution.skillGone": "这个技能已不存在。",
138
+ "evolution.skillGone": "原技能文件已删除、移动或无法读取。请恢复文件,或在新位置打开技能并重新生成草案。",
139
+ "evolution.originUnavailable": "旧记录未可靠保存原工作区。请切换到原工作区,在技能页打开该技能并重新生成草案;现有草案和证据会保留。",
140
+ "evolution.currentSkill": "当前技能文件",
138
141
  "evolution.skillChanged": "技能在建议之后被改过,生成草案前会以当前版本为准。",
139
142
  "evolution.adopted": "已采纳",
140
143
  "evolution.openSkill": "查看技能",
@@ -344,6 +347,10 @@
344
347
  "evolution.deleteTitle": "删除这条建议?",
345
348
  "evolution.deleteBody": "它生成的草案和证据引用会一起删掉,无法恢复。",
346
349
  "evolution.deleted": "已删除",
350
+ "evolution.missing": "该建议已删除",
351
+ "evolution.missingBody": "可以在左侧选择其他建议,或返回记录查看原始内容。",
352
+ "evolution.loadFailed": "无法加载这条建议",
353
+ "evolution.loadFailedBody": "请重试,或在左侧选择其他建议。",
347
354
  "tests.model": "全局模型",
348
355
  "tests.modelCard": "所选回测模型",
349
356
  "tests.modelAuto": "跟随默认"
@@ -361,6 +368,7 @@
361
368
  "tab.market": "Market",
362
369
  "tab.trash": "Trash",
363
370
  "common.loading": "Loading",
371
+ "common.retry": "Retry",
364
372
  "common.cancel": "Cancel",
365
373
  "common.close": "Close",
366
374
  "common.delete": "Delete",
@@ -483,7 +491,9 @@
483
491
  "evolution.noAgent": "No task Agent is available",
484
492
  "evolution.drafting": "Drafting",
485
493
  "evolution.draftFailed": "Drafting failed: {error}",
486
- "evolution.skillGone": "This skill no longer exists.",
494
+ "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.",
495
+ "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.",
496
+ "evolution.currentSkill": "Current skill file",
487
497
  "evolution.skillChanged": "The skill changed after this suggestion; the draft starts from its current version.",
488
498
  "evolution.adopted": "Adopted",
489
499
  "evolution.openSkill": "Open skill",
@@ -693,6 +703,10 @@
693
703
  "evolution.deleteTitle": "Delete this suggestion?",
694
704
  "evolution.deleteBody": "Its draft and the evidence references it cites go with it, and this cannot be undone.",
695
705
  "evolution.deleted": "Deleted",
706
+ "evolution.missing": "This suggestion has been deleted",
707
+ "evolution.missingBody": "Select another suggestion on the left, or return to Evidence to read the original record.",
708
+ "evolution.loadFailed": "Could not load this suggestion",
709
+ "evolution.loadFailedBody": "Retry, or select another suggestion on the left.",
696
710
  "tests.model": "Global model",
697
711
  "tests.modelCard": "Selected model",
698
712
  "tests.modelAuto": "Follow default"