dsh-continual-evolve 0.1.1 → 0.3.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 +172 -17
- package/README.zh.md +87 -10
- package/lib/apply.js +3 -1
- package/lib/approval.d.ts +25 -0
- package/lib/approval.js +9 -1
- package/lib/auto.d.ts +99 -5
- package/lib/auto.js +165 -6
- package/lib/benchmark-command.d.ts +9 -0
- package/lib/benchmark-command.js +331 -0
- package/lib/benchmark.d.ts +84 -0
- package/lib/benchmark.js +107 -1
- package/lib/command.js +33 -221
- package/lib/evaluate.d.ts +43 -7
- package/lib/evaluate.js +172 -43
- package/lib/evolve-event.d.ts +38 -0
- package/lib/evolve-event.js +49 -0
- package/lib/failures.d.ts +39 -0
- package/lib/failures.js +170 -0
- package/lib/fate.d.ts +128 -0
- package/lib/fate.js +342 -0
- package/lib/goal-command.d.ts +7 -0
- package/lib/goal-command.js +37 -0
- package/lib/index.d.ts +51 -21
- package/lib/index.js +32 -2
- package/lib/inject.d.ts +8 -0
- package/lib/inject.js +51 -4
- package/lib/llm-text.d.ts +30 -0
- package/lib/llm-text.js +49 -0
- package/lib/mount-command.d.ts +10 -0
- package/lib/mount-command.js +48 -0
- package/lib/mount.js +5 -0
- package/lib/plan.js +5 -0
- package/lib/planner.d.ts +8 -1
- package/lib/planner.js +40 -39
- package/lib/render.d.ts +1 -3
- package/lib/render.js +2 -5
- package/lib/review.d.ts +5 -2
- package/lib/review.js +27 -38
- package/lib/rollback.d.ts +1 -3
- package/lib/rollback.js +0 -8
- package/lib/score.d.ts +37 -4
- package/lib/score.js +120 -10
- package/lib/service.d.ts +2 -2
- package/lib/service.js +5 -2
- package/lib/skill-render.d.ts +15 -0
- package/lib/skill-render.js +30 -0
- package/lib/skill.d.ts +12 -7
- package/lib/skill.js +36 -31
- package/lib/skillquality.d.ts +80 -0
- package/lib/skillquality.js +311 -0
- package/lib/store.d.ts +1 -3
- package/lib/store.js +0 -7
- package/lib/tool.js +28 -4
- package/lib/types.d.ts +39 -0
- package/lib/types.js +19 -0
- package/lib/usage.d.ts +32 -0
- package/lib/usage.js +84 -0
- package/lib/validate.d.ts +12 -2
- package/lib/validate.js +51 -2
- package/lib/wrapup-command.d.ts +8 -0
- package/lib/wrapup-command.js +211 -0
- package/lib/wrapup.d.ts +215 -0
- package/lib/wrapup.js +427 -0
- package/package.json +8 -8
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { RefinementResult } from "./types.js";
|
|
2
|
+
/** The structured event payload emitted after every successful refinement. */
|
|
3
|
+
export interface EvolveCompleteEvent {
|
|
4
|
+
/** Event discriminator for consumers. */
|
|
5
|
+
type: "evolve_complete";
|
|
6
|
+
/** The refinement id (same as the result). */
|
|
7
|
+
refinementId: string;
|
|
8
|
+
/** One-line summary of the refinement. */
|
|
9
|
+
summary: string;
|
|
10
|
+
/** Number of edits that were actually applied. */
|
|
11
|
+
appliedEdits: number;
|
|
12
|
+
/** Number of edits that failed to apply. */
|
|
13
|
+
failedEdits: number;
|
|
14
|
+
/** Scope of the refinement ("local" or "global"). */
|
|
15
|
+
scope: string;
|
|
16
|
+
/** What triggered this refinement (e.g. "auto_review", "manual_plan", "manual_tool"). */
|
|
17
|
+
trigger: string;
|
|
18
|
+
/** Session id that owns the refinement (auto or manual). */
|
|
19
|
+
sessionId: string;
|
|
20
|
+
/** ISO timestamp. */
|
|
21
|
+
timestamp: string;
|
|
22
|
+
/** Per-edit summaries (kind + id + action) for consumers that want detail. */
|
|
23
|
+
edits: {
|
|
24
|
+
action: string;
|
|
25
|
+
kind: string;
|
|
26
|
+
id: string;
|
|
27
|
+
applied: boolean;
|
|
28
|
+
}[];
|
|
29
|
+
}
|
|
30
|
+
/** Build a structured evolve-complete event from a refinement result. */
|
|
31
|
+
export declare function buildEvolveCompleteEvent(result: RefinementResult, trigger: string, sessionId: string): EvolveCompleteEvent;
|
|
32
|
+
/**
|
|
33
|
+
* Emit an evolve_complete event to the reviews.jsonl audit trail. The event
|
|
34
|
+
* is JSONL-formatted (one line) so consumers can tail and parse it. This is
|
|
35
|
+
* a best-effort write — failure never blocks the refinement path.
|
|
36
|
+
*/
|
|
37
|
+
export declare function emitEvolveComplete(baseDir: string, event: EvolveCompleteEvent): void;
|
|
38
|
+
//# sourceMappingURL=evolve-event.d.ts.map
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structured "evolve complete" events (gap C4): a durable, machine-readable
|
|
3
|
+
* record emitted after every successful refinement application — whether
|
|
4
|
+
* auto-gated or manual. Third-party consumers can observe these events via
|
|
5
|
+
* the plugin log (JSONL) and the reviews.jsonl audit trail.
|
|
6
|
+
*
|
|
7
|
+
* Design: prime-agent `/refine` emits `refine_complete{id,summary,
|
|
8
|
+
* appliedEdits,scope}` extension events. We follow the same pattern with
|
|
9
|
+
* added provenance (trigger, source) so consumers know WHY the refinement
|
|
10
|
+
* happened and WHO initiated it.
|
|
11
|
+
*/
|
|
12
|
+
import { appendFileSync, mkdirSync } from "node:fs";
|
|
13
|
+
import { join } from "node:path";
|
|
14
|
+
/** Build a structured evolve-complete event from a refinement result. */
|
|
15
|
+
export function buildEvolveCompleteEvent(result, trigger, sessionId) {
|
|
16
|
+
return {
|
|
17
|
+
type: "evolve_complete",
|
|
18
|
+
refinementId: result.id,
|
|
19
|
+
summary: result.summary,
|
|
20
|
+
appliedEdits: result.appliedEdits.filter((e) => e.applied).length,
|
|
21
|
+
failedEdits: result.appliedEdits.filter((e) => !e.applied).length,
|
|
22
|
+
scope: result.scope ?? "local",
|
|
23
|
+
trigger,
|
|
24
|
+
sessionId,
|
|
25
|
+
timestamp: new Date().toISOString(),
|
|
26
|
+
edits: result.appliedEdits.map((e) => ({
|
|
27
|
+
action: e.action,
|
|
28
|
+
kind: e.kind,
|
|
29
|
+
id: e.id,
|
|
30
|
+
applied: e.applied,
|
|
31
|
+
})),
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Emit an evolve_complete event to the reviews.jsonl audit trail. The event
|
|
36
|
+
* is JSONL-formatted (one line) so consumers can tail and parse it. This is
|
|
37
|
+
* a best-effort write — failure never blocks the refinement path.
|
|
38
|
+
*/
|
|
39
|
+
export function emitEvolveComplete(baseDir, event) {
|
|
40
|
+
try {
|
|
41
|
+
const dir = join(baseDir, "evolve");
|
|
42
|
+
mkdirSync(dir, { recursive: true });
|
|
43
|
+
appendFileSync(join(dir, "reviews.jsonl"), `${JSON.stringify(event)}\n`, "utf8");
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
// Event emission is diagnostic; never interrupt the refinement path.
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=evolve-event.js.map
|
|
@@ -0,0 +1,39 @@
|
|
|
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): FailureSummary;
|
|
37
|
+
/** Human-readable report for the command line. */
|
|
38
|
+
export declare function formatFailureSummary(summary: FailureSummary): string;
|
|
39
|
+
//# sourceMappingURL=failures.d.ts.map
|
package/lib/failures.js
ADDED
|
@@ -0,0 +1,170 @@
|
|
|
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
|
+
return summarizeFailures([...readReviewFailures(baseDir), ...readBenchmarkFailures(baseDir)]);
|
|
147
|
+
}
|
|
148
|
+
/** Human-readable report for the command line. */
|
|
149
|
+
export function formatFailureSummary(summary) {
|
|
150
|
+
const lines = [`failure summary: ${summary.total} total`];
|
|
151
|
+
const kinds = Object.entries(summary.byKind);
|
|
152
|
+
if (kinds.length > 0) {
|
|
153
|
+
lines.push("by class:");
|
|
154
|
+
for (const [kind, count] of kinds) {
|
|
155
|
+
lines.push(` ${kind}: ${count}`);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
else {
|
|
159
|
+
lines.push("by class: (none)");
|
|
160
|
+
}
|
|
161
|
+
const sources = Object.entries(summary.bySource);
|
|
162
|
+
if (sources.length > 0) {
|
|
163
|
+
lines.push("by source:");
|
|
164
|
+
for (const [source, count] of sources) {
|
|
165
|
+
lines.push(` ${source}: ${count}`);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return lines.join("\n");
|
|
169
|
+
}
|
|
170
|
+
//# sourceMappingURL=failures.js.map
|
package/lib/fate.d.ts
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
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 WrapupCandidate, type WrapupItem } from "./wrapup.js";
|
|
38
|
+
/** Turns a declined local-fate proposal stays silent before being offered again. */
|
|
39
|
+
export declare const FATE_CONSULT_COOLDOWN_TURNS = 10;
|
|
40
|
+
/** What the gate decided to do with the session's local entries. */
|
|
41
|
+
export interface FatePlan {
|
|
42
|
+
candidates: readonly WrapupCandidate[];
|
|
43
|
+
/** Whole promotions that passed the deterministic global-coverage guard. */
|
|
44
|
+
promotable: WrapupItem[];
|
|
45
|
+
/** Split promotions (archive + cleaned promote payload) that passed the guard. */
|
|
46
|
+
splits: {
|
|
47
|
+
item: WrapupItem;
|
|
48
|
+
candidate: WrapupCandidate;
|
|
49
|
+
}[];
|
|
50
|
+
/** Archives that may proceed silently (covered globally / no real distillation source). */
|
|
51
|
+
silentArchives: WrapupItem[];
|
|
52
|
+
/** Archives that must ask the user first (uncovered + real source). */
|
|
53
|
+
reviewArchives: WrapupItem[];
|
|
54
|
+
/** Promotes blocked by the deterministic guard, with why. */
|
|
55
|
+
skipped: {
|
|
56
|
+
key: string;
|
|
57
|
+
reason: string;
|
|
58
|
+
}[];
|
|
59
|
+
/** Split promotions blocked by the deterministic guard, with why. */
|
|
60
|
+
splitSkipped: {
|
|
61
|
+
key: string;
|
|
62
|
+
reason: string;
|
|
63
|
+
}[];
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Partition an assessed wrap-up classification into concrete fate actions,
|
|
67
|
+
* re-running the deterministic guards against the LIVE global store (state
|
|
68
|
+
* may have changed while the LLM call was in flight). Pure and unit-tested;
|
|
69
|
+
* mirrors the partition step of the wrap-up command.
|
|
70
|
+
*/
|
|
71
|
+
export declare function planLocalFates(items: readonly WrapupItem[], candidates: readonly WrapupCandidate[], globalState: HarnessState): FatePlan;
|
|
72
|
+
/**
|
|
73
|
+
* The cooldown key of a candidate set: the sorted `kind:id` list. The set is
|
|
74
|
+
* the unit of consultation — a declined proposal is not offered again within
|
|
75
|
+
* the cooldown window, and a changed set (new entries appeared) starts a
|
|
76
|
+
* fresh consultation.
|
|
77
|
+
*/
|
|
78
|
+
export declare function fateSetKey(candidates: readonly WrapupCandidate[]): string;
|
|
79
|
+
/**
|
|
80
|
+
* Whether the local-fate dimension is due for this gate run. Turn-interval
|
|
81
|
+
* gates respect the fate cadence (an independent counter — goal-driven
|
|
82
|
+
* sessions run the review EVERY round, the fate assessment must not);
|
|
83
|
+
* compaction is unconditional: experiences about to be summarized away get
|
|
84
|
+
* their fate check regardless. Goal-blocked assessments are unconditional
|
|
85
|
+
* here too — the gate's own streak counter (auto.ts runGoalBlockedFate)
|
|
86
|
+
* already gates their frequency, so the cadence must not re-block them.
|
|
87
|
+
*/
|
|
88
|
+
export declare function fateCadenceDue(state: GateState, reason: AutoRefineReason, intervalTurns: number): boolean;
|
|
89
|
+
export interface FateConsultResult {
|
|
90
|
+
approved: boolean;
|
|
91
|
+
asked: boolean;
|
|
92
|
+
reason: "nothing-to-ask" | "consented" | "declined" | "cooldown" | "unavailable" | "error";
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Ask the user whether to execute the gate's local-fate proposal. ONE dialog
|
|
96
|
+
* covers every governed action (promotes, split promotions, review-required
|
|
97
|
+
* archives) — the gate never spams questions. Conservative on every edge:
|
|
98
|
+
* no question service → not approved; the question call fails → not approved;
|
|
99
|
+
* the same candidate set was declined within the cooldown → not asked again.
|
|
100
|
+
* A decline records the cooldown (the consultSkillEdits pattern).
|
|
101
|
+
*/
|
|
102
|
+
export declare function consultLocalFates(ctx: Context, agent: Agent, plan: FatePlan, gate: GateState): Promise<FateConsultResult>;
|
|
103
|
+
export type FateApplyMode = "full" | "silent-only";
|
|
104
|
+
export interface FateApplyResult {
|
|
105
|
+
/** Human-readable lines of what was applied (for the notice and the audit record). */
|
|
106
|
+
applied: string[];
|
|
107
|
+
/** Every refinement result produced, for rollback discovery. */
|
|
108
|
+
results: RefinementResult[];
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Deterministically apply the fate plan. `"full"` applies everything
|
|
112
|
+
* (promotes, splits, silent + review archives); `"silent-only"` applies only
|
|
113
|
+
* the deterministic silent archives (the compaction path — nothing governed).
|
|
114
|
+
* Promotes go through the SAME proposals as the wrap-up command
|
|
115
|
+
* (wholePromoteProposals / splitPromoteProposals), so both paths write
|
|
116
|
+
* identical global entries and local retirement stamps.
|
|
117
|
+
*/
|
|
118
|
+
export declare function applyLocalFates(engine: EvolutionEngine, sessionId: string, plan: FatePlan, localState: HarnessState, mode: FateApplyMode): FateApplyResult;
|
|
119
|
+
/**
|
|
120
|
+
* The gate's local-fate phase. Runs after the review phase on every gate
|
|
121
|
+
* trigger (turn_interval / compact), subject to cadence and cooldown. All
|
|
122
|
+
* failures are contained and recorded — a broken fate dimension never
|
|
123
|
+
* disturbs the agent loop.
|
|
124
|
+
*/
|
|
125
|
+
export declare function runLocalFatePhase(ctx: Context, engine: EvolutionEngine, agent: Agent, config: AutoReviewConfig, state: GateState, reason: AutoRefineReason, record: (entry: Omit<ReviewRecord, "timestamp">) => void): Promise<void>;
|
|
126
|
+
/** The user-visible notice after the gate applied local-fate actions. */
|
|
127
|
+
export declare function buildFateNotice(applied: readonly string[]): string;
|
|
128
|
+
//# sourceMappingURL=fate.d.ts.map
|