@lmzhen/dsh-evolution-core 0.3.80 → 0.3.82

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.
@@ -34,16 +34,10 @@
34
34
  */
35
35
  import { transactIo, type EvolutionIoLike } from './io.ts';
36
36
  import { type Probe } from './probe.ts';
37
+ import type { SkillLimits } from './limits.ts';
37
38
  import { type MutationRecord } from './mutations.ts';
38
39
  import { type SkillHealthAssessment, type SkillHealthThresholds } from './skill-health.ts';
39
40
  import type { EvolutionSkillMutatedEvent } from './events.ts';
40
- export interface SkillLimits {
41
- maxNameLength: number;
42
- maxDescriptionLength: number;
43
- maxSkillContentChars: number;
44
- maxSkillFileBytes: number;
45
- }
46
- export declare const DEFAULT_SKILL_LIMITS: SkillLimits;
47
41
  export interface SkillSummary {
48
42
  name: string;
49
43
  description: string;
@@ -246,155 +240,6 @@ export declare function resolveExecOrigins(exec: EvolutionExecOriginView | undef
246
240
  * consumers that must probe markers without re-deriving the name (curator's
247
241
  * archive-copy bundled probe, 0.3.26 V4-02). */
248
242
  export declare function markerEntryName(marker: 'bundled' | 'hub-installed' | 'pinned' | 'hermes-managed'): string;
249
- export interface Frontmatter {
250
- name?: string;
251
- description?: string;
252
- [key: string]: unknown;
253
- }
254
- export declare function frontmatterBlock(content: string): {
255
- block: string;
256
- lines: string[];
257
- end: number;
258
- nl: string;
259
- } | null;
260
- /**
261
- * One frontmatter read (V27 G2.1): the values, the body, and every signal the
262
- * strict-YAML platform catalog derives from the same block. Returned by
263
- * {@link parseFrontmatter} so a caller never has to parse the block twice to
264
- * reach a description and the catalog verdict.
265
- */
266
- export interface FrontmatterRead {
267
- frontmatter: Frontmatter;
268
- body: string;
269
- /** Raw entries whose UNQUOTED value the strict catalog cannot load as
270
- * written. Quotes are included, so a value already normalized by the write
271
- * path (`normalizeFrontmatter`) is never re-flagged. */
272
- unsafeValues: Array<{
273
- key: string;
274
- value: string;
275
- }>;
276
- /** Whether the frontmatter is not valid AS WRITTEN for the strict platform
277
- * catalog: the strict parser rejects the block, or an unquoted value would
278
- * read as something other than its text (a dropped ` # ` comment, a
279
- * number/bool shorthand the catalog refuses as a string field), or a
280
- * platform string field carries a non-string value. The write path quotes
281
- * such a value on its next edit. */
282
- catalogInvalid: boolean;
283
- /** Platform string fields (`name`/`description`/`whenToUse`) whose YAML value
284
- * is not a string: the strict catalog reads such a field as ABSENT and, for an
285
- * absent name/description, ignores the whole file (P2-9/v37). */
286
- platformStringSplit: PlatformStringSplit[];
287
- }
288
- /** One `name`/`description`/`whenToUse` entry the strict catalog cannot read as
289
- * a string, with the YAML kind the parser found. */
290
- export interface PlatformStringSplit {
291
- key: string;
292
- /** The YAML type read for this field. `scalar` covers number/boolean (the
293
- * E-47 auto-quote repair handles those); `sequence`/`mapping` are the shapes
294
- * no rewrite can repair without inventing text — see `validateFrontmatter`. */
295
- kind: 'sequence' | 'mapping' | 'scalar' | 'null';
296
- }
297
- /**
298
- * Parse a SKILL.md: its frontmatter values and body, or `null` when the file
299
- * has no frontmatter block or no body. Every consumer of frontmatter values
300
- * goes through here — the write path's validation, `list()`'s published
301
- * description, `relatedSkillNames` and the audit — so all of them read the same
302
- * bytes the same way.
303
- *
304
- * @param content - the SKILL.md text.
305
- * @returns the read, or `null` when there is no block or no body.
306
- */
307
- export declare function parseFrontmatter(content: string): FrontmatterRead | null;
308
- /**
309
- * Whether this file's frontmatter is valid as written for the strict platform
310
- * catalog (see `FrontmatterRead.catalogInvalid`). Body-independent (a body-less
311
- * file is still judged), and derived from the same read as `parseFrontmatter` —
312
- * so the audit's verdict and the values the family publishes for one file can
313
- * never disagree (V27 G2.1).
314
- *
315
- * @param content - the SKILL.md text.
316
- * @returns `true` when the strict parser rejects the block or an unquoted value would read as something else.
317
- */
318
- export declare function frontmatterCatalogInvalid(content: string): boolean;
319
- /** YAML plain-scalar hazards that make an UNQUOTED frontmatter value
320
- * unloadable to the platform catalog (strict YAML parser): `: ` (mapping
321
- * separator), ` #` (comment start), a trailing `:` (a mapping marker),
322
- * or a leading YAML indicator. The evolution `parseFrontmatter` is
323
- * deliberately lenient, so violations silently split family-visibility from
324
- * platform-visibility (0.3.11 inkos-harness case: the description carried
325
- * "…: " and the catalog dropped the whole skill). Already-quoted values and
326
- * well-formed flow collections (`[a, b]` / `{a: b}`) are considered safe.
327
- * 0.3.16 (E-47): null/bool/number-shaped plain scalars are flagged too — they
328
- * parse as booleans/numbers on the platform while the family keeps the string
329
- * (a `description: true` split-brain).
330
- * This rule is only the FAST PATH — the write path re-verifies every rewrite
331
- * with the real YAML parser (see normalizeFrontmatter), so an incomplete
332
- * approximation can never corrupt a multiline flow value (P3-4). */
333
- export declare function yamlPlainScalarNeedsQuotes(value: string): boolean;
334
- export interface FrontmatterNormalizeResult {
335
- content: string;
336
- changed: boolean;
337
- /** Frontmatter keys whose values were auto-quoted. */
338
- fields: string[];
339
- /** Values that cannot be auto-quoted safely (control characters, or a
340
- * rewrite that failed the real-parser verification — a multiline flow
341
- * collection line etc. is left untouched and reported here, so the write
342
- * path rejects instead of silently damaging a value; 0.3.14). */
343
- issues: string[];
344
- }
345
- /**
346
- * Normalize a SKILL.md frontmatter block into catalog-loadable YAML: values
347
- * that YAML forbids unquoted get quotes — double quotes normally, single
348
- * quotes (with `''` doubling) when the value contains `"` or `\` (both legal
349
- * unescaped inside single-quoted YAML). Idempotent; only single-line
350
- * `key: value` entries are touched; body text is never modified; line-ending
351
- * style is preserved. **Every rewrite is re-verified with the real YAML
352
- * parser** (js-yaml — the same parser the platform catalog uses): if the
353
- * rewritten block no longer parses, or a rewritten value's parsed content
354
- * differs from the original, the rewrite is rolled back and reported in
355
- * `issues` (fail-loud, never a silent value corruption — P3-4).
356
- *
357
- * V10-02 (P2-3): the rewrite decision is PER LINE — each entry parses its own
358
- * value, so a duplicated key can never route one entry's unsafe value into a
359
- * different line's rewrite (the old key→Map lookup rewrote the FIRST (safe)
360
- * line with the SECOND line's quoted value, and the last-wins YAML reader
361
- * masked the damage). A duplicated key is itself invalid input and is
362
- * reported in `issues` (the write path refuses) instead of being rewritten.
363
- */
364
- export declare function normalizeFrontmatter(content: string): FrontmatterNormalizeResult;
365
- /**
366
- * Skill names referenced by a SKILL.md's `related_skills` frontmatter
367
- * (B-line G3, rc.44): the single parsing source for the quality references
368
- * factor and the learning-graph edges. The DSH frontmatter parser keeps the
369
- * YAML value as a string (`"[a, b]"`), so names are scanned out of it; each
370
- * must satisfy the skill-name shape and the referencing skill itself is
371
- * excluded. Pure and deduplicated.
372
- */
373
- export declare function relatedSkillNames(content: string, exclude?: string): string[];
374
- export declare function validateFrontmatter(content: string, expectedName?: string, limits?: SkillLimits,
375
- /** On-disk bytes, so a NET SHRINK of an over-limit file is allowed (S1.2). */
376
- current?: string | null): string | null;
377
- /** Hermes authoring quality bar for descriptions — see constants.ts
378
- * (0.3.16 T-4 moved the single source there; the public re-export sits behind
379
- * the package root, which re-exports constants anyway). */
380
- export interface AuthoringFeedback {
381
- /** Frontmatter description length in characters (0 when absent). */
382
- descriptionChars: number;
383
- /** Whether the description exceeds the authoring bar (60) while still passing the platform limit. */
384
- over60: boolean;
385
- /** Whether the description contains a colon (the standard requires double-quote wrapping). */
386
- hasColon: boolean;
387
- /** Advice lines appended to mutation success messages. */
388
- lines: string[];
389
- }
390
- /**
391
- * Advisory authoring feedback (P0): evaluate frontmatter against the
392
- * authoring bar WITHOUT changing platform validation semantics. The bar is
393
- * the quality target, `validateFrontmatter`'s limits are the compatibility
394
- * floor, and this bridge layer tells the model when its text would be
395
- * truncated or route-poor instead of silently shipping it.
396
- */
397
- export declare function authoringFeedback(frontmatter: Frontmatter): AuthoringFeedback;
398
243
  /** A1-6 (v18): the regex alone admits `references/nul.md` (a Windows device
399
244
  * stem), which the support-file layer refuses. Restructure must use the same
400
245
  * rule, or it creates an orphan the later patch/write/remove paths refuse.
@@ -747,4 +592,8 @@ export interface NewSkillLibraryOptions {
747
592
  threatExemptLabels?: readonly string[] | undefined;
748
593
  }
749
594
  export declare function newSkillLibrary(options: NewSkillLibraryOptions): SkillLibrary;
595
+ export { DEFAULT_SKILL_LIMITS } from './limits.ts';
596
+ export type { SkillLimits } from './limits.ts';
597
+ export { authoringFeedback, frontmatterBlock, frontmatterCatalogInvalid, normalizeFrontmatter, parseFrontmatter, relatedSkillNames, validateFrontmatter, yamlPlainScalarNeedsQuotes } from './frontmatter.ts';
598
+ export type { AuthoringFeedback, Frontmatter, FrontmatterNormalizeResult, FrontmatterRead, PlatformStringSplit } from './frontmatter.ts';
750
599
  //# sourceMappingURL=skill-store.d.ts.map
@@ -13,8 +13,8 @@
13
13
  * REPORT findings: they stay visible to operators and tests but never reject a
14
14
  * write. Blocking them turned every emoji into a security event.
15
15
  */
16
- export type ThreatScope = 'all' | 'context' | 'strict';
17
- export interface ThreatFinding {
16
+ type ThreatScope = 'all' | 'context' | 'strict';
17
+ interface ThreatFinding {
18
18
  label: string;
19
19
  category: string;
20
20
  scope: ThreatScope;
@@ -74,4 +74,5 @@ export declare function scanContentThreats(text: string, maxScanChars?: number,
74
74
  * returns the scan message verbatim, so the hint rides along there too.
75
75
  */
76
76
  export declare const THREAT_EXEMPTION_HINT = " A deployment that needs a specific label can exempt it via threatExemptLabels (see the README env/dial reference).";
77
+ export {};
77
78
  //# sourceMappingURL=threats.d.ts.map
@@ -105,6 +105,16 @@ export declare class ToolDispatchNormalizer {
105
105
  constructor(options?: {
106
106
  maxTracked?: number;
107
107
  });
108
+ /**
109
+ * v43 audit (FLOW4-4): the ledger key. A normalizer shared by every session
110
+ * (skill-usage's live listener holds ONE process-wide instance) used the bare
111
+ * call id, so two sessions that produced the same id — PTC sub-call ids are
112
+ * short, and an id-less payload falls back to a type+payload key that is not
113
+ * unique by construction — collided: the second session's read was absorbed as
114
+ * "already seen" and never counted, and a settle in one session flipped the
115
+ * other's `ok`. Callers that span sessions pass the session id as `scope`.
116
+ */
117
+ private keyOf;
108
118
  /**
109
119
  * Absorb one session event.
110
120
  * @param event - the event to absorb; any non-dispatch event is ignored.
@@ -115,7 +125,7 @@ export declare class ToolDispatchNormalizer {
115
125
  advance(event: {
116
126
  type: string;
117
127
  data?: unknown;
118
- }): ToolDispatchSignal | null;
128
+ }, scope?: string): ToolDispatchSignal | null;
119
129
  /**
120
130
  * S2-P2-12: the SETTLE channel. \`advance\` answers \`null\` for the paired
121
131
  * event that settles an already-emitted dispatch, so a consumer that must act
@@ -129,7 +139,7 @@ export declare class ToolDispatchNormalizer {
129
139
  settledSignalOf(event: {
130
140
  type: string;
131
141
  data?: unknown;
132
- }): ToolDispatchSignal | null;
142
+ }, scope?: string): ToolDispatchSignal | null;
133
143
  /** Oldest-first eviction once the ledger (or the settle markers) overflows. */
134
144
  private evict;
135
145
  /** Every emitted dispatch, in first-seen order. */
@@ -3,7 +3,7 @@
3
3
  * Format-compatible with Hermes Agent / hermes-claw core fields.
4
4
  */
5
5
  import { type EvolutionIoLike } from './io.ts';
6
- export type SkillState = 'active' | 'stale' | 'archived';
6
+ type SkillState = 'active' | 'stale' | 'archived';
7
7
  export interface UsageRecord {
8
8
  created_by: string | null;
9
9
  use_count: number;
@@ -127,4 +127,5 @@ export declare function saveSuppressedNames(root: string, names: ReadonlySet<str
127
127
  * sharing DSH_HOME cannot interleave its RMW. Best-effort posture unchanged.
128
128
  */
129
129
  export declare function updateSuppressedNames(root: string, io: EvolutionIoLike, task: (names: Set<string>) => void | Promise<void>): Promise<void>;
130
+ export {};
130
131
  //# sourceMappingURL=usage.d.ts.map
@@ -16,7 +16,7 @@
16
16
  * @module @lmzhen/dsh-evolution-core/src/write-inventory
17
17
  */
18
18
  /** How concurrent writers of one site are kept apart. */
19
- export type WriteSerialization = 'transact' | 'write-lock' | 'instance-claim';
19
+ type WriteSerialization = 'transact' | 'write-lock' | 'instance-claim';
20
20
  /** One declared persisted write site. */
21
21
  export interface PersistedWriteSite {
22
22
  /** Stable id; referenced by the gate's failure text and by the specs. */
@@ -43,8 +43,19 @@ export declare const INSTANCE_KEYS: {
43
43
  /** The per-home curator: report writing + the retention sweep. */
44
44
  readonly curator: "evolution-curator";
45
45
  };
46
- /** The declared persisted write sites, in file order. */
47
- export declare const PERSISTED_WRITE_SITES: readonly PersistedWriteSite[];
46
+ /**
47
+ * The declared persisted write sites, in file order.
48
+ *
49
+ * v43 audit (S2-3 / P1-10): this used to be a module-scope readFileSync plus
50
+ * parse, so an unshipped asset threw AT IMPORT — one absent file took the whole
51
+ * family's load down (0.3.79 shipped a tarball without this asset and every
52
+ * package failed to load). The read is lazy now: importing the package never
53
+ * fails on this asset, while the first caller still gets a loud, descriptive
54
+ * failure instead of an empty table ("no declared write sites" would silently
55
+ * disable rule N20).
56
+ * @returns the sites, in file order.
57
+ */
58
+ export declare function persistedWriteSites(): readonly PersistedWriteSite[];
48
59
  /** Sites serialized by the per-home instance claim, with their instance keys. */
49
60
  export declare function instanceClaimedWriteSites(): readonly (PersistedWriteSite & {
50
61
  readonly instance: string;
@@ -52,4 +63,5 @@ export declare function instanceClaimedWriteSites(): readonly (PersistedWriteSit
52
63
  /** One declared site by id. An undeclared id throws — a stale caller must fail
53
64
  * loud rather than read "nothing is declared". */
54
65
  export declare function persistedWriteSite(id: string): PersistedWriteSite;
66
+ export {};
55
67
  //# sourceMappingURL=write-inventory.d.ts.map
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@lmzhen/dsh-evolution-core",
3
3
  "description": "Shared stores, prompts, signals and lifecycle logic for the dsh-evolution plugin family (community build)",
4
- "version": "0.3.80",
4
+ "version": "0.3.82",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -61,11 +61,10 @@
61
61
  "id": "curator-reports",
62
62
  "path": "<evolutionHome>/reports/curator-*.json",
63
63
  "writer": "evolution-curator/src/index.ts",
64
- "serializedBy": "instance-claim",
65
- "marker": "claimInstance(",
66
- "instance": "evolution-curator",
64
+ "serializedBy": "transact",
65
+ "marker": "transactIo(this.io, reportsSweepLockTarget()",
67
66
  "state": [],
68
- "note": "Curator run reports + their retention sweep. No per-file lock: the writer holds the per-home instance claim, so a second curator yields instead of racing the sweep."
67
+ "note": "Curator run reports + their retention sweep. The sweep is a multi-step list + delete-beyond-the-window over a DIRECTORY, so it has no per-file lock: v43 FLOW2-1 put it inside the IO backend's cross-process write lock (transactIo) on a per-home lock target, <evolutionHome>/reports/.retention (lock file <target>.lock, pid:token body). The former declaration here was instance-claim, which is in-process ONLY (instance-scope.ts is a module-scope Map). Two curator ROWS in one process still cannot sweep at once (the loser yields on that claim), and report writes are name-unique per runId. Trigger for the remaining gap: a second PROCESS sharing DSH_HOME now WAITS on the lock instead of sweeping concurrently, and on a backend without transact there is no lock at all, so the sweep degrades to best-effort idempotent deletes."
69
68
  },
70
69
  {
71
70
  "id": "skill-tree",