dsh-continual-evolve 0.2.0 → 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.
Files changed (62) hide show
  1. package/README.md +45 -7
  2. package/README.zh.md +44 -7
  3. package/lib/apply.js +1 -1
  4. package/lib/approval.d.ts +6 -0
  5. package/lib/approval.js +9 -1
  6. package/lib/auto.d.ts +38 -4
  7. package/lib/auto.js +58 -5
  8. package/lib/benchmark-command.d.ts +9 -0
  9. package/lib/benchmark-command.js +331 -0
  10. package/lib/benchmark.d.ts +70 -0
  11. package/lib/benchmark.js +107 -1
  12. package/lib/command.js +25 -442
  13. package/lib/evaluate.d.ts +7 -0
  14. package/lib/evaluate.js +22 -7
  15. package/lib/evolve-event.d.ts +38 -0
  16. package/lib/evolve-event.js +49 -0
  17. package/lib/failures.d.ts +39 -0
  18. package/lib/failures.js +170 -0
  19. package/lib/fate.d.ts +3 -1
  20. package/lib/fate.js +8 -4
  21. package/lib/goal-command.d.ts +7 -0
  22. package/lib/goal-command.js +37 -0
  23. package/lib/index.d.ts +29 -25
  24. package/lib/index.js +14 -0
  25. package/lib/inject.d.ts +8 -0
  26. package/lib/inject.js +51 -4
  27. package/lib/llm-text.d.ts +30 -0
  28. package/lib/llm-text.js +49 -0
  29. package/lib/mount-command.d.ts +10 -0
  30. package/lib/mount-command.js +48 -0
  31. package/lib/plan.js +5 -0
  32. package/lib/planner.d.ts +1 -1
  33. package/lib/planner.js +13 -39
  34. package/lib/render.d.ts +1 -3
  35. package/lib/render.js +0 -4
  36. package/lib/review.d.ts +4 -1
  37. package/lib/review.js +10 -38
  38. package/lib/rollback.d.ts +1 -3
  39. package/lib/rollback.js +0 -8
  40. package/lib/score.d.ts +15 -0
  41. package/lib/score.js +74 -5
  42. package/lib/service.d.ts +2 -2
  43. package/lib/service.js +5 -2
  44. package/lib/skill-render.d.ts +15 -0
  45. package/lib/skill-render.js +30 -0
  46. package/lib/skill.d.ts +2 -5
  47. package/lib/skill.js +2 -29
  48. package/lib/skillquality.d.ts +1 -2
  49. package/lib/skillquality.js +2 -2
  50. package/lib/store.d.ts +1 -3
  51. package/lib/store.js +0 -7
  52. package/lib/tool.js +22 -1
  53. package/lib/types.d.ts +8 -0
  54. package/lib/usage.d.ts +32 -0
  55. package/lib/usage.js +84 -0
  56. package/lib/validate.d.ts +12 -2
  57. package/lib/validate.js +26 -1
  58. package/lib/wrapup-command.d.ts +8 -0
  59. package/lib/wrapup-command.js +211 -0
  60. package/lib/wrapup.d.ts +14 -9
  61. package/lib/wrapup.js +24 -36
  62. package/package.json +8 -8
package/lib/wrapup.js CHANGED
@@ -1,7 +1,9 @@
1
- import { BlockAssembler, createUserMessage, ReasoningEffortId } from "@deepseek-ai/dsh-llm";
2
1
  import { ARCHIVED_AT_KEY, PROMOTED_AT_KEY, PROMOTED_TO_KEY, SOURCE_SEQS_KEY, SOURCE_SESSION_KEY, SOURCED_FROM_KEY, isArchived } from "./types.js";
3
2
  import { extractJsonObject } from "./plan.js";
4
3
  import { compactText } from "./render.js";
4
+ import { streamText } from "./llm-text.js";
5
+ import { getUsageCount, loadUsage } from "./usage.js";
6
+ import { recencyScore } from "./inject.js";
5
7
  export function candidateKey(kind, id) {
6
8
  return `${kind}:${id}`;
7
9
  }
@@ -71,7 +73,11 @@ export function globalHintsFor(globalState, kind, entry) {
71
73
  * `coveredGlobally` flag so the assessor never wastes a promote on a topic
72
74
  * the global store already owns.
73
75
  */
74
- export function listLocalCandidates(state, globalState) {
76
+ /** Staleness threshold: entries with recency below this AND zero usage are stale. */
77
+ const STALE_RECENCY_THRESHOLD = 0.1;
78
+ export function listLocalCandidates(state, globalState, baseDir) {
79
+ const usage = baseDir ? loadUsage(baseDir) : undefined;
80
+ const now = Date.now();
75
81
  const candidates = [];
76
82
  for (const kind of Object.keys(state.entries)) {
77
83
  for (const entry of Object.values(state.entries[kind])) {
@@ -81,6 +87,8 @@ export function listLocalCandidates(state, globalState) {
81
87
  continue;
82
88
  if (typeof entry.metadata[PROMOTED_TO_KEY] === "string")
83
89
  continue;
90
+ const injectionCount = usage ? getUsageCount(usage, kind, entry.id) : 0;
91
+ const stale = injectionCount === 0 && recencyScore(entry, now) < STALE_RECENCY_THRESHOLD;
84
92
  candidates.push({
85
93
  kind,
86
94
  id: entry.id,
@@ -91,6 +99,8 @@ export function listLocalCandidates(state, globalState) {
91
99
  metadata: entry.metadata,
92
100
  coveredGlobally: globalCoverageDetected(globalState, kind, entry),
93
101
  globalHints: globalHintsFor(globalState, kind, entry),
102
+ injectionCount,
103
+ stale,
94
104
  });
95
105
  }
96
106
  }
@@ -342,12 +352,17 @@ listed entry exactly once:
342
352
  procedure or skill. Future sessions would benefit from seeing it.
343
353
  - "archive" — the content is session-specific task progress, one-off noise,
344
354
  superseded or obsolete, or already covered by the global store (note
345
- "covered globally" in the reason).
355
+ "covered globally" in the reason), or stale (old + never injected — note
356
+ "stale (injectionCount=0, recency low)" in the reason).
346
357
  - "keep" — still actively useful to this session, or genuinely uncertain.
347
358
 
348
359
  Rules:
349
360
  - When an entry is marked "covered globally" in the listing, prefer "archive"
350
361
  or "keep" over "promote" — promoting a duplicate gains nothing.
362
+ - When an entry is marked "stale" (injectionCount=0 and low recency), prefer
363
+ "archive" — the entry has never been used and is old, so it is unlikely to
364
+ be needed again. Only "keep" if the content is clearly valuable despite low
365
+ usage (e.g. a safety policy that rarely triggers but is critical).
351
366
  - Do not promote local task state, work-in-progress notes, or content tied to
352
367
  one session's ephemeral details.
353
368
  - Skills: only "promote" a skill entry that is a genuinely reusable procedure
@@ -387,10 +402,11 @@ export async function assessLocalEntries(ctx, agent, candidates, options = {}) {
387
402
  .map((candidate) => {
388
403
  const key = candidateKey(candidate.kind, candidate.id);
389
404
  const covered = candidate.coveredGlobally ? " (covered globally)" : "";
405
+ const stale = candidate.stale ? ` (stale: injectionCount=${candidate.injectionCount}, recency low)` : "";
390
406
  const hints = candidate.globalHints.length > 0
391
407
  ? ` | global≈${candidate.globalHints.map((hint) => hint.id + ":" + hint.title).join(", ")}`
392
408
  : "";
393
- return `- ${key} [${candidate.path}, v${candidate.version}] "${candidate.title}"${covered}${hints}: ${compactText(candidate.content, 220)}`;
409
+ return `- ${key} [${candidate.path}, v${candidate.version}] "${candidate.title}"${covered}${stale}${hints}: ${compactText(candidate.content, 220)}`;
394
410
  })
395
411
  .join("\n");
396
412
  const userPrompt = [
@@ -398,42 +414,14 @@ export async function assessLocalEntries(ctx, agent, candidates, options = {}) {
398
414
  `<local_entries>\n${candidateText}\n</local_entries>`,
399
415
  "Return only JSON. Every item must reference one of the keys above.",
400
416
  ].join("\n\n");
401
- const assembler = new BlockAssembler();
402
- for await (const chunk of ctx.llm.stream({
417
+ const text = await streamText(ctx, {
403
418
  provider: agent.options.provider,
404
419
  model: agent.options.model,
405
420
  system: WRAPUP_ASSESS_SYSTEM_PROMPT,
406
- messages: [
407
- createUserMessage({
408
- content: [{ type: "text", text: userPrompt }],
409
- source: { kind: "plugin", plugin: "dsh-continual-evolve" },
410
- }),
411
- ],
412
- // Force non-reasoning output so the budget lands in the JSON verdicts.
413
- reasoningEffort: ReasoningEffortId("off"),
421
+ prompt: userPrompt,
414
422
  maxTokens: options.maxOutputTokens ?? 4096,
415
- ...(options.signal ? { signal: options.signal } : {}),
416
- })) {
417
- assembler.push(chunk);
418
- }
419
- const finish = assembler.finish;
420
- if (finish.kind === "error") {
421
- throw new Error(`evolve: wrap-up assessor call failed: ${finish.failure?.message ?? "unknown"}`);
422
- }
423
- if (finish.kind === "aborted") {
424
- throw new Error("evolve: wrap-up assessor call aborted");
425
- }
426
- if (finish.kind === "max-tokens") {
427
- throw new Error("evolve: wrap-up assessor output budget exhausted (max-tokens)");
428
- }
429
- const text = assembler
430
- .blocks()
431
- .filter((block) => block.type === "text")
432
- .map((block) => block.text)
433
- .join("\n");
434
- if (text.length === 0) {
435
- throw new Error("evolve: wrap-up assessor produced no text");
436
- }
423
+ signal: options.signal,
424
+ });
437
425
  return parseWrapupAssessment(text, candidates);
438
426
  }
439
427
  //# sourceMappingURL=wrapup.js.map
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "dsh-continual-evolve",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Continual self-evolution plugin for DeepSeek Harness: versioned, auditable, rollback-safe harness state (prompt notes, memories, skills, subagent specs) refined from session trajectories.",
5
5
  "license": "MIT",
6
6
  "repository": {
7
7
  "type": "git",
8
- "url": "https://github.com/ZK-Andy/dsh-continual-evolve.git"
8
+ "url": "git+https://github.com/ZK-Andy/dsh-continual-evolve.git"
9
9
  },
10
10
  "homepage": "https://github.com/ZK-Andy/dsh-continual-evolve",
11
11
  "bugs": {
@@ -62,12 +62,12 @@
62
62
  },
63
63
  "devDependencies": {
64
64
  "@deepseek-ai/cordis": "^4.0.1",
65
- "@deepseek-ai/dsh-agent": "0.1.0-rc.6",
66
- "@deepseek-ai/dsh-commands": "0.1.0-rc.6",
67
- "@deepseek-ai/dsh-home-paths": "0.1.0-rc.6",
68
- "@deepseek-ai/dsh-llm": "0.1.0-rc.6",
69
- "@deepseek-ai/dsh-system-prompt": "0.1.0-rc.6",
70
- "@deepseek-ai/dsh-tools": "0.1.0-rc.6",
65
+ "@deepseek-ai/dsh-agent": "0.1.0-rc.7",
66
+ "@deepseek-ai/dsh-commands": "0.1.0-rc.7",
67
+ "@deepseek-ai/dsh-home-paths": "0.1.0-rc.7",
68
+ "@deepseek-ai/dsh-llm": "0.1.0-rc.7",
69
+ "@deepseek-ai/dsh-system-prompt": "0.1.0-rc.7",
70
+ "@deepseek-ai/dsh-tools": "0.1.0-rc.7",
71
71
  "@deepseek-ai/schemastery": "^3.18.1",
72
72
  "@types/node": "^22.10.0",
73
73
  "oxlint": "^0.16.0",