@lmzhen/dsh-evolution-core 0.4.1 → 0.6.0
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 +7118 -5056
- package/lib/types/citations.d.ts +109 -0
- package/lib/types/constants.d.ts +31 -0
- package/lib/types/cost.d.ts +63 -0
- package/lib/types/curator.d.ts +7 -0
- package/lib/types/drift-signals.d.ts +48 -1
- package/lib/types/frontmatter.d.ts +8 -0
- package/lib/types/index.d.ts +4 -0
- package/lib/types/limits.d.ts +68 -0
- package/lib/types/params.d.ts +195 -0
- package/lib/types/prompts.d.ts +3 -3
- package/lib/types/reference-rewrite.d.ts +90 -0
- package/lib/types/skill-health.d.ts +2 -0
- package/lib/types/skill-store.d.ts +46 -2
- package/lib/types/tool-dispatch.d.ts +23 -0
- package/lib/types/usage.d.ts +18 -0
- package/package.json +1 -1
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Citation resolution for skill bodies and support files (design §5.2).
|
|
3
|
+
*
|
|
4
|
+
* WHY this exists beside `supportRefs` (skill-store.ts): that scanner answers
|
|
5
|
+
* "does this text contain a path-shaped token", which is the right question
|
|
6
|
+
* when a package is being ARCHIVED — every such path may dangle — and the wrong
|
|
7
|
+
* one when a section is only being MOVED, where nothing leaves and the only
|
|
8
|
+
* thing that changes is the directory a citation is read from. Measured false
|
|
9
|
+
* positives of the token scan: a URL tail (`https://host/references/x.md`), a
|
|
10
|
+
* category path the original Hermes documents as legitimate
|
|
11
|
+
* (`skills/scripts/foo.py`), and a prose command line
|
|
12
|
+
* (`npm run scripts/build.mjs`).
|
|
13
|
+
*
|
|
14
|
+
* CONTRACT: a token whose path STARTS WITH a support directory is a citation of
|
|
15
|
+
* this skill and is resolved from the SKILL ROOT, in whichever file it is
|
|
16
|
+
* written. A token that merely contains a support directory further along its
|
|
17
|
+
* path belongs to something else: it is reported as `foreign` and never
|
|
18
|
+
* resolved.
|
|
19
|
+
*
|
|
20
|
+
* PURE: existence is decided against the caller's file list, so identical
|
|
21
|
+
* inputs always answer the same report.
|
|
22
|
+
*/
|
|
23
|
+
/** Support directories that make a path a citation of the owning skill. */
|
|
24
|
+
export declare const CITATION_SUPPORT_DIRS: readonly string[];
|
|
25
|
+
/** How one path-shaped token was classified. */
|
|
26
|
+
export type CitationKind =
|
|
27
|
+
/** Starts with a support directory: a citation of this skill. */
|
|
28
|
+
'citation'
|
|
29
|
+
/** Inside a fenced code block: illustrative, never resolved. */
|
|
30
|
+
| 'fence'
|
|
31
|
+
/** Tail of a URL. */
|
|
32
|
+
| 'url'
|
|
33
|
+
/** Contains a support directory further along its path (`skills/scripts/foo.py`). */
|
|
34
|
+
| 'foreign'
|
|
35
|
+
/** A command line naming a path (`npm run scripts/build.mjs`). */
|
|
36
|
+
| 'prose';
|
|
37
|
+
/** One scanned token with its classification and resolution. */
|
|
38
|
+
export interface CitationRef {
|
|
39
|
+
/** The matched token, verbatim, after trailing sentence punctuation is stripped. */
|
|
40
|
+
raw: string;
|
|
41
|
+
/** 1-based line number in the scanned content. */
|
|
42
|
+
line: number;
|
|
43
|
+
/** Skill-root-relative target, or null when the token is not a citation of this skill. */
|
|
44
|
+
target: string | null;
|
|
45
|
+
/** The base a citation is read from; this scanner resolves the skill root only. */
|
|
46
|
+
base: 'root';
|
|
47
|
+
kind: CitationKind;
|
|
48
|
+
/** Whether `target` names a file in the caller's file list. */
|
|
49
|
+
exists: boolean;
|
|
50
|
+
/** `#fragment` carried by the token, when present. */
|
|
51
|
+
fragment?: string | undefined;
|
|
52
|
+
}
|
|
53
|
+
/** One scan of a body or support file. */
|
|
54
|
+
export interface CitationReport {
|
|
55
|
+
refs: readonly CitationRef[];
|
|
56
|
+
/** Citations whose target is provably absent from the file list. */
|
|
57
|
+
dangling: readonly CitationRef[];
|
|
58
|
+
/** Citations a non-recursive listing cannot decide (target sits under a listed directory). */
|
|
59
|
+
unverified: readonly CitationRef[];
|
|
60
|
+
/** Path-shaped tokens that are not citations of this skill (foreign/url/prose/fence). */
|
|
61
|
+
foreign: readonly CitationRef[];
|
|
62
|
+
/** True when the scan stopped at the budget: a partial report never reads as clean. */
|
|
63
|
+
truncated: boolean;
|
|
64
|
+
}
|
|
65
|
+
/** Scan budget: bounded so a pathological body cannot stall a maintenance run. */
|
|
66
|
+
export declare const DEFAULT_CITATION_REF_BUDGET = 500;
|
|
67
|
+
/**
|
|
68
|
+
* Resolve every path-shaped token in `content`.
|
|
69
|
+
* @param input - content, the skill-root-relative path of its owner, the skill's
|
|
70
|
+
* file list, and an optional scan budget.
|
|
71
|
+
* @returns the refs plus the dangling and foreign subsets; `truncated` marks a
|
|
72
|
+
* report that stopped at the budget.
|
|
73
|
+
*/
|
|
74
|
+
export declare function resolveCitations(input: {
|
|
75
|
+
content: string;
|
|
76
|
+
file: string;
|
|
77
|
+
files: readonly string[];
|
|
78
|
+
budget?: number | undefined;
|
|
79
|
+
}): CitationReport;
|
|
80
|
+
/** The sanctioned body hook (design §5.6): a list item whose payload is
|
|
81
|
+
* exactly one support-file path, so the file stays discoverable in the body. */
|
|
82
|
+
export declare const HOOK_LINE_RE: RegExp;
|
|
83
|
+
/** The retirement escape hatch (design §5.6): a standalone body comment that
|
|
84
|
+
* names the support file it exempts and the reason it must survive. The path
|
|
85
|
+
* is part of the marker because the proposal list is per FILE, and a reason is
|
|
86
|
+
* required — an unexplained exemption is what this hook exists to prevent. */
|
|
87
|
+
export declare const KEEP_LINE_RE: RegExp;
|
|
88
|
+
/** Whether a skill-relative path names a FILE rather than a directory: the same
|
|
89
|
+
* rule the scanner applies to a token's final segment (`name.ext`). The support
|
|
90
|
+
* listing is one level deep and includes directory entries (a real library has
|
|
91
|
+
* `references/archive`), which no pointer or retirement list may treat as a file.
|
|
92
|
+
* @param path - a skill-root-relative support path.
|
|
93
|
+
* @returns true when the final segment is file-shaped.
|
|
94
|
+
*/
|
|
95
|
+
export declare function isFileShapedPath(path: string): boolean;
|
|
96
|
+
/** Result of one hook scan over a body. */
|
|
97
|
+
export interface BodyHookScan {
|
|
98
|
+
/** Targets named by a sanctioned hook line, in file order, deduplicated. */
|
|
99
|
+
targets: readonly string[];
|
|
100
|
+
/** Target -> the reason its `keep` marker records. */
|
|
101
|
+
kept: ReadonlyMap<string, string>;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Scan a body for sanctioned hooks and `keep` markers.
|
|
105
|
+
* @param content - the SKILL.md body (frontmatter included).
|
|
106
|
+
* @returns hook targets plus the `keep` reasons keyed by target.
|
|
107
|
+
*/
|
|
108
|
+
export declare function scanBodyHooks(content: string): BodyHookScan;
|
|
109
|
+
//# sourceMappingURL=citations.d.ts.map
|
package/lib/types/constants.d.ts
CHANGED
|
@@ -52,6 +52,26 @@ export declare const PROTECTED_BUILTIN_SKILLS: ReadonlySet<string>;
|
|
|
52
52
|
export declare const MAX_SKILL_NAME_LENGTH = 64;
|
|
53
53
|
export declare const MAX_DESCRIPTION_LENGTH = 1024;
|
|
54
54
|
export declare const MAX_SKILL_CONTENT_CHARS = 100000;
|
|
55
|
+
/** The authoring DISCIPLINE band, taken from the upstream standard (archive §5):
|
|
56
|
+
* peer skills sit at 8-14k characters and a body pushing past 20k belongs in
|
|
57
|
+
* `references/*.md`. Deliberately separate from `MAX_SKILL_CONTENT_CHARS`: the
|
|
58
|
+
* hard ceiling is a deployment-tunable limit, this band is the authoring
|
|
59
|
+
* standard — deriving the band from the ceiling is exactly what let a 40k
|
|
60
|
+
* ceiling hide a 99k body without a single signal saying "split me" (V3). */
|
|
61
|
+
export declare const AUTHORING_SPLIT_LINE_CHARS = 20000;
|
|
62
|
+
/** Upstream's conversion basis for CHARACTER LIMITS: 2.75 chars/token, labelled
|
|
63
|
+
* model-independent in the config template (\`cli-config.yaml.example:538\`), and the
|
|
64
|
+
* basis behind every quoted token figure there — memory 2200 chars ≈ 800 tokens,
|
|
65
|
+
* user 1375 ≈ 500, SKILL.md 100_000 ≈ 36k (\`tools/skill_manager_tool.py:455\`).
|
|
66
|
+
* Limits are deliberately conservative, so this is the basis a BORROWED LIMIT must
|
|
67
|
+
* be converted with. */
|
|
68
|
+
export declare const UPSTREAM_LIMIT_CHARS_PER_TOKEN = 2.75;
|
|
69
|
+
/** The platform's own estimate basis: \`CHARS_PER_TOKEN = 4\` in
|
|
70
|
+
* \`@deepseek-ai/dsh-token-meter/estimate.ts\` (its comment reads "used until exact
|
|
71
|
+
* tokenization is needed"), matching upstream's ESTIMATE-side heuristic — "~4
|
|
72
|
+
* chars/token is the usual English heuristic" (\`agent/prompt_builder.py:1179\`).
|
|
73
|
+
* Estimates use this; limits use the constant above. */
|
|
74
|
+
export declare const PLATFORM_ESTIMATE_CHARS_PER_TOKEN = 4;
|
|
55
75
|
export declare const MAX_SKILL_FILE_BYTES = 1048576;
|
|
56
76
|
export declare const DEFAULT_REVIEW_MEMORY_INTERVAL = 10;
|
|
57
77
|
export declare const DEFAULT_REVIEW_SKILL_INTERVAL = 10;
|
|
@@ -86,6 +106,12 @@ export declare const DEFAULT_REVIEW_CONTEXT_MESSAGES = 60;
|
|
|
86
106
|
export declare const DEFAULT_REVIEW_MESSAGE_CHARS = 2000;
|
|
87
107
|
export declare const DEFAULT_CURATOR_BOOT_GRACE_SECONDS = 10;
|
|
88
108
|
export declare const DEFAULT_CURATOR_REVIEW_MAX_TOKENS = 2048;
|
|
109
|
+
/** Threat-scan WINDOW SIZE (not a total cap — E-12: the whole text is scanned in
|
|
110
|
+
* overlapping windows, so content beyond this stays in scope). The coverage
|
|
111
|
+
* floor is `PATTERN_OVERLAP + 1` (V6-05). Single home for the core scanners'
|
|
112
|
+
* default parameter and clamp fallback, plus evolution-threat's Config default
|
|
113
|
+
* — the two packages previously wrote 65_536 independently (G0/S0.1). */
|
|
114
|
+
export declare const DEFAULT_THREAT_MAX_SCAN_CHARS = 65536;
|
|
89
115
|
/** 0.3.17 (S3.10, T-1): control-plane fields a model-facing write call may
|
|
90
116
|
* never carry — single source for plan-validator, evolution-policy and the
|
|
91
117
|
* threat scanner (they used to each hardcode the list).
|
|
@@ -102,6 +128,11 @@ export declare const EVOLUTION_WRITE_TOOLS: readonly ["memory", "skill_manage"];
|
|
|
102
128
|
* 0.3.16 (T-4): moved here from skill-store.ts so drift-signals (pure, no IO)
|
|
103
129
|
* can reference it without importing the skill-store module. */
|
|
104
130
|
export declare const AUTHORING_DESCRIPTION_BAR = 60;
|
|
131
|
+
/** The split hint both size refusals share (0.5.0 V1). The same sentence used to
|
|
132
|
+
* be copied into validateFrontmatter AND the patch path, and neither copy named
|
|
133
|
+
* where the content should go — the upstream cap message names the destination
|
|
134
|
+
* directories, and that is the part a model actually acts on. */
|
|
135
|
+
export declare const CONTENT_SPLIT_HINT = "Consider splitting into a smaller SKILL.md with supporting files in references/ or templates.";
|
|
105
136
|
/** V27 G2.4: the largest millisecond delay a timer accepts. `AbortSignal.timeout`
|
|
106
137
|
* (and `setTimeout`) coerce anything larger to 1ms after a Node warning, so a
|
|
107
138
|
* timeout configured above this ceiling silently collapses to "immediately
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Context-cost accounting for skill bodies (design §5.2).
|
|
3
|
+
*
|
|
4
|
+
* A raw character count is a poor budget: the same 100k characters cost very
|
|
5
|
+
* different amounts of context in Chinese-heavy prose and in English. The
|
|
6
|
+
* weighted unit makes the two comparable, and the token range exists only to
|
|
7
|
+
* make the load cost visible next to the character count — it is an ESTIMATE,
|
|
8
|
+
* never a measurement.
|
|
9
|
+
*
|
|
10
|
+
* Accounting basis: the ON-DISK form (`skillMdOnDisk()`, the same view the
|
|
11
|
+
* write limit uses), so a body that is exactly at the limit cannot read as
|
|
12
|
+
* over-limit here and pass there (the v37 P2-1 deadlock class).
|
|
13
|
+
*/
|
|
14
|
+
/** Token weight of one CJK / kana / full-width code unit on the LIMIT basis —
|
|
15
|
+
* upstream's model-independent conversion is 2.75 chars/token for prose, and CJK
|
|
16
|
+
* prose is ~1 token per character (the conservative end of the 0.6–1.0 range the
|
|
17
|
+
* estimate side reports). Thresholds are compared on THIS basis so a borrowed
|
|
18
|
+
* character line keeps its meaning; the estimate range below reports the spread. */
|
|
19
|
+
export declare const TOKEN_CJK_WEIGHT = 1;
|
|
20
|
+
/** Token weight of one non-CJK code unit on the LIMIT basis (1 / 2.75 ≈ 0.364). */
|
|
21
|
+
export declare const TOKEN_ASCII_WEIGHT: number;
|
|
22
|
+
/** Token-per-unit bounds of the estimate range (CJK: 0.6–1.0, non-CJK: 1/4–1/3).
|
|
23
|
+
* The range is the 4-chars/token estimate family, so on ASCII-heavy bodies the
|
|
24
|
+
* LIMIT-basis `tokens` point can sit above `tokensHigh` (1/2.75 > 1/3): the two
|
|
25
|
+
* answer different questions and are never nested by construction. */
|
|
26
|
+
export declare const COST_CJK_TOKEN_LOW = 0.6;
|
|
27
|
+
export declare const COST_CJK_TOKEN_HIGH = 1;
|
|
28
|
+
export declare const COST_ASCII_TOKEN_LOW = 0.25;
|
|
29
|
+
export declare const COST_ASCII_TOKEN_HIGH: number;
|
|
30
|
+
/** The token line a borrowed CHARACTER threshold draws for ONE body: 20k characters
|
|
31
|
+
* of THIS composition, expressed in the same tokens `bodyCost` counts. Comparing
|
|
32
|
+
* `tokens` against this line is deliberately equivalent to "chars >= line" — that
|
|
33
|
+
* equivalence is what keeps a borrowed line's textual effect identical — while the
|
|
34
|
+
* judgment itself, and every number reported, stays on the token scale.
|
|
35
|
+
* @param lineChars - the borrowed character line (whole-body characters).
|
|
36
|
+
* @param cost - the body's own cost breakdown.
|
|
37
|
+
* @returns the equivalent token line (0 when the body is empty). */
|
|
38
|
+
export declare function tokenLineFor(lineChars: number, cost: BodyCost): number;
|
|
39
|
+
/** Cost of one body: raw counts, the weighted total, and the estimate range. */
|
|
40
|
+
export interface BodyCost {
|
|
41
|
+
/** Code units of the on-disk form — the same measure `maxSkillContentChars` bounds. */
|
|
42
|
+
chars: number;
|
|
43
|
+
/** Code units matched by the CJK ranges. */
|
|
44
|
+
cjk: number;
|
|
45
|
+
/** Code units that are not CJK (includes surrogate halves and syntax). */
|
|
46
|
+
ascii: number;
|
|
47
|
+
/** Single token count on the LIMIT basis: `cjk * TOKEN_CJK_WEIGHT + ascii *
|
|
48
|
+
* TOKEN_ASCII_WEIGHT`, rounded. This is the number every threshold compares
|
|
49
|
+
* against, so a threshold borrowed as a character line converts faithfully. */
|
|
50
|
+
tokens: number;
|
|
51
|
+
/** Lower bound of the token estimate. */
|
|
52
|
+
tokensLow: number;
|
|
53
|
+
/** Upper bound of the token estimate. */
|
|
54
|
+
tokensHigh: number;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Cost of one skill body.
|
|
58
|
+
* @param content - the body text as written (trailing whitespace is normalized
|
|
59
|
+
* away by the on-disk accounting, so callers need not pre-trim).
|
|
60
|
+
* @returns the cost breakdown; an empty body answers all-zero.
|
|
61
|
+
*/
|
|
62
|
+
export declare function bodyCost(content: string): BodyCost;
|
|
63
|
+
//# sourceMappingURL=cost.d.ts.map
|
package/lib/types/curator.d.ts
CHANGED
|
@@ -81,6 +81,12 @@ export interface CuratorRunReport {
|
|
|
81
81
|
llmReviewEnabled?: boolean;
|
|
82
82
|
/** V6-35 (0.3.36): lenient-parse shape notes from the LLM nomination block. */
|
|
83
83
|
nominationsWarnings?: string[];
|
|
84
|
+
/** 0.5.0 V1 (design §16.6-④): archived entries past the retention window that
|
|
85
|
+
* the CURRENT policy keeps rather than deletes. Present (even empty) only when
|
|
86
|
+
* the retention policy was `report`, so a report cannot read as "nothing
|
|
87
|
+
* expired" for a run that pruned under a different policy. `null` means the
|
|
88
|
+
* listing could not be read — unknown, never "nothing expired". */
|
|
89
|
+
wouldPrune?: string[] | null;
|
|
84
90
|
}
|
|
85
91
|
export interface CuratorReportInput {
|
|
86
92
|
runId: string;
|
|
@@ -99,6 +105,7 @@ export interface CuratorReportInput {
|
|
|
99
105
|
snapshotPath?: string;
|
|
100
106
|
llmReviewEnabled?: boolean;
|
|
101
107
|
nominationsWarnings?: readonly string[];
|
|
108
|
+
wouldPrune?: readonly string[] | null;
|
|
102
109
|
}
|
|
103
110
|
export declare function buildCuratorRunReport(input: CuratorReportInput): CuratorRunReport;
|
|
104
111
|
/**
|
|
@@ -10,6 +10,8 @@
|
|
|
10
10
|
*
|
|
11
11
|
* Distinct from `signals.ts` — the session-level review signal gate.
|
|
12
12
|
*/
|
|
13
|
+
import { type CitationReport } from './citations.ts';
|
|
14
|
+
import { type BodyCost } from './cost.ts';
|
|
13
15
|
/** One skill's library state; the assembler (not this module) reads IO. */
|
|
14
16
|
export interface DriftSkillSnapshot {
|
|
15
17
|
name: string;
|
|
@@ -27,6 +29,23 @@ export interface DriftSkillSnapshot {
|
|
|
27
29
|
protected?: string | null | undefined;
|
|
28
30
|
/** Frontmatter values the strict-YAML platform catalog cannot load (0.3.11). */
|
|
29
31
|
catalogInvalid?: boolean | undefined;
|
|
32
|
+
/** Weighted context cost of the body (design §5.2); undefined = not measured. */
|
|
33
|
+
cost?: BodyCost | undefined;
|
|
34
|
+
/** Citation scan of the body (design §5.2); undefined = not scanned. */
|
|
35
|
+
citations?: CitationReport | undefined;
|
|
36
|
+
/** Per-support-file read counts (design §5.5); undefined = no evidence. */
|
|
37
|
+
demand?: Readonly<Record<string, number>> | undefined;
|
|
38
|
+
/** Idle age of the owning skill (design §5.6); undefined = no record to age. */
|
|
39
|
+
liveness?: SkillLiveness | undefined;
|
|
40
|
+
/** Character counts of support files that can possibly exceed the content cap
|
|
41
|
+
* (design §16.6, V4); undefined = not measured. The assembler pre-filters by
|
|
42
|
+
* byte size, which is complete for the oversize question. */
|
|
43
|
+
supportChars?: Readonly<Record<string, number>> | undefined;
|
|
44
|
+
}
|
|
45
|
+
/** Retirement-proposal input for one skill (design §5.6). */
|
|
46
|
+
export interface SkillLiveness {
|
|
47
|
+
/** Idle days since the lifecycle age anchor (`last activity ?? created_at`). */
|
|
48
|
+
idleDays: number;
|
|
30
49
|
}
|
|
31
50
|
/** verdict=over means "relatively positioned above the threshold", never a violation. */
|
|
32
51
|
type DriftVerdict = 'pass' | 'over' | 'unknown';
|
|
@@ -56,11 +75,39 @@ export interface DriftReport {
|
|
|
56
75
|
/** Physical line length at/above which a body line is reported overlong (011 §4). */
|
|
57
76
|
export declare const DRIFT_MAX_LINE_CHARS = 1500;
|
|
58
77
|
/** Signal-set version: bump whenever ids/thresholds change (011 §7 version coupling). */
|
|
59
|
-
export declare const DRIFT_SIGNALS_VERSION = "
|
|
78
|
+
export declare const DRIFT_SIGNALS_VERSION = "3";
|
|
60
79
|
/** Render-time nouns for the MAINTAIN_PROMPT placeholders (single vocabulary with the facts block). */
|
|
61
80
|
export declare const DRIFT_SIGNAL_NOUNS: Readonly<Record<string, string>>;
|
|
62
81
|
/** Detect support files the body never references (by basename or relative path). */
|
|
63
82
|
export declare function missingSupportPointers(body: string, supportFiles: readonly string[]): string[];
|
|
83
|
+
/** Support files the body mentions WITHOUT the sanctioned hook form (design
|
|
84
|
+
* §5.6). Such a mention still counts as a pointer for `pointer_missing`, but
|
|
85
|
+
* only the hook form keeps the file discoverable after a move — detail only.
|
|
86
|
+
* A file carrying a `keep` marker is exempt: its mention IS the marker. */
|
|
87
|
+
export declare function unhookedSupportPointers(body: string, supportFiles: readonly string[]): string[];
|
|
88
|
+
/** Why a retirement report lists nothing (design §5.6). */
|
|
89
|
+
export type RetirementStatus = 'listed' | 'none' | 'no-age' | 'unscanned';
|
|
90
|
+
/** One support file proposed for retirement review. */
|
|
91
|
+
export interface RetirementCandidate {
|
|
92
|
+
path: string;
|
|
93
|
+
/** Whole idle days of the owning skill at scan time. */
|
|
94
|
+
idleDays: number;
|
|
95
|
+
}
|
|
96
|
+
/** Retirement proposal for one skill: candidates plus the reason when empty. */
|
|
97
|
+
export interface RetirementReport {
|
|
98
|
+
candidates: readonly RetirementCandidate[];
|
|
99
|
+
status: RetirementStatus;
|
|
100
|
+
}
|
|
101
|
+
/** Support files with no readers, no citations and no `keep` marker whose owning
|
|
102
|
+
* skill has been idle for at least the lifecycle stale window. PROPOSAL INPUT
|
|
103
|
+
* only — nothing retires a file on its own — and an unmeasurable input (no age
|
|
104
|
+
* evidence, no citation scan) yields an empty list WITH its reason, never a
|
|
105
|
+
* silent "nothing qualifies".
|
|
106
|
+
* @param snapshot - the skill's drift snapshot.
|
|
107
|
+
* @param supportFiles - its enumerated support files, in listing order.
|
|
108
|
+
* @returns the candidates plus the status that explains an empty list.
|
|
109
|
+
*/
|
|
110
|
+
export declare function retirementReport(snapshot: DriftSkillSnapshot, supportFiles: readonly string[]): RetirementReport;
|
|
64
111
|
/** Duplicate `## heading` occurrences: singleton results default to head of the file. */
|
|
65
112
|
export declare function duplicateHeadings(body: string): Array<{
|
|
66
113
|
heading: string;
|
|
@@ -134,6 +134,14 @@ export declare function normalizeFrontmatter(content: string): FrontmatterNormal
|
|
|
134
134
|
* excluded. Pure and deduplicated.
|
|
135
135
|
*/
|
|
136
136
|
export declare function relatedSkillNames(content: string, exclude?: string): string[];
|
|
137
|
+
/** S1.2 (v37 P2-1): the content limit applies to the bytes that LAND ON DISK.
|
|
138
|
+
* Every write normalizes with `trimEnd() + '\n'`, so judging the raw argument let
|
|
139
|
+
* a 100_000-character body with no trailing newline land as 100_001 bytes — and
|
|
140
|
+
* every later patch/update of that skill was then refused, which made it
|
|
141
|
+
* unmaintainable through `skill_manage` with no repair path at all. */
|
|
142
|
+
/** The bytes that land on disk for a SKILL.md write — the ONE accounting basis
|
|
143
|
+
* shared by the content limit and the cost estimate (design §5.2). */
|
|
144
|
+
export declare function skillMdOnDisk(content: string): string;
|
|
137
145
|
/** Whether `content` would exceed `limit` once written. */
|
|
138
146
|
export declare function exceedsContentLimit(content: string, limit: number): boolean;
|
|
139
147
|
/** S1.2: the repair path — a write that makes an already-over-limit file smaller.
|
package/lib/types/index.d.ts
CHANGED
|
@@ -25,6 +25,9 @@
|
|
|
25
25
|
* `evolution-events.ts`, `io.ts` (the ctx.evolutionIo seam itself).
|
|
26
26
|
* @module @lmzhen/dsh-evolution-core
|
|
27
27
|
*/
|
|
28
|
+
export * from './citations.ts';
|
|
29
|
+
export * from './reference-rewrite.ts';
|
|
30
|
+
export * from './cost.ts';
|
|
28
31
|
export * from './curator.ts';
|
|
29
32
|
export * from './evolution-events.ts';
|
|
30
33
|
export * from './gates.ts';
|
|
@@ -53,6 +56,7 @@ export * from './threats.ts';
|
|
|
53
56
|
export * from './tool-dispatch.ts';
|
|
54
57
|
export * from './usage.ts';
|
|
55
58
|
export * from './constants.ts';
|
|
59
|
+
export * from './params.ts';
|
|
56
60
|
export * from './numeric.ts';
|
|
57
61
|
export * from './opt-in.ts';
|
|
58
62
|
//# sourceMappingURL=index.d.ts.map
|
package/lib/types/limits.d.ts
CHANGED
|
@@ -5,11 +5,79 @@
|
|
|
5
5
|
* store share one declaration site. Re-exported by skill-store.ts: the package
|
|
6
6
|
* export surface is unchanged.
|
|
7
7
|
*/
|
|
8
|
+
/** How a restructure treats a section carrying support-file citations (design §2.2). */
|
|
9
|
+
export type CitationPolicy = 'verify' | 'refuse';
|
|
10
|
+
/** `verify` checks that every cited target exists and otherwise allows the move;
|
|
11
|
+
* `refuse` restores the pre-0.5 behaviour of refusing ANY citation-carrying
|
|
12
|
+
* section. Resolved at the call site, so every existing limits object keeps
|
|
13
|
+
* working (unknown policy never silently changes a write path). */
|
|
14
|
+
export declare const DEFAULT_CITATION_POLICY: CitationPolicy;
|
|
15
|
+
/** How a consolidation treats the source's support files (design §16.7).
|
|
16
|
+
* `off` refuses as before; `plan` keeps refusing but reports what a re-home
|
|
17
|
+
* would have to rewrite; `apply` (V2) performs the re-home and the rewrites, but
|
|
18
|
+
* only when the plan proves it leaves NOTHING dangling — otherwise it refuses
|
|
19
|
+
* exactly like `plan`. */
|
|
20
|
+
export type ReferenceRewritePolicy = 'off' | 'plan' | 'apply';
|
|
21
|
+
/** Default: report the plan on refusal, never write (behaviour unchanged). */
|
|
22
|
+
export declare const DEFAULT_REFERENCE_REWRITE_POLICY: ReferenceRewritePolicy;
|
|
23
|
+
/** What happens to `.archive` entries past the retention window (design §16.6-④).
|
|
24
|
+
* `report` (the default) names them and deletes nothing; `prune` restores the
|
|
25
|
+
* pre-0.5 deletion, which upstream never does. */
|
|
26
|
+
export type ArchiveRetentionPolicy = 'report' | 'prune';
|
|
27
|
+
/** Default: report-only, because upstream's hard invariant is never to delete. */
|
|
28
|
+
export declare const DEFAULT_ARCHIVE_RETENTION_POLICY: ArchiveRetentionPolicy;
|
|
29
|
+
/** How the 100k-character cap treats SUPPORT files (design §16.6, V4). Upstream
|
|
30
|
+
* applies the cap to every written file; we land it in `report` mode first (the
|
|
31
|
+
* write goes through with an advisory) and `enforce` refuses — with the same
|
|
32
|
+
* net-shrink repair path SKILL.md already has, so an over-cap legacy file can
|
|
33
|
+
* always be brought back under the cap instead of becoming unmaintainable. */
|
|
34
|
+
export type SupportFileCharPolicy = 'report' | 'enforce';
|
|
35
|
+
/** Default: report, so the cap cannot brick an existing oversize file on upgrade
|
|
36
|
+
* (the live library carries a 189k-character release log today). */
|
|
37
|
+
export declare const DEFAULT_SUPPORT_FILE_CHAR_POLICY: SupportFileCharPolicy;
|
|
38
|
+
/** The four stage defaults in ONE object, so the policy schema's `z.default(...)`
|
|
39
|
+
* calls and the store's fallbacks cannot drift apart. */
|
|
40
|
+
export declare const POLICY_STAGE_DEFAULTS: Readonly<{
|
|
41
|
+
citationPolicy: "verify";
|
|
42
|
+
referenceRewrite: "plan";
|
|
43
|
+
archiveRetention: "report";
|
|
44
|
+
supportFileCharPolicy: "report";
|
|
45
|
+
}>;
|
|
46
|
+
/** The policy-snapshot fields that select a write-behaviour STAGE (design §16).
|
|
47
|
+
* Structural, not imported from evolution-policy, so core stays a leaf. */
|
|
48
|
+
export interface PolicyStageFields {
|
|
49
|
+
citationPolicy?: CitationPolicy | undefined;
|
|
50
|
+
referenceRewrite?: ReferenceRewritePolicy | undefined;
|
|
51
|
+
archiveRetention?: ArchiveRetentionPolicy | undefined;
|
|
52
|
+
supportFileCharPolicy?: SupportFileCharPolicy | undefined;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* The ONE conversion from the deployment policy snapshot to library limits
|
|
56
|
+
* (design §16.7): every plugin that owns a writable SkillLibrary spreads this into
|
|
57
|
+
* its limits, so a stage selected in cordis.yml reaches every write path. A
|
|
58
|
+
* per-plugin copy would be the third home for the same threshold.
|
|
59
|
+
*
|
|
60
|
+
* Only PRESENT fields are copied: an absent policy field must fall through to the
|
|
61
|
+
* library default rather than pinning `undefined` onto an optional limit (which
|
|
62
|
+
* `exactOptionalPropertyTypes` forbids and which would defeat the `?? DEFAULT`
|
|
63
|
+
* resolution inside the store).
|
|
64
|
+
* @param snapshot - the evolutionPolicy snapshot, or undefined when unmounted.
|
|
65
|
+
* @returns the stage fields the snapshot actually carries.
|
|
66
|
+
*/
|
|
67
|
+
export declare function policyStageLimits(snapshot: PolicyStageFields | undefined): PolicyStageFields;
|
|
8
68
|
export interface SkillLimits {
|
|
9
69
|
maxNameLength: number;
|
|
10
70
|
maxDescriptionLength: number;
|
|
11
71
|
maxSkillContentChars: number;
|
|
12
72
|
maxSkillFileBytes: number;
|
|
73
|
+
/** See CitationPolicy. Optional so existing limits objects stay valid. */
|
|
74
|
+
citationPolicy?: CitationPolicy | undefined;
|
|
75
|
+
/** See ReferenceRewritePolicy; absent means the default (`plan`). */
|
|
76
|
+
referenceRewrite?: ReferenceRewritePolicy | undefined;
|
|
77
|
+
/** See ArchiveRetentionPolicy; absent means the default (`report`). */
|
|
78
|
+
archiveRetention?: ArchiveRetentionPolicy | undefined;
|
|
79
|
+
/** See SupportFileCharPolicy; absent means the default (`report`). */
|
|
80
|
+
supportFileCharPolicy?: SupportFileCharPolicy | undefined;
|
|
13
81
|
}
|
|
14
82
|
export declare const DEFAULT_SKILL_LIMITS: SkillLimits;
|
|
15
83
|
//# sourceMappingURL=limits.d.ts.map
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parameter id consolidation (G0/S0.2): one semantic gets ONE id.
|
|
3
|
+
*
|
|
4
|
+
* Nine places in this family carry the same value under two carriers: three use
|
|
5
|
+
* the SAME name in two carriers (reviewMode, staleAfterDays, archiveAfterDays —
|
|
6
|
+
* resolved by the existing policy-shadows-row rule) and six use DIFFERENT names.
|
|
7
|
+
* This module owns the six: the policy/snapshot name is the canonical id, the
|
|
8
|
+
* plugin-row name is a deprecated alias kept readable for one minor version
|
|
9
|
+
* (0.6.x) and removable in 0.7.0.
|
|
10
|
+
*
|
|
11
|
+
* Reading stays compatible (a carrier still spelling the legacy name resolves),
|
|
12
|
+
* writing is strict (the write path accepts canonical ids only, so no new
|
|
13
|
+
* document is created under a deprecated name).
|
|
14
|
+
* @module
|
|
15
|
+
*/
|
|
16
|
+
/** Deprecated alias (plugin-row name) -> canonical id (policy/snapshot name). */
|
|
17
|
+
export declare const PARAM_ALIASES: Readonly<Record<string, string>>;
|
|
18
|
+
/**
|
|
19
|
+
* Resolve any parameter id to its canonical form.
|
|
20
|
+
* @param id - canonical id or deprecated alias.
|
|
21
|
+
* @returns the canonical id; ids without an alias pass through unchanged.
|
|
22
|
+
*/
|
|
23
|
+
export declare function resolveParamId(id: string): string;
|
|
24
|
+
/**
|
|
25
|
+
* Whether an id is a deprecated alias.
|
|
26
|
+
* @param id - parameter id to test.
|
|
27
|
+
* @returns true when the id must be migrated to its canonical form.
|
|
28
|
+
*/
|
|
29
|
+
export declare function isDeprecatedParamId(id: string): boolean;
|
|
30
|
+
/**
|
|
31
|
+
* Guard for the write path: only canonical ids may be written.
|
|
32
|
+
* @param id - parameter id a caller intends to write.
|
|
33
|
+
* @returns the canonical id.
|
|
34
|
+
* @throws {Error} when the id is a deprecated alias; the message names both ids.
|
|
35
|
+
*/
|
|
36
|
+
export declare function canonicalWriteId(id: string): string;
|
|
37
|
+
/**
|
|
38
|
+
* Read a parameter from a carrier that may still spell the legacy name.
|
|
39
|
+
* @param carrier - config/snapshot object to read from, or undefined.
|
|
40
|
+
* @param id - canonical id (a deprecated alias is accepted and resolved first).
|
|
41
|
+
* @returns the canonical value when present, else the alias value, else undefined.
|
|
42
|
+
*/
|
|
43
|
+
export declare function readParam(carrier: object | undefined, id: string): unknown;
|
|
44
|
+
/** Exposure group (design §7.2): the unit a developer changes together. */
|
|
45
|
+
export type ParamGroup = 'library' | 'write-caps' | 'review' | 'memory' | 'curator' | 'deployment' | 'internal';
|
|
46
|
+
/** Exposure tier (design §7.1): the interface combination a parameter gets. */
|
|
47
|
+
export type ParamTier = 'E0' | 'E1' | 'E2' | 'E3' | 'E4';
|
|
48
|
+
/** Where the authoritative value lives. */
|
|
49
|
+
export type ParamAuthority = 'code' | 'cordis' | 'install';
|
|
50
|
+
/**
|
|
51
|
+
* One parameter's exposure contract (design §8.1).
|
|
52
|
+
*
|
|
53
|
+
* MACHINE-READ CONTRACT: the entries below are written ONE PER LINE with the
|
|
54
|
+
* key order id, group, tier, authority, owner, applies, docAnchor, summary so
|
|
55
|
+
* the .mjs generators (`gen-param-docs.mjs`, `verify-param-registry.mjs`) can
|
|
56
|
+
* parse this text without importing TypeScript. `param-registry.spec.ts`
|
|
57
|
+
* asserts that the parsed text and this runtime array agree, so the two sides
|
|
58
|
+
* cannot drift.
|
|
59
|
+
*
|
|
60
|
+
* `applies` is the SETTINGS-side timing: 'none' means the parameter is not
|
|
61
|
+
* writable through the user layer (a cordis.yml change still follows the
|
|
62
|
+
* deployment's patch-reload policy).
|
|
63
|
+
*/
|
|
64
|
+
export interface ParamExposure {
|
|
65
|
+
id: string;
|
|
66
|
+
group: ParamGroup;
|
|
67
|
+
tier: ParamTier;
|
|
68
|
+
authority: ParamAuthority;
|
|
69
|
+
owner: string;
|
|
70
|
+
applies: 'live' | 'restart' | 'none';
|
|
71
|
+
docAnchor: string;
|
|
72
|
+
summary: string;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* The parameter registry. G1/S1.1 seeds it with the review group (G-C); the
|
|
76
|
+
* remaining groups land in S2.1. E3 = behaviour preference the user may change
|
|
77
|
+
* (live); E2 = resource or identity knob that stays with the deployment.
|
|
78
|
+
*/
|
|
79
|
+
export declare const PARAM_EXPOSURE: readonly ParamExposure[];
|
|
80
|
+
/** The settings namespace each OWNER PACKAGE registers (design §7.2).
|
|
81
|
+
*
|
|
82
|
+
* Keyed by owner rather than by group because a namespace has exactly one
|
|
83
|
+
* registrant (the platform refuses a second registration of the same name),
|
|
84
|
+
* while a group may span packages — group 'memory' is served by memory-files and
|
|
85
|
+
* tool-memory, each owning its own section. A package without an entry has no
|
|
86
|
+
* user layer (its knobs stay deployment-only). */
|
|
87
|
+
export declare const PARAM_NAMESPACES: Readonly<Record<string, string>>;
|
|
88
|
+
/** Structural view of one registered settings scope (platform Service Definition).
|
|
89
|
+
* Declared locally so this module keeps its zero-import, zero-dependency shape. */
|
|
90
|
+
interface SettingsScopeLike {
|
|
91
|
+
get(): unknown;
|
|
92
|
+
watch(callback: (next: unknown, prev: unknown) => void): () => void;
|
|
93
|
+
}
|
|
94
|
+
/** Structural view of the platform settings provider; only the members the
|
|
95
|
+
* family uses are named. A missing `describe` disables the user layer loudly
|
|
96
|
+
* (see {@link paramSectionOverrides}) instead of reading as 'no overrides'. */
|
|
97
|
+
export interface SettingsProviderLike {
|
|
98
|
+
register(namespace: string, schema: unknown, options: {
|
|
99
|
+
base: unknown;
|
|
100
|
+
applies?: 'live' | 'restart';
|
|
101
|
+
/** Owner-side refusal of a resolved section (cross-field rules the schema
|
|
102
|
+
* cannot express); throwing refuses the WRITE that produced the value. */
|
|
103
|
+
validate?: (value: unknown) => void;
|
|
104
|
+
}): SettingsScopeLike;
|
|
105
|
+
/** Merge a patch into one namespace's user layer. A stale `expectedRevision`
|
|
106
|
+
* rejects with the platform's SETTINGS_CONFLICT error. */
|
|
107
|
+
update?(namespace: string, patch: object, expectedRevision?: number): Promise<void>;
|
|
108
|
+
describe?(options?: {
|
|
109
|
+
redactSecrets?: boolean;
|
|
110
|
+
}): {
|
|
111
|
+
ns: string;
|
|
112
|
+
/** Current resolved section (schema defaults < base < user). */
|
|
113
|
+
value?: unknown;
|
|
114
|
+
/** Raw user section: a key's PRESENCE marks a user override. */
|
|
115
|
+
user?: Record<string, unknown>;
|
|
116
|
+
/** Monotonic revision of that raw section; a write sends it back. */
|
|
117
|
+
revision?: number;
|
|
118
|
+
/** Owner's declared effect timing. */
|
|
119
|
+
applies?: 'live' | 'restart';
|
|
120
|
+
}[];
|
|
121
|
+
}
|
|
122
|
+
/** Hooks a caller may supply when a section attaches to the settings service.
|
|
123
|
+
* @typeParam T - the section's value type (what `validate` inspects). */
|
|
124
|
+
export interface ParamSectionOptions<T extends object = object> {
|
|
125
|
+
/** Called once when the user layer turns unreadable (a warning, never silent). */
|
|
126
|
+
warn?: (message: string) => void;
|
|
127
|
+
/** Called after every committed change that the reader can observe — the place
|
|
128
|
+
* to REBUILD registration-level facts (the platform's own `installSection`
|
|
129
|
+
* documents the same hook shape). Consumers that read at use time pass nothing. */
|
|
130
|
+
onChange?: () => void;
|
|
131
|
+
/** Refuse a resolved section the owner could not act on: a cross-field rule the
|
|
132
|
+
* schema cannot express (the platform applies this hook to the RESOLVED section,
|
|
133
|
+
* so a user value is judged together with the deployment layer beneath it).
|
|
134
|
+
* Throwing refuses the write that produced the value. */
|
|
135
|
+
validate?: (value: T) => void;
|
|
136
|
+
}
|
|
137
|
+
/** Reader for one parameter section: presence-aware user overrides. */
|
|
138
|
+
export interface ParamOverrides<T extends object> {
|
|
139
|
+
/** The namespace this reader is bound to. */
|
|
140
|
+
readonly namespace: string;
|
|
141
|
+
/** The user-set value for one key, or undefined when the user never set it. */
|
|
142
|
+
get<K extends keyof T & string>(key: K): T[K] | undefined;
|
|
143
|
+
/** The resolved section (defaults < base < user) — display and tests. */
|
|
144
|
+
resolved(): T;
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* G3: expose one parameter section to the user layer.
|
|
148
|
+
*
|
|
149
|
+
* Precedence stays 'user > deployment > default': the caller keeps reading its
|
|
150
|
+
* deployment carriers (policy snapshot, then the plugin row) and consults
|
|
151
|
+
* {@link ParamOverrides.get} FIRST — an unset key returns undefined, so the
|
|
152
|
+
* deployment value keeps winning and the family's shadowing rules survive.
|
|
153
|
+
*
|
|
154
|
+
* Failure posture: a provider without `describe` (or one whose describe throws)
|
|
155
|
+
* leaves the user layer UNAVAILABLE and warns once — deployment values then
|
|
156
|
+
* apply. Treating an unreadable user layer as 'no overrides' would silently
|
|
157
|
+
* ignore a setting the user did write, so the warning names it.
|
|
158
|
+
* @typeParam T - the section's value type.
|
|
159
|
+
* @param provider - the platform settings provider, or undefined when absent.
|
|
160
|
+
* @param namespace - namespace to register.
|
|
161
|
+
* @param schema - schemastery schema the platform validates against.
|
|
162
|
+
* @param base - composition base layer (the plugin row's values).
|
|
163
|
+
* @param options - warning sink and the change hook.
|
|
164
|
+
* @returns a reader bound to the namespace.
|
|
165
|
+
*/
|
|
166
|
+
export declare function paramSectionOverrides<T extends object>(provider: SettingsProviderLike | undefined, namespace: string, schema: unknown, base: T, options?: ParamSectionOptions<T>): ParamOverrides<T>;
|
|
167
|
+
/** Minimal structural view of the cordis context used to attach a section.
|
|
168
|
+
* The callback takes `unknown` on purpose: cordis's own `inject` declares a
|
|
169
|
+
* `Context` parameter, and a callback accepting `unknown` is assignable to it
|
|
170
|
+
* (parameter contravariance) while a narrower shape is not. */
|
|
171
|
+
export interface SettingsHostLike {
|
|
172
|
+
inject(names: string[], callback: (ctx: unknown) => void): unknown;
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Attach a parameter section through the optional settings service.
|
|
176
|
+
* @typeParam T - the section's value type.
|
|
177
|
+
* @param host - the plugin context (structurally typed).
|
|
178
|
+
* @param namespace - namespace to register.
|
|
179
|
+
* @param schema - schemastery schema the platform validates against.
|
|
180
|
+
* @param base - composition base layer (the plugin row's values).
|
|
181
|
+
* @param options - warning sink and the change hook.
|
|
182
|
+
* @returns a reader that follows the provider when it appears.
|
|
183
|
+
*/
|
|
184
|
+
export declare function installParamSection<T extends object>(host: SettingsHostLike, namespace: string, schema: unknown, base: T, options?: ParamSectionOptions<T>): ParamOverrides<T>;
|
|
185
|
+
/**
|
|
186
|
+
* Number-typed read over {@link readParam}: the family's tunables are numbers,
|
|
187
|
+
* and a value of another type reads as absent so the caller's default applies
|
|
188
|
+
* (the same outcome the numeric clamps produce for a malformed value).
|
|
189
|
+
* @param carrier - config/snapshot object to read from, or undefined.
|
|
190
|
+
* @param id - canonical id (a deprecated alias is accepted and resolved first).
|
|
191
|
+
* @returns the resolved number, or undefined when absent or not a number.
|
|
192
|
+
*/
|
|
193
|
+
export declare function readNumberParam(carrier: object | undefined, id: string): number | undefined;
|
|
194
|
+
export {};
|
|
195
|
+
//# sourceMappingURL=params.d.ts.map
|