dsh-continual-evolve 0.15.3 → 0.15.4

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 CHANGED
@@ -7,7 +7,7 @@ English | [中文](README.zh.md)
7
7
  [![CI](https://github.com/ZK-Andy/dsh-continual-evolve/actions/workflows/ci.yml/badge.svg)](https://github.com/ZK-Andy/dsh-continual-evolve/actions/workflows/ci.yml)
8
8
  [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
9
9
  [![Node](https://img.shields.io/badge/node-%5E22.19%20%7C%7C%20%3E%3D24-339933)](package.json)
10
- [![Tests](https://img.shields.io/badge/tests-194%20passing-brightgreen)]()
10
+ [![Tests](https://img.shields.io/badge/tests-199%20passing-brightgreen)]()
11
11
  [![Coverage · statements](https://img.shields.io/badge/coverage_statements-97%25-brightgreen)]()
12
12
  [![Coverage · branches](https://img.shields.io/badge/coverage_branches-91%25-green)]()
13
13
  [![Coverage · functions](https://img.shields.io/badge/coverage_functions-100%25-brightgreen)]()
@@ -71,7 +71,7 @@ Profile patch example:
71
71
 
72
72
  ```bash
73
73
  pnpm install && pnpm build # deps + clean build -> lib/
74
- pnpm test # vitest (194 tests)
74
+ pnpm test # vitest (199 tests)
75
75
  pnpm test:coverage # v8 coverage, CI-enforced thresholds
76
76
  pnpm lint # oxlint src test client
77
77
  pnpm check:pack # published-artifact consistency (also runs on prepack)
package/README.zh.md CHANGED
@@ -7,7 +7,7 @@
7
7
  [![CI](https://github.com/ZK-Andy/dsh-continual-evolve/actions/workflows/ci.yml/badge.svg)](https://github.com/ZK-Andy/dsh-continual-evolve/actions/workflows/ci.yml)
8
8
  [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
9
9
  [![Node](https://img.shields.io/badge/node-%5E22.19%20%7C%7C%20%3E%3D24-339933)](package.json)
10
- [![Tests](https://img.shields.io/badge/tests-194%20passing-brightgreen)]()
10
+ [![Tests](https://img.shields.io/badge/tests-199%20passing-brightgreen)]()
11
11
  [![Coverage · statements](https://img.shields.io/badge/coverage_statements-97%25-brightgreen)]()
12
12
  [![Coverage · branches](https://img.shields.io/badge/coverage_branches-91%25-green)]()
13
13
  [![Coverage · functions](https://img.shields.io/badge/coverage_functions-100%25-brightgreen)]()
@@ -71,7 +71,7 @@ profile patch 示例:
71
71
 
72
72
  ```bash
73
73
  pnpm install && pnpm build # 依赖 + 干净构建 -> lib/
74
- pnpm test # vitest(194 例)
74
+ pnpm test # vitest(199 例)
75
75
  pnpm test:coverage # v8 覆盖率,CI 强制阈值
76
76
  pnpm lint # oxlint src test client
77
77
  pnpm check:pack # 发布产物一致性(prepack 也会跑)
@@ -34,8 +34,11 @@ export interface RunOptions {
34
34
  incrementMaxChars?: number;
35
35
  }
36
36
  /**
37
- * Run one extraction pass for a target. Never throws: every terminal path
38
- * is either a landed application, a ledgered skip, or a ledgered failure.
37
+ * Run one extraction pass for a target. Never throws unless the ledger
38
+ * itself is unwritable: every terminal path is either a landed application,
39
+ * a ledgered skip, or a ledgered failure — anything that escapes the
40
+ * per-stage handling inside lands a failed `unexpected-error` ledger row
41
+ * here, so no failure ends as a scheduler warn without an account trace.
39
42
  */
40
43
  export declare function runExtraction(deps: ExtractionDeps, target: ExtractionTarget, opts: RunOptions): Promise<ExtractionResult>;
41
44
  /** The event wiring surface (duck-typed cordis context). */
package/lib/extraction.js CHANGED
@@ -3,11 +3,57 @@ import { EXTRACTION_SYSTEM_PROMPT, extractionPrompt, manifestLineOf, parseExtrac
3
3
  import { cwdOf } from "./memory-section.js";
4
4
  /** Head length of a failed run's raw answer kept in the ledger `files` column. */
5
5
  const FAILURE_RAW_MAX = 2048;
6
+ /** Head length of an unexpected error's message/stack trace kept in the ledger `files` column. */
7
+ const UNEXPECTED_ERROR_RAW_MAX = 2048;
8
+ /** Head length of an error message embedded in a ledger `skip_reason`. */
9
+ const ERROR_REASON_MAX = 300;
10
+ /** Split an unexpected throw into a ledger-sized reason head and file body. */
11
+ function errorText(error) {
12
+ const message = error instanceof Error && error.message.length > 0 ? error.message : String(error);
13
+ const stack = error instanceof Error && typeof error.stack === "string" ? error.stack : message;
14
+ return { message, detail: `${message}\n${stack}`.slice(0, UNEXPECTED_ERROR_RAW_MAX) };
15
+ }
6
16
  /**
7
- * Run one extraction pass for a target. Never throws: every terminal path
8
- * is either a landed application, a ledgered skip, or a ledgered failure.
17
+ * Run one extraction pass for a target. Never throws unless the ledger
18
+ * itself is unwritable: every terminal path is either a landed application,
19
+ * a ledgered skip, or a ledgered failure — anything that escapes the
20
+ * per-stage handling inside lands a failed `unexpected-error` ledger row
21
+ * here, so no failure ends as a scheduler warn without an account trace.
9
22
  */
10
23
  export async function runExtraction(deps, target, opts) {
24
+ const started = Date.now();
25
+ try {
26
+ return await runExtractionInner(deps, target, opts);
27
+ }
28
+ catch (error) {
29
+ const { message, detail } = errorText(error);
30
+ const reason = `unexpected-error: ${message.slice(0, ERROR_REASON_MAX)}`;
31
+ try {
32
+ deps.store.logSkipped({
33
+ sessionId: target.sessionId,
34
+ workspaceId: target.workspaceId,
35
+ trigger: opts.trigger,
36
+ skipReason: reason,
37
+ status: "failed",
38
+ model: target.model,
39
+ durationMs: Date.now() - started,
40
+ files: detail,
41
+ });
42
+ }
43
+ catch (ledgerError) {
44
+ // The ledger itself is down — nothing left to write to. Chain
45
+ // both errors so the scheduler's last-resort row keeps the
46
+ // triggering failure, not just the ledger fault.
47
+ throw new Error(`extraction failed (${message}); failure ledger write also failed (${errorText(ledgerError).message})`);
48
+ }
49
+ return { status: "failed", skipReason: reason };
50
+ }
51
+ }
52
+ /**
53
+ * The pipeline body: one bounded increment of one session, one LLM call, one
54
+ * gated transactional application, one ledger row, one cursor.
55
+ */
56
+ async function runExtractionInner(deps, target, opts) {
11
57
  const { store } = deps;
12
58
  const ledgerBase = {
13
59
  sessionId: target.sessionId,
@@ -37,8 +83,14 @@ export async function runExtraction(deps, target, opts) {
37
83
  all = Array.isArray(snapshot.events) ? snapshot.events : [];
38
84
  }
39
85
  catch (error) {
40
- store.logSkipped({ ...ledgerBase, skipReason: "surface-read-failed", status: "failed" });
41
- return { status: "failed", skipReason: `surface read failed: ${error instanceof Error ? error.message : String(error)}` };
86
+ const { message, detail } = errorText(error);
87
+ try {
88
+ store.logSkipped({ ...ledgerBase, skipReason: "surface-read-failed", status: "failed", files: detail });
89
+ }
90
+ catch (ledgerError) {
91
+ throw new Error(`surface read failed (${message}); failure ledger write also failed (${errorText(ledgerError).message})`);
92
+ }
93
+ return { status: "failed", skipReason: `surface read failed: ${message}` };
42
94
  }
43
95
  const events = eventsAfterCursor(all, cursor === undefined ? undefined : `seq:${cursor}`);
44
96
  const slice = evaluateSlice({ events, internalAgent: false });
@@ -72,8 +124,14 @@ export async function runExtraction(deps, target, opts) {
72
124
  usage = answer.usage;
73
125
  }
74
126
  catch (error) {
75
- store.logSkipped({ ...ledgerBase, skipReason: "llm-failed", status: "failed", durationMs: Date.now() - started });
76
- return { status: "failed", skipReason: `llm call failed: ${error instanceof Error ? error.message : String(error)}` };
127
+ const { message, detail } = errorText(error);
128
+ try {
129
+ store.logSkipped({ ...ledgerBase, skipReason: "llm-failed", status: "failed", durationMs: Date.now() - started, files: detail });
130
+ }
131
+ catch (ledgerError) {
132
+ throw new Error(`llm call failed (${message}); failure ledger write also failed (${errorText(ledgerError).message})`);
133
+ }
134
+ return { status: "failed", skipReason: `llm call failed: ${message}` };
77
135
  }
78
136
  const parsed = parseExtractionAnswer(text);
79
137
  if (parsed.decision === "error") {
@@ -213,7 +271,26 @@ export function createExtractionScheduler(host, deps) {
213
271
  provider: target.provider,
214
272
  model: target.model,
215
273
  }, { trigger }).catch((error) => {
216
- host.logger("continual-evolve").warn(`extraction run failed unexpectedly: ${error instanceof Error ? error.message : String(error)}`);
274
+ // Last resort: runExtraction ledgers its own unexpected failures,
275
+ // so this fires only when even that failed (the ledger write
276
+ // threw) — retry one ledger row here before the warn, so the
277
+ // failure still has an account trace.
278
+ const { message, detail } = errorText(error);
279
+ try {
280
+ deps.store.logSkipped({
281
+ sessionId: target.sessionId,
282
+ workspaceId: cwdOf(target.agent),
283
+ trigger,
284
+ skipReason: `unexpected-error: ${message.slice(0, ERROR_REASON_MAX)}`,
285
+ status: "failed",
286
+ model: target.model,
287
+ files: detail,
288
+ });
289
+ }
290
+ catch {
291
+ // The ledger is down — the warn below is the last resort.
292
+ }
293
+ host.logger("continual-evolve").warn(`extraction run failed unexpectedly: ${message}`);
217
294
  }).finally(() => {
218
295
  slot.running = false;
219
296
  if (slot.pending !== null) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-continual-evolve",
3
- "version": "0.15.3",
3
+ "version": "0.15.4",
4
4
  "description": "ZCode-style workspace memory for DeepSeek Harness: one central SQLite store partitioned by workspace, index injection, and sole-writer gated writes.",
5
5
  "license": "MIT",
6
6
  "repository": {