dsh-mnemon-source-skills 0.5.9 → 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 +2 -0
- package/lib/client.js +150 -36
- package/lib/contracts-CwhbQi-u.js +119 -0
- package/lib/contracts.d.ts +49 -1
- package/lib/contracts.js +2 -41
- package/lib/index.js +213 -16
- package/lib/types/auto-adoption.d.ts +9 -0
- package/lib/types/client/api.d.ts +2 -0
- package/lib/types/contracts.d.ts +39 -0
- package/package.json +2 -2
- package/presentation/locales.json +56 -2
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
|
|
@@ -535,7 +536,34 @@ window.__ModuleLoader__.load({
|
|
|
535
536
|
"evolution.loadFailedBody": "请重试,或在左侧选择其他建议。",
|
|
536
537
|
"tests.model": "全局模型",
|
|
537
538
|
"tests.modelCard": "所选回测模型",
|
|
538
|
-
"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 已停止,不自动重启此建议"
|
|
539
567
|
};
|
|
540
568
|
var en = {
|
|
541
569
|
"page.title": "Skills",
|
|
@@ -891,7 +919,34 @@ window.__ModuleLoader__.load({
|
|
|
891
919
|
"evolution.loadFailedBody": "Retry, or select another suggestion on the left.",
|
|
892
920
|
"tests.model": "Global model",
|
|
893
921
|
"tests.modelCard": "Selected model",
|
|
894
|
-
"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"
|
|
895
950
|
};
|
|
896
951
|
//#endregion
|
|
897
952
|
//#region \0source-css:/home/runner/work/dsh-mnemon/dsh-mnemon/plugins/dsh-mnemon-source-skills/presentation/page.module.css.mjs
|
|
@@ -3730,6 +3785,7 @@ window.__ModuleLoader__.load({
|
|
|
3730
3785
|
context.refresh();
|
|
3731
3786
|
} catch (error) {
|
|
3732
3787
|
context.fail(error);
|
|
3788
|
+
context.refresh();
|
|
3733
3789
|
} finally {
|
|
3734
3790
|
setBusy(false);
|
|
3735
3791
|
}
|
|
@@ -3821,6 +3877,7 @@ window.__ModuleLoader__.load({
|
|
|
3821
3877
|
className: css.skWarn,
|
|
3822
3878
|
children: t("evolution.skillChanged")
|
|
3823
3879
|
}),
|
|
3880
|
+
suggestion.autoAdoption !== void 0 && suggestion.autoAdoption.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AutoAdoptionHistory, { events: suggestion.autoAdoption }),
|
|
3824
3881
|
suggestion.status === "drafting" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Busy, { label: t("evolution.drafting") }),
|
|
3825
3882
|
suggestion.lastError !== void 0 && suggestion.status === "pending" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
3826
3883
|
className: css.inlineError,
|
|
@@ -3979,6 +4036,7 @@ window.__ModuleLoader__.load({
|
|
|
3979
4036
|
context.refresh();
|
|
3980
4037
|
} catch (error) {
|
|
3981
4038
|
context.fail(error);
|
|
4039
|
+
context.refresh();
|
|
3982
4040
|
} finally {
|
|
3983
4041
|
setBusy(false);
|
|
3984
4042
|
}
|
|
@@ -4004,7 +4062,8 @@ window.__ModuleLoader__.load({
|
|
|
4004
4062
|
});
|
|
4005
4063
|
await context.client.linkRun(draft.id, started.runId);
|
|
4006
4064
|
});
|
|
4007
|
-
const redraft = (note) => act(() => context.client.generateDraft({
|
|
4065
|
+
const redraft = (note, backtest = false) => act(() => context.client.generateDraft({
|
|
4066
|
+
...backtest ? { backtest: true } : {},
|
|
4008
4067
|
draftId: draft.id,
|
|
4009
4068
|
...draft.suggestionId === void 0 ? {} : { suggestionId: draft.suggestionId },
|
|
4010
4069
|
guidance: note,
|
|
@@ -4045,7 +4104,7 @@ window.__ModuleLoader__.load({
|
|
|
4045
4104
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
4046
4105
|
variant: "primary",
|
|
4047
4106
|
size: "sm",
|
|
4048
|
-
disabled: !context.writable || busy || editing !== null || running || !originalAvailable,
|
|
4107
|
+
disabled: !context.writable || busy || editing !== null || running || view.conflict !== void 0 || !originalAvailable,
|
|
4049
4108
|
onClick: () => regressed ? setConfirmAdopt(true) : void adopt(),
|
|
4050
4109
|
children: t("draft.adopt")
|
|
4051
4110
|
}),
|
|
@@ -4074,6 +4133,16 @@ window.__ModuleLoader__.load({
|
|
|
4074
4133
|
className: css.skWarn,
|
|
4075
4134
|
children: t(view.resolution === "origin-unavailable" ? "evolution.originUnavailable" : "evolution.skillGone")
|
|
4076
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
|
+
}),
|
|
4077
4146
|
draft.notes !== "" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Clamp, { text: draft.notes }),
|
|
4078
4147
|
draft.changes.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MoreList, {
|
|
4079
4148
|
items: draft.changes,
|
|
@@ -4082,37 +4151,53 @@ window.__ModuleLoader__.load({
|
|
|
4082
4151
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4083
4152
|
className: css.skBacktest,
|
|
4084
4153
|
"data-tone": regressed ? "bad" : candidateScore !== void 0 && baselineScore !== void 0 && candidateScore > baselineScore ? "good" : void 0,
|
|
4085
|
-
children: [
|
|
4086
|
-
|
|
4087
|
-
|
|
4088
|
-
|
|
4089
|
-
|
|
4090
|
-
|
|
4091
|
-
|
|
4092
|
-
|
|
4093
|
-
|
|
4094
|
-
|
|
4095
|
-
|
|
4096
|
-
|
|
4097
|
-
|
|
4098
|
-
|
|
4099
|
-
|
|
4100
|
-
|
|
4101
|
-
|
|
4102
|
-
|
|
4103
|
-
|
|
4104
|
-
|
|
4105
|
-
|
|
4106
|
-
|
|
4107
|
-
|
|
4108
|
-
|
|
4109
|
-
|
|
4110
|
-
|
|
4111
|
-
|
|
4112
|
-
|
|
4113
|
-
|
|
4114
|
-
|
|
4115
|
-
|
|
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
|
+
]
|
|
4116
4201
|
}),
|
|
4117
4202
|
excluded > 0 && !running && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
4118
4203
|
className: css.skMuted,
|
|
@@ -4208,6 +4293,35 @@ window.__ModuleLoader__.load({
|
|
|
4208
4293
|
]
|
|
4209
4294
|
});
|
|
4210
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
|
+
}
|
|
4211
4325
|
//#endregion
|
|
4212
4326
|
//#region src/contracts.ts
|
|
4213
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 };
|
package/lib/contracts.d.ts
CHANGED
|
@@ -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';
|
|
@@ -192,6 +202,7 @@ interface SkillSuggestion {
|
|
|
192
202
|
status: SuggestionStatus;
|
|
193
203
|
draftId?: string;
|
|
194
204
|
lastError?: string;
|
|
205
|
+
autoAdoption?: AutoAdoptionEvent[];
|
|
195
206
|
createdAt: string;
|
|
196
207
|
updatedAt: string;
|
|
197
208
|
expiresAt: string;
|
|
@@ -292,6 +303,8 @@ interface CaseResult {
|
|
|
292
303
|
error?: string;
|
|
293
304
|
/** Kept under verdict=error so older readers also exclude it from scores. */
|
|
294
305
|
unmeasuredReason?: 'requires-vision';
|
|
306
|
+
/** Every expected judge check was explicitly returned; absent on older runs. */
|
|
307
|
+
fullyGraded?: boolean;
|
|
295
308
|
}
|
|
296
309
|
type RunStatus = 'running' | 'completed' | 'failed' | 'interrupted';
|
|
297
310
|
interface BacktestRun {
|
|
@@ -304,6 +317,11 @@ interface BacktestRun {
|
|
|
304
317
|
total: number;
|
|
305
318
|
};
|
|
306
319
|
caseIds: string[];
|
|
320
|
+
/** Source-verified case inputs at start; absent on older runs. */
|
|
321
|
+
casesHash?: string;
|
|
322
|
+
model?: ModelRoute & {
|
|
323
|
+
reasoningEffort?: string;
|
|
324
|
+
};
|
|
307
325
|
results: CaseResult[];
|
|
308
326
|
/** Over the graded cases; absent when none could be graded. */
|
|
309
327
|
passRate?: number;
|
|
@@ -330,6 +348,10 @@ interface ReviewLogEntry {
|
|
|
330
348
|
error?: string;
|
|
331
349
|
/** The conversation turn the reviewed window ended with. */
|
|
332
350
|
turn?: number;
|
|
351
|
+
autoAdoptions?: Array<AutoAdoptionEvent & {
|
|
352
|
+
suggestionId: string;
|
|
353
|
+
skillName: string;
|
|
354
|
+
}>;
|
|
333
355
|
/** The suggestions this review created or strengthened. */
|
|
334
356
|
suggestionIds?: string[];
|
|
335
357
|
}
|
|
@@ -433,6 +455,8 @@ interface ReviewRecordResult {
|
|
|
433
455
|
reviewId: string;
|
|
434
456
|
trajectoryId?: string;
|
|
435
457
|
suggestionIds: string[];
|
|
458
|
+
/** Newly inserted ids only; older Sources omit this capability. */
|
|
459
|
+
createdSuggestionIds?: string[];
|
|
436
460
|
inserted: number;
|
|
437
461
|
updated: number;
|
|
438
462
|
casesAdded: number;
|
|
@@ -487,6 +511,8 @@ interface SuggestionView {
|
|
|
487
511
|
}
|
|
488
512
|
interface DraftView {
|
|
489
513
|
draft: SkillDraft;
|
|
514
|
+
/** File changes need a new draft, never a retry with a newer revision. */
|
|
515
|
+
conflict?: 'base-changed' | 'target-exists';
|
|
490
516
|
/** The file the draft replaces, as it is now. */
|
|
491
517
|
base?: SkillDocument;
|
|
492
518
|
resolution?: SkillResolution;
|
|
@@ -637,5 +663,27 @@ interface MarketInstall {
|
|
|
637
663
|
files: number;
|
|
638
664
|
installedAt: string;
|
|
639
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
|
+
}
|
|
640
688
|
//#endregion
|
|
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 };
|
|
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 };
|
package/lib/contracts.js
CHANGED
|
@@ -1,41 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
"project-dsh",
|
|
4
|
-
"project-agents",
|
|
5
|
-
"user-dsh",
|
|
6
|
-
"user-agents"
|
|
7
|
-
];
|
|
8
|
-
const SKILL_ROOT_RANK = {
|
|
9
|
-
"project-dsh": 100,
|
|
10
|
-
"project-agents": 200,
|
|
11
|
-
"user-dsh": 400,
|
|
12
|
-
"user-agents": 500
|
|
13
|
-
};
|
|
14
|
-
const EVOLUTION_SIGNALS = [
|
|
15
|
-
"correction",
|
|
16
|
-
"directive",
|
|
17
|
-
"tool-failure",
|
|
18
|
-
"approach-change",
|
|
19
|
-
"procedure",
|
|
20
|
-
"skill-gap",
|
|
21
|
-
"skill-misleading",
|
|
22
|
-
"success"
|
|
23
|
-
];
|
|
24
|
-
const SUGGESTION_STATUSES = [
|
|
25
|
-
"pending",
|
|
26
|
-
"drafting",
|
|
27
|
-
"review",
|
|
28
|
-
"adopted",
|
|
29
|
-
"dismissed",
|
|
30
|
-
"expired",
|
|
31
|
-
"invalidated"
|
|
32
|
-
];
|
|
33
|
-
const DEFAULT_SKILLS_POLICY = Object.freeze({
|
|
34
|
-
allowCreate: true,
|
|
35
|
-
allowUpdate: true,
|
|
36
|
-
minConfidence: .6,
|
|
37
|
-
expiryDays: 7,
|
|
38
|
-
maxPendingPerSession: 5
|
|
39
|
-
});
|
|
40
|
-
//#endregion
|
|
41
|
-
export { DEFAULT_SKILLS_POLICY, EVOLUTION_SIGNALS, SKILL_ROOT_KINDS, SKILL_ROOT_RANK, SUGGESTION_STATUSES };
|
|
1
|
+
import { a as SKILL_ROOT_KINDS, c as compareAutomaticSkillRuns, i as EVOLUTION_SIGNALS, l as skillCaseSignature, n as AUTO_ADOPTION_STAGES, o as SKILL_ROOT_RANK, r as DEFAULT_SKILLS_POLICY, s as SUGGESTION_STATUSES, t as AUTO_ADOPTION_REASONS } from "./contracts-CwhbQi-u.js";
|
|
2
|
+
export { AUTO_ADOPTION_REASONS, AUTO_ADOPTION_STAGES, DEFAULT_SKILLS_POLICY, EVOLUTION_SIGNALS, SKILL_ROOT_KINDS, SKILL_ROOT_RANK, SUGGESTION_STATUSES, compareAutomaticSkillRuns, skillCaseSignature };
|
package/lib/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { a as SKILL_ROOT_KINDS, c as compareAutomaticSkillRuns, i as EVOLUTION_SIGNALS, l as skillCaseSignature, n as AUTO_ADOPTION_STAGES, o as SKILL_ROOT_RANK, r as DEFAULT_SKILLS_POLICY, t as AUTO_ADOPTION_REASONS } from "./contracts-CwhbQi-u.js";
|
|
2
2
|
import { defineMemoryPlugin, defineMemorySource, installMemory, memoryConfigurationDigest, memoryInputRecord, memoryInputStringArray, memoryInputText, withMemoryStorageLock } from "dsh-mnemon/extension-sdk";
|
|
3
3
|
import { homedir } from "node:os";
|
|
4
4
|
import { basename, dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
@@ -837,15 +837,39 @@ function orphanedDraft(suggestion) {
|
|
|
837
837
|
*/
|
|
838
838
|
function expireSuggestions(ledger, now) {
|
|
839
839
|
let changed = false;
|
|
840
|
-
for (const suggestion of ledger.suggestions)
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
840
|
+
for (const suggestion of ledger.suggestions) {
|
|
841
|
+
const lastAuto = suggestion.autoAdoption?.at(-1);
|
|
842
|
+
if (lastAuto !== void 0 && [
|
|
843
|
+
"queued",
|
|
844
|
+
"drafting",
|
|
845
|
+
"backtesting",
|
|
846
|
+
"rewriting"
|
|
847
|
+
].includes(lastAuto.stage) && (!Number.isFinite(Date.parse(lastAuto.at)) || Date.parse(lastAuto.at) < PROCESS_STARTED_AT)) {
|
|
848
|
+
const event = {
|
|
849
|
+
at: now.toISOString(),
|
|
850
|
+
stage: "paused",
|
|
851
|
+
attempt: lastAuto.attempt,
|
|
852
|
+
reason: "interrupted"
|
|
853
|
+
};
|
|
854
|
+
suggestion.autoAdoption = [...suggestion.autoAdoption, event].slice(-16);
|
|
855
|
+
const review = ledger.reviews.find((entry) => entry.id === suggestion.reviewId);
|
|
856
|
+
if (review !== void 0) review.autoAdoptions = [...review.autoAdoptions ?? [], {
|
|
857
|
+
...event,
|
|
858
|
+
suggestionId: suggestion.id,
|
|
859
|
+
skillName: suggestion.skillName
|
|
860
|
+
}].slice(-160);
|
|
861
|
+
changed = true;
|
|
862
|
+
}
|
|
863
|
+
if (suggestion.status === "pending" && Date.parse(suggestion.expiresAt) < now.getTime()) {
|
|
864
|
+
suggestion.status = "expired";
|
|
865
|
+
suggestion.updatedAt = now.toISOString();
|
|
866
|
+
changed = true;
|
|
867
|
+
} else if (suggestion.status === "drafting" && orphanedDraft(suggestion)) {
|
|
868
|
+
suggestion.status = "pending";
|
|
869
|
+
suggestion.updatedAt = now.toISOString();
|
|
870
|
+
suggestion.lastError = ORPHAN_DRAFT_ERROR;
|
|
871
|
+
changed = true;
|
|
872
|
+
}
|
|
849
873
|
}
|
|
850
874
|
return changed;
|
|
851
875
|
}
|
|
@@ -900,6 +924,7 @@ function admitReview(context, input) {
|
|
|
900
924
|
const steps = (values) => [...new Set(values.filter((value) => Number.isInteger(value) && stepIndexes.has(value)))].sort((a, b) => a - b);
|
|
901
925
|
const skipped = [];
|
|
902
926
|
const allowed = (action) => (action === "create" ? policy.allowCreate : policy.allowUpdate) !== false;
|
|
927
|
+
const createdSuggestionIds = [];
|
|
903
928
|
let inserted = 0;
|
|
904
929
|
let updated = 0;
|
|
905
930
|
let casesAdded = 0;
|
|
@@ -1038,6 +1063,7 @@ function admitReview(context, input) {
|
|
|
1038
1063
|
expiresAt: new Date(now.getTime() + policy.expiryDays * 24 * 60 * 60 * 1e3).toISOString()
|
|
1039
1064
|
};
|
|
1040
1065
|
ledger.suggestions.unshift(suggestion);
|
|
1066
|
+
createdSuggestionIds.push(suggestion.id);
|
|
1041
1067
|
touched.push(suggestion);
|
|
1042
1068
|
inserted++;
|
|
1043
1069
|
}
|
|
@@ -1151,6 +1177,7 @@ function admitReview(context, input) {
|
|
|
1151
1177
|
reviewId,
|
|
1152
1178
|
trajectoryId,
|
|
1153
1179
|
suggestionIds: touched.map((suggestion) => suggestion.id),
|
|
1180
|
+
createdSuggestionIds,
|
|
1154
1181
|
inserted,
|
|
1155
1182
|
updated,
|
|
1156
1183
|
casesAdded,
|
|
@@ -3266,15 +3293,17 @@ var SkillsService = class {
|
|
|
3266
3293
|
const ledger = this.store.ledger();
|
|
3267
3294
|
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));
|
|
3268
3295
|
}
|
|
3269
|
-
draft(
|
|
3296
|
+
draft(workspace, id) {
|
|
3270
3297
|
const ledger = this.store.ledger();
|
|
3271
3298
|
const draft = ledger.drafts.find((candidate) => candidate.id === id);
|
|
3272
3299
|
if (draft === void 0) throw new Error("draft not found: " + id);
|
|
3273
3300
|
const original = draft.action === "update" ? this.draftOriginal(ledger, draft) : void 0;
|
|
3274
3301
|
const base = original?.skill;
|
|
3302
|
+
const conflict = draft.action === "update" ? base !== void 0 && base.hash !== draft.baseHash ? "base-changed" : void 0 : activeSkill(this.catalog(workspace), draft.skillName) !== void 0 ? "target-exists" : void 0;
|
|
3275
3303
|
const now = this.clock().getTime();
|
|
3276
3304
|
return {
|
|
3277
3305
|
draft,
|
|
3306
|
+
...conflict === void 0 ? {} : { conflict },
|
|
3278
3307
|
...original === void 0 ? {} : { resolution: original.resolution },
|
|
3279
3308
|
...base === void 0 ? {} : { base: readSkillDocument(base) },
|
|
3280
3309
|
runs: draft.runIds.flatMap((id) => {
|
|
@@ -3648,7 +3677,9 @@ var SkillsService = class {
|
|
|
3648
3677
|
}
|
|
3649
3678
|
/** The Host marks a suggestion `drafting` before a task Agent writes, and back to `pending` if it fails. */
|
|
3650
3679
|
suggestionStatus(input) {
|
|
3651
|
-
return this.withSuggestion(input.id, (suggestion) => {
|
|
3680
|
+
return this.withSuggestion(input.id, (suggestion, ledger) => {
|
|
3681
|
+
input.signal?.throwIfAborted();
|
|
3682
|
+
this.requireExpectedDraft(ledger, suggestion, input.expectedDraft);
|
|
3652
3683
|
if (input.status === "drafting") {
|
|
3653
3684
|
if (suggestion.status !== "pending" && suggestion.status !== "review") throw new Error("this suggestion is " + suggestion.status + "; only an open suggestion can be drafted");
|
|
3654
3685
|
suggestion.status = "drafting";
|
|
@@ -3661,6 +3692,28 @@ var SkillsService = class {
|
|
|
3661
3692
|
return suggestion;
|
|
3662
3693
|
});
|
|
3663
3694
|
}
|
|
3695
|
+
requireExpectedDraft(ledger, suggestion, expected) {
|
|
3696
|
+
if (expected === void 0) return;
|
|
3697
|
+
const current = ledger.drafts.find((draft) => draft.id === suggestion?.draftId);
|
|
3698
|
+
if (!(expected === null ? suggestion !== void 0 && suggestion.draftId === void 0 : current?.id === expected.id && current.status === "ready" && sha256(current.content) === expected.hash)) throw new Error("The suggestion or draft changed while this draft was being generated; leave it for manual review");
|
|
3699
|
+
}
|
|
3700
|
+
recordAutoAdoption(id, input) {
|
|
3701
|
+
return this.withSuggestion(id, (suggestion, ledger) => {
|
|
3702
|
+
const event = {
|
|
3703
|
+
...input,
|
|
3704
|
+
...input.detail === void 0 ? {} : { detail: clip(input.detail, 500) },
|
|
3705
|
+
at: this.clock().toISOString()
|
|
3706
|
+
};
|
|
3707
|
+
suggestion.autoAdoption = [...suggestion.autoAdoption ?? [], event].slice(-16);
|
|
3708
|
+
const review = ledger.reviews.find((entry) => entry.id === suggestion.reviewId);
|
|
3709
|
+
if (review !== void 0) review.autoAdoptions = [...review.autoAdoptions ?? [], {
|
|
3710
|
+
...event,
|
|
3711
|
+
suggestionId: id,
|
|
3712
|
+
skillName: suggestion.skillName
|
|
3713
|
+
}].slice(-160);
|
|
3714
|
+
return suggestion;
|
|
3715
|
+
});
|
|
3716
|
+
}
|
|
3664
3717
|
pinTrajectory(id, pinned) {
|
|
3665
3718
|
return this.write(() => {
|
|
3666
3719
|
const ledger = this.store.ledger();
|
|
@@ -3783,6 +3836,7 @@ var SkillsService = class {
|
|
|
3783
3836
|
}
|
|
3784
3837
|
recordDraft(workspace, input) {
|
|
3785
3838
|
return this.write(() => {
|
|
3839
|
+
input.signal?.throwIfAborted();
|
|
3786
3840
|
validateSkillContent(input.content, input.skillName);
|
|
3787
3841
|
const ledger = this.store.ledger();
|
|
3788
3842
|
const catalog = this.catalog(workspace);
|
|
@@ -3793,8 +3847,10 @@ var SkillsService = class {
|
|
|
3793
3847
|
const suggestion = input.suggestionId === void 0 ? void 0 : ledger.suggestions.find((candidate) => candidate.id === input.suggestionId);
|
|
3794
3848
|
if (input.suggestionId !== void 0 && suggestion === void 0) throw new Error("suggestion not found: " + input.suggestionId);
|
|
3795
3849
|
if (suggestion !== void 0 && suggestion.skillName !== input.skillName) throw new Error("suggestion is for another skill");
|
|
3850
|
+
this.requireExpectedDraft(ledger, suggestion, input.expectedDraft);
|
|
3796
3851
|
const original = prior?.action === "update" ? this.draftOriginal(ledger, prior) : suggestion?.action === "update" ? this.suggestionOriginal(ledger, suggestion) : void 0;
|
|
3797
3852
|
const loaded = original === void 0 ? activeSkill(catalog, input.skillName) : this.requireOriginal(original);
|
|
3853
|
+
if (input.expectedBaseHash !== void 0 && (loaded?.hash ?? null) !== input.expectedBaseHash) throw new Error("The skill changed while this draft was being generated; regenerate it from the current file");
|
|
3798
3854
|
const origin = loaded === void 0 ? void 0 : original?.origin ?? skillOrigin(catalog, loaded);
|
|
3799
3855
|
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)) {
|
|
3800
3856
|
draft.status = "discarded";
|
|
@@ -3848,6 +3904,19 @@ var SkillsService = class {
|
|
|
3848
3904
|
const ledger = this.store.ledger();
|
|
3849
3905
|
const draft = ledger.drafts.find((candidate) => candidate.id === id);
|
|
3850
3906
|
if (draft === void 0 || draft.status !== "ready") throw new Error("only a draft waiting for review can be adopted");
|
|
3907
|
+
let automaticResult;
|
|
3908
|
+
if (input.automatic !== void 0) {
|
|
3909
|
+
const proof = input.automatic;
|
|
3910
|
+
const suggestion = ledger.suggestions.find((entry) => entry.id === draft.suggestionId);
|
|
3911
|
+
const evals = this.store.evals();
|
|
3912
|
+
const cases = evals.cases.filter((entry) => entry.skillName === draft.skillName && entry.enabled);
|
|
3913
|
+
const casesHash = sha256(skillCaseSignature(cases));
|
|
3914
|
+
const before = evals.runs.find((run) => run.id === proof.baselineRunId);
|
|
3915
|
+
const after = evals.runs.find((run) => run.id === proof.draftRunId);
|
|
3916
|
+
automaticResult = compareAutomaticSkillRuns(cases, before, after, casesHash);
|
|
3917
|
+
if (suggestion?.status !== "review" || suggestion.draftId !== draft.id || Date.parse(suggestion.expiresAt) <= this.clock().getTime() || proof.draftHash !== sha256(draft.content) || casesHash !== proof.casesHash || before?.skillName !== draft.skillName || after?.skillName !== draft.skillName || !(Date.parse(before.startedAt) >= Date.parse(draft.createdAt)) || !(Date.parse(after.startedAt) >= Date.parse(draft.createdAt)) || after.target.kind !== "draft" || after.target.draftId !== draft.id || after.target.hash !== proof.draftHash || (draft.action === "update" ? before.target.kind !== "current" || before.target.hash !== draft.baseHash : before.target.kind !== "none") || automaticResult.outcome !== "not-worse") throw new Error("The draft, cases, or complete matching backtest results changed; automatic adoption requires manual review");
|
|
3918
|
+
}
|
|
3919
|
+
input.signal?.throwIfAborted();
|
|
3851
3920
|
validateSkillContent(draft.content, draft.skillName);
|
|
3852
3921
|
const catalog = this.catalog(workspace);
|
|
3853
3922
|
let key;
|
|
@@ -3885,6 +3954,22 @@ var SkillsService = class {
|
|
|
3885
3954
|
if (suggestion !== void 0) {
|
|
3886
3955
|
suggestion.status = "adopted";
|
|
3887
3956
|
suggestion.updatedAt = now;
|
|
3957
|
+
if (input.automatic !== void 0 && automaticResult !== void 0) {
|
|
3958
|
+
const event = {
|
|
3959
|
+
at: now,
|
|
3960
|
+
stage: "adopted",
|
|
3961
|
+
attempt: input.automatic.attempt,
|
|
3962
|
+
...automaticResult.baselineScore === void 0 ? {} : { baselineScore: automaticResult.baselineScore },
|
|
3963
|
+
...automaticResult.draftScore === void 0 ? {} : { draftScore: automaticResult.draftScore }
|
|
3964
|
+
};
|
|
3965
|
+
suggestion.autoAdoption = [...suggestion.autoAdoption ?? [], event].slice(-16);
|
|
3966
|
+
const review = ledger.reviews.find((entry) => entry.id === suggestion.reviewId);
|
|
3967
|
+
if (review !== void 0) review.autoAdoptions = [...review.autoAdoptions ?? [], {
|
|
3968
|
+
...event,
|
|
3969
|
+
suggestionId: suggestion.id,
|
|
3970
|
+
skillName: suggestion.skillName
|
|
3971
|
+
}].slice(-160);
|
|
3972
|
+
}
|
|
3888
3973
|
}
|
|
3889
3974
|
this.store.saveLedger(ledger);
|
|
3890
3975
|
return {
|
|
@@ -3945,6 +4030,26 @@ var SkillsService = class {
|
|
|
3945
4030
|
return { reclaimed: Math.max(0, before - after) };
|
|
3946
4031
|
});
|
|
3947
4032
|
}
|
|
4033
|
+
/** Persist terminal state so expired tasks cannot later revive themselves. */
|
|
4034
|
+
reclaimStaleRuns() {
|
|
4035
|
+
return this.write(() => {
|
|
4036
|
+
const evals = this.store.evals();
|
|
4037
|
+
const now = this.clock();
|
|
4038
|
+
let reclaimed = 0;
|
|
4039
|
+
for (const run of evals.runs) {
|
|
4040
|
+
if (run.status !== "running" || Number.isFinite(Date.parse(run.startedAt)) && now.getTime() - Date.parse(run.startedAt) <= RUN_STALE_MS) continue;
|
|
4041
|
+
run.status = "interrupted";
|
|
4042
|
+
run.finishedAt = now.toISOString();
|
|
4043
|
+
run.error = "Backtest exceeded its 45 minute deadline; run it again.";
|
|
4044
|
+
reclaimed++;
|
|
4045
|
+
}
|
|
4046
|
+
if (reclaimed > 0) {
|
|
4047
|
+
this.store.saveEvals(evals);
|
|
4048
|
+
this.touch();
|
|
4049
|
+
}
|
|
4050
|
+
return { reclaimed };
|
|
4051
|
+
});
|
|
4052
|
+
}
|
|
3948
4053
|
/** The content a run tests, resolved from its target. */
|
|
3949
4054
|
targetContent(workspace, skillName, target) {
|
|
3950
4055
|
if (target.kind === "none") return { target: { kind: "none" } };
|
|
@@ -3990,9 +4095,12 @@ var SkillsService = class {
|
|
|
3990
4095
|
}
|
|
3991
4096
|
startRun(input) {
|
|
3992
4097
|
return this.write(() => {
|
|
4098
|
+
input.signal?.throwIfAborted();
|
|
3993
4099
|
const evals = this.store.evals();
|
|
3994
4100
|
const cases = input.caseIds.map((id) => evals.cases.find((entry) => entry.id === id && entry.skillName === input.skillName));
|
|
3995
4101
|
if (cases.length === 0 || cases.some((entry) => entry === void 0)) throw new Error("a run needs at least one case of this skill");
|
|
4102
|
+
const casesHash = sha256(skillCaseSignature(cases));
|
|
4103
|
+
if (input.expectedCasesHash !== void 0 && input.expectedCasesHash !== casesHash) throw new Error("Eval cases changed before this backtest started");
|
|
3996
4104
|
const run = {
|
|
3997
4105
|
id: randomUUID(),
|
|
3998
4106
|
skillName: input.skillName,
|
|
@@ -4003,6 +4111,8 @@ var SkillsService = class {
|
|
|
4003
4111
|
total: cases.length
|
|
4004
4112
|
},
|
|
4005
4113
|
caseIds: input.caseIds,
|
|
4114
|
+
casesHash,
|
|
4115
|
+
...input.model === void 0 ? {} : { model: input.model },
|
|
4006
4116
|
results: [],
|
|
4007
4117
|
startedAt: this.clock().toISOString()
|
|
4008
4118
|
};
|
|
@@ -4029,6 +4139,7 @@ var SkillsService = class {
|
|
|
4029
4139
|
}
|
|
4030
4140
|
progressRun(input) {
|
|
4031
4141
|
return this.write(() => {
|
|
4142
|
+
input.signal?.throwIfAborted();
|
|
4032
4143
|
const evals = this.store.evals();
|
|
4033
4144
|
const run = evals.runs.find((candidate) => candidate.id === input.id);
|
|
4034
4145
|
if (run === void 0 || run.status !== "running") throw new Error("run is not running: " + input.id);
|
|
@@ -4055,9 +4166,11 @@ var SkillsService = class {
|
|
|
4055
4166
|
}
|
|
4056
4167
|
finishRun(input) {
|
|
4057
4168
|
return this.write(() => {
|
|
4169
|
+
input.signal?.throwIfAborted();
|
|
4058
4170
|
const evals = this.store.evals();
|
|
4059
4171
|
const run = evals.runs.find((candidate) => candidate.id === input.id);
|
|
4060
4172
|
if (run === void 0) throw new Error("run not found: " + input.id);
|
|
4173
|
+
if (run.status !== "running") return run;
|
|
4061
4174
|
run.status = input.status;
|
|
4062
4175
|
run.finishedAt = this.clock().toISOString();
|
|
4063
4176
|
if (input.error !== void 0) run.error = clip(input.error, 500);
|
|
@@ -4551,6 +4664,55 @@ function draftModelInput(value) {
|
|
|
4551
4664
|
model: memoryInputText(picked.model, "model.model", 200)
|
|
4552
4665
|
};
|
|
4553
4666
|
}
|
|
4667
|
+
function backtestModel(value) {
|
|
4668
|
+
if (value === void 0) return {};
|
|
4669
|
+
const model = draftModelInput(value);
|
|
4670
|
+
if (model === null) throw new Error("a backtest model must name its provider and model");
|
|
4671
|
+
const input = memoryInputRecord(value, "model");
|
|
4672
|
+
return { model: {
|
|
4673
|
+
...model,
|
|
4674
|
+
...typeof input.reasoningEffort === "string" ? { reasoningEffort: memoryInputText(input.reasoningEffort, "reasoningEffort", 100) } : {}
|
|
4675
|
+
} };
|
|
4676
|
+
}
|
|
4677
|
+
function draftExpectation(value) {
|
|
4678
|
+
if (value === void 0) return {};
|
|
4679
|
+
if (value === null) return { expectedDraft: null };
|
|
4680
|
+
const expected = memoryInputRecord(value, "expectedDraft");
|
|
4681
|
+
return { expectedDraft: {
|
|
4682
|
+
id: memoryInputText(expected.id, "expectedDraft.id", 100),
|
|
4683
|
+
hash: memoryInputText(expected.hash, "expectedDraft.hash", 100)
|
|
4684
|
+
} };
|
|
4685
|
+
}
|
|
4686
|
+
function automaticAdoption(value) {
|
|
4687
|
+
if (value === void 0) return {};
|
|
4688
|
+
const input = memoryInputRecord(value, "automatic");
|
|
4689
|
+
if (!Number.isInteger(input.attempt) || Number(input.attempt) < 1 || Number(input.attempt) > 3) throw new Error("automatic adoption attempt must be 1..3");
|
|
4690
|
+
return { automatic: {
|
|
4691
|
+
attempt: Number(input.attempt),
|
|
4692
|
+
draftHash: memoryInputText(input.draftHash, "draftHash", 100),
|
|
4693
|
+
baselineRunId: memoryInputText(input.baselineRunId, "baselineRunId", 100),
|
|
4694
|
+
draftRunId: memoryInputText(input.draftRunId, "draftRunId", 100),
|
|
4695
|
+
casesHash: memoryInputText(input.casesHash, "casesHash", 100)
|
|
4696
|
+
} };
|
|
4697
|
+
}
|
|
4698
|
+
function autoAdoptionEvent(value) {
|
|
4699
|
+
if (!AUTO_ADOPTION_STAGES.includes(value.stage)) throw new Error("unknown automatic adoption stage");
|
|
4700
|
+
if (value.reason !== void 0 && !AUTO_ADOPTION_REASONS.includes(value.reason)) throw new Error("unknown automatic adoption reason");
|
|
4701
|
+
if (!Number.isInteger(value.attempt) || Number(value.attempt) < 0 || Number(value.attempt) > 3) throw new Error("automatic adoption attempt must be 0..3");
|
|
4702
|
+
const score = (key) => {
|
|
4703
|
+
if (value[key] === void 0) return {};
|
|
4704
|
+
if (typeof value[key] !== "number" || !Number.isFinite(value[key]) || value[key] < 0 || value[key] > 1) throw new Error("invalid automatic adoption score");
|
|
4705
|
+
return { [key]: value[key] };
|
|
4706
|
+
};
|
|
4707
|
+
return {
|
|
4708
|
+
stage: value.stage,
|
|
4709
|
+
attempt: Number(value.attempt),
|
|
4710
|
+
...value.reason === void 0 ? {} : { reason: value.reason },
|
|
4711
|
+
...typeof value.detail === "string" ? { detail: value.detail } : {},
|
|
4712
|
+
...score("baselineScore"),
|
|
4713
|
+
...score("draftScore")
|
|
4714
|
+
};
|
|
4715
|
+
}
|
|
4554
4716
|
function caseInput(value, skillName) {
|
|
4555
4717
|
const input = memoryInputRecord(value, "case");
|
|
4556
4718
|
const checks = input.checks;
|
|
@@ -4610,7 +4772,8 @@ function caseResult(value) {
|
|
|
4610
4772
|
answer: typeof input.answer === "string" ? input.answer : "",
|
|
4611
4773
|
...typeof input.summary === "string" ? { summary: input.summary } : {},
|
|
4612
4774
|
...typeof input.error === "string" ? { error: input.error } : {},
|
|
4613
|
-
...unmeasuredReason === void 0 ? {} : { unmeasuredReason }
|
|
4775
|
+
...unmeasuredReason === void 0 ? {} : { unmeasuredReason },
|
|
4776
|
+
...typeof input.fullyGraded === "boolean" ? { fullyGraded: input.fullyGraded } : {}
|
|
4614
4777
|
};
|
|
4615
4778
|
}
|
|
4616
4779
|
function signal(value) {
|
|
@@ -4777,6 +4940,11 @@ function createSkillsMemorySource(config = {}) {
|
|
|
4777
4940
|
...effective.agentsHome === void 0 ? {} : { agentsHome: effective.agentsHome }
|
|
4778
4941
|
});
|
|
4779
4942
|
service.reclaimOrphanDrafts().catch(() => void 0);
|
|
4943
|
+
const recovery = service.reclaimStaleRuns();
|
|
4944
|
+
const reaper = setInterval(() => {
|
|
4945
|
+
service.reclaimStaleRuns().catch(() => void 0);
|
|
4946
|
+
}, 6e4);
|
|
4947
|
+
reaper.unref?.();
|
|
4780
4948
|
const facts = (scope, management) => management ? {
|
|
4781
4949
|
sourceInstanceKey: context.sourceInstanceKey,
|
|
4782
4950
|
sourceTypeId: SKILLS_SOURCE_TYPE_ID,
|
|
@@ -4798,6 +4966,9 @@ function createSkillsMemorySource(config = {}) {
|
|
|
4798
4966
|
hints: { reason: "management-only" }
|
|
4799
4967
|
};
|
|
4800
4968
|
return {
|
|
4969
|
+
dispose() {
|
|
4970
|
+
clearInterval(reaper);
|
|
4971
|
+
},
|
|
4801
4972
|
facts(request) {
|
|
4802
4973
|
return facts(request.scope, request.scenario.startsWith("management."));
|
|
4803
4974
|
},
|
|
@@ -4805,6 +4976,7 @@ function createSkillsMemorySource(config = {}) {
|
|
|
4805
4976
|
return { fragments: [] };
|
|
4806
4977
|
},
|
|
4807
4978
|
async manage(request) {
|
|
4979
|
+
await recovery;
|
|
4808
4980
|
const root = workspace(request.scope);
|
|
4809
4981
|
const input = request.input ?? {};
|
|
4810
4982
|
const fields = typeof input === "object" && input !== null && !Array.isArray(input) ? input : {};
|
|
@@ -4931,12 +5103,17 @@ function createSkillsMemorySource(config = {}) {
|
|
|
4931
5103
|
const status = fields.status;
|
|
4932
5104
|
if (status !== "drafting" && status !== "pending") throw new Error("status must be drafting or pending");
|
|
4933
5105
|
value = await service.suggestionStatus({
|
|
5106
|
+
...request.signal === void 0 ? {} : { signal: request.signal },
|
|
4934
5107
|
id: memoryInputText(fields.id, "id", 100),
|
|
4935
5108
|
status,
|
|
5109
|
+
...draftExpectation(fields.expectedDraft),
|
|
4936
5110
|
...typeof fields.error === "string" ? { error: fields.error } : {}
|
|
4937
5111
|
});
|
|
4938
5112
|
break;
|
|
4939
5113
|
}
|
|
5114
|
+
case "auto-adoption-record":
|
|
5115
|
+
value = await service.recordAutoAdoption(memoryInputText(fields.id, "id", 100), autoAdoptionEvent(fields));
|
|
5116
|
+
break;
|
|
4940
5117
|
case "trajectory-pin":
|
|
4941
5118
|
value = await service.pinTrajectory(memoryInputText(fields.id, "id", 100), bool(fields.pinned, "pinned"));
|
|
4942
5119
|
break;
|
|
@@ -4962,6 +5139,9 @@ function createSkillsMemorySource(config = {}) {
|
|
|
4962
5139
|
}
|
|
4963
5140
|
case "draft-record":
|
|
4964
5141
|
value = await service.recordDraft(root, {
|
|
5142
|
+
...request.signal === void 0 ? {} : { signal: request.signal },
|
|
5143
|
+
...draftExpectation(fields.expectedDraft),
|
|
5144
|
+
...fields.expectedBaseHash === void 0 ? {} : { expectedBaseHash: fields.expectedBaseHash === null ? null : memoryInputText(fields.expectedBaseHash, "expectedBaseHash", 100) },
|
|
4965
5145
|
...optionalText(fields.suggestionId, "suggestionId", 100) === void 0 ? {} : { suggestionId: optionalText(fields.suggestionId, "suggestionId", 100) },
|
|
4966
5146
|
...optionalText(fields.draftId, "draftId", 100) === void 0 ? {} : { draftId: optionalText(fields.draftId, "draftId", 100) },
|
|
4967
5147
|
skillName: memoryInputText(fields.skillName, "skillName", 64),
|
|
@@ -4979,7 +5159,11 @@ function createSkillsMemorySource(config = {}) {
|
|
|
4979
5159
|
});
|
|
4980
5160
|
break;
|
|
4981
5161
|
case "draft-adopt":
|
|
4982
|
-
value = await service.adoptDraft(root, memoryInputText(fields.id, "id", 100), {
|
|
5162
|
+
value = await service.adoptDraft(root, memoryInputText(fields.id, "id", 100), {
|
|
5163
|
+
...request.signal === void 0 ? {} : { signal: request.signal },
|
|
5164
|
+
...automaticAdoption(fields.automatic),
|
|
5165
|
+
...rootKind(fields.root) === void 0 ? {} : { root: rootKind(fields.root) }
|
|
5166
|
+
});
|
|
4983
5167
|
break;
|
|
4984
5168
|
case "draft-discard":
|
|
4985
5169
|
value = await service.discardDraft(memoryInputText(fields.id, "id", 100));
|
|
@@ -5002,9 +5186,12 @@ function createSkillsMemorySource(config = {}) {
|
|
|
5002
5186
|
}
|
|
5003
5187
|
case "run-start":
|
|
5004
5188
|
value = await service.startRun({
|
|
5189
|
+
...request.signal === void 0 ? {} : { signal: request.signal },
|
|
5005
5190
|
skillName: memoryInputText(fields.skillName, "skillName", 64),
|
|
5006
5191
|
target: runTarget(fields.target),
|
|
5007
|
-
caseIds: memoryInputStringArray(fields.caseIds, "caseIds",
|
|
5192
|
+
caseIds: memoryInputStringArray(fields.caseIds, "caseIds", 40) ?? [],
|
|
5193
|
+
...fields.expectedCasesHash === void 0 ? {} : { expectedCasesHash: memoryInputText(fields.expectedCasesHash, "expectedCasesHash", 100) },
|
|
5194
|
+
...backtestModel(fields.model)
|
|
5008
5195
|
});
|
|
5009
5196
|
break;
|
|
5010
5197
|
case "run-link":
|
|
@@ -5012,13 +5199,23 @@ function createSkillsMemorySource(config = {}) {
|
|
|
5012
5199
|
break;
|
|
5013
5200
|
case "run-progress":
|
|
5014
5201
|
value = await service.progressRun({
|
|
5202
|
+
...request.signal === void 0 ? {} : { signal: request.signal },
|
|
5015
5203
|
id: memoryInputText(fields.id, "id", 100),
|
|
5016
5204
|
result: caseResult(fields.result)
|
|
5017
5205
|
});
|
|
5018
5206
|
break;
|
|
5207
|
+
case "run-cancel":
|
|
5208
|
+
value = await service.finishRun({
|
|
5209
|
+
...request.signal === void 0 ? {} : { signal: request.signal },
|
|
5210
|
+
id: memoryInputText(fields.id, "id", 100),
|
|
5211
|
+
status: "interrupted",
|
|
5212
|
+
error: "Backtest cancelled by the user."
|
|
5213
|
+
});
|
|
5214
|
+
break;
|
|
5019
5215
|
case "run-finish": {
|
|
5020
|
-
const status = fields.status === "completed" ? "completed" : "failed";
|
|
5216
|
+
const status = fields.status === "completed" ? "completed" : fields.status === "interrupted" ? "interrupted" : "failed";
|
|
5021
5217
|
value = await service.finishRun({
|
|
5218
|
+
...request.signal === void 0 ? {} : { signal: request.signal },
|
|
5022
5219
|
id: memoryInputText(fields.id, "id", 100),
|
|
5023
5220
|
status,
|
|
5024
5221
|
...typeof fields.error === "string" ? { error: fields.error } : {}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { BacktestRun, EvalCase } from './contracts.ts';
|
|
2
|
+
/** Stable evaluation inputs, excluding presentation timestamps and provenance. Hash at the server boundary. */
|
|
3
|
+
export declare function skillCaseSignature(cases: readonly EvalCase[]): string;
|
|
4
|
+
/** Automatic decisions require full matching coverage, not the UI's scored intersection. */
|
|
5
|
+
export declare function compareAutomaticSkillRuns(cases: readonly EvalCase[], baseline: BacktestRun | undefined, draft: BacktestRun | undefined, casesHash: string): {
|
|
6
|
+
outcome: 'unmeasured' | 'regressed' | 'not-worse';
|
|
7
|
+
baselineScore?: number;
|
|
8
|
+
draftScore?: number;
|
|
9
|
+
};
|
|
@@ -101,6 +101,7 @@ export interface SkillsPageClient {
|
|
|
101
101
|
discardDraft(id: string): Promise<SkillDraft>;
|
|
102
102
|
/** Attach a baseline run to the draft it is compared with. */
|
|
103
103
|
linkRun(draftId: string, runId: string): Promise<void>;
|
|
104
|
+
cancelRun(id: string): Promise<BacktestRun>;
|
|
104
105
|
/** The model each piece of model work follows: the two global picks and their per-item overrides. */
|
|
105
106
|
draftPreferences(): Promise<DraftPreferencesView>;
|
|
106
107
|
/** Pick the model for drafts, for backtests, or for one suggestion or skill; `null` follows the task Agent again. */
|
|
@@ -140,6 +141,7 @@ export interface SkillsPageClient {
|
|
|
140
141
|
skill?: string;
|
|
141
142
|
guidance?: string;
|
|
142
143
|
targetRoot?: SkillRootKind;
|
|
144
|
+
backtest?: boolean;
|
|
143
145
|
provider?: string;
|
|
144
146
|
model?: string;
|
|
145
147
|
}): Promise<{
|
package/lib/types/contracts.d.ts
CHANGED
|
@@ -174,6 +174,7 @@ export interface SkillSuggestion {
|
|
|
174
174
|
status: SuggestionStatus;
|
|
175
175
|
draftId?: string;
|
|
176
176
|
lastError?: string;
|
|
177
|
+
autoAdoption?: AutoAdoptionEvent[];
|
|
177
178
|
createdAt: string;
|
|
178
179
|
updatedAt: string;
|
|
179
180
|
expiresAt: string;
|
|
@@ -274,6 +275,8 @@ export interface CaseResult {
|
|
|
274
275
|
error?: string;
|
|
275
276
|
/** Kept under verdict=error so older readers also exclude it from scores. */
|
|
276
277
|
unmeasuredReason?: 'requires-vision';
|
|
278
|
+
/** Every expected judge check was explicitly returned; absent on older runs. */
|
|
279
|
+
fullyGraded?: boolean;
|
|
277
280
|
}
|
|
278
281
|
export type RunStatus = 'running' | 'completed' | 'failed' | 'interrupted';
|
|
279
282
|
export interface BacktestRun {
|
|
@@ -286,6 +289,11 @@ export interface BacktestRun {
|
|
|
286
289
|
total: number;
|
|
287
290
|
};
|
|
288
291
|
caseIds: string[];
|
|
292
|
+
/** Source-verified case inputs at start; absent on older runs. */
|
|
293
|
+
casesHash?: string;
|
|
294
|
+
model?: ModelRoute & {
|
|
295
|
+
reasoningEffort?: string;
|
|
296
|
+
};
|
|
289
297
|
results: CaseResult[];
|
|
290
298
|
/** Over the graded cases; absent when none could be graded. */
|
|
291
299
|
passRate?: number;
|
|
@@ -312,6 +320,10 @@ export interface ReviewLogEntry {
|
|
|
312
320
|
error?: string;
|
|
313
321
|
/** The conversation turn the reviewed window ended with. */
|
|
314
322
|
turn?: number;
|
|
323
|
+
autoAdoptions?: Array<AutoAdoptionEvent & {
|
|
324
|
+
suggestionId: string;
|
|
325
|
+
skillName: string;
|
|
326
|
+
}>;
|
|
315
327
|
/** The suggestions this review created or strengthened. */
|
|
316
328
|
suggestionIds?: string[];
|
|
317
329
|
}
|
|
@@ -415,6 +427,8 @@ export interface ReviewRecordResult {
|
|
|
415
427
|
reviewId: string;
|
|
416
428
|
trajectoryId?: string;
|
|
417
429
|
suggestionIds: string[];
|
|
430
|
+
/** Newly inserted ids only; older Sources omit this capability. */
|
|
431
|
+
createdSuggestionIds?: string[];
|
|
418
432
|
inserted: number;
|
|
419
433
|
updated: number;
|
|
420
434
|
casesAdded: number;
|
|
@@ -469,6 +483,8 @@ export interface SuggestionView {
|
|
|
469
483
|
}
|
|
470
484
|
export interface DraftView {
|
|
471
485
|
draft: SkillDraft;
|
|
486
|
+
/** File changes need a new draft, never a retry with a newer revision. */
|
|
487
|
+
conflict?: 'base-changed' | 'target-exists';
|
|
472
488
|
/** The file the draft replaces, as it is now. */
|
|
473
489
|
base?: SkillDocument;
|
|
474
490
|
resolution?: SkillResolution;
|
|
@@ -620,3 +636,26 @@ export interface MarketInstall {
|
|
|
620
636
|
files: number;
|
|
621
637
|
installedAt: string;
|
|
622
638
|
}
|
|
639
|
+
export declare const AUTO_ADOPTION_STAGES: readonly ["queued", "drafting", "backtesting", "rewriting", "adopted", "paused", "review"];
|
|
640
|
+
export declare const AUTO_ADOPTION_REASONS: readonly ["disabled", "no-cases", "case-limit", "unmeasured", "regressed", "changed", "failed", "queue-full", "model-unavailable", "interrupted"];
|
|
641
|
+
export interface AutoAdoptionEvent {
|
|
642
|
+
at: string;
|
|
643
|
+
stage: typeof AUTO_ADOPTION_STAGES[number];
|
|
644
|
+
attempt: number;
|
|
645
|
+
reason?: typeof AUTO_ADOPTION_REASONS[number];
|
|
646
|
+
detail?: string;
|
|
647
|
+
baselineScore?: number;
|
|
648
|
+
draftScore?: number;
|
|
649
|
+
}
|
|
650
|
+
export type DraftExpectation = {
|
|
651
|
+
id: string;
|
|
652
|
+
hash: string;
|
|
653
|
+
} | null;
|
|
654
|
+
export interface AutomaticAdoption {
|
|
655
|
+
attempt: number;
|
|
656
|
+
draftHash: string;
|
|
657
|
+
baselineRunId: string;
|
|
658
|
+
draftRunId: string;
|
|
659
|
+
casesHash: string;
|
|
660
|
+
}
|
|
661
|
+
export { skillCaseSignature, compareAutomaticSkillRuns } from './auto-adoption.ts';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-mnemon-source-skills",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.10",
|
|
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.
|
|
65
|
+
"dsh-mnemon": "^0.5.43",
|
|
66
66
|
"jsdom": "^26.0.0",
|
|
67
67
|
"react": "^18.2.0",
|
|
68
68
|
"react-dom": "^18.2.0",
|
|
@@ -353,7 +353,34 @@
|
|
|
353
353
|
"evolution.loadFailedBody": "请重试,或在左侧选择其他建议。",
|
|
354
354
|
"tests.model": "全局模型",
|
|
355
355
|
"tests.modelCard": "所选回测模型",
|
|
356
|
-
"tests.modelAuto": "跟随默认"
|
|
356
|
+
"tests.modelAuto": "跟随默认",
|
|
357
|
+
"draft.baseChanged": "原技能已变更,此草案已过期。请基于当前文件重新生成并回测。",
|
|
358
|
+
"draft.targetExists": "同名技能已存在。请重新生成为改进草案并回测,再确认采纳。",
|
|
359
|
+
"draft.refreshGuidance": "基于当前技能文件重新生成,保留最近的修改,并重新运行回测。",
|
|
360
|
+
"draft.regenerate": "重新生成并回测",
|
|
361
|
+
"draft.cancelBacktest": "取消回测",
|
|
362
|
+
"draft.interrupted": "回测已中断。可以重新回测,或在检查草案后手动采纳。",
|
|
363
|
+
"evolution.auto.title": "自动采纳",
|
|
364
|
+
"evolution.auto.history": "自动采纳活动记录",
|
|
365
|
+
"evolution.auto.attempt": "第 {count} 轮",
|
|
366
|
+
"evolution.auto.scores": "基线 {baseline} / 草案 {draft}",
|
|
367
|
+
"evolution.auto.stage.queued": "等待处理",
|
|
368
|
+
"evolution.auto.stage.drafting": "正在起草",
|
|
369
|
+
"evolution.auto.stage.backtesting": "正在对比回测",
|
|
370
|
+
"evolution.auto.stage.rewriting": "依据退步结果重写",
|
|
371
|
+
"evolution.auto.stage.adopted": "已自动采纳",
|
|
372
|
+
"evolution.auto.stage.paused": "已停止自动处理",
|
|
373
|
+
"evolution.auto.stage.review": "需要人工审查",
|
|
374
|
+
"evolution.auto.reason.disabled": "自动采纳或所需权限已关闭",
|
|
375
|
+
"evolution.auto.reason.no-cases": "没有启用的回测用例",
|
|
376
|
+
"evolution.auto.reason.case-limit": "启用的用例超过每轮上限;调整用例或上限后手动处理本建议",
|
|
377
|
+
"evolution.auto.reason.unmeasured": "回测未完整测出所有检查项,或存在图片依赖、错误、中断",
|
|
378
|
+
"evolution.auto.reason.regressed": "至少一个用例或原先通过的检查项出现退步",
|
|
379
|
+
"evolution.auto.reason.changed": "建议、草案或用例已变动",
|
|
380
|
+
"evolution.auto.reason.failed": "自动处理失败,保留人工处理入口",
|
|
381
|
+
"evolution.auto.reason.queue-full": "自动处理队列已满",
|
|
382
|
+
"evolution.auto.reason.model-unavailable": "无法确定起草或回测模型",
|
|
383
|
+
"evolution.auto.reason.interrupted": "Host 已停止,不自动重启此建议"
|
|
357
384
|
},
|
|
358
385
|
"en": {
|
|
359
386
|
"page.title": "Skills",
|
|
@@ -709,6 +736,33 @@
|
|
|
709
736
|
"evolution.loadFailedBody": "Retry, or select another suggestion on the left.",
|
|
710
737
|
"tests.model": "Global model",
|
|
711
738
|
"tests.modelCard": "Selected model",
|
|
712
|
-
"tests.modelAuto": "Follow default"
|
|
739
|
+
"tests.modelAuto": "Follow default",
|
|
740
|
+
"draft.baseChanged": "The original skill changed. This draft is out of date; regenerate it against the current file and backtest it.",
|
|
741
|
+
"draft.targetExists": "A skill with this name now exists. Regenerate an update and backtest it before adopting.",
|
|
742
|
+
"draft.refreshGuidance": "Regenerate against the current skill file, preserving its latest changes, then run backtests.",
|
|
743
|
+
"draft.regenerate": "Regenerate and backtest",
|
|
744
|
+
"draft.cancelBacktest": "Cancel backtests",
|
|
745
|
+
"draft.interrupted": "Backtests were interrupted. Run them again, or review the draft before adopting manually.",
|
|
746
|
+
"evolution.auto.title": "Automatic adoption",
|
|
747
|
+
"evolution.auto.history": "Automatic adoption activity",
|
|
748
|
+
"evolution.auto.attempt": "Attempt {count}",
|
|
749
|
+
"evolution.auto.scores": "Baseline {baseline} / draft {draft}",
|
|
750
|
+
"evolution.auto.stage.queued": "Queued",
|
|
751
|
+
"evolution.auto.stage.drafting": "Drafting",
|
|
752
|
+
"evolution.auto.stage.backtesting": "Backtesting a pair",
|
|
753
|
+
"evolution.auto.stage.rewriting": "Rewriting after regression",
|
|
754
|
+
"evolution.auto.stage.adopted": "Automatically adopted",
|
|
755
|
+
"evolution.auto.stage.paused": "Automatic work stopped",
|
|
756
|
+
"evolution.auto.stage.review": "Manual review required",
|
|
757
|
+
"evolution.auto.reason.disabled": "Automatic adoption or its required permissions were disabled",
|
|
758
|
+
"evolution.auto.reason.no-cases": "No enabled evaluation cases",
|
|
759
|
+
"evolution.auto.reason.case-limit": "Enabled cases exceed the per-run limit; adjust cases or the limit and review this suggestion manually",
|
|
760
|
+
"evolution.auto.reason.unmeasured": "Some checks were not fully measured, or a case needs images, failed or was interrupted",
|
|
761
|
+
"evolution.auto.reason.regressed": "At least one case or previously passing check regressed",
|
|
762
|
+
"evolution.auto.reason.changed": "The suggestion, draft or evaluation cases changed",
|
|
763
|
+
"evolution.auto.reason.failed": "Automatic work failed; manual review remains available",
|
|
764
|
+
"evolution.auto.reason.queue-full": "Automatic queue is full",
|
|
765
|
+
"evolution.auto.reason.model-unavailable": "No resolved draft or backtest model",
|
|
766
|
+
"evolution.auto.reason.interrupted": "Host stopped; this suggestion will not restart automatically"
|
|
713
767
|
}
|
|
714
768
|
}
|