@lmzhen/dsh-evolution-core 0.3.63 → 0.3.65
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 +14 -12
- package/lib/index.js +460 -153
- package/lib/types/constants.d.ts +10 -1
- package/lib/types/events.d.ts +3 -1
- package/lib/types/evolution-events.d.ts +11 -0
- package/lib/types/io.d.ts +15 -0
- package/lib/types/memory-store.d.ts +4 -0
- package/lib/types/quality.d.ts +2 -2
- package/lib/types/skill-store.d.ts +47 -14
- package/lib/types/threats.d.ts +16 -4
- package/lib/types/usage.d.ts +8 -2
- package/package.json +1 -1
package/lib/types/constants.d.ts
CHANGED
|
@@ -19,7 +19,13 @@
|
|
|
19
19
|
* threshold, which are intentionally left where they are used.
|
|
20
20
|
* @module @lmzhen/dsh-evolution-core
|
|
21
21
|
*/
|
|
22
|
-
/** Skill frontmatter `name` validated for the file name (lowercase + hyphen).
|
|
22
|
+
/** Skill frontmatter `name` validated for the file name (lowercase + hyphen).
|
|
23
|
+
* 计划 B-4 (v18): tightened to the UPSTREAM `SKILL_NAME` shape
|
|
24
|
+
* (`/^[a-z0-9]+(?:-[a-z0-9]+)*$/`, packages/skill/skill/src/index.ts:20). The
|
|
25
|
+
* old form admitted trailing/consecutive hyphens, which upstream
|
|
26
|
+
* `validateCandidate` throws on — and that throw aborts the WHOLE `ctx.skills`
|
|
27
|
+
* collection. The catalog provider still filters such legacy tree entries so
|
|
28
|
+
* an existing tree cannot break a session. */
|
|
23
29
|
export declare const SKILL_NAME_RE: RegExp;
|
|
24
30
|
/** Allowed skill support-file subdirectories (path-traversal boundary). */
|
|
25
31
|
export declare const SUPPORT_DIRS: readonly ["references", "templates", "scripts", "assets"];
|
|
@@ -53,6 +59,9 @@ export declare const DEFAULT_MEMORY_CHAR_LIMIT = 2200;
|
|
|
53
59
|
export declare const DEFAULT_USER_CHAR_LIMIT = 1375;
|
|
54
60
|
/** Consolidation-failure backoff cap, shared by MemoryStore and memory-files' Config default. */
|
|
55
61
|
export declare const DEFAULT_CONSOLIDATION_FAILURES = 3;
|
|
62
|
+
/** F-20 (v18): the authored-body budget and the hard ceiling are the same
|
|
63
|
+
* number today. Derive it so a future divergence is one edit, not two names
|
|
64
|
+
* that silently disagree. */
|
|
56
65
|
export declare const DEFAULT_SKILL_CONTENT_CHARS = 100000;
|
|
57
66
|
/** P3-19 (v14): defaults that were written twice (schema `.default()` AND the
|
|
58
67
|
* clamp fallback literal) now have one home per value. */
|
package/lib/types/events.d.ts
CHANGED
|
@@ -12,10 +12,12 @@
|
|
|
12
12
|
* these events off `session.append`). Plan-outcome durability lives in the
|
|
13
13
|
* evolution-activity store, not the session log.
|
|
14
14
|
*/
|
|
15
|
+
import type { ReviewKind } from './signals.ts';
|
|
15
16
|
export interface EvolutionReviewScheduledEvent {
|
|
16
17
|
/** Owning session (payload v2): process events carry no session envelope. */
|
|
17
18
|
sessionId: string;
|
|
18
|
-
|
|
19
|
+
/** F-20 (v18): single definition point — `ReviewKind` in signals.ts. */
|
|
20
|
+
kind: ReviewKind;
|
|
19
21
|
toolCalls: number;
|
|
20
22
|
userChars: number;
|
|
21
23
|
assistantChars: number;
|
|
@@ -68,6 +68,17 @@ export interface EvolutionEvent {
|
|
|
68
68
|
} | undefined;
|
|
69
69
|
}
|
|
70
70
|
export declare function eventsFile(home: string): string;
|
|
71
|
+
/** I-5 (v18): one lightweight description of the durable event payload
|
|
72
|
+
* contract. The log is a FILE boundary (a host, a script or an older version
|
|
73
|
+
* can write it), so `appendEvolutionEvent` refuses a record no consumer can
|
|
74
|
+
* fold instead of persisting it and failing silently later. The process event
|
|
75
|
+
* bus stays unvalidated — that is a typed same-process boundary.
|
|
76
|
+
* @param event - the candidate event record.
|
|
77
|
+
* @returns a human-readable issue, or null when the record is well-formed.
|
|
78
|
+
*/
|
|
79
|
+
export declare function evolutionEventPayloadIssue(event: {
|
|
80
|
+
type?: unknown;
|
|
81
|
+
} & Partial<EvolutionEvent>): string | null;
|
|
71
82
|
export declare function parseEvolutionEvents(raw: string | null): EvolutionEvent[];
|
|
72
83
|
/**
|
|
73
84
|
* List the numeric archives under the log's directory, sorted ascending by
|
package/lib/types/io.d.ts
CHANGED
|
@@ -106,6 +106,21 @@ export declare function renameWithRetry(tmp: string, target: string, fn?: (from:
|
|
|
106
106
|
* failing `handle.sync()` deterministically.
|
|
107
107
|
*/
|
|
108
108
|
export declare function writeDurableTmp(target: string, content: string, openImpl?: typeof open): Promise<string>;
|
|
109
|
+
/**
|
|
110
|
+
* True when the pid is alive (EPERM = alive but unowned; ESRCH = gone).
|
|
111
|
+
* V18 single source: the node backend's lock takeover and SkillLibrary's
|
|
112
|
+
* stranded-lock sweep must use the same liveness rule.
|
|
113
|
+
*/
|
|
114
|
+
export declare function isProcessAlive(pid: number): boolean;
|
|
115
|
+
/** F-17 (v18): the write-lock protocol is a cross-module contract — the lock
|
|
116
|
+
* file is `<target>.lock` and its body is `<pid>:<token>`. This module creates
|
|
117
|
+
* them (`withWriteLock`) and `skill-store`'s probes/sweepers parse them, so both
|
|
118
|
+
* consume these two constants instead of repeating the literals. */
|
|
119
|
+
export declare const LOCK_SUFFIX = ".lock";
|
|
120
|
+
/** Writer-lock body shape: a decimal pid, a colon, then the claim token. A
|
|
121
|
+
* torn body (no parsable pid) still matches the `\\d+:` prefix rule only when
|
|
122
|
+
* the pid part is intact, which is what the takeover probe needs. */
|
|
123
|
+
export declare const LOCK_BODY_RE: RegExp;
|
|
109
124
|
/**
|
|
110
125
|
* Build the Node IO backend. `lockAttempts` scales the write-lock retry budget
|
|
111
126
|
* (attempts × 50ms); the default 40 (~2s, rc.69) covers production contention,
|
|
@@ -57,6 +57,10 @@ export declare class MemoryStore {
|
|
|
57
57
|
* label (scanMemoryThreats already embeds it) plus the self-heal hint. */
|
|
58
58
|
private memoryThreatBlock;
|
|
59
59
|
limitFor(target: MemoryTarget): number;
|
|
60
|
+
/** P2-1 (v18): the generated date prefix participates in duplicate detection
|
|
61
|
+
* only when THIS store writes it. With addDatePrefix=false a fact's own
|
|
62
|
+
* leading `## YYYY-MM-DD\n` is content, not a generated prefix. */
|
|
63
|
+
private dedupeKey;
|
|
60
64
|
/**
|
|
61
65
|
* Read-guard probe: `{ size, limit }` when the on-disk file exceeds
|
|
62
66
|
* `limit * READ_GUARD_FACTOR` bytes, `null` when it is absent, unknown
|
package/lib/types/quality.d.ts
CHANGED
|
@@ -11,9 +11,9 @@
|
|
|
11
11
|
*/
|
|
12
12
|
import type { UsageMap } from './usage.ts';
|
|
13
13
|
export interface QualityFactors {
|
|
14
|
-
/** 0.25 —
|
|
14
|
+
/** 0.25 — skill LOADS per day of age (view_count + use_count), capped at 1. */
|
|
15
15
|
usageFrequency: number;
|
|
16
|
-
/** 0.20 — 1 − patch/
|
|
16
|
+
/** 0.20 — 1 − patch/load (zero loads = stable). */
|
|
17
17
|
stability: number;
|
|
18
18
|
/** 0.20 — 1 under 30 idle days, linear decay to 0 at 180. */
|
|
19
19
|
recency: number;
|
|
@@ -22,8 +22,15 @@ export interface SkillSummary {
|
|
|
22
22
|
description: string;
|
|
23
23
|
path: string;
|
|
24
24
|
protectedBy: string | null;
|
|
25
|
+
/** A1-17 (v18): the marker probe itself failed (EACCES/EIO), so "no marker"
|
|
26
|
+
* cannot be told apart from "directory unreadable". Consumers must treat this
|
|
27
|
+
* as protected, never as unprotected. */
|
|
28
|
+
protectionUnknown: boolean;
|
|
25
29
|
managed: boolean;
|
|
26
|
-
|
|
30
|
+
/** E-11 (v18): the frontmatter `whenToUse` routing hint, published so the
|
|
31
|
+
* platform catalog keeps it while this provider shadows the upstream
|
|
32
|
+
* filesystem provider. Absent when the frontmatter has none. */
|
|
33
|
+
whenToUse?: string;
|
|
27
34
|
}
|
|
28
35
|
export interface SkillActionResult {
|
|
29
36
|
ok: boolean;
|
|
@@ -99,6 +106,21 @@ export declare function skillsRoot(env?: NodeJS.ProcessEnv): string;
|
|
|
99
106
|
export declare function resolveSkillsRoot(config?: {
|
|
100
107
|
root?: string | undefined;
|
|
101
108
|
}): string;
|
|
109
|
+
/** E-7 (v18): every family row reads ONE root key. `root` is canonical;
|
|
110
|
+
* `skillsRoot` is a deprecated alias honoured only while `root` is empty (so a
|
|
111
|
+
* deployment that sets both keeps the canonical one) and removed after 0.3.65.
|
|
112
|
+
* Callers log their own deprecation warning.
|
|
113
|
+
* @param config - the raw plugin config, carrying `root` and/or `skillsRoot`.
|
|
114
|
+
* @returns the effective root (empty when neither key is set) and whether the
|
|
115
|
+
* deprecated alias supplied it.
|
|
116
|
+
*/
|
|
117
|
+
export declare function resolveRootConfig(config?: {
|
|
118
|
+
root?: string | undefined;
|
|
119
|
+
skillsRoot?: string | undefined;
|
|
120
|
+
}): {
|
|
121
|
+
root: string;
|
|
122
|
+
usedDeprecatedAlias: boolean;
|
|
123
|
+
};
|
|
102
124
|
/**
|
|
103
125
|
* Map a requesting session onto the two origin surfaces (rc.44 plan M2-2.3):
|
|
104
126
|
* the APPROVAL surface treats every delegated subagent as the autonomous
|
|
@@ -228,13 +250,11 @@ export interface AuthoringFeedback {
|
|
|
228
250
|
* truncated or route-poor instead of silently shipping it.
|
|
229
251
|
*/
|
|
230
252
|
export declare function authoringFeedback(frontmatter: Frontmatter): AuthoringFeedback;
|
|
231
|
-
/**
|
|
232
|
-
*
|
|
233
|
-
*
|
|
234
|
-
*
|
|
235
|
-
|
|
236
|
-
* consume this one set — a third copy would drift. */
|
|
237
|
-
export declare const WIN32_RESERVED_DEVICE_NAMES: ReadonlySet<string>;
|
|
253
|
+
/** A1-6 (v18): the regex alone admits `references/nul.md` (a Windows device
|
|
254
|
+
* stem), which the support-file layer refuses. Restructure must use the same
|
|
255
|
+
* rule, or it creates an orphan the later patch/write/remove paths refuse.
|
|
256
|
+
* Single source shared with the plan validator. */
|
|
257
|
+
export declare function validateRestructureTarget(filePath: string): string | null;
|
|
238
258
|
export declare class SkillLibrary {
|
|
239
259
|
readonly root: string;
|
|
240
260
|
readonly limits: SkillLimits;
|
|
@@ -340,6 +360,7 @@ export declare class SkillLibrary {
|
|
|
340
360
|
* marker write is the only state change; content is untouched.
|
|
341
361
|
*/
|
|
342
362
|
setPinned(name: string, pinned: boolean, origin?: WriteOrigin): Promise<SkillActionResult>;
|
|
363
|
+
private setPinnedCore;
|
|
343
364
|
create(name: string, content: string, origin?: WriteOrigin): Promise<SkillActionResult>;
|
|
344
365
|
private createCore;
|
|
345
366
|
update(rawName: string, content: string, origin?: WriteOrigin): Promise<SkillActionResult>;
|
|
@@ -378,15 +399,27 @@ export declare class SkillLibrary {
|
|
|
378
399
|
* or be swept as residue — the v16 first cut matched on suffix alone,
|
|
379
400
|
* which permanently refused archiving and deleted user content on restore. */
|
|
380
401
|
private isWriterLock;
|
|
381
|
-
/** P2 (v16): best-effort removal of lock residue inside a RESTORED tree
|
|
382
|
-
*
|
|
383
|
-
*
|
|
384
|
-
*
|
|
385
|
-
*
|
|
402
|
+
/** P2 (v16): best-effort removal of lock residue inside a RESTORED tree.
|
|
403
|
+
* A1-2/A1-7 (v18): the sweep now covers the marker locks the probe checks
|
|
404
|
+
* (`SKILL.md.lock`/`.pinned.lock`/`.hermes-managed.lock`) and only removes
|
|
405
|
+
* a lock whose holder pid is NOT alive — a live writer's lock is never
|
|
406
|
+
* stolen by the sweep. A dead-pid residue would otherwise permanently
|
|
407
|
+
* refuse archive/restore. */
|
|
386
408
|
private deleteStrandedLocks;
|
|
387
409
|
/** Remove `lockPath` only when its body has the writer-lock `pid:token`
|
|
388
|
-
* shape; anything else (a user support file
|
|
410
|
+
* shape AND the holder pid is not alive; anything else (a user support file
|
|
411
|
+
* or a live writer's lock) is left untouched. */
|
|
389
412
|
private sweepLockIfStranded;
|
|
413
|
+
/** A1-4 (v18): a manifest-declared name is copied with `join(root, name)`;
|
|
414
|
+
* only a single, non-traversing path component is safe. Dotfiles
|
|
415
|
+
* (`.usage.json`) stay allowed — sidecars are legitimately dot-prefixed.
|
|
416
|
+
* P2-4 (v19): a non-string entry (`skills: [123]`) is refused structurally
|
|
417
|
+
* instead of throwing `name.includes is not a function`. */
|
|
418
|
+
private safeSnapshotEntryName;
|
|
419
|
+
/** A1-7 (v18): a root-level lock whose holder is alive must refuse the
|
|
420
|
+
* restore; a dead residue is swept so a crashed writer cannot block
|
|
421
|
+
* recovery. A non-lock body shape is left alone (user file). */
|
|
422
|
+
private refuseLiveLockOrSweep;
|
|
390
423
|
archive(rawName: string, options?: ArchiveOptions): Promise<SkillActionResult>;
|
|
391
424
|
/**
|
|
392
425
|
* Merge the bodies of `sources` into `target` and archive the sources with
|
package/lib/types/threats.d.ts
CHANGED
|
@@ -2,15 +2,25 @@
|
|
|
2
2
|
* Threat scanning for agent-authored memory and skill content.
|
|
3
3
|
*
|
|
4
4
|
* Ported as a small, dependency-free subset of Hermes Agent's
|
|
5
|
-
* `tools/threat_patterns.py` + hermes-claw `threats.ts`.
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* `tools/threat_patterns.py` + hermes-claw `threats.ts`.
|
|
6
|
+
*
|
|
7
|
+
* Policy (P1-1, v19): a finding either BLOCKS the write or only REPORTS.
|
|
8
|
+
* Blocking is reserved for shapes with no legitimate use in stored knowledge
|
|
9
|
+
* (prompt-injection phrasing, credential exfiltration, the invisible-character
|
|
10
|
+
* smuggling core). Typography and presentation characters that are legitimate
|
|
11
|
+
* in ordinary prose — variation selectors (❤️), zero-width non-joiner, soft
|
|
12
|
+
* hyphen from PDF paste, Arabic letter mark, Mongolian vowel separator — are
|
|
13
|
+
* REPORT findings: they stay visible to operators and tests but never reject a
|
|
14
|
+
* write. Blocking them turned every emoji into a security event.
|
|
8
15
|
*/
|
|
9
16
|
export type ThreatScope = 'all' | 'context' | 'strict';
|
|
10
17
|
export interface ThreatFinding {
|
|
11
18
|
label: string;
|
|
12
19
|
category: string;
|
|
13
20
|
scope: ThreatScope;
|
|
21
|
+
/** P1-1 (v19): `block` (default) refuses the write; `report` is an audit
|
|
22
|
+
* trail entry only. Absent means `block`. */
|
|
23
|
+
severity?: 'block' | 'report';
|
|
14
24
|
}
|
|
15
25
|
/**
|
|
16
26
|
* Optional scan controls. Default behavior (`options` omitted) is unchanged:
|
|
@@ -39,7 +49,9 @@ export declare const PATTERN_OVERLAP = 4096;
|
|
|
39
49
|
* characters (skill files may run to 100,000) is no longer a blind zone.
|
|
40
50
|
*/
|
|
41
51
|
export declare function scanThreats(text: string, scope?: ThreatScope, maxScanChars?: number, options?: ScanOptions): ThreatFinding[];
|
|
42
|
-
/** Blocking policy:
|
|
52
|
+
/** Blocking policy (P1-1, v19): a finding blocks unless it is explicitly
|
|
53
|
+
* `report`-only. Pattern findings carry no severity and therefore block as
|
|
54
|
+
* before. */
|
|
43
55
|
export declare function evaluateThreat(text: string, scope?: ThreatScope, maxScanChars?: number, options?: ScanOptions): {
|
|
44
56
|
blocked: boolean;
|
|
45
57
|
findings: ThreatFinding[];
|
package/lib/types/usage.d.ts
CHANGED
|
@@ -52,7 +52,13 @@ export declare function loadUsage(root: string, io?: EvolutionIoLike): Promise<U
|
|
|
52
52
|
* cannot interleave its RMW and lose a counter update. Callers keep their own
|
|
53
53
|
* single-process serialize chain as the second layer.
|
|
54
54
|
*/
|
|
55
|
-
export
|
|
55
|
+
export interface UsageMutateOptions {
|
|
56
|
+
/** P2-9 (v19): called when malformed entries had to be quarantined before the
|
|
57
|
+
* task could run. The guard preserves bytes AND keeps the facility working;
|
|
58
|
+
* this callback is how that stays observable. */
|
|
59
|
+
onQuarantine?: ((message: string) => void) | undefined;
|
|
60
|
+
}
|
|
61
|
+
export declare function mutateUsage(root: string, io: EvolutionIoLike, task: (map: UsageMap) => void | Promise<void>, options?: UsageMutateOptions): Promise<void>;
|
|
56
62
|
/**
|
|
57
63
|
* Curator-owned usage fields (rc.67 K-2): the curator writes ONLY this set —
|
|
58
64
|
* lifecycle state, archive stamp, the six-factor quality pair, and the
|
|
@@ -85,7 +91,7 @@ export declare function applyCuratorMetaFields(disk: UsageRecord, curated: Usage
|
|
|
85
91
|
* transitioned — a concurrent curator run's archive/restore is never reverted
|
|
86
92
|
* by a stale snapshot; without it both pairs apply everywhere.
|
|
87
93
|
*/
|
|
88
|
-
export declare function foldCuratorFields(disk: UsageMap, curated: UsageMap, stateOwned?: ReadonlySet<string>):
|
|
94
|
+
export declare function foldCuratorFields(disk: UsageMap, curated: UsageMap, stateOwned?: ReadonlySet<string>, runStartStates?: ReadonlyMap<string, string>): string[];
|
|
89
95
|
/** Whole-file usage write (V6-37, 0.3.37): this is the ONE path that bypasses
|
|
90
96
|
* the malformed-defense and the transact lock — prefer `mutateUsage` for any
|
|
91
97
|
* read-modify-write so a concurrent writer cannot lose its update and a
|
package/package.json
CHANGED