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/validate.js
DELETED
|
@@ -1,181 +0,0 @@
|
|
|
1
|
-
import { MEMORY_TYPE_KEY, isMemoryType } from "./types.js";
|
|
2
|
-
import { validateSkillEntryContent } from "./skillquality.js";
|
|
3
|
-
const ACTIONS = new Set(["create", "update", "delete", "archive"]);
|
|
4
|
-
const KINDS = new Set(["prompt", "memory", "skill", "subagent"]);
|
|
5
|
-
export const BASE_SYSTEM_PROMPT_ID = "base_system_prompt";
|
|
6
|
-
/**
|
|
7
|
-
* Gap C2: mechanical check that an edit's declared blast radius is coherent
|
|
8
|
-
* with the scope it targets. A session-scoped edit claiming "general" would
|
|
9
|
-
* silently read like a cross-project tactical rule; a global edit claiming
|
|
10
|
-
* "session" would contradict its persistence. Absent blastRadius is NOT
|
|
11
|
-
* rejected (pre-C2 data and manual edits stay compatible) — the planner is
|
|
12
|
-
* instructed to always declare it, and this rule catches what it declares
|
|
13
|
-
* incoherently.
|
|
14
|
-
*/
|
|
15
|
-
export function validateBlastRadiusScope(scope, blastRadius) {
|
|
16
|
-
if (scope === "local" && blastRadius === "general") {
|
|
17
|
-
return "local-scope edit must declare blastRadius \"session\" or \"project\" (\"general\" would claim a cross-project rule)";
|
|
18
|
-
}
|
|
19
|
-
if (scope === "global" && blastRadius === "session") {
|
|
20
|
-
return "global-scope edit must declare blastRadius \"general\" or \"project\" (\"session\" contradicts cross-session persistence)";
|
|
21
|
-
}
|
|
22
|
-
if (scope === "project" && blastRadius === "session") {
|
|
23
|
-
return "project-scope edit must declare blastRadius \"project\" or \"general\" (\"session\" contradicts cross-session persistence)";
|
|
24
|
-
}
|
|
25
|
-
return undefined;
|
|
26
|
-
}
|
|
27
|
-
/**
|
|
28
|
-
* Returns a human-readable failure reason, or undefined when the edit passes.
|
|
29
|
-
*
|
|
30
|
-
* `before` (the entry the edit targets, absent for creates/unknown ids) lets
|
|
31
|
-
* update rules distinguish "carrying the persisted value" from "changing it"
|
|
32
|
-
* — e.g. a rollback inverse re-carries the stored skill_kind, which must
|
|
33
|
-
* pass, while a genuine executable↔guidance switch must not.
|
|
34
|
-
*/
|
|
35
|
-
export function validateEdit(edit, computedId, scope, before) {
|
|
36
|
-
if (!ACTIONS.has(edit.action)) {
|
|
37
|
-
return `unsupported action ${String(edit.action)}`;
|
|
38
|
-
}
|
|
39
|
-
if (!KINDS.has(edit.kind)) {
|
|
40
|
-
return `unsupported kind ${String(edit.kind)}`;
|
|
41
|
-
}
|
|
42
|
-
if (edit.kind === "prompt" && (edit.id === BASE_SYSTEM_PROMPT_ID || computedId === BASE_SYSTEM_PROMPT_ID)) {
|
|
43
|
-
return "base system prompt is not editable";
|
|
44
|
-
}
|
|
45
|
-
if (edit.action !== "create" && !edit.id) {
|
|
46
|
-
return `${edit.action} requires id`;
|
|
47
|
-
}
|
|
48
|
-
// Gap C2: blast-radius/scope coherence is a mechanical property of the
|
|
49
|
-
// edit payload itself — checked for every action, not only create/update.
|
|
50
|
-
if (scope && edit.blastRadius !== undefined) {
|
|
51
|
-
const blastError = validateBlastRadiusScope(scope, edit.blastRadius);
|
|
52
|
-
if (blastError)
|
|
53
|
-
return blastError;
|
|
54
|
-
}
|
|
55
|
-
// Archive only names an existing entry: no title/content payload, and the
|
|
56
|
-
// base system prompt stays immutable under every action.
|
|
57
|
-
if (edit.action === "archive") {
|
|
58
|
-
return undefined;
|
|
59
|
-
}
|
|
60
|
-
// Create needs the full payload (nothing to fall back to). Update may
|
|
61
|
-
// carry any subset — apply merges with `?? before` — but must carry at
|
|
62
|
-
// least one change (review audit 2026-08-28 B3/B5: the old blanket
|
|
63
|
-
// "update requires title and content" broke every partial-update path:
|
|
64
|
-
// skill archive/demote, consolidate, and the evolve_update tool contract
|
|
65
|
-
// all construct payload-subset updates).
|
|
66
|
-
if (edit.action === "create" && (!edit.title || !edit.content)) {
|
|
67
|
-
return "create requires title and content";
|
|
68
|
-
}
|
|
69
|
-
if (edit.action === "update" &&
|
|
70
|
-
edit.title === undefined &&
|
|
71
|
-
edit.content === undefined &&
|
|
72
|
-
edit.path === undefined &&
|
|
73
|
-
edit.metadata === undefined &&
|
|
74
|
-
edit.reference === undefined &&
|
|
75
|
-
edit.arguments === undefined) {
|
|
76
|
-
return "update carries no changes";
|
|
77
|
-
}
|
|
78
|
-
if (edit.kind === "memory" && (edit.action === "create" || edit.action === "update")) {
|
|
79
|
-
// One-fact typed memories: every memory create declares its recall
|
|
80
|
-
// type; feedback/project (pitfalls, conventions) additionally carry
|
|
81
|
-
// the Why + How-to-apply structure so the fact is reusable, not just
|
|
82
|
-
// recorded. Updates are partial — only carried fields are checked,
|
|
83
|
-
// resolved against the persisted entry.
|
|
84
|
-
const carriedType = edit.metadata?.[MEMORY_TYPE_KEY];
|
|
85
|
-
if (edit.action === "create" && !isMemoryType(carriedType)) {
|
|
86
|
-
return `create memory requires metadata.${MEMORY_TYPE_KEY} one of user|feedback|project|reference (one fact per entry; update the existing entry instead of adding a duplicate)`;
|
|
87
|
-
}
|
|
88
|
-
if (carriedType !== undefined && !isMemoryType(carriedType)) {
|
|
89
|
-
return `invalid metadata.${MEMORY_TYPE_KEY} ${JSON.stringify(carriedType)} (one of user|feedback|project|reference)`;
|
|
90
|
-
}
|
|
91
|
-
const effectiveType = isMemoryType(carriedType)
|
|
92
|
-
? carriedType
|
|
93
|
-
: edit.action === "update"
|
|
94
|
-
? before?.metadata?.[MEMORY_TYPE_KEY]
|
|
95
|
-
: undefined;
|
|
96
|
-
if ((effectiveType === "feedback" || effectiveType === "project") &&
|
|
97
|
-
(edit.action === "create" || edit.content !== undefined)) {
|
|
98
|
-
const content = edit.content ?? "";
|
|
99
|
-
if (!/why/i.test(content) || !/how/i.test(content)) {
|
|
100
|
-
return `${effectiveType} memory content must carry a Why and a How to apply section (the fact plus why it matters and how to apply it next time)`;
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
if (edit.action === "create" && edit.kind === "skill") {
|
|
105
|
-
// Guidance skills are SKILL.md documents: no python reference (a
|
|
106
|
-
// reference on a guidance skill would be an invented contract) and
|
|
107
|
-
// no arguments contract. Executable skills keep the full contract.
|
|
108
|
-
if (edit.skill_kind === "guidance") {
|
|
109
|
-
if (edit.reference !== undefined && Object.keys(edit.reference).length > 0) {
|
|
110
|
-
return "guidance skill must not carry a python reference (it is a SKILL.md document, not an executable)";
|
|
111
|
-
}
|
|
112
|
-
if (edit.arguments !== undefined && Object.keys(edit.arguments).length > 0) {
|
|
113
|
-
return "guidance skill must not carry an arguments contract (only executable skills declare inputs)";
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
else {
|
|
117
|
-
const contractError = validateSkillContract(edit);
|
|
118
|
-
if (contractError)
|
|
119
|
-
return contractError;
|
|
120
|
-
}
|
|
121
|
-
// The entry body materializes as a SKILL.md under generated
|
|
122
|
-
// frontmatter; content-level mechanics (no shadowing `---`, no
|
|
123
|
-
// escaping resource refs) are code-enforced so a bad body never
|
|
124
|
-
// reaches the store (mirrors skill-creator's validate-frontmatter).
|
|
125
|
-
const contentProblems = validateSkillEntryContent(edit.content ?? "");
|
|
126
|
-
if (contentProblems.length > 0) {
|
|
127
|
-
return contentProblems.join("; ");
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
if (edit.action === "update" && edit.kind === "skill") {
|
|
131
|
-
// skill_kind is immutable on update: a switch without the matching
|
|
132
|
-
// contract pair would leave an invalid entry (guidance carrying a
|
|
133
|
-
// python reference, or an executable with none). Re-carrying the
|
|
134
|
-
// persisted value (rollback inverses do) passes. Recreate the entry
|
|
135
|
-
// to change the kind.
|
|
136
|
-
if (edit.skill_kind !== undefined && before !== undefined && before.skill_kind !== edit.skill_kind) {
|
|
137
|
-
return "update cannot change skill_kind — delete and recreate the entry instead";
|
|
138
|
-
}
|
|
139
|
-
// A carried non-empty reference replaces the contract wholesale, so it
|
|
140
|
-
// must be a complete executable contract on its own. Empty references
|
|
141
|
-
// (guidance skills) and absent payloads pass through — apply keeps
|
|
142
|
-
// the persisted values.
|
|
143
|
-
if (edit.reference !== undefined && Object.keys(edit.reference).length > 0) {
|
|
144
|
-
const contractError = validateSkillContract(edit);
|
|
145
|
-
if (contractError)
|
|
146
|
-
return contractError;
|
|
147
|
-
}
|
|
148
|
-
if (edit.content !== undefined) {
|
|
149
|
-
const contentProblems = validateSkillEntryContent(edit.content);
|
|
150
|
-
if (contentProblems.length > 0) {
|
|
151
|
-
return contentProblems.join("; ");
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
return undefined;
|
|
156
|
-
}
|
|
157
|
-
function validateSkillContract(edit) {
|
|
158
|
-
if (edit.arguments === undefined) {
|
|
159
|
-
return "create/update skill requires arguments";
|
|
160
|
-
}
|
|
161
|
-
const reference = edit.reference;
|
|
162
|
-
if (!reference || typeof reference !== "object") {
|
|
163
|
-
return "create/update skill requires python reference";
|
|
164
|
-
}
|
|
165
|
-
const ref = reference;
|
|
166
|
-
if (ref.type !== "python") {
|
|
167
|
-
return "create/update skill reference.type must be python";
|
|
168
|
-
}
|
|
169
|
-
const hasImport = (typeof ref.import === "string" && ref.import.length > 0) ||
|
|
170
|
-
(typeof ref.python_import === "string" && ref.python_import.length > 0);
|
|
171
|
-
const hasCallable = (typeof ref.callable === "string" && ref.callable.length > 0) ||
|
|
172
|
-
(typeof ref.call_pattern === "string" && ref.call_pattern.length > 0);
|
|
173
|
-
if (!hasImport) {
|
|
174
|
-
return "create/update skill requires python import";
|
|
175
|
-
}
|
|
176
|
-
if (!hasCallable) {
|
|
177
|
-
return "create/update skill requires callable or call_pattern";
|
|
178
|
-
}
|
|
179
|
-
return undefined;
|
|
180
|
-
}
|
|
181
|
-
//# sourceMappingURL=validate.js.map
|
|
@@ -1,127 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Wrap-up candidate domain: the mechanical audit of a session's local
|
|
3
|
-
* entries (candidates, global coverage detection, usage hints), the LLM
|
|
4
|
-
* assessment parser, and the shared wrap-up types. Pure logic — the LLM
|
|
5
|
-
* call lives in `wrapup.ts`, the proposal builders in
|
|
6
|
-
* `wrapup-proposals.ts`.
|
|
7
|
-
*/
|
|
8
|
-
import type { HarnessEntry, HarnessState, RefinementKind } from "./types.js";
|
|
9
|
-
export type WrapupVerdict = "promote" | "archive" | "keep";
|
|
10
|
-
/** A classified local entry: `key` matches one audited candidate exactly. */
|
|
11
|
-
export interface WrapupItem {
|
|
12
|
-
/** `kind:id` of the candidate this verdict refers to. */
|
|
13
|
-
key: string;
|
|
14
|
-
verdict: WrapupVerdict;
|
|
15
|
-
reason: string;
|
|
16
|
-
/**
|
|
17
|
-
* Optional split-promotion payload (verdict "archive" only): the entry is
|
|
18
|
-
* archived as a whole, but a CLEANED cross-session-reusable part is
|
|
19
|
-
* offered for promotion — the durable fact distilled out of the mixed
|
|
20
|
-
* entry, with the ephemeral snapshot left behind in the archive.
|
|
21
|
-
*/
|
|
22
|
-
promote?: {
|
|
23
|
-
title: string;
|
|
24
|
-
content: string;
|
|
25
|
-
};
|
|
26
|
-
/**
|
|
27
|
-
* Valence signal (P1 效价反馈): the assessor found trajectory or global
|
|
28
|
-
* evidence contradicting this entry's content (user correction, overridden
|
|
29
|
-
* fact). Strictly parsed — only an explicit JSON `true` sets it. Keep
|
|
30
|
-
* verdicts with this flag get a negative-valence stamp; the assessor is
|
|
31
|
-
* instructed to prefer "archive" for contradicted entries.
|
|
32
|
-
*/
|
|
33
|
-
contradicted?: boolean;
|
|
34
|
-
}
|
|
35
|
-
/** A real global entry worth showing the assessor for the same topic. */
|
|
36
|
-
export interface GlobalHint {
|
|
37
|
-
id: string;
|
|
38
|
-
title: string;
|
|
39
|
-
}
|
|
40
|
-
/** The model's full classification of a session's local entries. */
|
|
41
|
-
export interface WrapupAssessment {
|
|
42
|
-
items: WrapupItem[];
|
|
43
|
-
rationale: string;
|
|
44
|
-
}
|
|
45
|
-
/** A local entry offered for assessment, plus its deterministic audit flags. */
|
|
46
|
-
export interface WrapupCandidate {
|
|
47
|
-
kind: RefinementKind;
|
|
48
|
-
id: string;
|
|
49
|
-
title: string;
|
|
50
|
-
content: string;
|
|
51
|
-
path: string;
|
|
52
|
-
version: number;
|
|
53
|
-
metadata: Record<string, unknown>;
|
|
54
|
-
/**
|
|
55
|
-
* True when the global store already covers this topic by a STRONG
|
|
56
|
-
* signal: a title that normalizes equal to, or (beyond a length floor)
|
|
57
|
-
* contains, the candidate's title. Collisions on id alone with a wildly
|
|
58
|
-
* different title are deliberately NOT coverage — see {@link globalHintsFor}.
|
|
59
|
-
*/
|
|
60
|
-
coveredGlobally: boolean;
|
|
61
|
-
/**
|
|
62
|
-
* Actual global entries that touch the same topic (same id, equal
|
|
63
|
-
* normalized title, or title overlap). Shown to the assessor so it judges
|
|
64
|
-
* against real titles instead of a bare boolean; a bare same-id collision
|
|
65
|
-
* shows up here precisely so the model can tell whether the global copy
|
|
66
|
-
* really covers the local content.
|
|
67
|
-
*/
|
|
68
|
-
globalHints: GlobalHint[];
|
|
69
|
-
/**
|
|
70
|
-
* Injection usage count (gap B1): how many times this entry was included
|
|
71
|
-
* in a system-prompt assembly. Zero means the entry was never used — a
|
|
72
|
-
* strong staleness signal the assessor can weigh.
|
|
73
|
-
*/
|
|
74
|
-
injectionCount: number;
|
|
75
|
-
/**
|
|
76
|
-
* Staleness flag (gap B2): true when the entry has both zero injection
|
|
77
|
-
* usage AND a recency score below the staleness threshold (old + unused).
|
|
78
|
-
* The assessor is instructed to prefer "archive" for stale entries.
|
|
79
|
-
*/
|
|
80
|
-
stale: boolean;
|
|
81
|
-
/**
|
|
82
|
-
* Negative-valence counter (P1 效价反馈): how many prior assessments marked
|
|
83
|
-
* this entry contradicted (metadata {@link VALENCE_NEGATIVE_KEY}). Shown to
|
|
84
|
-
* the assessor so it can prefer "archive"; also demotes the entry in
|
|
85
|
-
* injection ranking (inject.ts rankEntries).
|
|
86
|
-
*/
|
|
87
|
-
negativeCount: number;
|
|
88
|
-
}
|
|
89
|
-
export declare function candidateKey(kind: RefinementKind, id: string): string;
|
|
90
|
-
/**
|
|
91
|
-
* Deterministic global-coverage check (STRONG signal): the global store
|
|
92
|
-
* already covers a topic when it holds a title that normalizes equal to, or
|
|
93
|
-
* (beyond a length floor) contains, the candidate's normalized title.
|
|
94
|
-
* Archived global entries count too — the topic was already judged
|
|
95
|
-
* cross-session; a local duplicate would only re-sediment it.
|
|
96
|
-
*
|
|
97
|
-
* The bare same-id case is deliberately NOT coverage: ids are slugs derived
|
|
98
|
-
* from titles, so a real collision is usually caught by the title check
|
|
99
|
-
* below. A same-id entry with a wildly different title is a weak signal — the
|
|
100
|
-
* caller routes it through {@link globalHintsFor} for the assessor to judge
|
|
101
|
-
* against the actual global title (real case: local `memory` "用户产品愿景与
|
|
102
|
-
* 收入需求(本会话)" vs global `memory` "用户画像(持续更新)").
|
|
103
|
-
*/
|
|
104
|
-
export declare function globalCoverageDetected(globalState: HarnessState, kind: RefinementKind, entry: Pick<HarnessEntry, "id" | "title">): boolean;
|
|
105
|
-
/**
|
|
106
|
-
* The actual global entries that touch the same topic as a local candidate:
|
|
107
|
-
* same id (regardless of title — the weak collision signal that is NOT
|
|
108
|
-
* coverage on its own), equal normalized title, or title overlap. The raw ids
|
|
109
|
-
* and titles let the assessor judge enrichment against real global content
|
|
110
|
-
* (does the global copy already hold what the local one adds?) rather than a
|
|
111
|
-
* bare boolean. Bounded: a handful of best matches, never the whole store.
|
|
112
|
-
*/
|
|
113
|
-
export declare function globalHintsFor(globalState: HarnessState, kind: RefinementKind, entry: Pick<HarnessEntry, "id" | "title">): GlobalHint[];
|
|
114
|
-
export declare function listLocalCandidates(state: HarnessState, globalState: HarnessState, baseDir?: string): WrapupCandidate[];
|
|
115
|
-
/**
|
|
116
|
-
* Parse and validate the model's assessment JSON. Defense is mechanical:
|
|
117
|
-
* keys outside the candidate list are dropped, verdicts outside the enum
|
|
118
|
-
* collapse to "keep", and candidates the model omitted default to "keep" —
|
|
119
|
-
* a malformed reply can never change an entry's fate by itself.
|
|
120
|
-
*
|
|
121
|
-
* Split promotion (verdict "archive" with a `promote` sub-object): the
|
|
122
|
-
* sub-object is accepted ONLY on archive verdicts and ONLY when both cleaned
|
|
123
|
-
* title and content are non-empty strings — a dropped/malformed sub-object
|
|
124
|
-
* silently degrades to a plain archive (the entry is never half-promoted).
|
|
125
|
-
*/
|
|
126
|
-
export declare function parseWrapupAssessment(text: string, candidates: readonly WrapupCandidate[]): WrapupAssessment;
|
|
127
|
-
//# sourceMappingURL=wrapup-candidates.d.ts.map
|
package/lib/wrapup-candidates.js
DELETED
|
@@ -1,161 +0,0 @@
|
|
|
1
|
-
import { PROMOTED_TO_KEY, VALENCE_NEGATIVE_KEY, isArchived } from "./types.js";
|
|
2
|
-
import { extractJsonObject } from "./plan.js";
|
|
3
|
-
import { getUsageCount, loadUsage } from "./usage.js";
|
|
4
|
-
import { recencyScore } from "./injection-rank.js";
|
|
5
|
-
export function candidateKey(kind, id) {
|
|
6
|
-
return `${kind}:${id}`;
|
|
7
|
-
}
|
|
8
|
-
/** Lowercase, punctuation-stripped title used for cheap coverage matching. */
|
|
9
|
-
function normalizeKey(value) {
|
|
10
|
-
return value.toLowerCase().replace(/[^a-z0-9\u3400-\u9fff]+/g, "").trim();
|
|
11
|
-
}
|
|
12
|
-
/**
|
|
13
|
-
* Deterministic global-coverage check (STRONG signal): the global store
|
|
14
|
-
* already covers a topic when it holds a title that normalizes equal to, or
|
|
15
|
-
* (beyond a length floor) contains, the candidate's normalized title.
|
|
16
|
-
* Archived global entries count too — the topic was already judged
|
|
17
|
-
* cross-session; a local duplicate would only re-sediment it.
|
|
18
|
-
*
|
|
19
|
-
* The bare same-id case is deliberately NOT coverage: ids are slugs derived
|
|
20
|
-
* from titles, so a real collision is usually caught by the title check
|
|
21
|
-
* below. A same-id entry with a wildly different title is a weak signal — the
|
|
22
|
-
* caller routes it through {@link globalHintsFor} for the assessor to judge
|
|
23
|
-
* against the actual global title (real case: local `memory` "用户产品愿景与
|
|
24
|
-
* 收入需求(本会话)" vs global `memory` "用户画像(持续更新)").
|
|
25
|
-
*/
|
|
26
|
-
export function globalCoverageDetected(globalState, kind, entry) {
|
|
27
|
-
const records = globalState.entries[kind];
|
|
28
|
-
const title = normalizeKey(entry.title);
|
|
29
|
-
if (title.length === 0)
|
|
30
|
-
return false;
|
|
31
|
-
for (const other of Object.values(records)) {
|
|
32
|
-
const otherTitle = normalizeKey(other.title);
|
|
33
|
-
if (otherTitle.length === 0)
|
|
34
|
-
continue;
|
|
35
|
-
if (otherTitle === title)
|
|
36
|
-
return true;
|
|
37
|
-
if (title.length >= 4 && otherTitle.length >= 4 && (title.includes(otherTitle) || otherTitle.includes(title))) {
|
|
38
|
-
return true;
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
return false;
|
|
42
|
-
}
|
|
43
|
-
/**
|
|
44
|
-
* The actual global entries that touch the same topic as a local candidate:
|
|
45
|
-
* same id (regardless of title — the weak collision signal that is NOT
|
|
46
|
-
* coverage on its own), equal normalized title, or title overlap. The raw ids
|
|
47
|
-
* and titles let the assessor judge enrichment against real global content
|
|
48
|
-
* (does the global copy already hold what the local one adds?) rather than a
|
|
49
|
-
* bare boolean. Bounded: a handful of best matches, never the whole store.
|
|
50
|
-
*/
|
|
51
|
-
export function globalHintsFor(globalState, kind, entry) {
|
|
52
|
-
const records = globalState.entries[kind];
|
|
53
|
-
const title = normalizeKey(entry.title);
|
|
54
|
-
const hints = [];
|
|
55
|
-
for (const other of Object.values(records)) {
|
|
56
|
-
const otherTitle = normalizeKey(other.title);
|
|
57
|
-
if (otherTitle.length === 0 && other.id !== entry.id)
|
|
58
|
-
continue;
|
|
59
|
-
const matches = other.id === entry.id ||
|
|
60
|
-
(otherTitle.length > 0 && (otherTitle === title || (title.length >= 4 && otherTitle.length >= 4 && (title.includes(otherTitle) || otherTitle.includes(title)))));
|
|
61
|
-
if (matches) {
|
|
62
|
-
hints.push({ id: other.id, title: other.title });
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
return hints;
|
|
66
|
-
}
|
|
67
|
-
/**
|
|
68
|
-
* The auditable local candidates of a session: every non-archived local
|
|
69
|
-
* entry that has not already been promoted (a promoted entry's lifecycle is
|
|
70
|
-
* finished — the global copy is the live one). Each carries its
|
|
71
|
-
* `coveredGlobally` flag so the assessor never wastes a promote on a topic
|
|
72
|
-
* the global store already owns.
|
|
73
|
-
*/
|
|
74
|
-
/** Staleness threshold: entries with recency below this AND zero usage are stale. */
|
|
75
|
-
const STALE_RECENCY_THRESHOLD = 0.1;
|
|
76
|
-
export function listLocalCandidates(state, globalState, baseDir) {
|
|
77
|
-
const usage = baseDir ? loadUsage(baseDir) : undefined;
|
|
78
|
-
const now = Date.now();
|
|
79
|
-
const candidates = [];
|
|
80
|
-
for (const kind of Object.keys(state.entries)) {
|
|
81
|
-
for (const entry of Object.values(state.entries[kind])) {
|
|
82
|
-
if (entry.scope !== "local")
|
|
83
|
-
continue;
|
|
84
|
-
if (isArchived(entry))
|
|
85
|
-
continue;
|
|
86
|
-
if (typeof entry.metadata[PROMOTED_TO_KEY] === "string")
|
|
87
|
-
continue;
|
|
88
|
-
const injectionCount = usage ? getUsageCount(usage, kind, entry.id) : 0;
|
|
89
|
-
const stale = injectionCount === 0 && recencyScore(entry, now) < STALE_RECENCY_THRESHOLD;
|
|
90
|
-
const rawNegative = entry.metadata[VALENCE_NEGATIVE_KEY];
|
|
91
|
-
const negativeCount = typeof rawNegative === "number" && Number.isFinite(rawNegative) && rawNegative > 0 ? rawNegative : 0;
|
|
92
|
-
candidates.push({
|
|
93
|
-
kind,
|
|
94
|
-
id: entry.id,
|
|
95
|
-
title: entry.title,
|
|
96
|
-
content: entry.content,
|
|
97
|
-
path: entry.path,
|
|
98
|
-
version: entry.version,
|
|
99
|
-
metadata: entry.metadata,
|
|
100
|
-
coveredGlobally: globalCoverageDetected(globalState, kind, entry),
|
|
101
|
-
globalHints: globalHintsFor(globalState, kind, entry),
|
|
102
|
-
injectionCount,
|
|
103
|
-
stale,
|
|
104
|
-
negativeCount,
|
|
105
|
-
});
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
return candidates;
|
|
109
|
-
}
|
|
110
|
-
/**
|
|
111
|
-
* Parse and validate the model's assessment JSON. Defense is mechanical:
|
|
112
|
-
* keys outside the candidate list are dropped, verdicts outside the enum
|
|
113
|
-
* collapse to "keep", and candidates the model omitted default to "keep" —
|
|
114
|
-
* a malformed reply can never change an entry's fate by itself.
|
|
115
|
-
*
|
|
116
|
-
* Split promotion (verdict "archive" with a `promote` sub-object): the
|
|
117
|
-
* sub-object is accepted ONLY on archive verdicts and ONLY when both cleaned
|
|
118
|
-
* title and content are non-empty strings — a dropped/malformed sub-object
|
|
119
|
-
* silently degrades to a plain archive (the entry is never half-promoted).
|
|
120
|
-
*/
|
|
121
|
-
export function parseWrapupAssessment(text, candidates) {
|
|
122
|
-
const value = extractJsonObject(text);
|
|
123
|
-
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
124
|
-
throw new Error("wrap-up assessment JSON must be an object");
|
|
125
|
-
}
|
|
126
|
-
const record = value;
|
|
127
|
-
const allowed = new Set(candidates.map((candidate) => candidateKey(candidate.kind, candidate.id)));
|
|
128
|
-
const items = [];
|
|
129
|
-
if (Array.isArray(record["items"])) {
|
|
130
|
-
for (const raw of record["items"]) {
|
|
131
|
-
if (typeof raw !== "object" || raw === null)
|
|
132
|
-
continue;
|
|
133
|
-
const item = raw;
|
|
134
|
-
const key = typeof item["key"] === "string" ? item["key"] : "";
|
|
135
|
-
if (!allowed.has(key))
|
|
136
|
-
continue;
|
|
137
|
-
const verdict = item["verdict"] === "promote" || item["verdict"] === "archive" ? item["verdict"] : "keep";
|
|
138
|
-
const built = { key, verdict, reason: typeof item["reason"] === "string" ? item["reason"] : "" };
|
|
139
|
-
if (item["contradicted"] === true) {
|
|
140
|
-
built.contradicted = true;
|
|
141
|
-
}
|
|
142
|
-
if (verdict === "archive" && typeof item["promote"] === "object" && item["promote"] !== null) {
|
|
143
|
-
const sub = item["promote"];
|
|
144
|
-
const subTitle = typeof sub["title"] === "string" ? sub["title"].trim() : "";
|
|
145
|
-
const subContent = typeof sub["content"] === "string" ? sub["content"].trim() : "";
|
|
146
|
-
if (subTitle.length > 0 && subContent.length > 0) {
|
|
147
|
-
built.promote = { title: subTitle, content: subContent };
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
items.push(built);
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
for (const candidate of candidates) {
|
|
154
|
-
const key = candidateKey(candidate.kind, candidate.id);
|
|
155
|
-
if (!items.some((item) => item.key === key)) {
|
|
156
|
-
items.push({ key, verdict: "keep", reason: "not mentioned by the assessor" });
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
return { items, rationale: typeof record["rationale"] === "string" ? record["rationale"] : "" };
|
|
160
|
-
}
|
|
161
|
-
//# sourceMappingURL=wrapup-candidates.js.map
|
package/lib/wrapup-command.d.ts
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The `/evolve wrapup` subcommand handler. Extracted from command.ts (P2-2).
|
|
3
|
-
*/
|
|
4
|
-
import type { Context } from "@deepseek-ai/cordis";
|
|
5
|
-
import type { CommandInvocation, CommandResult } from "@deepseek-ai/dsh-commands";
|
|
6
|
-
import type { EvolutionEngine } from "./service.js";
|
|
7
|
-
import { type PromotionPolicy } from "./promotion.js";
|
|
8
|
-
import { type RecordLanguagePreference } from "./record-language.js";
|
|
9
|
-
export declare function executeWrapupCommand(ctx: Context, engine: EvolutionEngine, invocation: CommandInvocation, policy?: PromotionPolicy, recordLanguage?: RecordLanguagePreference): Promise<CommandResult>;
|
|
10
|
-
//# sourceMappingURL=wrapup-command.d.ts.map
|