dsh-continual-evolve 0.11.0 → 0.12.1
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 +42 -115
- package/README.zh.md +33 -93
- package/client/client.js +253 -0
- package/cordis.patch.yml +2 -2
- package/lib/card-routes.d.ts +32 -0
- package/lib/card-routes.js +92 -0
- package/lib/index.d.ts +65 -217
- package/lib/index.js +74 -234
- package/lib/known-workspaces.d.ts +22 -0
- package/lib/known-workspaces.js +42 -0
- package/lib/memory-guide.d.ts +13 -14
- package/lib/memory-guide.js +34 -21
- package/lib/memory-section.d.ts +77 -0
- package/lib/memory-section.js +189 -0
- package/lib/memory-snapshot.d.ts +54 -0
- package/lib/memory-snapshot.js +125 -0
- package/package.json +8 -17
- package/lib/apply.d.ts +0 -22
- package/lib/apply.js +0 -157
- package/lib/approval.d.ts +0 -73
- package/lib/approval.js +0 -127
- package/lib/audit-log.d.ts +0 -19
- package/lib/audit-log.js +0 -52
- package/lib/auto.d.ts +0 -236
- package/lib/auto.js +0 -773
- package/lib/autocase.d.ts +0 -38
- package/lib/autocase.js +0 -76
- package/lib/benchmark-command.d.ts +0 -9
- package/lib/benchmark-command.js +0 -357
- package/lib/benchmark.d.ts +0 -151
- package/lib/benchmark.js +0 -250
- package/lib/command-util.d.ts +0 -70
- package/lib/command-util.js +0 -123
- package/lib/command.d.ts +0 -13
- package/lib/command.js +0 -134
- package/lib/consolidate.d.ts +0 -90
- package/lib/consolidate.js +0 -180
- package/lib/copy.d.ts +0 -37
- package/lib/copy.js +0 -58
- package/lib/declines.d.ts +0 -103
- package/lib/declines.js +0 -179
- package/lib/evaluate.d.ts +0 -74
- package/lib/evaluate.js +0 -271
- package/lib/evolve-event.d.ts +0 -40
- package/lib/evolve-event.js +0 -60
- package/lib/extraction-phase.d.ts +0 -132
- package/lib/extraction-phase.js +0 -236
- package/lib/failures.d.ts +0 -42
- package/lib/failures.js +0 -171
- package/lib/fate.d.ts +0 -130
- package/lib/fate.js +0 -350
- package/lib/goal-command.d.ts +0 -7
- package/lib/goal-command.js +0 -37
- package/lib/goal.d.ts +0 -72
- package/lib/goal.js +0 -72
- package/lib/inject.d.ts +0 -90
- package/lib/inject.js +0 -164
- package/lib/injection-caps.d.ts +0 -4
- package/lib/injection-caps.js +0 -4
- package/lib/injection-directory.d.ts +0 -29
- package/lib/injection-directory.js +0 -78
- package/lib/injection-rank.d.ts +0 -85
- package/lib/injection-rank.js +0 -161
- package/lib/listener.d.ts +0 -45
- package/lib/listener.js +0 -199
- package/lib/llm-text.d.ts +0 -104
- package/lib/llm-text.js +0 -140
- package/lib/logfile.d.ts +0 -71
- package/lib/logfile.js +0 -159
- package/lib/maintenance-commands.d.ts +0 -23
- package/lib/maintenance-commands.js +0 -136
- package/lib/memory-agent.d.ts +0 -93
- package/lib/memory-agent.js +0 -300
- package/lib/memory-apply.d.ts +0 -44
- package/lib/memory-apply.js +0 -202
- package/lib/memory-benchmark.d.ts +0 -76
- package/lib/memory-benchmark.js +0 -170
- package/lib/memory-index.d.ts +0 -114
- package/lib/memory-index.js +0 -197
- package/lib/memory-manifest.d.ts +0 -16
- package/lib/memory-manifest.js +0 -70
- package/lib/message-source.d.ts +0 -23
- package/lib/message-source.js +0 -12
- package/lib/mount-command.d.ts +0 -10
- package/lib/mount-command.js +0 -48
- package/lib/mount.d.ts +0 -42
- package/lib/mount.js +0 -212
- package/lib/notify.d.ts +0 -48
- package/lib/notify.js +0 -57
- package/lib/plan-command.d.ts +0 -12
- package/lib/plan-command.js +0 -38
- package/lib/plan.d.ts +0 -16
- package/lib/plan.js +0 -126
- package/lib/planner.d.ts +0 -61
- package/lib/planner.js +0 -144
- package/lib/pool.d.ts +0 -7
- package/lib/pool.js +0 -25
- package/lib/prefix-cache.d.ts +0 -79
- package/lib/prefix-cache.js +0 -168
- package/lib/project.d.ts +0 -28
- package/lib/project.js +0 -66
- package/lib/projection.d.ts +0 -22
- package/lib/projection.js +0 -112
- package/lib/promotion.d.ts +0 -94
- package/lib/promotion.js +0 -171
- package/lib/recall.d.ts +0 -85
- package/lib/recall.js +0 -142
- package/lib/record-language.d.ts +0 -85
- package/lib/record-language.js +0 -152
- package/lib/render.d.ts +0 -13
- package/lib/render.js +0 -80
- package/lib/review-scheduler.d.ts +0 -38
- package/lib/review-scheduler.js +0 -183
- package/lib/review.d.ts +0 -60
- package/lib/review.js +0 -145
- package/lib/rollback.d.ts +0 -9
- package/lib/rollback.js +0 -68
- package/lib/rubric.d.ts +0 -29
- package/lib/rubric.js +0 -119
- package/lib/runtime-commands.d.ts +0 -14
- package/lib/runtime-commands.js +0 -115
- package/lib/runtime.d.ts +0 -24
- package/lib/runtime.js +0 -87
- package/lib/score.d.ts +0 -64
- package/lib/score.js +0 -191
- package/lib/search.d.ts +0 -83
- package/lib/search.js +0 -136
- package/lib/service.d.ts +0 -63
- package/lib/service.js +0 -253
- package/lib/skill-render.d.ts +0 -23
- package/lib/skill-render.js +0 -69
- package/lib/skill.d.ts +0 -15
- package/lib/skill.js +0 -80
- package/lib/skillquality.d.ts +0 -85
- package/lib/skillquality.js +0 -320
- package/lib/source.d.ts +0 -30
- package/lib/source.js +0 -43
- package/lib/state.d.ts +0 -26
- package/lib/state.js +0 -151
- package/lib/store.d.ts +0 -61
- package/lib/store.js +0 -164
- package/lib/token-usage.d.ts +0 -68
- package/lib/token-usage.js +0 -211
- package/lib/tool.d.ts +0 -34
- package/lib/tool.js +0 -320
- package/lib/turn-snapshot.d.ts +0 -78
- package/lib/turn-snapshot.js +0 -252
- package/lib/types.d.ts +0 -218
- package/lib/types.js +0 -133
- package/lib/usage.d.ts +0 -36
- package/lib/usage.js +0 -98
- package/lib/validate.d.ts +0 -28
- package/lib/validate.js +0 -181
- package/lib/wrapup-candidates.d.ts +0 -127
- package/lib/wrapup-candidates.js +0 -161
- package/lib/wrapup-command.d.ts +0 -10
- package/lib/wrapup-command.js +0 -243
- package/lib/wrapup-proposals.d.ts +0 -101
- package/lib/wrapup-proposals.js +0 -281
- package/lib/wrapup.d.ts +0 -56
- package/lib/wrapup.js +0 -105
package/lib/consolidate.js
DELETED
|
@@ -1,180 +0,0 @@
|
|
|
1
|
-
import { ARCHIVED_AT_KEY, CONFLICT_HINT_KEY, MERGED_FROM_KEY, isArchived } from "./types.js";
|
|
2
|
-
import { getUsageCount } from "./usage.js";
|
|
3
|
-
/** Zero-use entries at least this old are stale candidates (30d, matching the injection recency half-life scale). */
|
|
4
|
-
export const STALE_MIN_AGE_MS = 30 * 24 * 60 * 60 * 1000;
|
|
5
|
-
const KINDS = ["prompt", "memory", "skill", "subagent"];
|
|
6
|
-
/**
|
|
7
|
-
* Parse one conflictHint metadata value. Returns undefined for anything that
|
|
8
|
-
* is not exactly `<kind>:<id>:<score>` with a known kind and a score in
|
|
9
|
-
* [0, 1] — foreign or legacy junk must never crash the scan.
|
|
10
|
-
*/
|
|
11
|
-
export function parseConflictHint(value) {
|
|
12
|
-
if (typeof value !== "string") {
|
|
13
|
-
return undefined;
|
|
14
|
-
}
|
|
15
|
-
const parts = value.split(":");
|
|
16
|
-
if (parts.length !== 3) {
|
|
17
|
-
return undefined;
|
|
18
|
-
}
|
|
19
|
-
const [kind, id, rawScore] = parts;
|
|
20
|
-
if (!kind || !id || !rawScore || !KINDS.includes(kind)) {
|
|
21
|
-
return undefined;
|
|
22
|
-
}
|
|
23
|
-
const score = Number(rawScore);
|
|
24
|
-
if (!Number.isFinite(score) || score < 0 || score > 1) {
|
|
25
|
-
return undefined;
|
|
26
|
-
}
|
|
27
|
-
return { kind: kind, id, score };
|
|
28
|
-
}
|
|
29
|
-
/**
|
|
30
|
-
* Entries stamped by the write-time conflict guard whose target still exists
|
|
31
|
-
* and is still active. The hinted (newer) entry is the archive candidate —
|
|
32
|
-
* the pointed-to original stays as the live copy.
|
|
33
|
-
*/
|
|
34
|
-
export function findConflictPairs(state) {
|
|
35
|
-
const candidates = [];
|
|
36
|
-
for (const kind of KINDS) {
|
|
37
|
-
for (const entry of Object.values(state.entries[kind])) {
|
|
38
|
-
if (isArchived(entry))
|
|
39
|
-
continue;
|
|
40
|
-
const hint = parseConflictHint(entry.metadata[CONFLICT_HINT_KEY]);
|
|
41
|
-
if (!hint || hint.kind !== kind)
|
|
42
|
-
continue;
|
|
43
|
-
const target = state.entries[hint.kind]?.[hint.id];
|
|
44
|
-
if (!target || isArchived(target))
|
|
45
|
-
continue;
|
|
46
|
-
candidates.push({
|
|
47
|
-
kind,
|
|
48
|
-
id: entry.id,
|
|
49
|
-
title: entry.title,
|
|
50
|
-
reason: `near-duplicate of ${hint.id} 「${target.title}」 (${Math.round(hint.score * 100)}%) — keep the original`,
|
|
51
|
-
mergeInto: { kind: hint.kind, id: hint.id, title: target.title },
|
|
52
|
-
});
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
return candidates;
|
|
56
|
-
}
|
|
57
|
-
/**
|
|
58
|
-
* Active global entries never injected in any session and untouched for at
|
|
59
|
-
* least `minAgeMs`: prime staleness candidates. Operates on whatever state
|
|
60
|
-
* it is handed (callers pass the global store).
|
|
61
|
-
*/
|
|
62
|
-
export function findStaleEntries(state, store, now, minAgeMs = STALE_MIN_AGE_MS) {
|
|
63
|
-
const candidates = [];
|
|
64
|
-
for (const kind of KINDS) {
|
|
65
|
-
for (const entry of Object.values(state.entries[kind])) {
|
|
66
|
-
if (isArchived(entry))
|
|
67
|
-
continue;
|
|
68
|
-
if (getUsageCount(store, kind, entry.id) !== 0)
|
|
69
|
-
continue;
|
|
70
|
-
const updatedAt = Date.parse(entry.updated_at);
|
|
71
|
-
if (Number.isNaN(updatedAt) || now - updatedAt < minAgeMs)
|
|
72
|
-
continue;
|
|
73
|
-
candidates.push({
|
|
74
|
-
kind,
|
|
75
|
-
id: entry.id,
|
|
76
|
-
title: entry.title,
|
|
77
|
-
reason: `0 injections since ${entry.updated_at.slice(0, 10)} (stale)`,
|
|
78
|
-
});
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
return candidates;
|
|
82
|
-
}
|
|
83
|
-
/**
|
|
84
|
-
* Append a source entry's content to a survivor with an attributed divider,
|
|
85
|
-
* so the survivor's reader can see which part came from which entry.
|
|
86
|
-
*/
|
|
87
|
-
export function mergeContent(target, source, sourceRef, dateIso) {
|
|
88
|
-
const body = source.trim();
|
|
89
|
-
if (body.length === 0) {
|
|
90
|
-
return target;
|
|
91
|
-
}
|
|
92
|
-
return `${target.trimEnd()}\n\n---\n[Merged from ${sourceRef} on ${dateIso.slice(0, 10)} — near-duplicate consolidated]\n${body}`;
|
|
93
|
-
}
|
|
94
|
-
/** Existing `<kind>:<id>` strings of a target's {@link MERGED_FROM_KEY} trail. */
|
|
95
|
-
function existingMergedFrom(metadata) {
|
|
96
|
-
const value = metadata[MERGED_FROM_KEY];
|
|
97
|
-
return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
|
|
98
|
-
}
|
|
99
|
-
/**
|
|
100
|
-
* Merge both scans (conflict reason wins on overlap), deduped by kind:id,
|
|
101
|
-
* and build the batch archive edits. Each edit preserves the entry's full
|
|
102
|
-
* content and existing metadata — only ARCHIVED_AT_KEY is added — so
|
|
103
|
-
* `/evolve unarchive` restores everything intact.
|
|
104
|
-
*
|
|
105
|
-
* With `opts.mergeDuplicates`, conflict-pair candidates additionally merge
|
|
106
|
-
* their content INTO the pointed-to survivor (an attributed `mergeContent`
|
|
107
|
-
* section + a `mergedFrom` provenance stamp) before archiving — the #11
|
|
108
|
-
* candidate-c tier the 2026-08-28 ecosystem review promoted. Per-target
|
|
109
|
-
* accumulation composes multiple hints into one survivor (update edits
|
|
110
|
-
* replace content/metadata wholesale, so parallel merge edits would clobber
|
|
111
|
-
* each other).
|
|
112
|
-
*
|
|
113
|
-
* @param now Epoch ms used for the archivedAt stamp (injected for tests).
|
|
114
|
-
*/
|
|
115
|
-
export function planConsolidation(state, store, now = Date.now(), opts) {
|
|
116
|
-
const byKey = new Map();
|
|
117
|
-
for (const candidate of [...findConflictPairs(state), ...findStaleEntries(state, store, now, opts?.minAgeMs)]) {
|
|
118
|
-
const key = `${candidate.kind}:${candidate.id}`;
|
|
119
|
-
if (!byKey.has(key)) {
|
|
120
|
-
byKey.set(key, candidate);
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
const candidates = [...byKey.values()];
|
|
124
|
-
const dateIso = new Date(now).toISOString();
|
|
125
|
-
// A merge survivor must not itself be an archive candidate in the same
|
|
126
|
-
// batch: its archive edit is built from the ORIGINAL state snapshot, so it
|
|
127
|
-
// would overwrite the merge edit's content/mergedFrom wholesale and then
|
|
128
|
-
// archive the survivor (review audit 2026-08-28 S2). Survivors stay live;
|
|
129
|
-
// their merge lands, the source archives.
|
|
130
|
-
const mergeTargets = new Set(candidates.filter((candidate) => candidate.mergeInto).map((candidate) => `${candidate.mergeInto.kind}:${candidate.mergeInto.id}`));
|
|
131
|
-
const archiveCandidates = candidates.filter((candidate) => !mergeTargets.has(`${candidate.kind}:${candidate.id}`));
|
|
132
|
-
const edits = archiveCandidates.map((candidate) => {
|
|
133
|
-
const entry = state.entries[candidate.kind][candidate.id];
|
|
134
|
-
const metadata = { ...entry?.metadata, [ARCHIVED_AT_KEY]: dateIso };
|
|
135
|
-
return {
|
|
136
|
-
action: "update",
|
|
137
|
-
kind: candidate.kind,
|
|
138
|
-
id: candidate.id,
|
|
139
|
-
title: candidate.title,
|
|
140
|
-
content: entry?.content ?? "",
|
|
141
|
-
metadata,
|
|
142
|
-
};
|
|
143
|
-
});
|
|
144
|
-
if (opts?.mergeDuplicates) {
|
|
145
|
-
const merges = new Map();
|
|
146
|
-
for (const candidate of candidates) {
|
|
147
|
-
if (!candidate.mergeInto)
|
|
148
|
-
continue;
|
|
149
|
-
const source = state.entries[candidate.kind][candidate.id];
|
|
150
|
-
const target = state.entries[candidate.mergeInto.kind]?.[candidate.mergeInto.id];
|
|
151
|
-
if (!source || !target)
|
|
152
|
-
continue;
|
|
153
|
-
const key = `${candidate.mergeInto.kind}:${candidate.mergeInto.id}`;
|
|
154
|
-
const acc = merges.get(key) ??
|
|
155
|
-
{
|
|
156
|
-
kind: candidate.mergeInto.kind,
|
|
157
|
-
id: candidate.mergeInto.id,
|
|
158
|
-
title: target.title,
|
|
159
|
-
content: target.content,
|
|
160
|
-
mergedFrom: existingMergedFrom(target.metadata),
|
|
161
|
-
metadata: target.metadata,
|
|
162
|
-
};
|
|
163
|
-
acc.content = mergeContent(acc.content, source.content ?? "", `${candidate.kind}:${candidate.id}`, dateIso);
|
|
164
|
-
acc.mergedFrom.push(`${candidate.kind}:${candidate.id}`);
|
|
165
|
-
merges.set(key, acc);
|
|
166
|
-
}
|
|
167
|
-
for (const acc of merges.values()) {
|
|
168
|
-
edits.unshift({
|
|
169
|
-
action: "update",
|
|
170
|
-
kind: acc.kind,
|
|
171
|
-
id: acc.id,
|
|
172
|
-
title: acc.title,
|
|
173
|
-
content: acc.content,
|
|
174
|
-
metadata: { ...acc.metadata, [MERGED_FROM_KEY]: acc.mergedFrom },
|
|
175
|
-
});
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
return { candidates, edits };
|
|
179
|
-
}
|
|
180
|
-
//# sourceMappingURL=consolidate.js.map
|
package/lib/copy.d.ts
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Backend user-visible copy for the evolution confirmation dialogs, in both
|
|
3
|
-
* shipped languages. Labels stay literal per language (the parsers match
|
|
4
|
-
* them); guidance lives in `description`.
|
|
5
|
-
*
|
|
6
|
-
* Language is never hardcoded at a call site: every builder takes an
|
|
7
|
-
* explicit `lang`, and dialog entry points resolve it per call
|
|
8
|
-
* (explicit config upstream, else the durable DSH client preference,
|
|
9
|
-
* else `en`) when the caller does not supply one.
|
|
10
|
-
*/
|
|
11
|
-
import type { RecordLanguage } from "./record-language.js";
|
|
12
|
-
export interface ConfirmOption {
|
|
13
|
-
label: string;
|
|
14
|
-
description: string;
|
|
15
|
-
}
|
|
16
|
-
export interface ConfirmCopy {
|
|
17
|
-
question: string;
|
|
18
|
-
options: ConfirmOption[];
|
|
19
|
-
}
|
|
20
|
-
/** Maximum edit-summary chars carried into a dialog; longer text truncates. */
|
|
21
|
-
export declare const COPY_WHAT_MAX_CHARS = 300;
|
|
22
|
-
/**
|
|
23
|
-
* Persistent-scope write approval (`approve-global-evolve`).
|
|
24
|
-
*
|
|
25
|
-
* @param scope Target persistent scope.
|
|
26
|
-
* @param what Edit summary shown in the dialog body.
|
|
27
|
-
* @param lang Dialog language.
|
|
28
|
-
*/
|
|
29
|
-
export declare function approvalCopy(scope: "project" | "global", what: string, lang: RecordLanguage): ConfirmCopy;
|
|
30
|
-
/**
|
|
31
|
-
* Single-entry wrap-up archive confirm (`evolve-wrapup-archive-review`).
|
|
32
|
-
*
|
|
33
|
-
* @param title Entry title shown in the headline.
|
|
34
|
-
* @param lang Dialog language.
|
|
35
|
-
*/
|
|
36
|
-
export declare function wrapupArchiveCopy(title: string, lang: RecordLanguage): ConfirmCopy;
|
|
37
|
-
//# sourceMappingURL=copy.d.ts.map
|
package/lib/copy.js
DELETED
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
/** Maximum edit-summary chars carried into a dialog; longer text truncates. */
|
|
2
|
-
export const COPY_WHAT_MAX_CHARS = 300;
|
|
3
|
-
function compactWhat(what) {
|
|
4
|
-
return what.length > COPY_WHAT_MAX_CHARS ? `${what.slice(0, COPY_WHAT_MAX_CHARS)}…` : what;
|
|
5
|
-
}
|
|
6
|
-
/**
|
|
7
|
-
* Persistent-scope write approval (`approve-global-evolve`).
|
|
8
|
-
*
|
|
9
|
-
* @param scope Target persistent scope.
|
|
10
|
-
* @param what Edit summary shown in the dialog body.
|
|
11
|
-
* @param lang Dialog language.
|
|
12
|
-
*/
|
|
13
|
-
export function approvalCopy(scope, what, lang) {
|
|
14
|
-
const body = compactWhat(what);
|
|
15
|
-
if (lang === "zh") {
|
|
16
|
-
const storeLabel = scope === "project" ? "本项目跨会话 store" : "跨会话全局 store";
|
|
17
|
-
return {
|
|
18
|
-
question: `写入${storeLabel}?\n${body}\n${scope === "project" ? "影响:仅本项目会话可见,可回滚。" : "影响:所有会话可见,可回滚。"}`,
|
|
19
|
-
options: [
|
|
20
|
-
{ label: "批准", description: scope === "project" ? "写入,仅本项目会话可见" : "写入,所有会话可见" },
|
|
21
|
-
{ label: "拒绝", description: "不写入,本次跳过" },
|
|
22
|
-
],
|
|
23
|
-
};
|
|
24
|
-
}
|
|
25
|
-
const storeLabel = scope === "project" ? "project cross-session store" : "global cross-session store";
|
|
26
|
-
return {
|
|
27
|
-
question: `Write to the ${storeLabel}?\n${body}\n${scope === "project" ? "Impact: visible to this project's sessions only, reversible." : "Impact: visible to all sessions, reversible."}`,
|
|
28
|
-
options: [
|
|
29
|
-
{ label: "Approve", description: scope === "project" ? "Write, visible to this project only" : "Write, visible to all sessions" },
|
|
30
|
-
{ label: "Decline", description: "Skip this write" },
|
|
31
|
-
],
|
|
32
|
-
};
|
|
33
|
-
}
|
|
34
|
-
/**
|
|
35
|
-
* Single-entry wrap-up archive confirm (`evolve-wrapup-archive-review`).
|
|
36
|
-
*
|
|
37
|
-
* @param title Entry title shown in the headline.
|
|
38
|
-
* @param lang Dialog language.
|
|
39
|
-
*/
|
|
40
|
-
export function wrapupArchiveCopy(title, lang) {
|
|
41
|
-
if (lang === "zh") {
|
|
42
|
-
return {
|
|
43
|
-
question: `wrapup 确认归档:条目「${title}」\n未被全局覆盖且源自真实对话。归档后不再注入,数据保留、可恢复。`,
|
|
44
|
-
options: [
|
|
45
|
-
{ label: "归档", description: "隐藏但可恢复" },
|
|
46
|
-
{ label: "保留", description: "继续注入" },
|
|
47
|
-
],
|
|
48
|
-
};
|
|
49
|
-
}
|
|
50
|
-
return {
|
|
51
|
-
question: `Wrap-up archive confirm: entry "${title}"\nNot covered globally and distilled from real conversation. Archiving stops injection; data stays restorable.`,
|
|
52
|
-
options: [
|
|
53
|
-
{ label: "Archive", description: "Hide but restorable" },
|
|
54
|
-
{ label: "Keep", description: "Keep injecting" },
|
|
55
|
-
],
|
|
56
|
-
};
|
|
57
|
-
}
|
|
58
|
-
//# sourceMappingURL=copy.js.map
|
package/lib/declines.d.ts
DELETED
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
/** Cap on remembered declines; oldest-first pruning keeps the file small. */
|
|
2
|
-
export declare const MAX_DECLINED_MEMORY_LEDGER = 50;
|
|
3
|
-
/** Cap on stored content tokens per entry; enough for coverage, never prose. */
|
|
4
|
-
export declare const MAX_DECLINED_ENTRY_TOKENS = 128;
|
|
5
|
-
/** Minimum coverage of an entry's tokens by the checkpoint to pre-suppress. */
|
|
6
|
-
export declare const DECLINED_PRECHECK_COVERAGE = 0.85;
|
|
7
|
-
/** Minimum overlapping tokens; tiny entries must never match vacuously. */
|
|
8
|
-
export declare const DECLINED_PRECHECK_MIN_TOKENS = 5;
|
|
9
|
-
/** One declined project/global batch, keyed by its content fingerprint. */
|
|
10
|
-
export interface DeclinedMemoryBatch {
|
|
11
|
-
/** Persistent scope that was declined. Local never asks, so never lands here. */
|
|
12
|
-
scope: "project" | "global";
|
|
13
|
-
/** sha256-16 over the normalized batch (same cut as evaluate.ts/project.ts). */
|
|
14
|
-
fingerprint: string;
|
|
15
|
-
/** First edit title (audit readability only, never matched on). */
|
|
16
|
-
title: string;
|
|
17
|
-
/** Deduplicated content tokens for checkpoint containment checks. */
|
|
18
|
-
tokens: string[];
|
|
19
|
-
/** ISO timestamp of the human rejection. */
|
|
20
|
-
declinedAt: string;
|
|
21
|
-
}
|
|
22
|
-
/** Minimal edit shape needed for fingerprinting (avoids importing the agent). */
|
|
23
|
-
export interface FingerprintableEdit {
|
|
24
|
-
action: string;
|
|
25
|
-
id?: string;
|
|
26
|
-
title?: string;
|
|
27
|
-
content?: string;
|
|
28
|
-
}
|
|
29
|
-
/** Full path of the decline ledger file. */
|
|
30
|
-
export declare function declinedMemoryPath(baseDir: string): string;
|
|
31
|
-
/**
|
|
32
|
-
* Fingerprint one scope batch. Normalization is whitespace-only on purpose:
|
|
33
|
-
* case folding could conflate distinct facts (e.g. env var names), and a
|
|
34
|
-
* missed match only costs one more popup (fail-closed).
|
|
35
|
-
*
|
|
36
|
-
* @param scope - persistent scope the batch targets.
|
|
37
|
-
* @param edits - the scope batch in proposal order (order is significant).
|
|
38
|
-
* @returns 16-char hex fingerprint.
|
|
39
|
-
*/
|
|
40
|
-
export declare function fingerprintMemoryBatch(scope: "project" | "global", edits: readonly FingerprintableEdit[]): string;
|
|
41
|
-
/**
|
|
42
|
-
* Load the decline ledger. Missing or malformed files read as empty so a
|
|
43
|
-
* broken ledger degrades to the status quo (ask anyway), never to a crash
|
|
44
|
-
* on the approval path.
|
|
45
|
-
*
|
|
46
|
-
* @param baseDir - engine base directory.
|
|
47
|
-
* @returns validated ledger entries (possibly empty).
|
|
48
|
-
*/
|
|
49
|
-
export declare function loadDeclinedMemory(baseDir: string): DeclinedMemoryBatch[];
|
|
50
|
-
/**
|
|
51
|
-
* Whether this exact batch was declined before.
|
|
52
|
-
*
|
|
53
|
-
* @param entries - loaded ledger.
|
|
54
|
-
* @param scope - persistent scope of the pending batch.
|
|
55
|
-
* @param fingerprint - fingerprint of the pending batch.
|
|
56
|
-
*/
|
|
57
|
-
export declare function isDeclinedRepeat(entries: readonly DeclinedMemoryBatch[], scope: "project" | "global", fingerprint: string): boolean;
|
|
58
|
-
/**
|
|
59
|
-
* Record one human decline, pruning oldest-first past the cap. The batch
|
|
60
|
-
* edits supply fingerprint material, match tokens, and the secret screen:
|
|
61
|
-
* a secret-shaped decline is still declined, but never lands in the ledger
|
|
62
|
-
* (it was never engine-screened, so it must not be persisted anywhere).
|
|
63
|
-
*
|
|
64
|
-
* @param baseDir - engine base directory.
|
|
65
|
-
* @param scope - persistent scope that was declined.
|
|
66
|
-
* @param edits - the declined scope batch in proposal order.
|
|
67
|
-
* @param title - first edit title, kept for audit readability.
|
|
68
|
-
* @returns the ledger file path, or undefined when skipped by the secret screen.
|
|
69
|
-
* @throws On filesystem write failure; callers on the approval path should
|
|
70
|
-
* contain it (suppression is an optimization, never load-bearing).
|
|
71
|
-
*/
|
|
72
|
-
export declare function recordDeclinedMemory(baseDir: string, scope: "project" | "global", edits: readonly FingerprintableEdit[], title: string): string | undefined;
|
|
73
|
-
/**
|
|
74
|
-
* Deduplicated content tokens of one batch, capped. Shared CJK-aware
|
|
75
|
-
* tokenizer with the manifest ranking, so both sides of the containment
|
|
76
|
-
* check speak the same token language.
|
|
77
|
-
*
|
|
78
|
-
* @param edits - batch edits.
|
|
79
|
-
* @returns at most MAX_DECLINED_ENTRY_TOKENS distinct tokens.
|
|
80
|
-
*/
|
|
81
|
-
export declare function declinedContentTokens(edits: readonly FingerprintableEdit[]): string[];
|
|
82
|
-
/** One checkpoint containment hit: which entry matched and how strongly. */
|
|
83
|
-
export interface DeclinedCheckpointHit {
|
|
84
|
-
/** Ledger entry whose content the checkpoint re-covers. */
|
|
85
|
-
entry: DeclinedMemoryBatch;
|
|
86
|
-
/** Fraction of the entry's tokens present in the checkpoint. */
|
|
87
|
-
coverage: number;
|
|
88
|
-
/** Overlapping token count. */
|
|
89
|
-
hits: number;
|
|
90
|
-
}
|
|
91
|
-
/**
|
|
92
|
-
* Match a pending checkpoint against declined content (entry ⊆ checkpoint).
|
|
93
|
-
* Conservative by design: high coverage plus an absolute token floor, so
|
|
94
|
-
* tiny or vague declines can never suppress anything.
|
|
95
|
-
*
|
|
96
|
-
* @param entries - loaded ledger.
|
|
97
|
-
* @param checkpointTokens - tokens of the pending checkpoint trajectory.
|
|
98
|
-
* @returns the strongest hit, or undefined when nothing clears the floor.
|
|
99
|
-
*/
|
|
100
|
-
/** Convenience: load the declined ledger and match one trajectory slice. */
|
|
101
|
-
export declare function matchDeclinedTrajectory(baseDir: string, trajectory: string): DeclinedCheckpointHit | undefined;
|
|
102
|
-
export declare function matchDeclinedCheckpoint(entries: readonly DeclinedMemoryBatch[], checkpointTokens: readonly string[]): DeclinedCheckpointHit | undefined;
|
|
103
|
-
//# sourceMappingURL=declines.d.ts.map
|
package/lib/declines.js
DELETED
|
@@ -1,179 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Cross-session ledger of declined memory proposals (the decline loop).
|
|
3
|
-
*
|
|
4
|
-
* In-memory `GateState.memoryDecisions` dies on restart and its decision
|
|
5
|
-
* cursor is per-checkpoint, so a declined project/global batch is re-asked
|
|
6
|
-
* in the next session. This file persists batch fingerprints plus content
|
|
7
|
-
* tokens under `<baseDir>/evolve/declined-memory.json`: exact batches are
|
|
8
|
-
* suppressed at approval time, and checkpoints overlapping declined content
|
|
9
|
-
* are suppressed before the agent is even invoked. Token matching is
|
|
10
|
-
* containment (entry ⊆ checkpoint) with a conservative floor; rephrasing
|
|
11
|
-
* below the floor still asks (documented limitation, not silent loss).
|
|
12
|
-
*/
|
|
13
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
14
|
-
import { createHash } from "node:crypto";
|
|
15
|
-
import { join } from "node:path";
|
|
16
|
-
import { secretLeakReason } from "./promotion.js";
|
|
17
|
-
import { tokenize } from "./search.js";
|
|
18
|
-
import { EVOLVE_DIR } from "./store.js";
|
|
19
|
-
/** Cap on remembered declines; oldest-first pruning keeps the file small. */
|
|
20
|
-
export const MAX_DECLINED_MEMORY_LEDGER = 50;
|
|
21
|
-
/** Cap on stored content tokens per entry; enough for coverage, never prose. */
|
|
22
|
-
export const MAX_DECLINED_ENTRY_TOKENS = 128;
|
|
23
|
-
/** Minimum coverage of an entry's tokens by the checkpoint to pre-suppress. */
|
|
24
|
-
export const DECLINED_PRECHECK_COVERAGE = 0.85;
|
|
25
|
-
/** Minimum overlapping tokens; tiny entries must never match vacuously. */
|
|
26
|
-
export const DECLINED_PRECHECK_MIN_TOKENS = 5;
|
|
27
|
-
const DECLINED_MEMORY_FILE = "declined-memory.json";
|
|
28
|
-
/** Full path of the decline ledger file. */
|
|
29
|
-
export function declinedMemoryPath(baseDir) {
|
|
30
|
-
return join(baseDir, EVOLVE_DIR, DECLINED_MEMORY_FILE);
|
|
31
|
-
}
|
|
32
|
-
/**
|
|
33
|
-
* Fingerprint one scope batch. Normalization is whitespace-only on purpose:
|
|
34
|
-
* case folding could conflate distinct facts (e.g. env var names), and a
|
|
35
|
-
* missed match only costs one more popup (fail-closed).
|
|
36
|
-
*
|
|
37
|
-
* @param scope - persistent scope the batch targets.
|
|
38
|
-
* @param edits - the scope batch in proposal order (order is significant).
|
|
39
|
-
* @returns 16-char hex fingerprint.
|
|
40
|
-
*/
|
|
41
|
-
export function fingerprintMemoryBatch(scope, edits) {
|
|
42
|
-
const parts = [scope];
|
|
43
|
-
for (const edit of edits) {
|
|
44
|
-
parts.push([edit.action, edit.id ?? "", edit.title ?? "", edit.content ?? ""].join("|"));
|
|
45
|
-
}
|
|
46
|
-
const material = parts.join("\n").replace(/\s+/g, " ").trim();
|
|
47
|
-
return createHash("sha256").update(material, "utf8").digest("hex").slice(0, 16);
|
|
48
|
-
}
|
|
49
|
-
/**
|
|
50
|
-
* Load the decline ledger. Missing or malformed files read as empty so a
|
|
51
|
-
* broken ledger degrades to the status quo (ask anyway), never to a crash
|
|
52
|
-
* on the approval path.
|
|
53
|
-
*
|
|
54
|
-
* @param baseDir - engine base directory.
|
|
55
|
-
* @returns validated ledger entries (possibly empty).
|
|
56
|
-
*/
|
|
57
|
-
export function loadDeclinedMemory(baseDir) {
|
|
58
|
-
const path = declinedMemoryPath(baseDir);
|
|
59
|
-
if (!existsSync(path))
|
|
60
|
-
return [];
|
|
61
|
-
try {
|
|
62
|
-
const raw = JSON.parse(readFileSync(path, "utf8"));
|
|
63
|
-
if (!Array.isArray(raw))
|
|
64
|
-
return [];
|
|
65
|
-
return raw
|
|
66
|
-
.filter((entry) => {
|
|
67
|
-
if (typeof entry !== "object" || entry === null)
|
|
68
|
-
return false;
|
|
69
|
-
const candidate = entry;
|
|
70
|
-
return ((candidate.scope === "project" || candidate.scope === "global") &&
|
|
71
|
-
typeof candidate.fingerprint === "string");
|
|
72
|
-
})
|
|
73
|
-
.map((entry) => ({
|
|
74
|
-
...entry,
|
|
75
|
-
tokens: Array.isArray(entry.tokens) ? entry.tokens.filter((token) => typeof token === "string") : [],
|
|
76
|
-
}));
|
|
77
|
-
}
|
|
78
|
-
catch {
|
|
79
|
-
return [];
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
/**
|
|
83
|
-
* Whether this exact batch was declined before.
|
|
84
|
-
*
|
|
85
|
-
* @param entries - loaded ledger.
|
|
86
|
-
* @param scope - persistent scope of the pending batch.
|
|
87
|
-
* @param fingerprint - fingerprint of the pending batch.
|
|
88
|
-
*/
|
|
89
|
-
export function isDeclinedRepeat(entries, scope, fingerprint) {
|
|
90
|
-
return entries.some((entry) => entry.scope === scope && entry.fingerprint === fingerprint);
|
|
91
|
-
}
|
|
92
|
-
/**
|
|
93
|
-
* Record one human decline, pruning oldest-first past the cap. The batch
|
|
94
|
-
* edits supply fingerprint material, match tokens, and the secret screen:
|
|
95
|
-
* a secret-shaped decline is still declined, but never lands in the ledger
|
|
96
|
-
* (it was never engine-screened, so it must not be persisted anywhere).
|
|
97
|
-
*
|
|
98
|
-
* @param baseDir - engine base directory.
|
|
99
|
-
* @param scope - persistent scope that was declined.
|
|
100
|
-
* @param edits - the declined scope batch in proposal order.
|
|
101
|
-
* @param title - first edit title, kept for audit readability.
|
|
102
|
-
* @returns the ledger file path, or undefined when skipped by the secret screen.
|
|
103
|
-
* @throws On filesystem write failure; callers on the approval path should
|
|
104
|
-
* contain it (suppression is an optimization, never load-bearing).
|
|
105
|
-
*/
|
|
106
|
-
export function recordDeclinedMemory(baseDir, scope, edits, title) {
|
|
107
|
-
const screenable = edits.map((edit) => `${edit.title ?? ""}\n${edit.content ?? ""}`).join("\n");
|
|
108
|
-
if (secretLeakReason(screenable) !== undefined)
|
|
109
|
-
return undefined;
|
|
110
|
-
const entries = loadDeclinedMemory(baseDir);
|
|
111
|
-
entries.push({
|
|
112
|
-
scope,
|
|
113
|
-
fingerprint: fingerprintMemoryBatch(scope, edits),
|
|
114
|
-
title,
|
|
115
|
-
tokens: declinedContentTokens(edits),
|
|
116
|
-
declinedAt: new Date().toISOString(),
|
|
117
|
-
});
|
|
118
|
-
entries.sort((left, right) => (left.declinedAt < right.declinedAt ? -1 : 1));
|
|
119
|
-
while (entries.length > MAX_DECLINED_MEMORY_LEDGER)
|
|
120
|
-
entries.shift();
|
|
121
|
-
const path = declinedMemoryPath(baseDir);
|
|
122
|
-
mkdirSync(join(baseDir, EVOLVE_DIR), { recursive: true });
|
|
123
|
-
writeFileSync(path, `${JSON.stringify(entries, null, 2)}\n`, "utf8");
|
|
124
|
-
return path;
|
|
125
|
-
}
|
|
126
|
-
/**
|
|
127
|
-
* Deduplicated content tokens of one batch, capped. Shared CJK-aware
|
|
128
|
-
* tokenizer with the manifest ranking, so both sides of the containment
|
|
129
|
-
* check speak the same token language.
|
|
130
|
-
*
|
|
131
|
-
* @param edits - batch edits.
|
|
132
|
-
* @returns at most MAX_DECLINED_ENTRY_TOKENS distinct tokens.
|
|
133
|
-
*/
|
|
134
|
-
export function declinedContentTokens(edits) {
|
|
135
|
-
const seen = new Set();
|
|
136
|
-
for (const edit of edits) {
|
|
137
|
-
for (const token of tokenize(`${edit.title ?? ""} ${edit.content ?? ""}`)) {
|
|
138
|
-
seen.add(token);
|
|
139
|
-
if (seen.size >= MAX_DECLINED_ENTRY_TOKENS)
|
|
140
|
-
return [...seen];
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
return [...seen];
|
|
144
|
-
}
|
|
145
|
-
/**
|
|
146
|
-
* Match a pending checkpoint against declined content (entry ⊆ checkpoint).
|
|
147
|
-
* Conservative by design: high coverage plus an absolute token floor, so
|
|
148
|
-
* tiny or vague declines can never suppress anything.
|
|
149
|
-
*
|
|
150
|
-
* @param entries - loaded ledger.
|
|
151
|
-
* @param checkpointTokens - tokens of the pending checkpoint trajectory.
|
|
152
|
-
* @returns the strongest hit, or undefined when nothing clears the floor.
|
|
153
|
-
*/
|
|
154
|
-
/** Convenience: load the declined ledger and match one trajectory slice. */
|
|
155
|
-
export function matchDeclinedTrajectory(baseDir, trajectory) {
|
|
156
|
-
return matchDeclinedCheckpoint(loadDeclinedMemory(baseDir), tokenize(trajectory));
|
|
157
|
-
}
|
|
158
|
-
export function matchDeclinedCheckpoint(entries, checkpointTokens) {
|
|
159
|
-
const pending = new Set(checkpointTokens);
|
|
160
|
-
let best;
|
|
161
|
-
for (const entry of entries) {
|
|
162
|
-
if (entry.tokens.length === 0)
|
|
163
|
-
continue;
|
|
164
|
-
let hits = 0;
|
|
165
|
-
for (const token of entry.tokens) {
|
|
166
|
-
if (pending.has(token))
|
|
167
|
-
hits += 1;
|
|
168
|
-
}
|
|
169
|
-
if (hits < DECLINED_PRECHECK_MIN_TOKENS)
|
|
170
|
-
continue;
|
|
171
|
-
const coverage = hits / entry.tokens.length;
|
|
172
|
-
if (coverage < DECLINED_PRECHECK_COVERAGE)
|
|
173
|
-
continue;
|
|
174
|
-
if (best === undefined || coverage > best.coverage)
|
|
175
|
-
best = { entry, coverage, hits };
|
|
176
|
-
}
|
|
177
|
-
return best;
|
|
178
|
-
}
|
|
179
|
-
//# sourceMappingURL=declines.js.map
|
package/lib/evaluate.d.ts
DELETED
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The evaluation matrix runner: executes every case × run as a TWO-STAGE
|
|
3
|
-
* structured-output subagent pair, with the provider/model frozen to the
|
|
4
|
-
* calling agent's own route.
|
|
5
|
-
*
|
|
6
|
-
* Stage 1 — executor: a fresh child performs the case task with its tools
|
|
7
|
-
* and records CONCRETE EVIDENCE of what it did and found. It NEVER sees the
|
|
8
|
-
* rubric (gap A1, evaluator/scorer separation): the agent under test cannot
|
|
9
|
-
* optimize its behavior toward the grading criteria, and cannot grade its
|
|
10
|
-
* own execution.
|
|
11
|
-
*
|
|
12
|
-
* Stage 2 — reviewer: an INDEPENDENT child grades the executor's evidence
|
|
13
|
-
* strictly against the rubric. The rubric plaintext is decrypted in the host
|
|
14
|
-
* and flows ONLY into this reviewer branch; the executor branch never
|
|
15
|
-
* touches it. A separate model instance grading someone else's evidence
|
|
16
|
-
* removes the self-serving bias of self-scoring.
|
|
17
|
-
*
|
|
18
|
-
* Failure-cell protocol (gap A2): a unit that cannot produce a score
|
|
19
|
-
* (decrypt error, executor crash, reviewer crash, protocol error) returns a
|
|
20
|
-
* cell with `status: "failed"` — NEVER a zero — so aggregation can exclude
|
|
21
|
-
* it and the acceptance rule can reject rounds with too many failures instead
|
|
22
|
-
* of silently averaging a 0 into the mean.
|
|
23
|
-
*
|
|
24
|
-
* Each cell carries the executor child's session id (gap A4): the score can
|
|
25
|
-
* be drilled back to the exact session transcript that produced the evidence.
|
|
26
|
-
*
|
|
27
|
-
* Uses the host-plane `subagents` service (available in every profile) with
|
|
28
|
-
* the native `outputSchema` structured-output seam: the provider validates
|
|
29
|
-
* each child's reply against its cell schema, so the host never parses model
|
|
30
|
-
* text for evaluations. (The workflow engine was rejected because the web
|
|
31
|
-
* profile keeps it in a per-agent isolated realm a host plugin cannot
|
|
32
|
-
* resolve.)
|
|
33
|
-
*/
|
|
34
|
-
import type { Context } from "@deepseek-ai/cordis";
|
|
35
|
-
import type { Agent } from "@deepseek-ai/dsh-agent";
|
|
36
|
-
import type { BenchmarkCase, CellScore } from "./benchmark.js";
|
|
37
|
-
export interface EvaluateOptions {
|
|
38
|
-
cases: readonly BenchmarkCase[];
|
|
39
|
-
runs: number;
|
|
40
|
-
passThreshold: number;
|
|
41
|
-
/** Serialized harness state under test (the candidate's guidance). */
|
|
42
|
-
harnessOverview: string;
|
|
43
|
-
label: string;
|
|
44
|
-
/** AES-256 key for the encrypted rubric envelopes (see src/rubric.ts). */
|
|
45
|
-
rubricKey?: Buffer;
|
|
46
|
-
signal?: AbortSignal;
|
|
47
|
-
}
|
|
48
|
-
export interface EvaluationOutcome {
|
|
49
|
-
label: string;
|
|
50
|
-
cells: CellScore[];
|
|
51
|
-
stopReason: string;
|
|
52
|
-
}
|
|
53
|
-
/** How many evaluation units may run concurrently (bounded subagent fan-out). */
|
|
54
|
-
export declare const DEFAULT_EVALUATION_CONCURRENCY = 4;
|
|
55
|
-
/** Evidence handed to the reviewer is capped so the grading call stays bounded. */
|
|
56
|
-
export declare const MAX_EVIDENCE_CHARS = 8000;
|
|
57
|
-
export declare function evaluateState(ctx: Context, agent: Agent, options: EvaluateOptions): Promise<EvaluationOutcome>;
|
|
58
|
-
export interface ExecutorResult {
|
|
59
|
-
caseId: string;
|
|
60
|
-
run: number;
|
|
61
|
-
evidence: string;
|
|
62
|
-
}
|
|
63
|
-
/**
|
|
64
|
-
* Deterministic hash of a case's statement + rubric envelope (gap A3):
|
|
65
|
-
* a 16-char SHA-256 prefix, hex-encoded. Used to detect material changes
|
|
66
|
-
* between reference and candidate evaluation runs (see
|
|
67
|
-
* `score.flagMaterialDrift`).
|
|
68
|
-
*/
|
|
69
|
-
export declare function caseHash(caseItem: BenchmarkCase): string;
|
|
70
|
-
/** Validate a provider-validated executor result; returns undefined when malformed. */
|
|
71
|
-
export declare function normalizeExecutor(value: unknown, caseId: string, run: number): ExecutorResult | undefined;
|
|
72
|
-
/** Validate a provider-validated structured cell; returns undefined when malformed. */
|
|
73
|
-
export declare function normalizeCell(value: unknown, caseId: string, run: number, passThreshold: number): CellScore | undefined;
|
|
74
|
-
//# sourceMappingURL=evaluate.d.ts.map
|