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.
Files changed (161) hide show
  1. package/README.md +42 -115
  2. package/README.zh.md +33 -93
  3. package/client/client.js +253 -0
  4. package/cordis.patch.yml +2 -2
  5. package/lib/card-routes.d.ts +32 -0
  6. package/lib/card-routes.js +92 -0
  7. package/lib/index.d.ts +65 -217
  8. package/lib/index.js +74 -234
  9. package/lib/known-workspaces.d.ts +22 -0
  10. package/lib/known-workspaces.js +42 -0
  11. package/lib/memory-guide.d.ts +13 -14
  12. package/lib/memory-guide.js +34 -21
  13. package/lib/memory-section.d.ts +77 -0
  14. package/lib/memory-section.js +189 -0
  15. package/lib/memory-snapshot.d.ts +54 -0
  16. package/lib/memory-snapshot.js +125 -0
  17. package/package.json +8 -17
  18. package/lib/apply.d.ts +0 -22
  19. package/lib/apply.js +0 -157
  20. package/lib/approval.d.ts +0 -73
  21. package/lib/approval.js +0 -127
  22. package/lib/audit-log.d.ts +0 -19
  23. package/lib/audit-log.js +0 -52
  24. package/lib/auto.d.ts +0 -236
  25. package/lib/auto.js +0 -773
  26. package/lib/autocase.d.ts +0 -38
  27. package/lib/autocase.js +0 -76
  28. package/lib/benchmark-command.d.ts +0 -9
  29. package/lib/benchmark-command.js +0 -357
  30. package/lib/benchmark.d.ts +0 -151
  31. package/lib/benchmark.js +0 -250
  32. package/lib/command-util.d.ts +0 -70
  33. package/lib/command-util.js +0 -123
  34. package/lib/command.d.ts +0 -13
  35. package/lib/command.js +0 -134
  36. package/lib/consolidate.d.ts +0 -90
  37. package/lib/consolidate.js +0 -180
  38. package/lib/copy.d.ts +0 -37
  39. package/lib/copy.js +0 -58
  40. package/lib/declines.d.ts +0 -103
  41. package/lib/declines.js +0 -179
  42. package/lib/evaluate.d.ts +0 -74
  43. package/lib/evaluate.js +0 -271
  44. package/lib/evolve-event.d.ts +0 -40
  45. package/lib/evolve-event.js +0 -60
  46. package/lib/extraction-phase.d.ts +0 -132
  47. package/lib/extraction-phase.js +0 -236
  48. package/lib/failures.d.ts +0 -42
  49. package/lib/failures.js +0 -171
  50. package/lib/fate.d.ts +0 -130
  51. package/lib/fate.js +0 -350
  52. package/lib/goal-command.d.ts +0 -7
  53. package/lib/goal-command.js +0 -37
  54. package/lib/goal.d.ts +0 -72
  55. package/lib/goal.js +0 -72
  56. package/lib/inject.d.ts +0 -90
  57. package/lib/inject.js +0 -164
  58. package/lib/injection-caps.d.ts +0 -4
  59. package/lib/injection-caps.js +0 -4
  60. package/lib/injection-directory.d.ts +0 -29
  61. package/lib/injection-directory.js +0 -78
  62. package/lib/injection-rank.d.ts +0 -85
  63. package/lib/injection-rank.js +0 -161
  64. package/lib/listener.d.ts +0 -45
  65. package/lib/listener.js +0 -199
  66. package/lib/llm-text.d.ts +0 -104
  67. package/lib/llm-text.js +0 -140
  68. package/lib/logfile.d.ts +0 -71
  69. package/lib/logfile.js +0 -159
  70. package/lib/maintenance-commands.d.ts +0 -23
  71. package/lib/maintenance-commands.js +0 -136
  72. package/lib/memory-agent.d.ts +0 -93
  73. package/lib/memory-agent.js +0 -300
  74. package/lib/memory-apply.d.ts +0 -44
  75. package/lib/memory-apply.js +0 -202
  76. package/lib/memory-benchmark.d.ts +0 -76
  77. package/lib/memory-benchmark.js +0 -170
  78. package/lib/memory-index.d.ts +0 -114
  79. package/lib/memory-index.js +0 -197
  80. package/lib/memory-manifest.d.ts +0 -16
  81. package/lib/memory-manifest.js +0 -70
  82. package/lib/message-source.d.ts +0 -23
  83. package/lib/message-source.js +0 -12
  84. package/lib/mount-command.d.ts +0 -10
  85. package/lib/mount-command.js +0 -48
  86. package/lib/mount.d.ts +0 -42
  87. package/lib/mount.js +0 -212
  88. package/lib/notify.d.ts +0 -48
  89. package/lib/notify.js +0 -57
  90. package/lib/plan-command.d.ts +0 -12
  91. package/lib/plan-command.js +0 -38
  92. package/lib/plan.d.ts +0 -16
  93. package/lib/plan.js +0 -126
  94. package/lib/planner.d.ts +0 -61
  95. package/lib/planner.js +0 -144
  96. package/lib/pool.d.ts +0 -7
  97. package/lib/pool.js +0 -25
  98. package/lib/prefix-cache.d.ts +0 -79
  99. package/lib/prefix-cache.js +0 -168
  100. package/lib/project.d.ts +0 -28
  101. package/lib/project.js +0 -66
  102. package/lib/projection.d.ts +0 -22
  103. package/lib/projection.js +0 -112
  104. package/lib/promotion.d.ts +0 -94
  105. package/lib/promotion.js +0 -171
  106. package/lib/recall.d.ts +0 -85
  107. package/lib/recall.js +0 -142
  108. package/lib/record-language.d.ts +0 -85
  109. package/lib/record-language.js +0 -152
  110. package/lib/render.d.ts +0 -13
  111. package/lib/render.js +0 -80
  112. package/lib/review-scheduler.d.ts +0 -38
  113. package/lib/review-scheduler.js +0 -183
  114. package/lib/review.d.ts +0 -60
  115. package/lib/review.js +0 -145
  116. package/lib/rollback.d.ts +0 -9
  117. package/lib/rollback.js +0 -68
  118. package/lib/rubric.d.ts +0 -29
  119. package/lib/rubric.js +0 -119
  120. package/lib/runtime-commands.d.ts +0 -14
  121. package/lib/runtime-commands.js +0 -115
  122. package/lib/runtime.d.ts +0 -24
  123. package/lib/runtime.js +0 -87
  124. package/lib/score.d.ts +0 -64
  125. package/lib/score.js +0 -191
  126. package/lib/search.d.ts +0 -83
  127. package/lib/search.js +0 -136
  128. package/lib/service.d.ts +0 -63
  129. package/lib/service.js +0 -253
  130. package/lib/skill-render.d.ts +0 -23
  131. package/lib/skill-render.js +0 -69
  132. package/lib/skill.d.ts +0 -15
  133. package/lib/skill.js +0 -80
  134. package/lib/skillquality.d.ts +0 -85
  135. package/lib/skillquality.js +0 -320
  136. package/lib/source.d.ts +0 -30
  137. package/lib/source.js +0 -43
  138. package/lib/state.d.ts +0 -26
  139. package/lib/state.js +0 -151
  140. package/lib/store.d.ts +0 -61
  141. package/lib/store.js +0 -164
  142. package/lib/token-usage.d.ts +0 -68
  143. package/lib/token-usage.js +0 -211
  144. package/lib/tool.d.ts +0 -34
  145. package/lib/tool.js +0 -320
  146. package/lib/turn-snapshot.d.ts +0 -78
  147. package/lib/turn-snapshot.js +0 -252
  148. package/lib/types.d.ts +0 -218
  149. package/lib/types.js +0 -133
  150. package/lib/usage.d.ts +0 -36
  151. package/lib/usage.js +0 -98
  152. package/lib/validate.d.ts +0 -28
  153. package/lib/validate.js +0 -181
  154. package/lib/wrapup-candidates.d.ts +0 -127
  155. package/lib/wrapup-candidates.js +0 -161
  156. package/lib/wrapup-command.d.ts +0 -10
  157. package/lib/wrapup-command.js +0 -243
  158. package/lib/wrapup-proposals.d.ts +0 -101
  159. package/lib/wrapup-proposals.js +0 -281
  160. package/lib/wrapup.d.ts +0 -56
  161. package/lib/wrapup.js +0 -105
@@ -1,236 +0,0 @@
1
- import { notifyMemoryExtraction } from "./notify.js";
2
- import { mergeHarnessStates } from "./state.js";
3
- import { projectKeyOf } from "./project.js";
4
- import { buildEvolveCompleteEvent, emitEvolveComplete } from "./evolve-event.js";
5
- import { compareReviewCursors, sliceTurnSnapshot } from "./turn-snapshot.js";
6
- import { matchDeclinedTrajectory } from "./declines.js";
7
- import { runMemoryAgent, } from "./memory-agent.js";
8
- import { applyMemoryExtractionProposal } from "./memory-apply.js";
9
- import { buildMemoryManifest } from "./memory-manifest.js";
10
- import { DEFAULT_REVIEWS_RETAIN } from "./types.js";
11
- import { resolveRecordLanguage } from "./record-language.js";
12
- export function stateFor(map, sessionId) {
13
- let state = map.get(sessionId);
14
- if (!state) {
15
- state = {
16
- turns: 0,
17
- completedTurn: 0,
18
- memoryDecisions: {},
19
- lastReviewAt: 0,
20
- goalBlockStreak: 0,
21
- };
22
- map.set(sessionId, state);
23
- }
24
- return state;
25
- }
26
- export function advanceMemoryCheckpoint(state, next) {
27
- if (state.memoryCheckpoint === undefined) {
28
- state.memoryCheckpoint = next;
29
- return;
30
- }
31
- const order = compareReviewCursors(next, state.memoryCheckpoint);
32
- if (order === undefined || order > 0)
33
- state.memoryCheckpoint = next;
34
- }
35
- function rememberMemoryDecision(state, key, decision) {
36
- state.memoryDecisions[key] = decision;
37
- const keys = Object.keys(state.memoryDecisions);
38
- while (keys.length > 128)
39
- delete state.memoryDecisions[keys.shift() ?? ""];
40
- }
41
- /**
42
- * The state view the extractor judges: the session's local entries merged
43
- * with the project and global stores, each entry carrying its real scope.
44
- * Without the global half the extractor cannot see that a topic is already
45
- * covered cross-session and happily re-sediments a local duplicate of it.
46
- *
47
- * The merged view is read-only context — applying still targets the raw
48
- * local state (baseline checks compare local entries only).
49
- */
50
- export function loadGateHarnessView(engine, sessionId, opts) {
51
- const projectState = opts?.projectKey ? engine.load("project", opts.projectKey) : undefined;
52
- return mergeHarnessStates(engine.load("global", undefined), engine.load("local", sessionId), projectState ? { projectState } : undefined);
53
- }
54
- /**
55
- * Gap C1: parse a "provider/model" or "model" string into its components.
56
- * Returns undefined when the input is empty (no override). A bare model name
57
- * falls back to the agent's provider, then "deepseek".
58
- * Exported for unit testing; production resolves it inside
59
- * runMemoryExtractionPhase.
60
- */
61
- export function parseReviewModel(reviewModel, fallbackProvider) {
62
- if (!reviewModel || reviewModel.trim().length === 0)
63
- return undefined;
64
- const slash = reviewModel.indexOf("/");
65
- if (slash > 0) {
66
- return { provider: reviewModel.slice(0, slash), model: reviewModel.slice(slash + 1) };
67
- }
68
- return { provider: fallbackProvider ?? "deepseek", model: reviewModel };
69
- }
70
- /**
71
- * One extraction run over a scheduler snapshot. Records noop / applied /
72
- * declined audit rows with duration and loop stats, advances the memory
73
- * checkpoint on settled outcomes, and queues a visible receipt only for an
74
- * applied goal-blocked moment (compaction and session-close stay audit-only).
75
- */
76
- export async function runMemoryExtractionPhase(ctx, engine, agent, config, state, snapshot, record, signal) {
77
- const sessionId = agent.id;
78
- const turnsSinceLastReview = state.turns - state.lastReviewAt;
79
- // This invocation becomes the new reference point for the next audit row.
80
- state.lastReviewAt = state.turns;
81
- const startedAt = Date.now();
82
- const memorySnapshot = state.memoryCheckpoint ? sliceTurnSnapshot(snapshot, state.memoryCheckpoint) : snapshot;
83
- if (!memorySnapshot.eligible || !memorySnapshot.trajectory) {
84
- advanceMemoryCheckpoint(state, snapshot.cursor);
85
- ctx.logger("continual-evolve").info(`memory agent no-op (${snapshot.reason}) [${agent.id}]: no new eligible evidence after checkpoint`);
86
- record({
87
- sessionId,
88
- reason: snapshot.reason,
89
- turnsSinceLastReview,
90
- outcome: "noop",
91
- rationale: `memory no-op (${snapshot.reason}): no new eligible evidence after checkpoint`,
92
- durationMs: Date.now() - startedAt,
93
- memoryTurns: 0,
94
- memorySearches: 0,
95
- appliedEdits: 0,
96
- });
97
- return;
98
- }
99
- const declinedHit = matchDeclinedTrajectory(engine.baseDir, memorySnapshot.trajectory);
100
- if (declinedHit !== undefined) {
101
- advanceMemoryCheckpoint(state, memorySnapshot.cursor);
102
- ctx.logger("continual-evolve").info(`memory agent pre-suppressed (${snapshot.reason}) [${agent.id}]: checkpoint overlaps declined ${declinedHit.entry.scope}:${declinedHit.entry.fingerprint} (coverage ${(declinedHit.coverage * 100).toFixed(0)}%, ${declinedHit.hits} tokens) — agent not invoked`);
103
- record({
104
- sessionId,
105
- reason: snapshot.reason,
106
- turnsSinceLastReview,
107
- outcome: "noop",
108
- rationale: `memory pre-suppressed (${snapshot.reason}): checkpoint overlaps declined ${declinedHit.entry.scope}:${declinedHit.entry.fingerprint} — agent not invoked`,
109
- durationMs: Date.now() - startedAt,
110
- memoryTurns: 0,
111
- memorySearches: 0,
112
- appliedEdits: 0,
113
- });
114
- return;
115
- }
116
- const projectKey = snapshot.projectKey ?? projectKeyOf(agent);
117
- const baselines = {
118
- local: engine.load("local", sessionId),
119
- global: engine.load("global", undefined),
120
- ...(projectKey ? { project: engine.load("project", projectKey) } : {}),
121
- };
122
- const manifest = buildMemoryManifest(loadGateHarnessView(engine, sessionId, projectKey ? { projectKey } : undefined));
123
- const overrideRoute = parseReviewModel(config.reviewModel, agent.options.provider);
124
- const provider = overrideRoute?.provider ?? agent.options.provider;
125
- const model = overrideRoute?.model ?? agent.options.model;
126
- if (!provider || !model)
127
- throw new Error("evolve: no provider/model route for the memory extraction agent");
128
- const tokenUsage = {
129
- baseDir: engine.baseDir,
130
- sessionId,
131
- retain: engine.retention.tokenUsage,
132
- onError: (cause) => ctx
133
- .logger("continual-evolve")
134
- .warn(`token-usage ledger failed for ${sessionId}: ${cause instanceof Error ? cause.message : String(cause)}`),
135
- };
136
- const run = await runMemoryAgent(ctx, {
137
- provider,
138
- model,
139
- sessionId,
140
- manifest,
141
- trajectory: memorySnapshot.trajectory,
142
- trajectoryEvents: memorySnapshot.events,
143
- maxOutputTokens: config.budgetTokens,
144
- ...(signal ? { signal } : {}),
145
- tokenUsage,
146
- language: resolveRecordLanguage({ configured: config.recordLanguage, ctx, trajectoryText: memorySnapshot.trajectory }),
147
- ...((config.prefixCacheMode !== undefined || config.prefixMaxChars !== undefined
148
- ? {
149
- prefixCache: {
150
- ...(config.prefixCacheMode !== undefined ? { mode: config.prefixCacheMode } : {}),
151
- ...(config.prefixMaxChars !== undefined ? { maxChars: config.prefixMaxChars } : {}),
152
- },
153
- }
154
- : {})),
155
- });
156
- if (run.proposal.edits.length === 0) {
157
- advanceMemoryCheckpoint(state, memorySnapshot.cursor);
158
- ctx.logger("continual-evolve").info(`memory agent no-op (${snapshot.reason}) [${sessionId}] after ${run.turns} turn(s): ${run.proposal.rationale}`);
159
- record({
160
- sessionId,
161
- reason: snapshot.reason,
162
- turnsSinceLastReview,
163
- outcome: "noop",
164
- rationale: `memory no-op (${snapshot.reason}) after ${run.turns} turn(s): ${run.proposal.rationale}`,
165
- durationMs: Date.now() - startedAt,
166
- memoryTurns: run.turns,
167
- memorySearches: run.searches,
168
- appliedEdits: 0,
169
- });
170
- return;
171
- }
172
- const source = { sessionId, ...(memorySnapshot.sourceSeqs.length > 0 ? { seqs: [...memorySnapshot.sourceSeqs] } : {}) };
173
- const application = await applyMemoryExtractionProposal(ctx, engine, run.proposal, {
174
- agent,
175
- baselines,
176
- ...(projectKey ? { projectKey } : {}),
177
- requireApproval: config.requireGlobalApproval ?? true,
178
- source,
179
- decisionCursor: memorySnapshot.cursor,
180
- scopeDecisions: state.memoryDecisions,
181
- onScopeDecision: (key, decision) => rememberMemoryDecision(state, key, decision),
182
- ...(config.recordLanguage !== undefined ? { recordLanguage: config.recordLanguage } : {}),
183
- ...(signal ? { signal } : {}),
184
- });
185
- for (const result of application.results) {
186
- emitEvolveComplete(engine.baseDir, buildEvolveCompleteEvent(result, `memory_agent:${snapshot.reason}`, sessionId), config.reviewsRetain ?? DEFAULT_REVIEWS_RETAIN);
187
- }
188
- state.memoryCheckpoint = memorySnapshot.cursor;
189
- const applied = application.results.reduce((count, result) => count + result.appliedEdits.filter((edit) => edit.applied).length, 0);
190
- const declined = application.declinedScopes.length > 0 ? `; declined scopes=${application.declinedScopes.join(",")}` : "";
191
- const durationMs = Date.now() - startedAt;
192
- for (const result of application.results) {
193
- record({
194
- sessionId,
195
- reason: snapshot.reason,
196
- turnsSinceLastReview,
197
- outcome: "applied",
198
- rationale: `memory applied (${snapshot.reason}) after ${run.turns} turn(s): ${run.proposal.rationale}${declined}`,
199
- refinementId: result.id,
200
- durationMs,
201
- memoryTurns: run.turns,
202
- memorySearches: run.searches,
203
- appliedEdits: result.appliedEdits.filter((edit) => edit.applied).length,
204
- });
205
- }
206
- if (application.declinedScopes.length > 0) {
207
- record({
208
- sessionId,
209
- reason: snapshot.reason,
210
- turnsSinceLastReview,
211
- outcome: "declined",
212
- rationale: `memory scopes declined (${snapshot.reason}): ${application.declinedScopes.join(",")}`,
213
- durationMs,
214
- memoryTurns: run.turns,
215
- memorySearches: run.searches,
216
- appliedEdits: 0,
217
- });
218
- }
219
- ctx.logger("continual-evolve").info(`memory agent applied ${applied} edit(s) across ${application.results.length} scope batch(es) [${sessionId}] after ${run.turns} turn(s)${declined}: ${run.proposal.rationale}`);
220
- // Visibility (P1 unified receipt): only applied outcomes wake the agent
221
- // with a follow-up — and only on the goal-blocked moment, which runs
222
- // mid-session; compaction and session-close stay audit-only
223
- // (reviews.jsonl) so they never wake the agent mid-compaction or after
224
- // disposal.
225
- if (config.notifyOnAutoReview && snapshot.reason === "goal_blocked" && applied > 0) {
226
- notifyMemoryExtraction(ctx, agent, {
227
- outcome: "applied",
228
- results: application.results,
229
- ...(application.declinedScopes.length > 0 ? { declinedScopes: [...application.declinedScopes] } : {}),
230
- turns: run.turns,
231
- searches: run.searches,
232
- durationMs,
233
- });
234
- }
235
- }
236
- //# sourceMappingURL=extraction-phase.js.map
package/lib/failures.d.ts DELETED
@@ -1,42 +0,0 @@
1
- export interface FailureRecord {
2
- /** When the failure was recorded (ISO). Unknown for aggregated benchmark cells without timestamps. */
3
- timestamp?: string;
4
- /** Where the failure came from: "review-gate" | "benchmark:<bid>:<caseId>". */
5
- source: string;
6
- /** Failure class (see classifyFailure). */
7
- kind: string;
8
- /** The original failure text (notes or rationale). */
9
- message: string;
10
- }
11
- export interface FailureSummary {
12
- total: number;
13
- /** Count per failure class, sorted descending (most frequent first). */
14
- byKind: Record<string, number>;
15
- /** Count per source (gate vs benchmark:bid). */
16
- bySource: Record<string, number>;
17
- }
18
- /**
19
- * Classify a failure message by prefix rules. Deterministic and additive:
20
- * unknown text falls into "other" so the summary never drops a failure.
21
- */
22
- export declare function classifyFailure(message: string): string;
23
- /** Aggregate a record list into counts. Empty input yields an all-zero summary. */
24
- export declare function summarizeFailures(records: readonly FailureRecord[]): FailureSummary;
25
- /**
26
- * Read failed review-gate records from `<baseDir>/evolve/reviews.jsonl`
27
- * (outcome === "failed"). Tolerant of a missing/corrupt file.
28
- */
29
- export declare function readReviewFailures(baseDir: string): FailureRecord[];
30
- /**
31
- * Read failed cells from every benchmark scoreboard under
32
- * `<baseDir>/evolve/benchmarks/<bid>/`. Tolerant of missing/corrupt data.
33
- */
34
- export declare function readBenchmarkFailures(baseDir: string): FailureRecord[];
35
- /** Combine both sources into one summary. */
36
- export declare function collectFailureSummary(baseDir: string): {
37
- summary: FailureSummary;
38
- records: FailureRecord[];
39
- };
40
- /** Human-readable report for the command line. */
41
- export declare function formatFailureSummary(summary: FailureSummary): string;
42
- //# sourceMappingURL=failures.d.ts.map
package/lib/failures.js DELETED
@@ -1,171 +0,0 @@
1
- /**
2
- * Failure-signature aggregation (gap R/D1 — observation stage): turn the
3
- * free-text failure records the system already produces (review-gate failures
4
- * in reviews.jsonl, failed cells in benchmark scoreboards) into a structured
5
- * count by failure CLASS. This is deliberately NOT the full failure-signature
6
- * Refiner from gap D1 — no routing, no policy — it is the data layer that
7
- * lets a later patch decide whether a given failure class recurs often enough
8
- * to deserve one.
9
- *
10
- * Classes are extracted with pure prefix rules (see classifyFailure), so the
11
- * aggregation is deterministic and unit-testable: the same failure text
12
- * always lands in the same class.
13
- */
14
- import { existsSync, readFileSync, readdirSync } from "node:fs";
15
- import { join } from "node:path";
16
- /**
17
- * Classify a failure message by prefix rules. Deterministic and additive:
18
- * unknown text falls into "other" so the summary never drops a failure.
19
- */
20
- export function classifyFailure(message) {
21
- const text = (message ?? "").trim();
22
- const lower = text.toLowerCase();
23
- if (lower.includes("rubric decrypt failed"))
24
- return "rubric-decrypt";
25
- if (lower.includes("materials changed"))
26
- return "material-drift";
27
- if (lower.includes("executor failed") || lower.includes("executor stopped"))
28
- return "executor";
29
- if (lower.includes("reviewer failed") || lower.includes("reviewer stopped"))
30
- return "reviewer";
31
- if (lower.includes("fate assessment error"))
32
- return "fate-assessor";
33
- if (lower.includes("trajectory unavailable"))
34
- return "trajectory";
35
- if (lower.includes("output budget exhausted") || lower.includes("max-tokens"))
36
- return "max-tokens";
37
- if (lower.includes("llm call aborted") || lower.includes("aborted"))
38
- return "aborted";
39
- if (lower.includes("llm call failed"))
40
- return "llm";
41
- if (lower.includes("gate error"))
42
- return "gate";
43
- if (lower.includes("casecheck") || lower.includes("case check"))
44
- return "casecheck";
45
- return "other";
46
- }
47
- /** Aggregate a record list into counts. Empty input yields an all-zero summary. */
48
- export function summarizeFailures(records) {
49
- const byKind = {};
50
- const bySource = {};
51
- for (const record of records) {
52
- byKind[record.kind] = (byKind[record.kind] ?? 0) + 1;
53
- bySource[record.source] = (bySource[record.source] ?? 0) + 1;
54
- }
55
- return {
56
- total: records.length,
57
- byKind: Object.fromEntries(Object.entries(byKind).sort((a, b) => b[1] - a[1])),
58
- bySource: Object.fromEntries(Object.entries(bySource).sort((a, b) => b[1] - a[1])),
59
- };
60
- }
61
- /**
62
- * Read failed review-gate records from `<baseDir>/evolve/reviews.jsonl`
63
- * (outcome === "failed"). Tolerant of a missing/corrupt file.
64
- */
65
- export function readReviewFailures(baseDir) {
66
- const path = join(baseDir, "evolve", "reviews.jsonl");
67
- if (!existsSync(path))
68
- return [];
69
- const records = [];
70
- try {
71
- for (const line of readFileSync(path, "utf8").split("\n")) {
72
- if (line.trim().length === 0)
73
- continue;
74
- try {
75
- const raw = JSON.parse(line);
76
- if (raw.outcome !== "failed" || !raw.rationale)
77
- continue;
78
- records.push({
79
- ...(raw.timestamp ? { timestamp: raw.timestamp } : {}),
80
- source: `review-gate${raw.reason ? `:${raw.reason}` : ""}`,
81
- kind: classifyFailure(raw.rationale),
82
- message: raw.rationale,
83
- });
84
- }
85
- catch {
86
- // skip malformed lines — the audit file must never break reporting
87
- }
88
- }
89
- }
90
- catch {
91
- return [];
92
- }
93
- return records;
94
- }
95
- /**
96
- * Read failed cells from every benchmark scoreboard under
97
- * `<baseDir>/evolve/benchmarks/<bid>/`. Tolerant of missing/corrupt data.
98
- */
99
- export function readBenchmarkFailures(baseDir) {
100
- const root = join(baseDir, "evolve", "benchmarks");
101
- if (!existsSync(root))
102
- return [];
103
- const records = [];
104
- let bids;
105
- try {
106
- bids = readdirSync(root);
107
- }
108
- catch {
109
- return [];
110
- }
111
- for (const bid of bids) {
112
- const boardPath = join(root, bid, "scoreboard.json");
113
- if (!existsSync(boardPath))
114
- continue;
115
- let board;
116
- try {
117
- board = JSON.parse(readFileSync(boardPath, "utf8"));
118
- }
119
- catch {
120
- continue;
121
- }
122
- const cellLists = [];
123
- if (board.reference?.cells)
124
- cellLists.push({ label: "reference", cells: board.reference.cells });
125
- for (const entry of board.candidates ?? []) {
126
- if (entry.cells)
127
- cellLists.push({ label: entry.label ?? "candidate", cells: entry.cells });
128
- }
129
- for (const list of cellLists) {
130
- for (const cell of list.cells) {
131
- const c = cell;
132
- if (c.status !== "failed" || !c.notes)
133
- continue;
134
- records.push({
135
- source: `benchmark:${bid}:${c.caseId ?? "?"}`,
136
- kind: classifyFailure(c.notes),
137
- message: c.notes,
138
- });
139
- }
140
- }
141
- }
142
- return records;
143
- }
144
- /** Combine both sources into one summary. */
145
- export function collectFailureSummary(baseDir) {
146
- const records = [...readReviewFailures(baseDir), ...readBenchmarkFailures(baseDir)];
147
- return { summary: summarizeFailures(records), records };
148
- }
149
- /** Human-readable report for the command line. */
150
- export function formatFailureSummary(summary) {
151
- const lines = [`failure summary: ${summary.total} total`];
152
- const kinds = Object.entries(summary.byKind);
153
- if (kinds.length > 0) {
154
- lines.push("by class:");
155
- for (const [kind, count] of kinds) {
156
- lines.push(` ${kind}: ${count}`);
157
- }
158
- }
159
- else {
160
- lines.push("by class: (none)");
161
- }
162
- const sources = Object.entries(summary.bySource);
163
- if (sources.length > 0) {
164
- lines.push("by source:");
165
- for (const [source, count] of sources) {
166
- lines.push(` ${source}: ${count}`);
167
- }
168
- }
169
- return lines.join("\n");
170
- }
171
- //# sourceMappingURL=failures.js.map
package/lib/fate.d.ts DELETED
@@ -1,130 +0,0 @@
1
- /**
2
- * Gate local-fate dimension (#11 P2): the automatic review gate also decides
3
- * what happens to a session's local entries while the session is STILL
4
- * running — not only at wrap-up.
5
- *
6
- * Local entries default to orphans: a later session (not on the parentSession
7
- * chain) never sees them, and nothing promotes or archives them — the
8
- * exploration results effectively "die" with the session. This module gives
9
- * the gate its own fate cadence on top of the existing review:
10
- *
11
- * - `promote` → the entry moves into the global store. The global store is a
12
- * governed resource: the user is consulted FIRST (the consultSkillEdits
13
- * pattern — never written silently), with a cooldown after a decline.
14
- * - `archive` → hidden from injection (data stays restorable). Archives that
15
- * would bury possibly-reusable content (not covered globally AND distilled
16
- * from real user messages) ask the user first; covered/operational entries
17
- * archive silently, exactly like the wrap-up command does.
18
- * - `keep` → nothing.
19
- *
20
- * At compaction the gate NEVER opens a dialog (the agent is mid-compaction):
21
- * only deterministic silent archives apply, everything governed is deferred
22
- * with an audit record pointing at `/evolve wrapup`.
23
- *
24
- * Division of labor is the same as wrap-up: the mechanical audit proposes
25
- * (listLocalCandidates + coverage guards), the LLM classifies
26
- * (assessLocalEntries), the user approves, the code applies deterministically
27
- * (wholePromoteProposals / splitPromoteProposals — the SAME edits the wrap-up
28
- * command applies). Every decision lands in reviews.jsonl via the gate's
29
- * record callback.
30
- */
31
- import type { Context } from "@deepseek-ai/cordis";
32
- import type { Agent } from "@deepseek-ai/dsh-agent";
33
- import type { HarnessState, RefinementResult } from "./types.js";
34
- import type { EvolutionEngine } from "./service.js";
35
- import type { AutoRefineReason } from "./review.js";
36
- import type { AutoReviewConfig, GateState, ReviewRecord } from "./auto.js";
37
- import { type PromotionPolicy } from "./promotion.js";
38
- import { type RecordLanguage } from "./record-language.js";
39
- import { type WrapupCandidate, type WrapupItem } from "./wrapup.js";
40
- /** Turns a declined local-fate proposal stays silent before being offered again. */
41
- export declare const FATE_CONSULT_COOLDOWN_TURNS = 10;
42
- /** What the gate decided to do with the session's local entries. */
43
- export interface FatePlan {
44
- candidates: readonly WrapupCandidate[];
45
- /** Whole promotions that passed the deterministic global-coverage guard. */
46
- promotable: WrapupItem[];
47
- /** Split promotions (archive + cleaned promote payload) that passed the guard. */
48
- splits: {
49
- item: WrapupItem;
50
- candidate: WrapupCandidate;
51
- }[];
52
- /** Archives that may proceed silently (covered globally / no real distillation source). */
53
- silentArchives: WrapupItem[];
54
- /** Archives that must ask the user first (uncovered + real source). */
55
- reviewArchives: WrapupItem[];
56
- /** Promotes blocked by the deterministic guard, with why. */
57
- skipped: {
58
- key: string;
59
- reason: string;
60
- }[];
61
- /** Split promotions blocked by the deterministic guard, with why. */
62
- splitSkipped: {
63
- key: string;
64
- reason: string;
65
- }[];
66
- }
67
- /**
68
- * Partition an assessed wrap-up classification into concrete fate actions,
69
- * re-running the deterministic guards against the LIVE global store (state
70
- * may have changed while the LLM call was in flight). Pure and unit-tested;
71
- * mirrors the partition step of the wrap-up command.
72
- */
73
- export declare function planLocalFates(items: readonly WrapupItem[], candidates: readonly WrapupCandidate[], globalState: HarnessState, policy?: PromotionPolicy): FatePlan;
74
- /**
75
- * The cooldown key of a candidate set: the sorted `kind:id` list. The set is
76
- * the unit of consultation — a declined proposal is not offered again within
77
- * the cooldown window, and a changed set (new entries appeared) starts a
78
- * fresh consultation.
79
- */
80
- export declare function fateSetKey(candidates: readonly WrapupCandidate[]): string;
81
- /**
82
- * Whether the local-fate dimension is due for this gate run. Turn-interval
83
- * gates respect the fate cadence (an independent counter — goal-driven
84
- * sessions run the review EVERY round, the fate assessment must not);
85
- * compaction is unconditional: experiences about to be summarized away get
86
- * their fate check regardless. Goal-blocked assessments are unconditional
87
- * here too — the gate's own streak counter (auto.ts runGoalBlockedFate)
88
- * already gates their frequency, so the cadence must not re-block them.
89
- */
90
- export declare function fateCadenceDue(state: GateState, reason: AutoRefineReason, intervalTurns: number): boolean;
91
- export interface FateConsultResult {
92
- approved: boolean;
93
- asked: boolean;
94
- reason: "nothing-to-ask" | "consented" | "declined" | "cooldown" | "unavailable" | "error";
95
- }
96
- /**
97
- * Ask the user whether to execute the gate's local-fate proposal. ONE dialog
98
- * covers every governed action (promotes, split promotions, review-required
99
- * archives) — the gate never spams questions. Conservative on every edge:
100
- * no question service → not approved; the question call fails → not approved;
101
- * the same candidate set was declined within the cooldown → not asked again.
102
- * A decline records the cooldown (the consultSkillEdits pattern).
103
- */
104
- export declare function consultLocalFates(ctx: Context, agent: Agent, plan: FatePlan, gate: GateState, language?: RecordLanguage): Promise<FateConsultResult>;
105
- export type FateApplyMode = "full" | "silent-only";
106
- export interface FateApplyResult {
107
- /** Human-readable lines of what was applied (for the notice and the audit record). */
108
- applied: string[];
109
- /** Every refinement result produced, for rollback discovery. */
110
- results: RefinementResult[];
111
- }
112
- /**
113
- * Deterministically apply the fate plan. `"full"` applies everything
114
- * (promotes, splits, silent + review archives); `"silent-only"` applies only
115
- * the deterministic silent archives (the compaction path — nothing governed).
116
- * Promotes go through the SAME proposals as the wrap-up command
117
- * (wholePromoteProposals / splitPromoteProposals), so both paths write
118
- * identical global entries and local retirement stamps.
119
- */
120
- export declare function applyLocalFates(engine: EvolutionEngine, sessionId: string, plan: FatePlan, localState: HarnessState, mode: FateApplyMode): FateApplyResult;
121
- /**
122
- * The gate's local-fate phase. Runs after the review phase on every gate
123
- * trigger (turn_snapshot / compact), subject to cadence and cooldown. All
124
- * failures are contained and recorded — a broken fate dimension never
125
- * disturbs the agent loop.
126
- */
127
- export declare function runLocalFatePhase(ctx: Context, engine: EvolutionEngine, agent: Agent, config: AutoReviewConfig, state: GateState, reason: AutoRefineReason, record: (entry: Omit<ReviewRecord, "timestamp">) => void): Promise<void>;
128
- /** The user-visible notice after the gate applied local-fate actions. */
129
- export declare function buildFateNotice(applied: readonly string[]): string;
130
- //# sourceMappingURL=fate.d.ts.map