dsh-continual-evolve 0.10.6 → 0.12.0
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 +35 -122
- package/README.zh.md +26 -97
- package/cordis.patch.yml +2 -2
- package/lib/index.d.ts +44 -241
- package/lib/index.js +30 -253
- package/lib/memory-guide.d.ts +13 -14
- package/lib/memory-guide.js +32 -21
- package/lib/memory-section.d.ts +77 -0
- package/lib/memory-section.js +189 -0
- package/package.json +2 -17
- package/lib/apply.d.ts +0 -22
- package/lib/apply.js +0 -157
- package/lib/approval.d.ts +0 -72
- package/lib/approval.js +0 -127
- 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.d.ts +0 -49
- package/lib/command.js +0 -568
- package/lib/consolidate.d.ts +0 -90
- package/lib/consolidate.js +0 -180
- package/lib/copy.d.ts +0 -74
- package/lib/copy.js +0 -160
- package/lib/declines.d.ts +0 -101
- package/lib/declines.js +0 -175
- 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 -59
- 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 -196
- package/lib/inject.js +0 -412
- 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/memory-agent.d.ts +0 -129
- package/lib/memory-agent.js +0 -568
- 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/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 -59
- package/lib/notify.js +0 -97
- 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 -78
- 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 -191
- 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.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 -49
- package/lib/service.js +0 -180
- 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 -62
- 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 -72
- package/lib/turn-snapshot.js +0 -238
- package/lib/types.d.ts +0 -208
- package/lib/types.js +0 -127
- 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-command.d.ts +0 -10
- package/lib/wrapup-command.js +0 -243
- package/lib/wrapup.d.ts +0 -260
- package/lib/wrapup.js +0 -539
package/lib/promotion.d.ts
DELETED
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Promotion policy: mechanical, code-enforced gates that decide whether a
|
|
3
|
-
* local entry MAY be promoted to the cross-session global store ("模型提议,
|
|
4
|
-
* 代码保证"). The LLM classification proposes; these guards dispose.
|
|
5
|
-
*
|
|
6
|
-
* Rationale (2026-08-22 store audit): the global store is shared by every
|
|
7
|
-
* project on one profile, so promoting project-scoped knowledge taxes every
|
|
8
|
-
* future session in every project. Measured failure modes:
|
|
9
|
-
* - absolute paths / session ids in promoted content (project-scoped),
|
|
10
|
-
* - near-duplicates of existing global entries re-promoted from later
|
|
11
|
-
* sessions (title matching alone missed them),
|
|
12
|
-
* - one-line facts whose framing costs more than their content.
|
|
13
|
-
*
|
|
14
|
-
* Pure functions only — the callers (wrapup command, gate local-fate phase)
|
|
15
|
-
* supply the resolved {@link PromotionPolicy}.
|
|
16
|
-
*/
|
|
17
|
-
import type { HarnessEntry, HarnessState, RefinementKind } from "./types.js";
|
|
18
|
-
export interface PromotionPolicy {
|
|
19
|
-
/** Content matching any pattern is project-scoped and stays local. */
|
|
20
|
-
blockPatterns: RegExp[];
|
|
21
|
-
/** Whole promotions below this content length stay local (chars). */
|
|
22
|
-
minPromoteChars: number;
|
|
23
|
-
/** Content overlap above this against a global entry = duplicate. */
|
|
24
|
-
maxContentOverlap: number;
|
|
25
|
-
}
|
|
26
|
-
export declare const DEFAULT_PROMOTION_POLICY: PromotionPolicy;
|
|
27
|
-
/**
|
|
28
|
-
* Write-time conflict guard (R2): a global create whose similarity against an
|
|
29
|
-
* existing same-kind entry reaches this score is rejected outright — a
|
|
30
|
-
* near-duplicate adds zero information and the model should evolve_update
|
|
31
|
-
* the existing entry instead.
|
|
32
|
-
*/
|
|
33
|
-
export declare const CONFLICT_BLOCK_SCORE = 0.8;
|
|
34
|
-
/** Similarity at/above this stamps {@link CONFLICT_HINT_KEY} but lets the write proceed. */
|
|
35
|
-
export declare const CONFLICT_WARN_SCORE = 0.5;
|
|
36
|
-
/**
|
|
37
|
-
* First reason the content reads as carrying a live credential, or undefined
|
|
38
|
-
* when it screens clean. Unlike {@link projectScopedReason} this guard is
|
|
39
|
-
* NOT policy-configurable — it protects every sink at once (promotion path,
|
|
40
|
-
* global writes at the engine throat, mount materialization).
|
|
41
|
-
*/
|
|
42
|
-
export declare function secretLeakReason(content: string): string | undefined;
|
|
43
|
-
/**
|
|
44
|
-
* Build a policy from config values (schemastery strings compiled here so
|
|
45
|
-
* the config layer never touches RegExp). Invalid patterns are skipped —
|
|
46
|
-
* a broken user pattern must not disable the remaining guards.
|
|
47
|
-
*/
|
|
48
|
-
export declare function resolvePromotionPolicy(options: {
|
|
49
|
-
blockPatterns?: readonly string[] | undefined;
|
|
50
|
-
minPromoteChars?: number | undefined;
|
|
51
|
-
maxContentOverlap?: number | undefined;
|
|
52
|
-
}): PromotionPolicy;
|
|
53
|
-
/**
|
|
54
|
-
* First reason the content reads as project-scoped, or undefined when it
|
|
55
|
-
* looks portable. Returns the matched pattern source so skip reports stay
|
|
56
|
-
* explainable in reviews.jsonl rationales.
|
|
57
|
-
*/
|
|
58
|
-
export declare function projectScopedReason(content: string, policy: PromotionPolicy): string | undefined;
|
|
59
|
-
/**
|
|
60
|
-
* Token set for cheap similarity, built on the search module's tokenizer —
|
|
61
|
-
* one home for "how this project splits words" (ASCII words lowercased plus
|
|
62
|
-
* CJK character bigrams; bigrams survive segmentation-free Chinese text).
|
|
63
|
-
* The set form feeds Jaccard overlap (review audit 2026-08-28 C: the
|
|
64
|
-
* previous private tokenizer duplicated the definition; the calibrated
|
|
65
|
-
* 0.6/0.8 thresholds are judgment values, not data-fitted, so the merge
|
|
66
|
-
* costs at most a test re-pin).
|
|
67
|
-
*/
|
|
68
|
-
export declare function normalizedTokens(text: string): Set<string>;
|
|
69
|
-
/** Jaccard similarity of two texts' normalized token sets (0..1). */
|
|
70
|
-
export declare function contentOverlap(a: string, b: string): number;
|
|
71
|
-
export interface SimilarEntryHit {
|
|
72
|
-
id: string;
|
|
73
|
-
title: string;
|
|
74
|
-
score: number;
|
|
75
|
-
}
|
|
76
|
-
/**
|
|
77
|
-
* Human/LLM-readable description of a similarity hit, shared by the block
|
|
78
|
-
* error and the approval-question suffix so both surfaces explain the same
|
|
79
|
-
* way.
|
|
80
|
-
*/
|
|
81
|
-
export declare function buildConflictNotice(hit: SimilarEntryHit): string;
|
|
82
|
-
/**
|
|
83
|
-
* The most similar entry of the list, above `minScore`. Title and content
|
|
84
|
-
* both feed the comparison (titles are short; content carries the real
|
|
85
|
-
* signal). Generic form of {@link mostSimilarGlobalEntry} — callers decide
|
|
86
|
-
* which corpus (global store, merged view) the candidates come from.
|
|
87
|
-
*/
|
|
88
|
-
export declare function mostSimilarEntry(entries: readonly HarnessEntry[], title: string, content: string, minScore: number): SimilarEntryHit | undefined;
|
|
89
|
-
/**
|
|
90
|
-
* The most similar non-archived global entry of the same kind, above the
|
|
91
|
-
* policy threshold.
|
|
92
|
-
*/
|
|
93
|
-
export declare function mostSimilarGlobalEntry(globalState: HarnessState, kind: RefinementKind, title: string, content: string, policy: PromotionPolicy): SimilarEntryHit | undefined;
|
|
94
|
-
//# sourceMappingURL=promotion.d.ts.map
|
package/lib/promotion.js
DELETED
|
@@ -1,171 +0,0 @@
|
|
|
1
|
-
import { isArchived } from "./types.js";
|
|
2
|
-
import { tokenize } from "./search.js";
|
|
3
|
-
/** Regex sources that mark content as project-scoped (never global). */
|
|
4
|
-
const DEFAULT_BLOCK_PATTERNS = [
|
|
5
|
-
String.raw `\/(?:mnt|home|Users)\/`, // absolute POSIX paths: "/home/…", "/mnt/…", "/Users/…"
|
|
6
|
-
String.raw `\bsession-[0-9a-f]{8}\b`, // session-scoped identifiers
|
|
7
|
-
String.raw `~/\.dsh\b`, // user harness home references
|
|
8
|
-
];
|
|
9
|
-
export const DEFAULT_PROMOTION_POLICY = {
|
|
10
|
-
blockPatterns: DEFAULT_BLOCK_PATTERNS.map((source) => new RegExp(source, "i")),
|
|
11
|
-
minPromoteChars: 100,
|
|
12
|
-
maxContentOverlap: 0.6,
|
|
13
|
-
};
|
|
14
|
-
/**
|
|
15
|
-
* Write-time conflict guard (R2): a global create whose similarity against an
|
|
16
|
-
* existing same-kind entry reaches this score is rejected outright — a
|
|
17
|
-
* near-duplicate adds zero information and the model should evolve_update
|
|
18
|
-
* the existing entry instead.
|
|
19
|
-
*/
|
|
20
|
-
export const CONFLICT_BLOCK_SCORE = 0.8;
|
|
21
|
-
/** Similarity at/above this stamps {@link CONFLICT_HINT_KEY} but lets the write proceed. */
|
|
22
|
-
export const CONFLICT_WARN_SCORE = 0.5;
|
|
23
|
-
/**
|
|
24
|
-
* Fixed secret-detection patterns — a security invariant, deliberately NOT
|
|
25
|
-
* part of the configurable {@link PromotionPolicy}: a user pattern typo must
|
|
26
|
-
* never be able to disable secret screening. Each entry pairs a human label
|
|
27
|
-
* with a regex tuned for low false positives (placeholders like
|
|
28
|
-
* "YOUR_API_KEY_HERE" don't match; realistic mixed literals do).
|
|
29
|
-
*/
|
|
30
|
-
const SECRET_PATTERNS = [
|
|
31
|
-
{ label: "AnySearch API key", regex: /\bas_sk_[A-Za-z0-9]{8,}\b/ },
|
|
32
|
-
{ label: "Anthropic API key", regex: /\bsk-ant-[A-Za-z0-9_-]{16,}\b/ },
|
|
33
|
-
{ label: "OpenAI-style API key", regex: /\bsk-(?:proj-)?[A-Za-z0-9_-]{16,}\b/ },
|
|
34
|
-
{ label: "GitHub token", regex: /\b(?:ghp|gho|ghu|ghs|ghr)_[A-Za-z0-9]{20,}\b/ },
|
|
35
|
-
{ label: "GitHub fine-grained PAT", regex: /\bgithub_pat_[A-Za-z0-9_]{20,}\b/ },
|
|
36
|
-
{ label: "AWS access key", regex: /\b(?:AKIA|ASIA)[0-9A-Z]{16}\b/ },
|
|
37
|
-
{ label: "Google API key", regex: /\bAIza[0-9A-Za-z_-]{35}\b/ },
|
|
38
|
-
{ label: "Slack token", regex: /\bxox[baprs]-[A-Za-z0-9-]{10,}\b/ },
|
|
39
|
-
{ label: "npm grant token", regex: /\bnpm_[A-Za-z0-9]{36}\b/ },
|
|
40
|
-
{ label: "private key block", regex: /-----BEGIN (?:RSA |EC |DSA |OPENSSH |PGP )?PRIVATE KEY(?: BLOCK)?-----/ },
|
|
41
|
-
{
|
|
42
|
-
label: "credential assignment",
|
|
43
|
-
// The credential-name group is manually case-insensitive ("apiKey" in
|
|
44
|
-
// JSON, "API_KEY" in env examples) so the placeholder lookahead stays
|
|
45
|
-
// case-sensitive: it excludes only ALL-CAPS placeholders
|
|
46
|
-
// ("YOUR_KEY_HERE"), not real mixed/lowercase literals.
|
|
47
|
-
regex: /\b(?:[Aa][Pp][Ii][_-]?[Kk][Ee][Yy]|[Ss][Ee][Cc][Rr][Ee][Tt]|[Tt][Oo][Kk][Ee][Nn]|[Pp][Aa][Ss][Ss][Ww][Oo][Rr][Dd]|[Pp][Aa][Ss][Ss][Ww][Dd]|[Pp][Ww][Dd])\b["']?\s*[:=]\s*["'](?!["']*[A-Z0-9_]+["'])[A-Za-z0-9+/_-]{16,}["']/,
|
|
48
|
-
},
|
|
49
|
-
];
|
|
50
|
-
/**
|
|
51
|
-
* Redact a matched secret for error/audit surfaces: enough to identify the
|
|
52
|
-
* credential family, never the full value (reasons land in reviews.jsonl,
|
|
53
|
-
* question texts, and logs).
|
|
54
|
-
*/
|
|
55
|
-
function redactSecret(matched) {
|
|
56
|
-
if (matched.length <= 10) {
|
|
57
|
-
return `${matched.slice(0, 3)}…`;
|
|
58
|
-
}
|
|
59
|
-
return `${matched.slice(0, 6)}…${matched.slice(-3)}`;
|
|
60
|
-
}
|
|
61
|
-
/**
|
|
62
|
-
* First reason the content reads as carrying a live credential, or undefined
|
|
63
|
-
* when it screens clean. Unlike {@link projectScopedReason} this guard is
|
|
64
|
-
* NOT policy-configurable — it protects every sink at once (promotion path,
|
|
65
|
-
* global writes at the engine throat, mount materialization).
|
|
66
|
-
*/
|
|
67
|
-
export function secretLeakReason(content) {
|
|
68
|
-
for (const { label, regex } of SECRET_PATTERNS) {
|
|
69
|
-
const match = regex.exec(content);
|
|
70
|
-
if (match?.[0]) {
|
|
71
|
-
return `possible ${label} ("${redactSecret(match[0])}") — secrets must never be sedimented into harness state; rotate the credential and keep it out of the store`;
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
return undefined;
|
|
75
|
-
}
|
|
76
|
-
/**
|
|
77
|
-
* Build a policy from config values (schemastery strings compiled here so
|
|
78
|
-
* the config layer never touches RegExp). Invalid patterns are skipped —
|
|
79
|
-
* a broken user pattern must not disable the remaining guards.
|
|
80
|
-
*/
|
|
81
|
-
export function resolvePromotionPolicy(options) {
|
|
82
|
-
const patterns = [...(options.blockPatterns ?? [])]
|
|
83
|
-
.map((source) => {
|
|
84
|
-
try {
|
|
85
|
-
return new RegExp(source, "i");
|
|
86
|
-
}
|
|
87
|
-
catch {
|
|
88
|
-
return undefined;
|
|
89
|
-
}
|
|
90
|
-
})
|
|
91
|
-
.filter((pattern) => pattern !== undefined);
|
|
92
|
-
return {
|
|
93
|
-
blockPatterns: patterns.length > 0 ? patterns : DEFAULT_PROMOTION_POLICY.blockPatterns,
|
|
94
|
-
minPromoteChars: options.minPromoteChars ?? DEFAULT_PROMOTION_POLICY.minPromoteChars,
|
|
95
|
-
maxContentOverlap: options.maxContentOverlap ?? DEFAULT_PROMOTION_POLICY.maxContentOverlap,
|
|
96
|
-
};
|
|
97
|
-
}
|
|
98
|
-
/**
|
|
99
|
-
* First reason the content reads as project-scoped, or undefined when it
|
|
100
|
-
* looks portable. Returns the matched pattern source so skip reports stay
|
|
101
|
-
* explainable in reviews.jsonl rationales.
|
|
102
|
-
*/
|
|
103
|
-
export function projectScopedReason(content, policy) {
|
|
104
|
-
for (const pattern of policy.blockPatterns) {
|
|
105
|
-
if (pattern.test(content)) {
|
|
106
|
-
return `project-scoped content (matches /${pattern.source}/)`;
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
return undefined;
|
|
110
|
-
}
|
|
111
|
-
/**
|
|
112
|
-
* Token set for cheap similarity, built on the search module's tokenizer —
|
|
113
|
-
* one home for "how this project splits words" (ASCII words lowercased plus
|
|
114
|
-
* CJK character bigrams; bigrams survive segmentation-free Chinese text).
|
|
115
|
-
* The set form feeds Jaccard overlap (review audit 2026-08-28 C: the
|
|
116
|
-
* previous private tokenizer duplicated the definition; the calibrated
|
|
117
|
-
* 0.6/0.8 thresholds are judgment values, not data-fitted, so the merge
|
|
118
|
-
* costs at most a test re-pin).
|
|
119
|
-
*/
|
|
120
|
-
export function normalizedTokens(text) {
|
|
121
|
-
return new Set(tokenize(text));
|
|
122
|
-
}
|
|
123
|
-
/** Jaccard similarity of two texts' normalized token sets (0..1). */
|
|
124
|
-
export function contentOverlap(a, b) {
|
|
125
|
-
const left = normalizedTokens(a);
|
|
126
|
-
const right = normalizedTokens(b);
|
|
127
|
-
if (left.size === 0 || right.size === 0) {
|
|
128
|
-
return 0;
|
|
129
|
-
}
|
|
130
|
-
let intersection = 0;
|
|
131
|
-
for (const token of left) {
|
|
132
|
-
if (right.has(token)) {
|
|
133
|
-
intersection += 1;
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
return intersection / (left.size + right.size - intersection);
|
|
137
|
-
}
|
|
138
|
-
/**
|
|
139
|
-
* Human/LLM-readable description of a similarity hit, shared by the block
|
|
140
|
-
* error and the approval-question suffix so both surfaces explain the same
|
|
141
|
-
* way.
|
|
142
|
-
*/
|
|
143
|
-
export function buildConflictNotice(hit) {
|
|
144
|
-
return `near-duplicate of ${hit.id} 「${hit.title}」 (similarity ${Math.round(hit.score * 100)}%)`;
|
|
145
|
-
}
|
|
146
|
-
/**
|
|
147
|
-
* The most similar entry of the list, above `minScore`. Title and content
|
|
148
|
-
* both feed the comparison (titles are short; content carries the real
|
|
149
|
-
* signal). Generic form of {@link mostSimilarGlobalEntry} — callers decide
|
|
150
|
-
* which corpus (global store, merged view) the candidates come from.
|
|
151
|
-
*/
|
|
152
|
-
export function mostSimilarEntry(entries, title, content, minScore) {
|
|
153
|
-
let best;
|
|
154
|
-
for (const other of entries) {
|
|
155
|
-
if (isArchived(other))
|
|
156
|
-
continue;
|
|
157
|
-
const score = Math.max(contentOverlap(title, other.title), contentOverlap(content, other.content));
|
|
158
|
-
if (score >= minScore && (best === undefined || score > best.score)) {
|
|
159
|
-
best = { id: other.id, title: other.title, score };
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
return best;
|
|
163
|
-
}
|
|
164
|
-
/**
|
|
165
|
-
* The most similar non-archived global entry of the same kind, above the
|
|
166
|
-
* policy threshold.
|
|
167
|
-
*/
|
|
168
|
-
export function mostSimilarGlobalEntry(globalState, kind, title, content, policy) {
|
|
169
|
-
return mostSimilarEntry(Object.values(globalState.entries[kind]), title, content, policy.maxContentOverlap);
|
|
170
|
-
}
|
|
171
|
-
//# sourceMappingURL=promotion.js.map
|
package/lib/recall.d.ts
DELETED
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Targeted memory recall (P1): the precise-reading counterpart of the cheap
|
|
3
|
-
* directory index.
|
|
4
|
-
*
|
|
5
|
-
* The injected directory view (`inject.ts`) shows one line per entry and the
|
|
6
|
-
* full `evolve_list` dumps whole stores; neither answers "give me everything
|
|
7
|
-
* about X". Recall fills that gap: filter by query/kind/scope/memory-type,
|
|
8
|
-
* rank with the same CJK BM25 machinery as injection, and return full
|
|
9
|
-
* content plus source, version, and staleness signals. Read-only — it never
|
|
10
|
-
* mutates a store.
|
|
11
|
-
*/
|
|
12
|
-
import type { EvolutionEngine } from "./service.js";
|
|
13
|
-
import type { HarnessScope, RefinementKind } from "./types.js";
|
|
14
|
-
/** Memory recall types (the `memoryType` taxonomy, not entry kinds). */
|
|
15
|
-
export declare const RECALL_MEMORY_TYPES: readonly ["user", "feedback", "project", "reference"];
|
|
16
|
-
export type RecallMemoryType = (typeof RECALL_MEMORY_TYPES)[number];
|
|
17
|
-
/** Default and maximum hits returned per recall call. */
|
|
18
|
-
export declare const DEFAULT_RECALL_LIMIT = 10;
|
|
19
|
-
export declare const MAX_RECALL_LIMIT = 50;
|
|
20
|
-
export interface RecallFilters {
|
|
21
|
-
/** Free-text relevance query; empty means "most recently updated first". */
|
|
22
|
-
query?: string;
|
|
23
|
-
/** Entry kinds to search; default is memory only. */
|
|
24
|
-
kinds?: RefinementKind[];
|
|
25
|
-
/** Stores to search; default is local + project + global. */
|
|
26
|
-
scopes?: HarnessScope[];
|
|
27
|
-
/** Memory-type filter (applies to memory entries only). */
|
|
28
|
-
memoryTypes?: RecallMemoryType[];
|
|
29
|
-
/** Maximum hits; clamped to 1..50, defaults to 10. */
|
|
30
|
-
limit?: number;
|
|
31
|
-
/** Include archived entries; default false. */
|
|
32
|
-
includeArchived?: boolean;
|
|
33
|
-
}
|
|
34
|
-
/** One recalled entry with full content and provenance. */
|
|
35
|
-
export interface RecallHit {
|
|
36
|
-
scope: HarnessScope;
|
|
37
|
-
kind: RefinementKind;
|
|
38
|
-
id: string;
|
|
39
|
-
title: string;
|
|
40
|
-
content: string;
|
|
41
|
-
path: string;
|
|
42
|
-
version: number;
|
|
43
|
-
createdAt: string;
|
|
44
|
-
updatedAt: string;
|
|
45
|
-
archived: boolean;
|
|
46
|
-
/** Memory-type hook for memory entries; undefined for other kinds. */
|
|
47
|
-
memoryType?: string;
|
|
48
|
-
/** Conflict-hint stamp left by the promotion guard, when present. */
|
|
49
|
-
staleHint?: string;
|
|
50
|
-
/** Session the entry was distilled from, when recorded. */
|
|
51
|
-
sourceSession?: string;
|
|
52
|
-
/** Source user-message seqs, when recorded. */
|
|
53
|
-
sourceSeqs?: number[];
|
|
54
|
-
}
|
|
55
|
-
export interface RecallResult {
|
|
56
|
-
hits: RecallHit[];
|
|
57
|
-
/** Skipped scopes with reasons (read-path honesty: never silently drop a store). */
|
|
58
|
-
notes: string[];
|
|
59
|
-
/** Candidates seen before ranking/truncation. */
|
|
60
|
-
totalCandidates: number;
|
|
61
|
-
}
|
|
62
|
-
export interface RecallContext {
|
|
63
|
-
/** Live session id for the local store; absent → local is skipped with a note. */
|
|
64
|
-
sessionId?: string | undefined;
|
|
65
|
-
/** Resolved project key; absent → project is skipped with a note. */
|
|
66
|
-
projectKey?: string | undefined;
|
|
67
|
-
}
|
|
68
|
-
/**
|
|
69
|
-
* Search the harness stores with mechanical filters and BM25 ranking.
|
|
70
|
-
*
|
|
71
|
-
* @param engine The evolution engine (read-only use).
|
|
72
|
-
* @param context Store identities for the session-scoped stores.
|
|
73
|
-
* @param filters Query/kind/scope/type/limit/archive filters.
|
|
74
|
-
* @returns Ranked hits plus a note for every scope that could not be read.
|
|
75
|
-
* @throws On an unknown kind, scope, or memory type — fail loud instead of
|
|
76
|
-
* silently narrowing the search.
|
|
77
|
-
*/
|
|
78
|
-
export declare function recallMemories(engine: EvolutionEngine, context: RecallContext, filters?: RecallFilters): RecallResult;
|
|
79
|
-
/**
|
|
80
|
-
* Render recall hits for a model or human reader: full content, version,
|
|
81
|
-
* source, and staleness on every hit; skipped scopes are always shown so
|
|
82
|
-
* "no results" can never hide an unread store.
|
|
83
|
-
*/
|
|
84
|
-
export declare function formatRecallResult(result: RecallResult, query?: string): string;
|
|
85
|
-
//# sourceMappingURL=recall.d.ts.map
|
package/lib/recall.js
DELETED
|
@@ -1,142 +0,0 @@
|
|
|
1
|
-
import { buildRelevanceIndex, relevanceScore } from "./search.js";
|
|
2
|
-
import { CONFLICT_HINT_KEY } from "./types.js";
|
|
3
|
-
import { isArchived, isMemoryType, ARCHIVED_AT_KEY, MEMORY_TYPE_KEY, SOURCE_SEQS_KEY, SOURCE_SESSION_KEY } from "./types.js";
|
|
4
|
-
/** Memory recall types (the `memoryType` taxonomy, not entry kinds). */
|
|
5
|
-
export const RECALL_MEMORY_TYPES = ["user", "feedback", "project", "reference"];
|
|
6
|
-
/** Entry kinds recall can filter on; memory is the default. */
|
|
7
|
-
const RECALL_KINDS = ["prompt", "memory", "skill", "subagent"];
|
|
8
|
-
/** Default and maximum hits returned per recall call. */
|
|
9
|
-
export const DEFAULT_RECALL_LIMIT = 10;
|
|
10
|
-
export const MAX_RECALL_LIMIT = 50;
|
|
11
|
-
/**
|
|
12
|
-
* Search the harness stores with mechanical filters and BM25 ranking.
|
|
13
|
-
*
|
|
14
|
-
* @param engine The evolution engine (read-only use).
|
|
15
|
-
* @param context Store identities for the session-scoped stores.
|
|
16
|
-
* @param filters Query/kind/scope/type/limit/archive filters.
|
|
17
|
-
* @returns Ranked hits plus a note for every scope that could not be read.
|
|
18
|
-
* @throws On an unknown kind, scope, or memory type — fail loud instead of
|
|
19
|
-
* silently narrowing the search.
|
|
20
|
-
*/
|
|
21
|
-
export function recallMemories(engine, context, filters = {}) {
|
|
22
|
-
const kinds = filters.kinds ?? ["memory"];
|
|
23
|
-
for (const kind of kinds) {
|
|
24
|
-
if (!RECALL_KINDS.includes(kind)) {
|
|
25
|
-
throw new Error(`evolve_recall: unknown kind "${kind}" (expected one of ${RECALL_KINDS.join(", ")})`);
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
const scopes = filters.scopes ?? ["local", "project", "global"];
|
|
29
|
-
for (const scope of scopes) {
|
|
30
|
-
if (scope !== "local" && scope !== "project" && scope !== "global") {
|
|
31
|
-
throw new Error(`evolve_recall: unknown scope "${scope}" (expected local, project, or global)`);
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
const memoryTypes = filters.memoryTypes ?? [];
|
|
35
|
-
for (const memoryType of memoryTypes) {
|
|
36
|
-
if (!RECALL_MEMORY_TYPES.includes(memoryType)) {
|
|
37
|
-
throw new Error(`evolve_recall: unknown memoryType "${memoryType}" (expected one of ${RECALL_MEMORY_TYPES.join(", ")})`);
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
const limit = typeof filters.limit === "number" && Number.isFinite(filters.limit) && filters.limit > 0
|
|
41
|
-
? Math.min(Math.floor(filters.limit), MAX_RECALL_LIMIT)
|
|
42
|
-
: DEFAULT_RECALL_LIMIT;
|
|
43
|
-
const query = filters.query?.trim() ?? "";
|
|
44
|
-
const candidates = [];
|
|
45
|
-
const notes = [];
|
|
46
|
-
for (const scope of scopes) {
|
|
47
|
-
const storeId = scope === "local" ? context.sessionId : scope === "project" ? context.projectKey : undefined;
|
|
48
|
-
if (scope !== "global" && storeId === undefined) {
|
|
49
|
-
notes.push(`${scope} store skipped: ${scope === "local" ? "no live session id" : "no project key for this session"}`);
|
|
50
|
-
continue;
|
|
51
|
-
}
|
|
52
|
-
let state;
|
|
53
|
-
try {
|
|
54
|
-
state = engine.load(scope, storeId);
|
|
55
|
-
}
|
|
56
|
-
catch (cause) {
|
|
57
|
-
notes.push(`${scope} store unreadable: ${cause instanceof Error ? cause.message : String(cause)}`);
|
|
58
|
-
continue;
|
|
59
|
-
}
|
|
60
|
-
for (const kind of kinds) {
|
|
61
|
-
for (const entry of Object.values(state.entries[kind])) {
|
|
62
|
-
if (!filters.includeArchived && isArchived(entry))
|
|
63
|
-
continue;
|
|
64
|
-
if (kind === "memory" && memoryTypes.length > 0) {
|
|
65
|
-
const type = entry.metadata[MEMORY_TYPE_KEY];
|
|
66
|
-
if (typeof type !== "string" || !memoryTypes.includes(type))
|
|
67
|
-
continue;
|
|
68
|
-
}
|
|
69
|
-
candidates.push(entry);
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
const ranked = query.length > 0 ? rankByRelevance(candidates, query) : [...candidates].sort((a, b) => (a.updated_at < b.updated_at ? 1 : a.updated_at > b.updated_at ? -1 : 0));
|
|
74
|
-
return { hits: ranked.slice(0, limit).map(toRecallHit), notes, totalCandidates: candidates.length };
|
|
75
|
-
}
|
|
76
|
-
function rankByRelevance(candidates, query) {
|
|
77
|
-
const index = buildRelevanceIndex(candidates);
|
|
78
|
-
const normalized = query.trim().toLowerCase();
|
|
79
|
-
return candidates
|
|
80
|
-
.map((entry) => {
|
|
81
|
-
let score = relevanceScore(index, entry, query);
|
|
82
|
-
// Exact scope:id addressing wins over fuzzy relevance (the
|
|
83
|
-
// memory-agent manifest search precedent).
|
|
84
|
-
if (`${entry.scope}:${entry.id}`.toLowerCase() === normalized)
|
|
85
|
-
score += 1000;
|
|
86
|
-
return { entry, score };
|
|
87
|
-
})
|
|
88
|
-
.filter((hit) => hit.score > 0)
|
|
89
|
-
.sort((a, b) => b.score - a.score || (a.entry.updated_at < b.entry.updated_at ? 1 : -1))
|
|
90
|
-
.map((hit) => hit.entry);
|
|
91
|
-
}
|
|
92
|
-
function toRecallHit(entry) {
|
|
93
|
-
const memoryType = entry.kind === "memory" && isMemoryType(entry.metadata[MEMORY_TYPE_KEY])
|
|
94
|
-
? String(entry.metadata[MEMORY_TYPE_KEY])
|
|
95
|
-
: undefined;
|
|
96
|
-
const staleHint = typeof entry.metadata[CONFLICT_HINT_KEY] === "string" && entry.metadata[CONFLICT_HINT_KEY].length > 0
|
|
97
|
-
? String(entry.metadata[CONFLICT_HINT_KEY])
|
|
98
|
-
: undefined;
|
|
99
|
-
const sourceSession = typeof entry.metadata[SOURCE_SESSION_KEY] === "string" ? String(entry.metadata[SOURCE_SESSION_KEY]) : undefined;
|
|
100
|
-
const rawSeqs = entry.metadata[SOURCE_SEQS_KEY];
|
|
101
|
-
const sourceSeqs = Array.isArray(rawSeqs) && rawSeqs.every((seq) => typeof seq === "number") ? rawSeqs : undefined;
|
|
102
|
-
return {
|
|
103
|
-
scope: entry.scope,
|
|
104
|
-
kind: entry.kind,
|
|
105
|
-
id: entry.id,
|
|
106
|
-
title: entry.title,
|
|
107
|
-
content: entry.content,
|
|
108
|
-
path: entry.path,
|
|
109
|
-
version: entry.version,
|
|
110
|
-
createdAt: entry.created_at,
|
|
111
|
-
updatedAt: entry.updated_at,
|
|
112
|
-
archived: isArchived(entry),
|
|
113
|
-
...(memoryType !== undefined ? { memoryType } : {}),
|
|
114
|
-
...(staleHint !== undefined ? { staleHint } : {}),
|
|
115
|
-
...(sourceSession !== undefined ? { sourceSession } : {}),
|
|
116
|
-
...(sourceSeqs !== undefined ? { sourceSeqs } : {}),
|
|
117
|
-
};
|
|
118
|
-
}
|
|
119
|
-
/**
|
|
120
|
-
* Render recall hits for a model or human reader: full content, version,
|
|
121
|
-
* source, and staleness on every hit; skipped scopes are always shown so
|
|
122
|
-
* "no results" can never hide an unread store.
|
|
123
|
-
*/
|
|
124
|
-
export function formatRecallResult(result, query) {
|
|
125
|
-
const lines = [`recall: ${result.hits.length} hit(s)${query?.trim() ? ` for "${query.trim()}"` : ""} (${result.totalCandidates} candidate(s))`];
|
|
126
|
-
for (const hit of result.hits) {
|
|
127
|
-
const type = hit.memoryType !== undefined ? ` · type=${hit.memoryType}` : "";
|
|
128
|
-
const archived = hit.archived ? " · archived" : "";
|
|
129
|
-
const stale = hit.staleHint !== undefined ? ` · conflict-hint=${hit.staleHint}` : "";
|
|
130
|
-
const source = hit.sourceSession !== undefined ? ` · src=${hit.sourceSession}${hit.sourceSeqs ? `:${hit.sourceSeqs.join(",")}` : ""}` : "";
|
|
131
|
-
lines.push(`- [${hit.scope}:${hit.kind}:${hit.id}] ${hit.title} (v${hit.version}${type}${archived}${stale}${source})`);
|
|
132
|
-
lines.push(` ${hit.content}`);
|
|
133
|
-
}
|
|
134
|
-
for (const note of result.notes) {
|
|
135
|
-
lines.push(`note: ${note}`);
|
|
136
|
-
}
|
|
137
|
-
if (result.hits.length === 0) {
|
|
138
|
-
lines.push(`(${ARCHIVED_AT_KEY} entries are hidden unless includeArchived is set)`);
|
|
139
|
-
}
|
|
140
|
-
return lines.join("\n");
|
|
141
|
-
}
|
|
142
|
-
//# sourceMappingURL=recall.js.map
|
package/lib/record-language.d.ts
DELETED
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Record language resolution for evolution writes.
|
|
3
|
-
*
|
|
4
|
-
* Every durable record (refinement summary/rationale, entry title/content,
|
|
5
|
-
* review and wrap-up verdicts) is generated prose. Its language is resolved
|
|
6
|
-
* per call through one chain — never hardcoded at any single site:
|
|
7
|
-
*
|
|
8
|
-
* 1. explicit `recordLanguage` plugin config (`zh`|`en`; `auto` defers);
|
|
9
|
-
* 2. the durable DSH client preference (`locale` namespace, `preference`
|
|
10
|
-
* field — the same source the desktop shell and the web GUI follow);
|
|
11
|
-
* 3. detection over recent direct user text (Han-character signal);
|
|
12
|
-
* 4. `en`, matching the DSH `FALLBACK_LOCALE`.
|
|
13
|
-
*
|
|
14
|
-
* The durable-preference read is intentionally fail-open: the settings
|
|
15
|
-
* service is host-owned and its shape may drift, so any mismatch yields
|
|
16
|
-
* `undefined` and the chain moves on instead of breaking the evolution run.
|
|
17
|
-
*/
|
|
18
|
-
/** Natural languages evolution records are authored in. */
|
|
19
|
-
export type RecordLanguage = "zh" | "en";
|
|
20
|
-
/** Plugin-facing preference: an explicit language or deferral to detection. */
|
|
21
|
-
export type RecordLanguagePreference = "auto" | RecordLanguage;
|
|
22
|
-
/**
|
|
23
|
-
* Upstream durable-settings address reused verbatim
|
|
24
|
-
* (`@deepseek-ai/dsh-client-locale`: `LOCALE_SETTINGS_NAMESPACE`,
|
|
25
|
-
* `LOCALE_PREFERENCE_FIELD`). New keys are never invented here.
|
|
26
|
-
*/
|
|
27
|
-
export declare const DSH_LOCALE_NAMESPACE = "locale";
|
|
28
|
-
export declare const DSH_LOCALE_PREFERENCE_FIELD = "preference";
|
|
29
|
-
/**
|
|
30
|
-
* Detect Chinese from user text by Han-character density.
|
|
31
|
-
*
|
|
32
|
-
* @param text Arbitrary user-supplied text.
|
|
33
|
-
* @returns `"zh"` on sufficient Han signal, otherwise `undefined` (no
|
|
34
|
-
* signal — never a default; the resolver owns the fallback).
|
|
35
|
-
*/
|
|
36
|
-
export declare function detectLanguageFromText(text: string): RecordLanguage | undefined;
|
|
37
|
-
/**
|
|
38
|
-
* Read the durable DSH client language preference through the host settings
|
|
39
|
-
* service. Fail-open by contract: a missing service, a throwing read, an
|
|
40
|
-
* async-shaped reply, or an unknown value all yield `undefined`.
|
|
41
|
-
*
|
|
42
|
-
* The service is resolved through `ctx.get("settings")` (the global registry),
|
|
43
|
-
* which sees services provided by sibling plugin entries. A direct
|
|
44
|
-
* `ctx.settings` walks only the caller's fiber ancestor chain and throws
|
|
45
|
-
* "cannot get property \"settings\" without inject" — the same failure mode
|
|
46
|
-
* documented in `goal.ts` — and a throw here would abort the whole snapshot
|
|
47
|
-
* gate, so the property access itself is guarded. Plain-object contexts
|
|
48
|
-
* (tests, non-Cordis hosts) fall back to the direct property.
|
|
49
|
-
*
|
|
50
|
-
* @param ctx Cordis context that may carry the host settings service.
|
|
51
|
-
* @returns The stored preference (`zh`/`en`, subtag-tolerant) or `undefined`.
|
|
52
|
-
*/
|
|
53
|
-
export declare function durableLocalePreference(ctx: unknown): string | undefined;
|
|
54
|
-
/**
|
|
55
|
-
* Normalize an explicit preference or durable value to a record language.
|
|
56
|
-
*
|
|
57
|
-
* @param value Candidate language id (config or durable preference).
|
|
58
|
-
* @returns `"zh"`/`"en"` on a recognized id, otherwise `undefined`.
|
|
59
|
-
*/
|
|
60
|
-
export declare function normalizeRecordLanguage(value: string | undefined): RecordLanguage | undefined;
|
|
61
|
-
export interface ResolveRecordLanguageInput {
|
|
62
|
-
/** Explicit plugin config; absent/`auto` defers to the chain below. */
|
|
63
|
-
configured?: RecordLanguagePreference | undefined;
|
|
64
|
-
/** Host context for the durable-preference read; absent skips that tier. */
|
|
65
|
-
ctx?: unknown;
|
|
66
|
-
/** Recent direct user text for the detection tier; absent skips it. */
|
|
67
|
-
trajectoryText?: string;
|
|
68
|
-
}
|
|
69
|
-
/**
|
|
70
|
-
* Resolve the authoring language for one evolution write.
|
|
71
|
-
*
|
|
72
|
-
* @param input Configured preference, host context, and trajectory sample.
|
|
73
|
-
* @returns The winning language; always defined (`en` is the final fallback).
|
|
74
|
-
*/
|
|
75
|
-
export declare function resolveRecordLanguage(input: ResolveRecordLanguageInput): RecordLanguage;
|
|
76
|
-
/**
|
|
77
|
-
* The one-line prompt addendum appended to every record-authoring system
|
|
78
|
-
* prompt. Machine contracts (JSON keys, ids, paths, code) stay untouched —
|
|
79
|
-
* only natural-language values follow the resolved language.
|
|
80
|
-
*
|
|
81
|
-
* @param lang Resolved record language.
|
|
82
|
-
* @returns The instruction line for `lang`.
|
|
83
|
-
*/
|
|
84
|
-
export declare function recordLanguageInstruction(lang: RecordLanguage): string;
|
|
85
|
-
//# sourceMappingURL=record-language.d.ts.map
|