dsh-continual-evolve 0.11.0 → 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 -115
- package/README.zh.md +26 -93
- package/cordis.patch.yml +2 -2
- package/lib/index.d.ts +44 -225
- package/lib/index.js +30 -239
- 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 -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/extraction-phase.js
DELETED
|
@@ -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
|