@lmzhen/dsh-evolution-core 0.3.81 → 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.
- package/lib/index.js +673 -480
- package/lib/types/drift-signals.d.ts +3 -2
- package/lib/types/events.d.ts +2 -1
- package/lib/types/evolution-events.d.ts +24 -15
- package/lib/types/frontmatter.d.ts +169 -0
- package/lib/types/fuzzy-match.d.ts +18 -0
- package/lib/types/gates.d.ts +3 -2
- package/lib/types/instance-scope.d.ts +21 -2
- package/lib/types/io.d.ts +31 -2
- package/lib/types/limits.d.ts +15 -0
- package/lib/types/opt-in.d.ts +34 -4
- package/lib/types/skill-health.d.ts +3 -2
- package/lib/types/skill-store.d.ts +5 -156
- package/lib/types/threats.d.ts +3 -2
- package/lib/types/tool-dispatch.d.ts +12 -2
- package/lib/types/usage.d.ts +2 -1
- package/lib/types/write-inventory.d.ts +15 -3
- package/package.json +1 -1
- package/persisted-write-inventory.json +3 -4
|
@@ -29,7 +29,7 @@ export interface DriftSkillSnapshot {
|
|
|
29
29
|
catalogInvalid?: boolean | undefined;
|
|
30
30
|
}
|
|
31
31
|
/** verdict=over means "relatively positioned above the threshold", never a violation. */
|
|
32
|
-
|
|
32
|
+
type DriftVerdict = 'pass' | 'over' | 'unknown';
|
|
33
33
|
export interface DriftSignal {
|
|
34
34
|
id: string;
|
|
35
35
|
verdict: DriftVerdict;
|
|
@@ -40,7 +40,7 @@ export interface DriftSignal {
|
|
|
40
40
|
/** Extra evidence (matched shapes, line numbers, group members). */
|
|
41
41
|
detail?: string | undefined;
|
|
42
42
|
}
|
|
43
|
-
|
|
43
|
+
interface DriftSkillAssessment {
|
|
44
44
|
name: string;
|
|
45
45
|
signals: ReadonlyArray<DriftSignal>;
|
|
46
46
|
/** Passthrough from the snapshot (0.3.11): protection marker, catalog loadability. */
|
|
@@ -80,4 +80,5 @@ export declare function narrowNameMatches(name: string): string[];
|
|
|
80
80
|
export declare function computeDriftSignals(snapshots: ReadonlyArray<DriftSkillSnapshot>): DriftReport;
|
|
81
81
|
/** Convenience: fetch one signal from an assessment or library list. */
|
|
82
82
|
export declare function findDriftSignal(signals: ReadonlyArray<DriftSignal>, id: string): DriftSignal | undefined;
|
|
83
|
+
export {};
|
|
83
84
|
//# sourceMappingURL=drift-signals.d.ts.map
|
package/lib/types/events.d.ts
CHANGED
|
@@ -73,7 +73,7 @@ export interface EvolutionSkillMutatedEvent {
|
|
|
73
73
|
/** 0.3.18 (E-6): a turn-end review pipeline failure was caught (never an
|
|
74
74
|
* unhandled rejection); this event lets operators/observability see it. The
|
|
75
75
|
* reason is already logged by the emitter — the event is a timestamped signal. */
|
|
76
|
-
|
|
76
|
+
interface EvolutionReviewErrorEvent {
|
|
77
77
|
sessionId: string;
|
|
78
78
|
}
|
|
79
79
|
declare module '@deepseek-ai/cordis' {
|
|
@@ -89,4 +89,5 @@ declare module '@deepseek-ai/cordis' {
|
|
|
89
89
|
'evolution/review-error'(event: EvolutionReviewErrorEvent): void;
|
|
90
90
|
}
|
|
91
91
|
}
|
|
92
|
+
export {};
|
|
92
93
|
//# sourceMappingURL=events.d.ts.map
|
|
@@ -75,7 +75,7 @@ export interface EvolutionEvent {
|
|
|
75
75
|
/** The durable-write shape of one event: `feedback` REQUIRES a `target` —
|
|
76
76
|
* the fold key the aggregate is keyed by (P2-10). The other tags keep every
|
|
77
77
|
* field optional, exactly as the runtime payload gate treats them. */
|
|
78
|
-
|
|
78
|
+
type EvolutionEventInput = (Omit<EvolutionEvent, 'seq' | 'at' | 'target'> & {
|
|
79
79
|
type: 'feedback';
|
|
80
80
|
target: string;
|
|
81
81
|
}) | (Omit<EvolutionEvent, 'seq' | 'at'> & {
|
|
@@ -128,28 +128,37 @@ export declare function appendEvolutionEvent(io: EvolutionIoLike, path: string,
|
|
|
128
128
|
* Best-effort per removal; exported for the retention test.
|
|
129
129
|
*/
|
|
130
130
|
export declare function retainEventArchives(io: EvolutionIoLike, path: string): Promise<void>;
|
|
131
|
-
|
|
131
|
+
interface EventLogRead {
|
|
132
132
|
events: EvolutionEvent[];
|
|
133
|
-
/** True when
|
|
134
|
-
*
|
|
135
|
-
*
|
|
136
|
-
* append
|
|
137
|
-
*
|
|
138
|
-
*
|
|
133
|
+
/** True when THIS read DROPPED events the file may hold, so the result must
|
|
134
|
+
* never be treated as the complete truth for that file. Three causes flag it:
|
|
135
|
+
* syntax-level damage and a READ error (EISDIR/EACCES) — both refused on
|
|
136
|
+
* append with their bytes untouched — and, since C-events-dispatch-1 (v43
|
|
137
|
+
* audit), a body whose `version` this reader cannot interpret: F-338 keeps
|
|
138
|
+
* such a body un-reshaped and never rewritten down, but its records ARE
|
|
139
|
+
* missing from the read. A well-formed body with a damaged `events` field
|
|
140
|
+
* stays UNflagged — REPLACEABLE garbage that reads as empty and is rewritten
|
|
141
|
+
* at the next append (rc.70 F-1: read and append agree on the same boundary). */
|
|
139
142
|
malformed: boolean;
|
|
140
143
|
}
|
|
141
|
-
/** Read the event log; a missing/whitespace-only file reads as empty,
|
|
142
|
-
*
|
|
143
|
-
*
|
|
144
|
-
*
|
|
145
|
-
*
|
|
144
|
+
/** Read the event log; a missing/whitespace-only file reads as empty, corrupt
|
|
145
|
+
* content is flagged (and refused on append). A well-formed future-version body
|
|
146
|
+
* is v1-incompatible: it reads as EMPTY and is now flagged malformed as well
|
|
147
|
+
* (C-events-dispatch-1, v43). F-338's own guarantees are untouched — the reader
|
|
148
|
+
* never mis-shapes a newer format and the append path refuses it up front, so
|
|
149
|
+
* the original bytes survive — while the flag reports what the old reader hid:
|
|
150
|
+
* every record that body holds is dropped from this read. */
|
|
146
151
|
export declare function readEvolutionEvents(io: EvolutionIoLike, path: string): Promise<EventLogRead>;
|
|
147
152
|
/**
|
|
148
153
|
* Read the full timeline (rc.71): active log + all archives, merged by seq
|
|
149
154
|
* (active copy wins, duplicates only arise from the rotation crash window),
|
|
150
155
|
* sorted ascending. Per-file malformed flag as in `readEvolutionEvents`; a
|
|
151
|
-
*
|
|
152
|
-
*
|
|
156
|
+
* flagged ARCHIVE (unreadable, damaged, or a future-version body this reader
|
|
157
|
+
* cannot interpret) is SKIPPED — it never bricks the boot, the returned events
|
|
158
|
+
* simply LACK that seq band, and `malformed` is the only signal that they do
|
|
159
|
+
* (C-events-dispatch-1, v43: the flag is the consumer's contract; a truncated
|
|
160
|
+
* timeline must never be folded back as if it were complete).
|
|
153
161
|
*/
|
|
154
162
|
export declare function readEvolutionTimeline(io: EvolutionIoLike, path: string, archives?: readonly string[]): Promise<EventLogRead>;
|
|
163
|
+
export {};
|
|
155
164
|
//# sourceMappingURL=evolution-events.d.ts.map
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Frontmatter parsing, normalization and validation for skill Markdown files.
|
|
3
|
+
*
|
|
4
|
+
* Split out of skill-store.ts (S2-1): pure functions over file text, no store
|
|
5
|
+
* state. skill-store.ts re-exports the same names it exported before the split,
|
|
6
|
+
* so the package export surface is unchanged.
|
|
7
|
+
*/
|
|
8
|
+
import type { SkillLimits } from './limits.ts';
|
|
9
|
+
export interface Frontmatter {
|
|
10
|
+
name?: string;
|
|
11
|
+
description?: string;
|
|
12
|
+
[key: string]: unknown;
|
|
13
|
+
}
|
|
14
|
+
export declare function frontmatterBlock(content: string): {
|
|
15
|
+
block: string;
|
|
16
|
+
lines: string[];
|
|
17
|
+
end: number;
|
|
18
|
+
nl: string;
|
|
19
|
+
} | null;
|
|
20
|
+
/**
|
|
21
|
+
* One frontmatter read (V27 G2.1): the values, the body, and every signal the
|
|
22
|
+
* strict-YAML platform catalog derives from the same block. Returned by
|
|
23
|
+
* {@link parseFrontmatter} so a caller never has to parse the block twice to
|
|
24
|
+
* reach a description and the catalog verdict.
|
|
25
|
+
*/
|
|
26
|
+
export interface FrontmatterRead {
|
|
27
|
+
frontmatter: Frontmatter;
|
|
28
|
+
body: string;
|
|
29
|
+
/** Raw entries whose UNQUOTED value the strict catalog cannot load as
|
|
30
|
+
* written. Quotes are included, so a value already normalized by the write
|
|
31
|
+
* path (`normalizeFrontmatter`) is never re-flagged. */
|
|
32
|
+
unsafeValues: Array<{
|
|
33
|
+
key: string;
|
|
34
|
+
value: string;
|
|
35
|
+
}>;
|
|
36
|
+
/** Whether the frontmatter is not valid AS WRITTEN for the strict platform
|
|
37
|
+
* catalog: the strict parser rejects the block, or an unquoted value would
|
|
38
|
+
* read as something other than its text (a dropped ` # ` comment, a
|
|
39
|
+
* number/bool shorthand the catalog refuses as a string field), or a
|
|
40
|
+
* platform string field carries a non-string value. The write path quotes
|
|
41
|
+
* such a value on its next edit. */
|
|
42
|
+
catalogInvalid: boolean;
|
|
43
|
+
/** Platform string fields (`name`/`description`/`whenToUse`) whose YAML value
|
|
44
|
+
* is not a string: the strict catalog reads such a field as ABSENT and, for an
|
|
45
|
+
* absent name/description, ignores the whole file (P2-9/v37). */
|
|
46
|
+
platformStringSplit: PlatformStringSplit[];
|
|
47
|
+
}
|
|
48
|
+
/** One `name`/`description`/`whenToUse` entry the strict catalog cannot read as
|
|
49
|
+
* a string, with the YAML kind the parser found. */
|
|
50
|
+
export interface PlatformStringSplit {
|
|
51
|
+
key: string;
|
|
52
|
+
/** The YAML type read for this field. `scalar` covers number/boolean (the
|
|
53
|
+
* E-47 auto-quote repair handles those); `sequence`/`mapping` are the shapes
|
|
54
|
+
* no rewrite can repair without inventing text — see `validateFrontmatter`. */
|
|
55
|
+
kind: 'sequence' | 'mapping' | 'scalar' | 'null';
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Parse a SKILL.md: its frontmatter values and body, or `null` when the file
|
|
59
|
+
* has no frontmatter block or no body. Every consumer of frontmatter values
|
|
60
|
+
* goes through here — the write path's validation, `list()`'s published
|
|
61
|
+
* description, `relatedSkillNames` and the audit — so all of them read the same
|
|
62
|
+
* bytes the same way.
|
|
63
|
+
*
|
|
64
|
+
* @param content - the SKILL.md text.
|
|
65
|
+
* @returns the read, or `null` when there is no block or no body.
|
|
66
|
+
*/
|
|
67
|
+
export declare function parseFrontmatter(content: string): FrontmatterRead | null;
|
|
68
|
+
/**
|
|
69
|
+
* Whether this file's frontmatter is valid as written for the strict platform
|
|
70
|
+
* catalog (see `FrontmatterRead.catalogInvalid`). Body-independent (a body-less
|
|
71
|
+
* file is still judged), and derived from the same read as `parseFrontmatter` —
|
|
72
|
+
* so the audit's verdict and the values the family publishes for one file can
|
|
73
|
+
* never disagree (V27 G2.1).
|
|
74
|
+
*
|
|
75
|
+
* @param content - the SKILL.md text.
|
|
76
|
+
* @returns `true` when the strict parser rejects the block or an unquoted value would read as something else.
|
|
77
|
+
*/
|
|
78
|
+
export declare function frontmatterCatalogInvalid(content: string): boolean;
|
|
79
|
+
/** YAML plain-scalar hazards that make an UNQUOTED frontmatter value
|
|
80
|
+
* unloadable to the platform catalog (strict YAML parser): `: ` (mapping
|
|
81
|
+
* separator), ` #` (comment start), a trailing `:` (a mapping marker),
|
|
82
|
+
* or a leading YAML indicator. The evolution `parseFrontmatter` is
|
|
83
|
+
* deliberately lenient, so violations silently split family-visibility from
|
|
84
|
+
* platform-visibility (0.3.11 inkos-harness case: the description carried
|
|
85
|
+
* "…: " and the catalog dropped the whole skill). Already-quoted values and
|
|
86
|
+
* well-formed flow collections (`[a, b]` / `{a: b}`) are considered safe.
|
|
87
|
+
* 0.3.16 (E-47): null/bool/number-shaped plain scalars are flagged too — they
|
|
88
|
+
* parse as booleans/numbers on the platform while the family keeps the string
|
|
89
|
+
* (a `description: true` split-brain).
|
|
90
|
+
* This rule is only the FAST PATH — the write path re-verifies every rewrite
|
|
91
|
+
* with the real YAML parser (see normalizeFrontmatter), so an incomplete
|
|
92
|
+
* approximation can never corrupt a multiline flow value (P3-4). */
|
|
93
|
+
export declare function yamlPlainScalarNeedsQuotes(value: string): boolean;
|
|
94
|
+
export interface FrontmatterNormalizeResult {
|
|
95
|
+
content: string;
|
|
96
|
+
changed: boolean;
|
|
97
|
+
/** Frontmatter keys whose values were auto-quoted. */
|
|
98
|
+
fields: string[];
|
|
99
|
+
/** Values that cannot be auto-quoted safely (control characters, or a
|
|
100
|
+
* rewrite that failed the real-parser verification — a multiline flow
|
|
101
|
+
* collection line etc. is left untouched and reported here, so the write
|
|
102
|
+
* path rejects instead of silently damaging a value; 0.3.14). */
|
|
103
|
+
issues: string[];
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Normalize a SKILL.md frontmatter block into catalog-loadable YAML: values
|
|
107
|
+
* that YAML forbids unquoted get quotes — double quotes normally, single
|
|
108
|
+
* quotes (with `''` doubling) when the value contains `"` or `\` (both legal
|
|
109
|
+
* unescaped inside single-quoted YAML). Idempotent; only single-line
|
|
110
|
+
* `key: value` entries are touched; body text is never modified; line-ending
|
|
111
|
+
* style is preserved. **Every rewrite is re-verified with the real YAML
|
|
112
|
+
* parser** (js-yaml — the same parser the platform catalog uses): if the
|
|
113
|
+
* rewritten block no longer parses, or a rewritten value's parsed content
|
|
114
|
+
* differs from the original, the rewrite is rolled back and reported in
|
|
115
|
+
* `issues` (fail-loud, never a silent value corruption — P3-4).
|
|
116
|
+
*
|
|
117
|
+
* V10-02 (P2-3): the rewrite decision is PER LINE — each entry parses its own
|
|
118
|
+
* value, so a duplicated key can never route one entry's unsafe value into a
|
|
119
|
+
* different line's rewrite (the old key→Map lookup rewrote the FIRST (safe)
|
|
120
|
+
* line with the SECOND line's quoted value, and the last-wins YAML reader
|
|
121
|
+
* masked the damage). A duplicated key is itself invalid input and is
|
|
122
|
+
* reported in `issues` (the write path refuses) instead of being rewritten.
|
|
123
|
+
*/
|
|
124
|
+
export declare function normalizeFrontmatter(content: string): FrontmatterNormalizeResult;
|
|
125
|
+
/**
|
|
126
|
+
* Skill names referenced by a SKILL.md's `related_skills` frontmatter
|
|
127
|
+
* (B-line G3, rc.44): the single parsing source for the quality references
|
|
128
|
+
* factor and the learning-graph edges. The DSH frontmatter parser keeps the
|
|
129
|
+
* YAML value as a string (`"[a, b]"`), so names are scanned out of it; each
|
|
130
|
+
* must satisfy the skill-name shape and the referencing skill itself is
|
|
131
|
+
* excluded. Pure and deduplicated.
|
|
132
|
+
*/
|
|
133
|
+
export declare function relatedSkillNames(content: string, exclude?: string): string[];
|
|
134
|
+
/** Whether `content` would exceed `limit` once written. */
|
|
135
|
+
export declare function exceedsContentLimit(content: string, limit: number): boolean;
|
|
136
|
+
/** S1.2: the repair path — a write that makes an already-over-limit file smaller.
|
|
137
|
+
* Only a NET SHRINK is exempt; an equal or larger write stays refused. */
|
|
138
|
+
export declare function shrinksOverLimit(next: string, current: string | null | undefined, limit: number): boolean;
|
|
139
|
+
export declare function validateFrontmatter(content: string, expectedName?: string, limits?: SkillLimits,
|
|
140
|
+
/** On-disk bytes, so a NET SHRINK of an over-limit file is allowed (S1.2). */
|
|
141
|
+
current?: string | null): string | null;
|
|
142
|
+
/** Hermes authoring quality bar for descriptions — see constants.ts
|
|
143
|
+
* (0.3.16 T-4 moved the single source there; the public re-export sits behind
|
|
144
|
+
* the package root, which re-exports constants anyway). */
|
|
145
|
+
export interface AuthoringFeedback {
|
|
146
|
+
/** Frontmatter description length in characters (0 when absent). */
|
|
147
|
+
descriptionChars: number;
|
|
148
|
+
/** Whether the description exceeds the authoring bar (60) while still passing the platform limit. */
|
|
149
|
+
over60: boolean;
|
|
150
|
+
/** Whether the description contains a colon (the standard requires double-quote wrapping). */
|
|
151
|
+
hasColon: boolean;
|
|
152
|
+
/** Advice lines appended to mutation success messages. */
|
|
153
|
+
lines: string[];
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Advisory authoring feedback (P0): evaluate frontmatter against the
|
|
157
|
+
* authoring bar WITHOUT changing platform validation semantics. The bar is
|
|
158
|
+
* the quality target, `validateFrontmatter`'s limits are the compatibility
|
|
159
|
+
* floor, and this bridge layer tells the model when its text would be
|
|
160
|
+
* truncated or route-poor instead of silently shipping it.
|
|
161
|
+
*/
|
|
162
|
+
export declare function authoringFeedback(frontmatter: Frontmatter): AuthoringFeedback;
|
|
163
|
+
/** A1-15 (v18) / P2-2 (v19): the io layer marks an error `committed: true` when
|
|
164
|
+
* the rename landed and only the directory fsync failed. Every single-file
|
|
165
|
+
* writer must treat that as "written, durability unconfirmed" — never as a
|
|
166
|
+
* plain failure (which a caller would retry, or a two-phase caller roll back).
|
|
167
|
+
* v28 G2.1 (EVO-IO-05): this is a delegation to the seam's own
|
|
168
|
+
* `isCommittedWarning` — the marker predicate has exactly one definition. */
|
|
169
|
+
//# sourceMappingURL=frontmatter.d.ts.map
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fuzzy string matching and replacement for patch/restructure edits of skill files.
|
|
3
|
+
*
|
|
4
|
+
* Split out of skill-store.ts (S2-1): pure text functions with no store state.
|
|
5
|
+
* The store imports the scan, the budgets and the replace entry points directly;
|
|
6
|
+
* none of them is re-exported, so the package export surface is unchanged.
|
|
7
|
+
*/
|
|
8
|
+
/** V6-17 (0.3.37): the fuzzy-patch scan is O(n·m) with no input bound; a
|
|
9
|
+
* non-exact anchor past these budgets would block the event loop (measured
|
|
10
|
+
* ~6s at 20k×20k). Exact matches go through the fast `includes` path and stay
|
|
11
|
+
* allowed regardless of size. */
|
|
12
|
+
export declare const FUZZY_MAX_PATTERN_CHARS = 4096;
|
|
13
|
+
export declare const FUZZY_MAX_WORK = 8000000;
|
|
14
|
+
/** Trim leading whitespace of the first line and trailing whitespace of the last line. */
|
|
15
|
+
export declare function trimPatternBoundaries(pattern: string): string;
|
|
16
|
+
export declare function fuzzyPatch(content: string, oldString: string, newString: string, replaceAll?: boolean): string | null;
|
|
17
|
+
/** Deterministic section-extraction plan facts; the caller owns the IO and the append semantics. */
|
|
18
|
+
//# sourceMappingURL=fuzzy-match.d.ts.map
|
package/lib/types/gates.d.ts
CHANGED
|
@@ -11,8 +11,8 @@
|
|
|
11
11
|
* filesystem and the write origin, not on a name list.
|
|
12
12
|
* @module @lmzhen/dsh-evolution-core
|
|
13
13
|
*/
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
type GateReason = 'excluded' | 'referenced' | 'suppressed' | 'protected-builtin';
|
|
15
|
+
interface GateSetInputs {
|
|
16
16
|
exclude?: ReadonlySet<string> | undefined;
|
|
17
17
|
referenced?: ReadonlySet<string> | undefined;
|
|
18
18
|
suppressed?: ReadonlySet<string> | undefined;
|
|
@@ -35,4 +35,5 @@ export declare function createGateSet(config: {
|
|
|
35
35
|
referencedSkillNames?: ReadonlySet<string>;
|
|
36
36
|
suppressedNames?: ReadonlySet<string>;
|
|
37
37
|
}): EvolutionGateSet;
|
|
38
|
+
export {};
|
|
38
39
|
//# sourceMappingURL=gates.d.ts.map
|
|
@@ -14,10 +14,27 @@
|
|
|
14
14
|
* the sidecar directory, so two instances resolving different homes (an
|
|
15
15
|
* isolated test fixture, a second DSH_HOME) do not contend, while two rows on
|
|
16
16
|
* one profile do.
|
|
17
|
+
*
|
|
18
|
+
* ## Scope (v43 FLOW2-1) — this registry is PER PROCESS
|
|
19
|
+
*
|
|
20
|
+
* `claims` below is a module-scope Map: two ROWS over one home in ONE process
|
|
21
|
+
* contend, while the SAME home in another process gets its own Map and is
|
|
22
|
+
* granted the key. That is by construction, not a gap to close here — the
|
|
23
|
+
* cross-process half of the contract is the IO backend's write lock
|
|
24
|
+
* (`transactIo`, core/io.ts), which serializes a per-target read-modify-write.
|
|
25
|
+
* The FLOW2-1 finding was three call sites reading a GRANTED claim as "no other
|
|
26
|
+
* process can be doing this work", so the caller contract is stated here:
|
|
27
|
+
* - granted means "no other row OF THIS PROCESS owns the key";
|
|
28
|
+
* - `instanceHolder()` answers "who holds it HERE"; `undefined` also covers
|
|
29
|
+
* "held by another process";
|
|
30
|
+
* - a foreign holder's LIVENESS cannot be decided from a claim at all (no pid
|
|
31
|
+
* is recorded here): a consumer that needs that decision must carry a pid in
|
|
32
|
+
* its own credential and probe it (`isProcessAlive`, core/io.ts), or state
|
|
33
|
+
* that its action is destructive.
|
|
17
34
|
* @module @lmzhen/dsh-evolution-core/src/instance-scope
|
|
18
35
|
*/
|
|
19
36
|
/** Outcome of a claim. `holder` is the current owner either way. */
|
|
20
|
-
|
|
37
|
+
interface InstanceClaimResult {
|
|
21
38
|
readonly granted: boolean;
|
|
22
39
|
readonly key: string;
|
|
23
40
|
readonly holder: string;
|
|
@@ -30,6 +47,8 @@ export declare function instanceClaimKey(home: string, key: string): string;
|
|
|
30
47
|
export declare function claimInstance(home: string, key: string, owner: string): InstanceClaimResult;
|
|
31
48
|
/** Release only the claim `owner` took — never another instance's. */
|
|
32
49
|
export declare function releaseInstance(home: string, key: string, owner: string): void;
|
|
33
|
-
/** The current holder of `key` at `home`, or undefined
|
|
50
|
+
/** The current holder of `key` at `home`, IN THIS PROCESS, or undefined
|
|
51
|
+
* (which also covers "another process holds it" — v43 FLOW2-1). */
|
|
34
52
|
export declare function instanceHolder(home: string, key: string): string | undefined;
|
|
53
|
+
export {};
|
|
35
54
|
//# sourceMappingURL=instance-scope.d.ts.map
|
package/lib/types/io.d.ts
CHANGED
|
@@ -68,6 +68,34 @@ export interface EvolutionIoLike {
|
|
|
68
68
|
* node backend's V5-03 short-circuit).
|
|
69
69
|
*/
|
|
70
70
|
export declare function transactIo(io: EvolutionIoLike, path: string, task: (current: string | null) => string | null | Promise<string | null>): Promise<void>;
|
|
71
|
+
/**
|
|
72
|
+
* V43 F-4 (S0-6): the shared "the transaction really invoked the task" guard.
|
|
73
|
+
* A backend that implements `transact` but never calls `task` resolves
|
|
74
|
+
* cleanly, so every caller that reads the outcome out of the task itself (a
|
|
75
|
+
* result slot filled inside it, a `Promise<void>` save path) reported success
|
|
76
|
+
* for a write that never happened — the silent-lost-write family the inline
|
|
77
|
+
* C-01 (memory-store) and V6-19 (skill-store) guards cover one call site at a
|
|
78
|
+
* time. Wrap the task handed to `transactIo`/`io.transact` and probe after
|
|
79
|
+
* the call: `invoked()` for a path that owns a structured refusal, or
|
|
80
|
+
* `assertInvoked()` for a void-returning path with no result channel (the
|
|
81
|
+
* family's fail-loud discipline, same wording as skill-store's guard).
|
|
82
|
+
*
|
|
83
|
+
* The wrapper is an identity pass-through — neither the backend nor the task
|
|
84
|
+
* sees a difference — and a task that IS invoked but skips the write (a
|
|
85
|
+
* dedupe no-op) counts as invoked: the probe answers "did the write path get
|
|
86
|
+
* to decide", not "did bytes change".
|
|
87
|
+
* @param what - the write being attempted, named in the thrown message
|
|
88
|
+
*/
|
|
89
|
+
export interface TransactTaskGuard {
|
|
90
|
+
/** Wrap the task handed to the transact backend, marking its invocation. */
|
|
91
|
+
wrap<A, R>(task: (current: A) => R): (current: A) => R;
|
|
92
|
+
/** Did the backend invoke the wrapped task? */
|
|
93
|
+
invoked(): boolean;
|
|
94
|
+
/** Throw when the backend never invoked it (nothing was written). */
|
|
95
|
+
assertInvoked(): void;
|
|
96
|
+
}
|
|
97
|
+
/** Build a {@link TransactTaskGuard} for one write path. See its doc. */
|
|
98
|
+
export declare function transactTaskGuard(what: string): TransactTaskGuard;
|
|
71
99
|
/** Lazy adapter over an IO provider registry, shared by every evolution consumer. */
|
|
72
100
|
export declare function evolutionIoAdapter(provider: () => EvolutionIoLike): EvolutionIoLike;
|
|
73
101
|
/**
|
|
@@ -181,9 +209,9 @@ export declare const LOCK_TEAR_TAKEOVER_MS = 3600000;
|
|
|
181
209
|
*/
|
|
182
210
|
export declare function isCommittedWarning(error: unknown): boolean;
|
|
183
211
|
/** V27 G1.1: the takeover branches, as a value. */
|
|
184
|
-
|
|
212
|
+
type TakeoverDecision = 'none' | 'dead' | 'empty' | 'corrupt';
|
|
185
213
|
/** V27 G1.1: one lock observation, plus the liveness probe for its pid. */
|
|
186
|
-
|
|
214
|
+
interface TakeoverProbe {
|
|
187
215
|
/** Raw lock body. An empty string means the file exists with no content. */
|
|
188
216
|
body: string;
|
|
189
217
|
/** Lock mtime in epoch ms. */
|
|
@@ -220,4 +248,5 @@ export declare function decideTakeover(probe: TakeoverProbe): TakeoverDecision;
|
|
|
220
248
|
* exceed the default budget and fail loud.
|
|
221
249
|
*/
|
|
222
250
|
export declare function nodeEvolutionIo(lockAttempts?: number): EvolutionIoLike;
|
|
251
|
+
export {};
|
|
223
252
|
//# sourceMappingURL=io.d.ts.map
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skill content limits: the byte/char budgets every write path validates against.
|
|
3
|
+
*
|
|
4
|
+
* Split out of skill-store.ts (S2-1) so the frontmatter validators and the
|
|
5
|
+
* store share one declaration site. Re-exported by skill-store.ts: the package
|
|
6
|
+
* export surface is unchanged.
|
|
7
|
+
*/
|
|
8
|
+
export interface SkillLimits {
|
|
9
|
+
maxNameLength: number;
|
|
10
|
+
maxDescriptionLength: number;
|
|
11
|
+
maxSkillContentChars: number;
|
|
12
|
+
maxSkillFileBytes: number;
|
|
13
|
+
}
|
|
14
|
+
export declare const DEFAULT_SKILL_LIMITS: SkillLimits;
|
|
15
|
+
//# sourceMappingURL=limits.d.ts.map
|
package/lib/types/opt-in.d.ts
CHANGED
|
@@ -21,9 +21,19 @@
|
|
|
21
21
|
* `sessionScoped` is what a deployment declares: true means "act only on a
|
|
22
22
|
* session that carries the family's model tools", which is the right question in
|
|
23
23
|
* BOTH install forms — at profile root every session carries them, inside a
|
|
24
|
-
* variant preset only the sessions that selected one do. The shipped bundles
|
|
25
|
-
* it
|
|
26
|
-
* and
|
|
24
|
+
* variant preset only the sessions that selected one do. The shipped bundles all
|
|
25
|
+
* set it, evolution-host included (S0-4 / v43 G-1); a bare library mount leaves
|
|
26
|
+
* it false and keeps the historical "every session" behavior. The host-only
|
|
27
|
+
* install thus matches nothing unless the model tool packages are mounted some
|
|
28
|
+
* other way, because that bundle mounts no model tool row.
|
|
29
|
+
*
|
|
30
|
+
* S0-4 (v43 J-1 / G-1): the gate's false used to be SILENT, which left exactly
|
|
31
|
+
* those deployments — host-only, or an overlay that disables tool-memory /
|
|
32
|
+
* tool-skill-manage — indistinguishable from an ordinary per-session skip while
|
|
33
|
+
* review injection and skill-usage telemetry stayed off for every session. The
|
|
34
|
+
* witness below records what the gate saw, the first scoped miss with no match
|
|
35
|
+
* ever leaves ONE warn per process, and `scopedProbeReport()` is the read side
|
|
36
|
+
* `/evolution doctor` renders as `scoped rows × probe`.
|
|
27
37
|
* @module
|
|
28
38
|
*/
|
|
29
39
|
import type { Context } from '@deepseek-ai/cordis';
|
|
@@ -35,6 +45,25 @@ import type { Context } from '@deepseek-ai/cordis';
|
|
|
35
45
|
* a family session. Two names, not one: a deployment may disable either row.
|
|
36
46
|
*/
|
|
37
47
|
export declare const FAMILY_SESSION_TOOL_NAMES: readonly string[];
|
|
48
|
+
/** What the session-scoped gate has seen in this process. */
|
|
49
|
+
export interface ScopedProbeReport {
|
|
50
|
+
/** `hit` — at least one session carried the family tools; `never-hit` — the
|
|
51
|
+
* gate evaluated sessions and rejected every one; `idle` — it has not been
|
|
52
|
+
* asked yet (no session event reached a scoped consumer since startup). */
|
|
53
|
+
verdict: 'hit' | 'never-hit' | 'idle';
|
|
54
|
+
/** Scoped evaluations that resolved true. */
|
|
55
|
+
hits: number;
|
|
56
|
+
/** Scoped evaluations that resolved false. */
|
|
57
|
+
misses: number;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Read the witness for a diagnostic surface (`/evolution doctor`). Read-only: it
|
|
61
|
+
* neither evaluates the probe nor consumes the one-time warn, so a report run
|
|
62
|
+
* cannot change what the next miss would have logged.
|
|
63
|
+
* @returns the verdict with both counts, zeroed in a process where the gate has
|
|
64
|
+
* not run.
|
|
65
|
+
*/
|
|
66
|
+
export declare function scopedProbeReport(): ScopedProbeReport;
|
|
38
67
|
/**
|
|
39
68
|
* Does this session's scope see the family's model tools?
|
|
40
69
|
*
|
|
@@ -56,7 +85,8 @@ export declare function sessionSeesFamilyTools(ctx: Context, sessionId: string):
|
|
|
56
85
|
* @returns true when the consumer may act on this session. A deployment that did
|
|
57
86
|
* not declare session scoping always answers true (the historical behavior);
|
|
58
87
|
* a scoped one answers true only for a session that carries the family's model
|
|
59
|
-
* tools.
|
|
88
|
+
* tools. Each scoped answer updates the process witness, and the first miss with
|
|
89
|
+
* no match ever leaves one warn (see {@link noteScopedProbeMiss}).
|
|
60
90
|
*/
|
|
61
91
|
export declare function sessionAudited(ctx: Context, sessionId: string, sessionScoped: boolean | undefined): boolean;
|
|
62
92
|
//# sourceMappingURL=opt-in.d.ts.map
|
|
@@ -54,7 +54,7 @@ export type SkillHealthVerdict = 'healthy' | 'warn' | 'needs-restructure';
|
|
|
54
54
|
* and `snapshotFromLibrary` feed `read()` verbatim. The field names predate
|
|
55
55
|
* that convention; the thresholds are calibrated against the whole file, so a
|
|
56
56
|
* caller must not strip frontmatter before measuring. */
|
|
57
|
-
|
|
57
|
+
interface SkillHealthSnapshot {
|
|
58
58
|
skillName: string;
|
|
59
59
|
bodyChars: number;
|
|
60
60
|
bodyText?: string | undefined;
|
|
@@ -65,7 +65,7 @@ export interface SkillHealthSnapshot {
|
|
|
65
65
|
/** Usage-side view count, when the caller has it (A2 churn dimension). */
|
|
66
66
|
readCount?: number | undefined;
|
|
67
67
|
}
|
|
68
|
-
|
|
68
|
+
interface SkillHealthDim {
|
|
69
69
|
bodyChars: number;
|
|
70
70
|
stampDensityPerKb: number | null;
|
|
71
71
|
supportGroups: number;
|
|
@@ -79,4 +79,5 @@ export interface SkillHealthAssessment {
|
|
|
79
79
|
reasons: string[];
|
|
80
80
|
}
|
|
81
81
|
export declare function assessStructureHealth(snapshot: SkillHealthSnapshot, thresholds?: SkillHealthThresholds): SkillHealthAssessment;
|
|
82
|
+
export {};
|
|
82
83
|
//# sourceMappingURL=skill-health.d.ts.map
|