@lmzhen/dsh-evolution-activity 0.3.74 → 0.3.76

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
@@ -30,3 +30,5 @@ Independent of request-prefix construction. This package does not alter the asse
30
30
 
31
31
 
32
32
  - Each event lands through `transactIo` (like `feedback.json`), so append cycles are cross-process atomic at the single-write granularity; the read-modify-write of one event is serialized in-process and atomic on disk. A multi-record batch is still one event at a time — no batch transaction exists.
33
+
34
+ **Runtime invariant:** No companion is published. The platform auto-assembles nothing and the family mounts no `<pkg>/invariant` cordis row, so a companion here would never execute (v37 S2.1 / I-3).
package/lib/index.js CHANGED
@@ -36,6 +36,7 @@ function applyActivityEvent(items, event, maxItems, at = Date.now()) {
36
36
  memoryApplied: event.memoryApplied,
37
37
  skillApplied: event.skillApplied,
38
38
  rejectedOps: event.rejectedOps,
39
+ ...event.skippedUnread !== void 0 ? { skippedUnread: event.skippedUnread } : {},
39
40
  ...event.executionFailures !== void 0 ? { executionFailures: event.executionFailures } : {},
40
41
  ...event.executionError !== void 0 ? { executionError: event.executionError } : {},
41
42
  ...event.evidenceQuotes !== void 0 ? { evidenceQuotes: event.evidenceQuotes } : {},
@@ -62,7 +63,7 @@ function parseActivityContent(raw) {
62
63
  const isOptionalCount = (value) => value === void 0 || isCount(value);
63
64
  try {
64
65
  const parsed = JSON.parse(raw);
65
- return (typeof parsed === "object" && parsed !== null && Array.isArray(parsed.items) ? parsed.items : []).filter((item) => typeof item === "object" && item !== null && typeof item.planId === "string" && typeof item.sessionId === "string" && isCount(item.memoryApplied) && isCount(item.skillApplied) && isCount(item.rejectedOps) && isTimestampEpochMs(item.at) && isOptionalCount(item.executionFailures) && isOptionalCount(item.evidenceQuotes) && isOptionalCount(item.estimatedInputChars));
66
+ return (typeof parsed === "object" && parsed !== null && Array.isArray(parsed.items) ? parsed.items : []).filter((item) => typeof item === "object" && item !== null && typeof item.planId === "string" && typeof item.sessionId === "string" && isCount(item.memoryApplied) && isCount(item.skillApplied) && isCount(item.rejectedOps) && isTimestampEpochMs(item.at) && isOptionalCount(item.skippedUnread) && isOptionalCount(item.executionFailures) && isOptionalCount(item.evidenceQuotes) && isOptionalCount(item.estimatedInputChars));
66
67
  } catch {
67
68
  return [];
68
69
  }
@@ -78,6 +79,18 @@ function parseActivityContent(raw) {
78
79
  async function loadActivity(root, io) {
79
80
  return parseActivityContent(await io.readText(activityFile(root)));
80
81
  }
82
+ /** True when bytes exist but are not a readable activity envelope: unparsable
83
+ * JSON, or a missing `items` array (a scalar/array/other-shaped file). A missing
84
+ * file (null) is NOT corruption — it is a first write. */
85
+ function isCorruptActivity(raw) {
86
+ if (raw === null) return false;
87
+ try {
88
+ const parsed = JSON.parse(raw);
89
+ return typeof parsed !== "object" || parsed === null || !Array.isArray(parsed.items);
90
+ } catch {
91
+ return true;
92
+ }
93
+ }
81
94
  const name = "evolution-activity";
82
95
  const Config = z.object({ maxItems: z.number().min(1).default(200) });
83
96
  function apply(ctx, rawConfig = {}) {
@@ -96,9 +109,16 @@ function apply(ctx, rawConfig = {}) {
96
109
  const root = evolutionHome();
97
110
  let chain = Promise.resolve();
98
111
  ioCtx.on("evolution/plan-applied", (event) => {
99
- const run = chain.then(() => transactIo(io, activityFile(root), (current) => {
100
- const items = applyActivityEvent(parseActivityContent(current), event, maxItems);
101
- return Promise.resolve(serializeActivity(items));
112
+ const run = chain.then(() => transactIo(io, activityFile(root), async (current) => {
113
+ const file = activityFile(root);
114
+ if (isCorruptActivity(current)) {
115
+ if (!await io.writeText(`${file}.corrupt`, current).then(() => true, () => false)) {
116
+ ioCtx.logger.warn(`evolution-activity: ${file} is unreadable; the .corrupt copy could not be written — refusing this write so the original bytes stay recoverable`);
117
+ return current;
118
+ }
119
+ ioCtx.logger.warn(`evolution-activity: ${file} is unreadable (unparsable JSON or wrong top-level shape); the original bytes were copied to ${file}.corrupt and the store restarts from the readable entries`);
120
+ }
121
+ return serializeActivity(applyActivityEvent(parseActivityContent(current), event, maxItems));
102
122
  }));
103
123
  chain = run.then(() => void 0, () => void 0);
104
124
  run.catch((error) => {
@@ -26,6 +26,10 @@ export interface EvolutionActivityRecord {
26
26
  memoryApplied: number;
27
27
  skillApplied: number;
28
28
  rejectedOps: number;
29
+ /** R-03: ops skipped because the session never read the named skill — a third
30
+ * "not done" cause that is neither a validation reject nor a failure. Optional
31
+ * so payloads/sidecars without it keep parsing (absent = none recorded). */
32
+ skippedUnread?: number | undefined;
29
33
  /** V6-10 (0.3.36): execution-layer failures (not validation rejections) —
30
34
  * optional so pre-0.3.36 sidecars keep parsing (missing = none recorded). */
31
35
  executionFailures?: number | undefined;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@lmzhen/dsh-evolution-activity",
3
3
  "description": "Durable activity store for self-evolution plan outcomes (community build)",
4
- "version": "0.3.74",
4
+ "version": "0.3.76",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -18,10 +18,6 @@
18
18
  "types": "./lib/types/index.d.ts",
19
19
  "default": "./lib/index.js"
20
20
  },
21
- "./invariant": {
22
- "types": "./lib/types/invariant.d.ts",
23
- "default": "./lib/invariant.js"
24
- },
25
21
  "./package.json": "./package.json"
26
22
  },
27
23
  "files": [
@@ -31,17 +27,15 @@
31
27
  "license": "MIT",
32
28
  "dependencies": {
33
29
  "@deepseek-ai/schemastery": "^3.18.1",
34
- "@lmzhen/dsh-evolution-core": "^0.3.74"
30
+ "@lmzhen/dsh-evolution-core": "^0.3.76"
35
31
  },
36
32
  "peerDependencies": {
37
- "@deepseek-ai/cordis": "^4.0.1",
38
- "@deepseek-ai/dsh-invariants": "^0.1.5-rc.2"
33
+ "@deepseek-ai/cordis": "^4.0.1"
39
34
  },
40
35
  "devDependencies": {
41
36
  "@deepseek-ai/cordis": "^4.0.1",
42
- "@deepseek-ai/dsh-invariants": "^0.1.5-rc.2",
43
- "@lmzhen/dsh-evolution-core": "^0.3.74",
44
- "@lmzhen/dsh-evolution-io": "^0.3.74",
45
- "@lmzhen/dsh-evolution-io-node": "^0.3.74"
37
+ "@lmzhen/dsh-evolution-core": "^0.3.76",
38
+ "@lmzhen/dsh-evolution-io": "^0.3.76",
39
+ "@lmzhen/dsh-evolution-io-node": "^0.3.76"
46
40
  }
47
41
  }
package/lib/invariant.js DELETED
@@ -1,8 +0,0 @@
1
- //#region lib/types/invariant.js
2
- const PACKAGE_NAME = "@lmzhen/dsh-evolution-activity";
3
- const name = "evolution-activity-invariant";
4
- const inject = ["invariants"];
5
- const install = () => {};
6
- const apply = (ctx) => Promise.resolve(ctx.invariants.register(PACKAGE_NAME, install));
7
- //#endregion
8
- export { apply, inject, name };
@@ -1,5 +0,0 @@
1
- import type { Context } from '@deepseek-ai/cordis';
2
- export declare const name = "evolution-activity-invariant";
3
- export declare const inject: string[];
4
- export declare const apply: (ctx: Context) => Promise<() => void>;
5
- //# sourceMappingURL=invariant.d.ts.map