dsh-continual-evolve 0.10.6 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (126) hide show
  1. package/README.md +35 -122
  2. package/README.zh.md +26 -97
  3. package/cordis.patch.yml +2 -2
  4. package/lib/index.d.ts +44 -241
  5. package/lib/index.js +30 -253
  6. package/lib/memory-guide.d.ts +13 -14
  7. package/lib/memory-guide.js +32 -21
  8. package/lib/memory-section.d.ts +77 -0
  9. package/lib/memory-section.js +189 -0
  10. package/package.json +2 -17
  11. package/lib/apply.d.ts +0 -22
  12. package/lib/apply.js +0 -157
  13. package/lib/approval.d.ts +0 -72
  14. package/lib/approval.js +0 -127
  15. package/lib/auto.d.ts +0 -236
  16. package/lib/auto.js +0 -773
  17. package/lib/autocase.d.ts +0 -38
  18. package/lib/autocase.js +0 -76
  19. package/lib/benchmark-command.d.ts +0 -9
  20. package/lib/benchmark-command.js +0 -357
  21. package/lib/benchmark.d.ts +0 -151
  22. package/lib/benchmark.js +0 -250
  23. package/lib/command.d.ts +0 -49
  24. package/lib/command.js +0 -568
  25. package/lib/consolidate.d.ts +0 -90
  26. package/lib/consolidate.js +0 -180
  27. package/lib/copy.d.ts +0 -74
  28. package/lib/copy.js +0 -160
  29. package/lib/declines.d.ts +0 -101
  30. package/lib/declines.js +0 -175
  31. package/lib/evaluate.d.ts +0 -74
  32. package/lib/evaluate.js +0 -271
  33. package/lib/evolve-event.d.ts +0 -40
  34. package/lib/evolve-event.js +0 -59
  35. package/lib/failures.d.ts +0 -42
  36. package/lib/failures.js +0 -171
  37. package/lib/fate.d.ts +0 -130
  38. package/lib/fate.js +0 -350
  39. package/lib/goal-command.d.ts +0 -7
  40. package/lib/goal-command.js +0 -37
  41. package/lib/goal.d.ts +0 -72
  42. package/lib/goal.js +0 -72
  43. package/lib/inject.d.ts +0 -196
  44. package/lib/inject.js +0 -412
  45. package/lib/llm-text.d.ts +0 -104
  46. package/lib/llm-text.js +0 -140
  47. package/lib/logfile.d.ts +0 -71
  48. package/lib/logfile.js +0 -159
  49. package/lib/memory-agent.d.ts +0 -129
  50. package/lib/memory-agent.js +0 -568
  51. package/lib/memory-benchmark.d.ts +0 -76
  52. package/lib/memory-benchmark.js +0 -170
  53. package/lib/memory-index.d.ts +0 -114
  54. package/lib/memory-index.js +0 -197
  55. package/lib/message-source.d.ts +0 -23
  56. package/lib/message-source.js +0 -12
  57. package/lib/mount-command.d.ts +0 -10
  58. package/lib/mount-command.js +0 -48
  59. package/lib/mount.d.ts +0 -42
  60. package/lib/mount.js +0 -212
  61. package/lib/notify.d.ts +0 -59
  62. package/lib/notify.js +0 -97
  63. package/lib/plan.d.ts +0 -16
  64. package/lib/plan.js +0 -126
  65. package/lib/planner.d.ts +0 -61
  66. package/lib/planner.js +0 -144
  67. package/lib/pool.d.ts +0 -7
  68. package/lib/pool.js +0 -25
  69. package/lib/prefix-cache.d.ts +0 -78
  70. package/lib/prefix-cache.js +0 -168
  71. package/lib/project.d.ts +0 -28
  72. package/lib/project.js +0 -66
  73. package/lib/projection.d.ts +0 -22
  74. package/lib/projection.js +0 -112
  75. package/lib/promotion.d.ts +0 -94
  76. package/lib/promotion.js +0 -171
  77. package/lib/recall.d.ts +0 -85
  78. package/lib/recall.js +0 -142
  79. package/lib/record-language.d.ts +0 -85
  80. package/lib/record-language.js +0 -152
  81. package/lib/render.d.ts +0 -13
  82. package/lib/render.js +0 -80
  83. package/lib/review-scheduler.d.ts +0 -38
  84. package/lib/review-scheduler.js +0 -191
  85. package/lib/review.d.ts +0 -60
  86. package/lib/review.js +0 -145
  87. package/lib/rollback.d.ts +0 -9
  88. package/lib/rollback.js +0 -68
  89. package/lib/rubric.d.ts +0 -29
  90. package/lib/rubric.js +0 -119
  91. package/lib/runtime.d.ts +0 -24
  92. package/lib/runtime.js +0 -87
  93. package/lib/score.d.ts +0 -64
  94. package/lib/score.js +0 -191
  95. package/lib/search.d.ts +0 -83
  96. package/lib/search.js +0 -136
  97. package/lib/service.d.ts +0 -49
  98. package/lib/service.js +0 -180
  99. package/lib/skill-render.d.ts +0 -23
  100. package/lib/skill-render.js +0 -69
  101. package/lib/skill.d.ts +0 -15
  102. package/lib/skill.js +0 -80
  103. package/lib/skillquality.d.ts +0 -85
  104. package/lib/skillquality.js +0 -320
  105. package/lib/source.d.ts +0 -30
  106. package/lib/source.js +0 -43
  107. package/lib/state.d.ts +0 -26
  108. package/lib/state.js +0 -151
  109. package/lib/store.d.ts +0 -62
  110. package/lib/store.js +0 -164
  111. package/lib/token-usage.d.ts +0 -68
  112. package/lib/token-usage.js +0 -211
  113. package/lib/tool.d.ts +0 -34
  114. package/lib/tool.js +0 -320
  115. package/lib/turn-snapshot.d.ts +0 -72
  116. package/lib/turn-snapshot.js +0 -238
  117. package/lib/types.d.ts +0 -208
  118. package/lib/types.js +0 -127
  119. package/lib/usage.d.ts +0 -36
  120. package/lib/usage.js +0 -98
  121. package/lib/validate.d.ts +0 -28
  122. package/lib/validate.js +0 -181
  123. package/lib/wrapup-command.d.ts +0 -10
  124. package/lib/wrapup-command.js +0 -243
  125. package/lib/wrapup.d.ts +0 -260
  126. package/lib/wrapup.js +0 -539
package/lib/mount.js DELETED
@@ -1,212 +0,0 @@
1
- /**
2
- * Hot mounting: materialize a skill-kind harness entry as a real cordis
3
- * plugin (v2 optional item, design.md §6) and load it into the live loader
4
- * tree — no process restart. The generated plugin registers a model-facing
5
- * tool that carries the skill's description, argument contract, and python
6
- * reference; the `skill` tool remains the way to actually execute it.
7
- *
8
- * Layout under `<baseDir>/evolve/mounted/`:
9
- * index.json mount ledger (id -> path, restored at boot)
10
- * <skillName>/package.json plugin manifest (ESM, main index.js)
11
- * <skillName>/index.js generated plugin: registers one tool
12
- *
13
- * The generated plugin imports nothing outside node builtins, so the mount
14
- * directory needs no node_modules of its own.
15
- */
16
- import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
17
- import { join } from "node:path";
18
- import { skillNameOf } from "./skill.js";
19
- import { secretLeakReason } from "./promotion.js";
20
- export function mountedDir(baseDir) {
21
- return join(baseDir, "evolve", "mounted");
22
- }
23
- export function ledgerPath(baseDir) {
24
- return join(mountedDir(baseDir), "index.json");
25
- }
26
- export function loadLedger(baseDir) {
27
- const path = ledgerPath(baseDir);
28
- if (!existsSync(path)) {
29
- return { mounted: [] };
30
- }
31
- try {
32
- const raw = JSON.parse(readFileSync(path, "utf8"));
33
- return { mounted: Array.isArray(raw.mounted) ? raw.mounted : [] };
34
- }
35
- catch {
36
- return { mounted: [] };
37
- }
38
- }
39
- export function saveLedger(baseDir, ledger) {
40
- mkdirSync(mountedDir(baseDir), { recursive: true });
41
- writeFileSync(ledgerPath(baseDir), `${JSON.stringify(ledger, null, 2)}\n`, "utf8");
42
- }
43
- /** Generate the plugin package files for one skill entry; returns the package dir. */
44
- export function renderMountPackage(baseDir, entry) {
45
- // Secret-leak quarantine: the entry content AND its reference contract are
46
- // embedded verbatim into the generated index.js, so a credential in either
47
- // would be written to disk (and executed in-process). Block before ANY
48
- // file is created (review audit 2026-08-28 B2: reference was unscreened).
49
- const secret = secretLeakReason([entry.title, entry.content, JSON.stringify(entry.reference ?? {}), JSON.stringify(entry.arguments ?? {})].join("\n"));
50
- if (secret) {
51
- throw new Error(`mount blocked: ${secret}`);
52
- }
53
- const dir = join(mountedDir(baseDir), skillNameOf(entry.id));
54
- mkdirSync(dir, { recursive: true });
55
- const toolName = `skill_${skillNameOf(entry.id)}`;
56
- writeFileSync(join(dir, "package.json"), `{\n "name": "evolve-skill-${skillNameOf(entry.id)}",\n "version": "1.0.0",\n "main": "index.js",\n "type": "module"\n}\n`, "utf8");
57
- writeFileSync(join(dir, "index.js"), renderPluginSource(toolName, entry), "utf8");
58
- return dir;
59
- }
60
- /** The generated plugin source: one tool registration, no external imports. */
61
- export function renderPluginSource(toolName, entry) {
62
- const description = `${oneLine(entry.title)}. ${oneLine(entry.content)}`.slice(0, 400);
63
- const parameters = renderParameters(entry);
64
- const reference = JSON.stringify(entry.reference ?? {}, null, 2);
65
- const entryId = JSON.stringify(entry.id);
66
- const entryVersion = JSON.stringify(entry.version);
67
- const entryTitle = JSON.stringify(oneLine(entry.title));
68
- const skillName = JSON.stringify(skillNameOf(entry.id));
69
- return `/**
70
- * Generated by dsh-continual-evolve mount (${entry.id} v${entry.version}).
71
- * Hot-mounted into the live loader tree; edit the harness entry and remount
72
- * to regenerate. Execute the skill through the \`skill\` tool — this plugin
73
- * exposes its contract to the model.
74
- */
75
- export const name = "evolve-skill-${skillNameOf(entry.id)}";
76
-
77
- // Cordis requires declared service access; without this, \`ctx.tools\` throws
78
- // "cannot get property \\"tools\\" without inject".
79
- export const inject = ["tools"];
80
-
81
- export function apply(ctx) {
82
- ctx.tools.register({
83
- name: ${JSON.stringify(toolName)},
84
- description: ${JSON.stringify(description)},
85
- parameters: ${JSON.stringify(parameters, null, 2)},
86
- output: {
87
- // No \`required\` on the string property: the direct-register value-schema
88
- // path rejects it ("required is not supported on type string").
89
- schema: { type: "object", additionalProperties: false, properties: { text: { type: "string" } } },
90
- render: (_args, value) => [{ type: "text", text: value?.text ?? "" }],
91
- },
92
- execute: async (args) => ({
93
- text: "Mounted skill " + ${entryId} + " (v" + ${entryVersion} + ").\\n" +
94
- "Description: " + ${entryTitle} + "\\n" +
95
- "Argument contract: " + JSON.stringify(args ?? {}) + "\\n" +
96
- "Python reference: " + ${JSON.stringify(reference)} + "\\n" +
97
- "Execute via the \`skill\` tool (skill name " + ${skillName} + ").",
98
- }),
99
- });
100
- }
101
- `;
102
- }
103
- /**
104
- * Map a skill entry's arguments contract to the tool-parameter schema.
105
- * Requiredness goes in a root-level `required` array (valid JSON Schema):
106
- * the mounted plugin registers via raw `ctx.tools.register`, and dsh-llm
107
- * sends `parameters` verbatim to the API, which rejects per-property
108
- * `required: true` ("true is not of type array").
109
- */
110
- export function renderParameters(entry) {
111
- const contract = entry.arguments ?? {};
112
- const properties = {};
113
- const required = [];
114
- for (const [key, spec] of Object.entries(contract)) {
115
- const record = typeof spec === "object" && spec !== null && !Array.isArray(spec) ? spec : {};
116
- const type = typeof record["type"] === "string" ? record["type"] : "string";
117
- const description = typeof record["description"] === "string" ? record["description"] : key;
118
- properties[key] = { type, description };
119
- if (record["required"] === true) {
120
- required.push(key);
121
- }
122
- }
123
- return {
124
- type: "object",
125
- additionalProperties: false,
126
- properties,
127
- ...(required.length > 0 ? { required } : {}),
128
- };
129
- }
130
- /**
131
- * Mount one skill entry into the live loader. Requires the `loader` service
132
- * (resolved lazily, see FAQ #1); without it the package is still written and
133
- * the ledger records the entry for the next boot.
134
- */
135
- export async function mountSkill(ctx, baseDir, entry) {
136
- // Guidance skills are SKILL.md documents with no python reference — there
137
- // is no function to mount as a tool; only executable skills can hot-mount.
138
- if (entry.skill_kind === "guidance" || Object.keys(entry.reference ?? {}).length === 0) {
139
- throw new Error(`skill ${entry.id} has no python reference (guidance skills cannot be mounted — load them with the skill tool instead)`);
140
- }
141
- const dir = renderMountPackage(baseDir, entry);
142
- const entryId = `evolve-mount-${skillNameOf(entry.id)}`;
143
- const loader = ctx.get("loader");
144
- if (loader) {
145
- try {
146
- // EntryOptions: {id, name (module specifier), config, group, disabled, inject} —
147
- // `name` must resolve as an ES module: Node ESM does not support
148
- // directory imports, so point at the generated index.js explicitly.
149
- await loader.create({ id: entryId, name: join(dir, "index.js") });
150
- }
151
- catch (cause) {
152
- throw new Error(`hot mount failed: ${cause instanceof Error ? cause.message : String(cause)}`);
153
- }
154
- }
155
- const record = {
156
- id: entry.id,
157
- entryId,
158
- path: dir,
159
- version: entry.version,
160
- mountedAt: new Date().toISOString(),
161
- };
162
- const ledger = loadLedger(baseDir);
163
- ledger.mounted = [...ledger.mounted.filter((m) => m.id !== entry.id), record];
164
- saveLedger(baseDir, ledger);
165
- return record;
166
- }
167
- /** Unmount a skill entry: remove the loader entry and the generated package. */
168
- export async function unmountSkill(ctx, baseDir, id) {
169
- const ledger = loadLedger(baseDir);
170
- const record = ledger.mounted.find((m) => m.id === id);
171
- if (!record) {
172
- return undefined;
173
- }
174
- const loader = ctx.get("loader");
175
- if (loader) {
176
- try {
177
- await loader.remove(record.entryId);
178
- }
179
- catch (cause) {
180
- throw new Error(`hot unmount failed: ${cause instanceof Error ? cause.message : String(cause)}`);
181
- }
182
- }
183
- if (existsSync(record.path)) {
184
- rmSync(record.path, { recursive: true, force: true });
185
- }
186
- ledger.mounted = ledger.mounted.filter((m) => m.id !== id);
187
- saveLedger(baseDir, ledger);
188
- return record;
189
- }
190
- /** Re-mount every ledger entry at plugin boot (restart persistence). */
191
- export async function restoreMounted(ctx, baseDir) {
192
- const ledger = loadLedger(baseDir);
193
- const loader = ctx.get("loader");
194
- for (const record of ledger.mounted) {
195
- if (!existsSync(join(record.path, "index.js"))) {
196
- continue; // package was removed; ledger prunes on next unmount
197
- }
198
- if (!loader) {
199
- continue;
200
- }
201
- try {
202
- await loader.create({ id: record.entryId, name: join(record.path, "index.js") });
203
- }
204
- catch (cause) {
205
- ctx.logger("continual-evolve").warn(`mount restore failed for ${record.id}: ${cause instanceof Error ? cause.message : String(cause)}`);
206
- }
207
- }
208
- }
209
- function oneLine(text) {
210
- return text.replace(/\s+/g, " ").trim();
211
- }
212
- //# sourceMappingURL=mount.js.map
package/lib/notify.d.ts DELETED
@@ -1,59 +0,0 @@
1
- /**
2
- * Auto-review visibility: the gate used to work fully in the background —
3
- * the user never saw a decision, a persisted entry, or the token spend. This
4
- * module queues a short follow-up turn after an approved gate run so the
5
- * user SEES what was persisted, how to inspect it, and how to roll it back.
6
- *
7
- * The notice is a `continual-evolve`-sourced user message (`agent.followup`),
8
- * so it is rendered in the session transcript like any other input and the
9
- * agent answers with a one-line confirmation. It never fakes tool or assistant
10
- * events, so session replay, the ordered surface, and derived history stay
11
- * untouched: the notice is a plain `user/message` carrying this plugin's own
12
- * source kind.
13
- *
14
- * Every mechanical property stays in code: the notice text is built from the
15
- * applied refinement result, never from model text.
16
- */
17
- import type { Context } from "@deepseek-ai/cordis";
18
- import type { Agent } from "@deepseek-ai/dsh-agent";
19
- import type { RefinementResult } from "./types.js";
20
- /**
21
- * Compose the user-visible gate notice from an applied refinement result.
22
- * Lists every successfully applied edit (kind + title + id) and the rollback
23
- * command; failed edits are summarized in one line so nothing is hidden.
24
- */
25
- export declare function buildGateNotice(result: RefinementResult, turnsSinceLastReview: number): string;
26
- /**
27
- * Queue the follow-up notice turn for the agent. Failure is contained: a
28
- * broken notification must never break the gate path that already recorded
29
- * the decision in reviews.jsonl.
30
- */
31
- export declare function notifyAutoReview(ctx: Context, agent: Agent, result: RefinementResult, turnsSinceLastReview: number): void;
32
- /** Unified extraction receipt: one shape for applied / declined / no-op. */
33
- export interface MemoryReceipt {
34
- outcome: "applied" | "declined" | "noop";
35
- /** Applied refinement batches (one per scope) for applied outcomes. */
36
- results?: RefinementResult[];
37
- /** Scopes the human explicitly declined. */
38
- declinedScopes?: string[];
39
- /** Extractor loop turns / manifest searches / wall time. */
40
- turns: number;
41
- searches: number;
42
- durationMs: number;
43
- }
44
- /**
45
- * Compose the user-visible memory receipt from applied refinement batches
46
- * (never model text): every landed memory edit with its rollback command.
47
- * No-op and declined-only outcomes render one quiet line each — a foreground
48
- * notice for zero landed edits would be nagging, so callers only queue
49
- * follow-ups for applied outcomes.
50
- */
51
- export declare function buildMemoryReceipt(receipt: MemoryReceipt): string;
52
- /**
53
- * Queue the memory receipt follow-up. Same containment as the gate notice:
54
- * notification failure only logs. Callers gate on applied outcomes — no-op
55
- * and declined-only runs stay audit-only (reviews.jsonl) and never wake
56
- * the agent.
57
- */
58
- export declare function notifyMemoryExtraction(ctx: Context, agent: Agent, receipt: MemoryReceipt): void;
59
- //# sourceMappingURL=notify.d.ts.map
package/lib/notify.js DELETED
@@ -1,97 +0,0 @@
1
- import { createUserMessage } from "@deepseek-ai/dsh-llm";
2
- import { EVOLVE_MESSAGE_SOURCE } from "./message-source.js";
3
- /**
4
- * Compose the user-visible gate notice from an applied refinement result.
5
- * Lists every successfully applied edit (kind + title + id) and the rollback
6
- * command; failed edits are summarized in one line so nothing is hidden.
7
- */
8
- export function buildGateNotice(result, turnsSinceLastReview) {
9
- const applied = result.appliedEdits.filter((edit) => edit.applied);
10
- const failed = result.appliedEdits.filter((edit) => !edit.applied);
11
- const kindLabel = { prompt: "提示词", memory: "记忆", skill: "技能", subagent: "子代理" };
12
- const lines = applied.map((edit) => `- ${kindLabel[edit.kind] ?? edit.kind}「${edit.title ?? edit.id}」(${edit.id})`);
13
- const linesText = lines.length > 0 ? lines.join("\n") : "(无条目成功应用)";
14
- const failedText = failed.length > 0 ? `\n另有 ${failed.length} 条编辑未应用。` : "";
15
- return [
16
- `🔎 自动进化门禁:会话第 ${turnsSinceLastReview} 回合检查完成,本次沉淀 ${applied.length} 条条目:`,
17
- linesText,
18
- failedText,
19
- `查看全部条目:/evolve list;回滚本次沉淀:/evolve rollback ${result.id}`,
20
- "请用一句话简短确认即可,不要调用任何工具。",
21
- ]
22
- .filter((part) => part !== "")
23
- .join("\n");
24
- }
25
- /**
26
- * Queue the follow-up notice turn for the agent. Failure is contained: a
27
- * broken notification must never break the gate path that already recorded
28
- * the decision in reviews.jsonl.
29
- */
30
- export function notifyAutoReview(ctx, agent, result, turnsSinceLastReview) {
31
- try {
32
- agent.followup(createUserMessage({
33
- content: [{ type: "text", text: buildGateNotice(result, turnsSinceLastReview) }],
34
- source: EVOLVE_MESSAGE_SOURCE,
35
- }));
36
- }
37
- catch (cause) {
38
- ctx
39
- .logger("continual-evolve")
40
- .warn(`auto-review notice failed for ${agent.id}: ${cause instanceof Error ? cause.message : String(cause)}`);
41
- }
42
- }
43
- /**
44
- * Compose the user-visible memory receipt from applied refinement batches
45
- * (never model text): every landed memory edit with its rollback command.
46
- * No-op and declined-only outcomes render one quiet line each — a foreground
47
- * notice for zero landed edits would be nagging, so callers only queue
48
- * follow-ups for applied outcomes.
49
- */
50
- export function buildMemoryReceipt(receipt) {
51
- const seconds = (receipt.durationMs / 1000).toFixed(1);
52
- const stats = `${receipt.turns} 轮推理 / ${receipt.searches} 次检索 / ${seconds}s`;
53
- if (receipt.outcome === "noop") {
54
- return `🧠 记忆提取:本轮无可沉淀的持久事实(no-op,${stats}),未写入任何条目。`;
55
- }
56
- const results = receipt.results ?? [];
57
- const declinedScopes = receipt.declinedScopes ?? [];
58
- if (receipt.outcome === "declined" || results.length === 0) {
59
- const scopes = declinedScopes.join("、") || "持久化作用域";
60
- return `🧠 记忆提取:${scopes}的写入未获批准,已跳过(${stats}),未写入任何条目。`;
61
- }
62
- const applied = results.flatMap((result) => result.appliedEdits.filter((edit) => edit.applied));
63
- const lines = applied.slice(0, 12).map((edit) => `- 记忆「${edit.title ?? edit.id}」(${edit.id})`);
64
- if (applied.length > 12)
65
- lines.push(`- …另有 ${applied.length - 12} 条(见 /evolve history)`);
66
- const declined = declinedScopes.length > 0 ? `\n另有作用域${declinedScopes.join("、")}未经批准(已跳过)。` : "";
67
- const rollbacks = [...new Set(results.map((result) => result.id))].map((id) => `/evolve rollback ${id}`).join(";");
68
- return [
69
- `🧠 记忆提取:本轮沉淀 ${applied.length} 条记忆(${results.length} 个作用域批次,${stats}):`,
70
- lines.length > 0 ? lines.join("\n") : "(无条目成功应用)",
71
- declined,
72
- `回看:/evolve recall <关键词>;回滚:${rollbacks}`,
73
- "请用一句话简短确认即可,不要调用任何工具。",
74
- ]
75
- .filter((part) => part !== "")
76
- .join("\n");
77
- }
78
- /**
79
- * Queue the memory receipt follow-up. Same containment as the gate notice:
80
- * notification failure only logs. Callers gate on applied outcomes — no-op
81
- * and declined-only runs stay audit-only (reviews.jsonl) and never wake
82
- * the agent.
83
- */
84
- export function notifyMemoryExtraction(ctx, agent, receipt) {
85
- try {
86
- agent.followup(createUserMessage({
87
- content: [{ type: "text", text: buildMemoryReceipt(receipt) }],
88
- source: EVOLVE_MESSAGE_SOURCE,
89
- }));
90
- }
91
- catch (cause) {
92
- ctx
93
- .logger("continual-evolve")
94
- .warn(`memory receipt notice failed for ${agent.id}: ${cause instanceof Error ? cause.message : String(cause)}`);
95
- }
96
- }
97
- //# sourceMappingURL=notify.js.map
package/lib/plan.d.ts DELETED
@@ -1,16 +0,0 @@
1
- /**
2
- * Parsing helpers for the model-produced proposal JSON. A planner reply is
3
- * untrusted text: it may carry prose, a fenced block, or be truncated by an
4
- * exhausted output budget. These helpers recover the JSON object when
5
- * possible and name the cause (truncation vs malformed) when not.
6
- */
7
- import type { RefinementProposal } from "./types.js";
8
- /** True when the text ends mid-string or with unclosed brackets. */
9
- export declare function isIncompleteJson(candidate: string): boolean;
10
- /** Parse a JSON candidate, distinguishing truncation from malformation. */
11
- export declare function parseJsonCandidate(candidate: string): unknown;
12
- /** Recover a JSON object from raw model text. */
13
- export declare function extractJsonObject(text: string): unknown;
14
- /** Parse and shape a proposal, dropping non-object edits. */
15
- export declare function parseProposal(text: string): RefinementProposal;
16
- //# sourceMappingURL=plan.d.ts.map
package/lib/plan.js DELETED
@@ -1,126 +0,0 @@
1
- /** True when the text ends mid-string or with unclosed brackets. */
2
- export function isIncompleteJson(candidate) {
3
- let depth = 0;
4
- let inString = false;
5
- let escaped = false;
6
- for (const char of candidate) {
7
- if (escaped) {
8
- escaped = false;
9
- continue;
10
- }
11
- if (inString) {
12
- if (char === "\\")
13
- escaped = true;
14
- else if (char === '"')
15
- inString = false;
16
- continue;
17
- }
18
- if (char === '"')
19
- inString = true;
20
- else if (char === "{" || char === "[")
21
- depth++;
22
- else if (char === "}" || char === "]")
23
- depth--;
24
- }
25
- return inString || depth > 0;
26
- }
27
- /** Parse a JSON candidate, distinguishing truncation from malformation. */
28
- export function parseJsonCandidate(candidate) {
29
- try {
30
- return JSON.parse(candidate);
31
- }
32
- catch (error) {
33
- if (isIncompleteJson(candidate)) {
34
- throw new Error("the model stopped before completing its JSON object (output budget exhausted?)");
35
- }
36
- throw new Error(`the model did not return valid JSON: ${error instanceof Error ? error.message : String(error)}`);
37
- }
38
- }
39
- /** Recover a JSON object from raw model text. */
40
- export function extractJsonObject(text) {
41
- const trimmed = text.trim();
42
- if (trimmed.startsWith("{") && trimmed.endsWith("}")) {
43
- return parseJsonCandidate(trimmed);
44
- }
45
- const fenced = trimmed.match(/```(?:json)?\s*([\s\S]*?)```/);
46
- if (fenced) {
47
- return parseJsonCandidate(fenced[1]?.trim() ?? "");
48
- }
49
- const start = trimmed.indexOf("{");
50
- const end = trimmed.lastIndexOf("}");
51
- if (start !== -1 && end > start) {
52
- try {
53
- return JSON.parse(trimmed.slice(start, end + 1));
54
- }
55
- catch {
56
- return parseJsonCandidate(trimmed.slice(start));
57
- }
58
- }
59
- if (isIncompleteJson(trimmed)) {
60
- throw new Error("the model stopped before completing its JSON object (output budget exhausted?)");
61
- }
62
- throw new Error("the planner did not return a JSON object");
63
- }
64
- function asString(value) {
65
- return typeof value === "string" ? value : undefined;
66
- }
67
- function asRecord(value) {
68
- if (typeof value !== "object" || value === null || Array.isArray(value)) {
69
- return undefined;
70
- }
71
- return value;
72
- }
73
- /** Assign a string field only when it is present, honoring exactOptionalPropertyTypes. */
74
- function assignIfString(target, key, value) {
75
- const str = asString(value);
76
- if (str !== undefined) {
77
- target[key] = str;
78
- }
79
- }
80
- /** Parse and shape a proposal, dropping non-object edits. */
81
- export function parseProposal(text) {
82
- const value = extractJsonObject(text);
83
- const record = asRecord(value);
84
- if (!record) {
85
- throw new Error("the planner JSON must be an object");
86
- }
87
- const edits = [];
88
- if (Array.isArray(record["edits"])) {
89
- for (const raw of record["edits"]) {
90
- const edit = asRecord(raw);
91
- if (!edit)
92
- continue;
93
- const built = {
94
- action: asString(edit["action"]),
95
- kind: asString(edit["kind"]),
96
- };
97
- assignIfString(built, "id", edit["id"]);
98
- assignIfString(built, "title", edit["title"]);
99
- assignIfString(built, "content", edit["content"]);
100
- assignIfString(built, "path", edit["path"]);
101
- assignIfString(built, "reason", edit["reason"]);
102
- // Gap C2: validate blastRadius values.
103
- const br = asString(edit["blastRadius"]);
104
- if (br === "general" || br === "project" || br === "session") {
105
- built.blastRadius = br;
106
- }
107
- const reference = asRecord(edit["reference"]);
108
- if (reference)
109
- built.reference = reference;
110
- const argumentsRecord = asRecord(edit["arguments"]);
111
- if (argumentsRecord)
112
- built.arguments = argumentsRecord;
113
- const metadata = asRecord(edit["metadata"]);
114
- if (metadata)
115
- built.metadata = metadata;
116
- edits.push(built);
117
- }
118
- }
119
- return {
120
- summary: asString(record["summary"]) ?? "Refined harness state",
121
- rationale: asString(record["rationale"]) ?? "",
122
- expectedOutcome: asString(record["expectedOutcome"]) ?? "",
123
- edits,
124
- };
125
- }
126
- //# sourceMappingURL=plan.js.map
package/lib/planner.d.ts DELETED
@@ -1,61 +0,0 @@
1
- /**
2
- * The LLM planning pass. Given the current harness state, refinement history,
3
- * and optional instructions, a direct model call produces a JSON proposal
4
- * which is parsed (truncation-aware) and validated by the pure core.
5
- *
6
- * The call routes through `ctx.llm` with the calling agent's own
7
- * provider/model so the plan uses the same model the session runs on.
8
- */
9
- import type { Context } from "@deepseek-ai/cordis";
10
- import type { Agent } from "@deepseek-ai/dsh-agent";
11
- import type { HarnessState, RefinementProposal, RefinementResult } from "./types.js";
12
- import { type PrefixCacheOptions } from "./prefix-cache.js";
13
- import { type TokenUsageTarget } from "./token-usage.js";
14
- import { type RecordLanguage } from "./record-language.js";
15
- export declare const PLANNER_SYSTEM_PROMPT = "You are the /evolve continual harness subsystem.\n\nYour job is to improve the editable continual harness state. Instead of\nsummarizing the conversation you emit precise Create, Update, or Delete edits\nto reusable state: prompt notes, memories, skills, and subagent specs.\n\nRules:\n- The base system prompt is immutable and MUST NOT be rewritten (never edit id \"base_system_prompt\").\n- Prefer small evidence-backed edits. If no useful edit is justified, return an empty edits array.\n- Repeated evidence is mandatory for prompt/skill/subagent proposals: the same\n pattern must recur at least twice in the trajectory. A single one-off\n interaction never justifies a prompt, skill, or subagent edit \u2014 return an\n empty edits array with a rationale instead. Session-scoped local prompt\n landings converge on /evolve wrapup human review; the turn pipeline must\n not silently persist one-off abstractions.\n- prompt = narrow behavioral policy addendums; memory = ONE FACT per entry, typed via metadata.memoryType\n- (user = who the user is; feedback = a pitfall/correction or confirmed approach, MUST carry Why + How to apply;\n- project = work goal/constraint not derivable from the repo; reference = URL/dashboard/ticket pointer).\n- Memory rules: one fact per entry (title = one-line retrieval hook, content = the fact alone);\n- never save what the repo already records (code structure, past fixes, git history, AGENTS.md) or what only\n- matters to this conversation \u2014 save the non-obvious point instead; before creating, update the existing\n- entry when one already covers the fact (creates carry metadata {memoryType} or they are rejected);\n- skill = repeatable procedures (must carry a python reference {type:\"python\", import, callable}\n and an arguments object); subagent = reusable delegation roles.\n- Skill entries are authored to the DSH skill quality standard\n (skill-creator, distilled from the official deepseek-harness 11 skills;\n the full facts are provided in the <skill_quality_standard> block below):\n only for a REAL trigger scenario grounded in the trajectory\n (who, in what real task, what signal) \u2014 never invent one to pad the store;\n never duplicate the official 11 skills or existing entries; content is a\n SKILL.md document (frontmatter routing with \"use when / do not use when\"\n description, boundary declaration, prerequisites and exclusions, layered\n information, verifiable completion criteria). Self-check every proposed\n skill against the 7 structural features and state the result in its\n reason field.\n- Repeated multi-step workflows (session start/end routines, recurring\n wrap-up or handoff procedures) may be proposed as guidance skills:\n kind=skill, skill_kind=\"guidance\", content = a SKILL.md document (no\n python reference \u2014 executable skills keep requiring reference +\n arguments). Only propose with repeated evidence in the trajectory, never\n for one-off flows. Guidance skills materialize as discoverable SKILL.md\n files under <skillsRoot>/<kebab-name>/SKILL.md and are always offered to\n the user for a decision before they land.\n- Local edits are session-scoped; global edits persist across sessions.\n- Gap C2: EVERY edit MUST include a \"blastRadius\" field indicating how broadly\n the edit applies: \"general\" (cross-project tactical rule, applies everywhere),\n \"project\" (valid for the current project/repo), or \"session\" (one-off,\n specific to this session's context). The review gate validates that\n local-scope edits use \"session\" or \"project\", and global-scope edits use\n \"general\" or \"project\". When in doubt, prefer narrower blast radius.\n- Ground every edit in evidence: the session trajectory (recent direct user\n messages) is provided when available; prefer edits backed by it over\n speculation, and never invent preferences the user did not express.\n- Stale entries (superseded by newer ones, never referenced in recent\n trajectories, obsolete facts): propose action \"archive\" instead of\n \"delete\" \u2014 archive hides the entry from injection while keeping its data\n restorable; it requires only kind + id.\n- Output JSON only, exactly this shape:\n{\n \"summary\": \"one sentence\",\n \"rationale\": \"why these edits are justified by the evidence\",\n \"expectedOutcome\": \"what should improve and how to validate it\",\n \"edits\": [\n {\n \"action\": \"create|update|delete\",\n \"kind\": \"prompt|memory|skill|subagent\",\n \"id\": \"stable id for update/delete, optional for create\",\n \"title\": \"required for create/update except delete\",\n \"content\": \"required for create/update except delete\",\n \"path\": \"optional grouping path\",\n \"reference\": {\"type\":\"python\",\"import\":\"pkg.mod\",\"callable\":\"fn\"} ,\n \"arguments\": {\"name\": {\"type\":\"string\",\"required\":true,\"description\":\"...\"}},\n \"skill_kind\": \"executable|guidance (optional; skill kind only \u2014 guidance = SKILL.md document without reference)\",\n \"metadata\": {},\n \"reason\": \"why this edit is useful\",\n \"blastRadius\": \"general|project|session\"\n }\n ]\n}";
16
- export interface PlanOptions {
17
- agent: Agent;
18
- state: HarnessState;
19
- history: readonly RefinementResult[];
20
- instructions?: string;
21
- /**
22
- * Explicit session-trajectory text (recent direct user messages). When
23
- * omitted, it is extracted from the agent's own session log via
24
- * `recentUserText` — the same extraction the injection ranking uses — so
25
- * every planning call is grounded in what the user actually said.
26
- */
27
- trajectory?: string;
28
- /**
29
- * Explicit session-trajectory rows for prefix-cache Route A. When supplied,
30
- * the planner sees only the scheduler snapshot rather than the full session.
31
- */
32
- trajectoryEvents?: readonly unknown[];
33
- /**
34
- * Skills root to read the skill-creator template facts from
35
- * (`<root>/skill-creator/references/template.md`). When omitted or the
36
- * skills are not installed, the builtin distilled quality guide is
37
- * injected instead — the skill standard is always present.
38
- */
39
- skillsRoot?: string;
40
- global?: boolean;
41
- signal?: AbortSignal;
42
- maxOutputTokens?: number;
43
- /**
44
- * Prefix-cache routing for the planning input. Route A prepends a
45
- * session-derived message prefix and drops the auto-extracted
46
- * trajectory block (an explicitly passed `trajectory` is always kept);
47
- * Route B keeps the legacy flat-text input. Absent → auto-detect with
48
- * the default budget.
49
- */
50
- prefixCache?: PrefixCacheOptions;
51
- /** Direct-call token ledger destination; the planner phase is fixed by this module. */
52
- tokenUsage?: TokenUsageTarget;
53
- /**
54
- * Authoring language for the proposal. Absent → resolved per call
55
- * (explicit config upstream, else durable client preference, else
56
- * trajectory detection, else `en`).
57
- */
58
- language?: RecordLanguage;
59
- }
60
- export declare function planWithLlm(ctx: Context, options: PlanOptions): Promise<RefinementProposal>;
61
- //# sourceMappingURL=planner.d.ts.map