@lmzhen/dsh-evolution-core 0.4.0 → 0.5.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 +6020 -5067
- package/lib/types/citations.d.ts +109 -0
- package/lib/types/constants.d.ts +25 -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 +3 -0
- package/lib/types/io.d.ts +18 -3
- package/lib/types/limits.d.ts +68 -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 +63 -5
- 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,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reference re-homing plans (0.5.0 V1, design §16.7).
|
|
3
|
+
*
|
|
4
|
+
* A consolidation moves one skill's body under ANOTHER skill root, where every
|
|
5
|
+
* support-dir reference resolves against that other root (design §2.1). This
|
|
6
|
+
* module plans what has to happen to the support files and to the body's
|
|
7
|
+
* references for the move to leave no dangling link. Pure: the caller owns IO,
|
|
8
|
+
* decides the destination names, and decides whether the plan is applied (V2)
|
|
9
|
+
* or only reported (V1). Cross-skill rewriting is out of scope by design (§14) —
|
|
10
|
+
* a plan covers ONE body.
|
|
11
|
+
*/
|
|
12
|
+
/** One re-homed support file: source-relative path -> target-relative path. */
|
|
13
|
+
export interface ReferenceMove {
|
|
14
|
+
/** Path inside the skill the body comes FROM. */
|
|
15
|
+
from: string;
|
|
16
|
+
/** Path inside the skill the body moves TO. */
|
|
17
|
+
to: string;
|
|
18
|
+
}
|
|
19
|
+
/** One reference the plan rewrites, in body line order. */
|
|
20
|
+
export interface ReferenceRewriteEdit {
|
|
21
|
+
/** 1-based line in the body being moved. */
|
|
22
|
+
line: number;
|
|
23
|
+
/** The resolved target before the move. */
|
|
24
|
+
from: string;
|
|
25
|
+
/** The resolved target after the move. */
|
|
26
|
+
to: string;
|
|
27
|
+
/** The verbatim token as it appears in the body (`raw`), so a rewrite replaces
|
|
28
|
+
* exactly what is there and leaves a trailing `#fragment` in place. */
|
|
29
|
+
raw: string;
|
|
30
|
+
}
|
|
31
|
+
/** What one consolidation plan does to one body's references. */
|
|
32
|
+
export interface ReferenceRewritePlan {
|
|
33
|
+
/** References whose target changes (a re-homed file). */
|
|
34
|
+
edits: readonly ReferenceRewriteEdit[];
|
|
35
|
+
/** Cited files the moves give no destination: applying this plan would dangle. */
|
|
36
|
+
unresolved: readonly string[];
|
|
37
|
+
/** Targets still absent from the target file list after the moves. */
|
|
38
|
+
residualDangling: readonly string[];
|
|
39
|
+
/** The moves the body actually NEEDS: a support file the body never cites stays
|
|
40
|
+
* with the archived package instead of being copied into the target for
|
|
41
|
+
* nothing (upstream's wording is "every needed support file"). */
|
|
42
|
+
moves: readonly ReferenceMove[];
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Provisional re-homing rule. The naming style for re-homed files is still an
|
|
46
|
+
* open decision (design §16.3 B5); this rule only fires on a COLLISION, where
|
|
47
|
+
* something has to give: a path that the target already occupies gets the source
|
|
48
|
+
* name as a prefix (`references/a.md` -> `references/<source>-a.md`). Nested
|
|
49
|
+
* destinations are deliberately avoided: the support listing is one level deep,
|
|
50
|
+
* so a file moved into a subdirectory would drop out of the listing entirely.
|
|
51
|
+
* @param sourceFiles - the moving skill's support files.
|
|
52
|
+
* @param targetFiles - the destination skill's support files.
|
|
53
|
+
* @param sourceName - the moving skill's name, used as the collision prefix.
|
|
54
|
+
* @returns the moves plus the paths that collided (for the report).
|
|
55
|
+
*/
|
|
56
|
+
export declare function planRehoming(sourceFiles: readonly string[], targetFiles: readonly string[], sourceName: string): {
|
|
57
|
+
moves: ReferenceMove[];
|
|
58
|
+
collisions: string[];
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* Plan the reference rewrites for one body that is moving into another root.
|
|
62
|
+
* @param input - the body, the MOVING skill's file list, the re-homing moves and
|
|
63
|
+
* the destination's file list (with the moved paths already included).
|
|
64
|
+
* @returns the edits, the cited files without a destination, and any target that
|
|
65
|
+
* the destination file list cannot prove (applying such a plan would dangle).
|
|
66
|
+
*/
|
|
67
|
+
export declare function planReferenceRewrite(input: {
|
|
68
|
+
content: string;
|
|
69
|
+
files: readonly string[];
|
|
70
|
+
moves: readonly ReferenceMove[];
|
|
71
|
+
targetFiles: readonly string[];
|
|
72
|
+
}): ReferenceRewritePlan;
|
|
73
|
+
/**
|
|
74
|
+
* Apply a plan's edits to the body (V2). Line-wise and literal: line N gets its
|
|
75
|
+
* FIRST occurrence of the recorded token replaced, everything else is copied
|
|
76
|
+
* byte for byte — no regex, so a token that happens to contain regex
|
|
77
|
+
* metacharacters cannot rewrite more than it matched.
|
|
78
|
+
* @param content - the body the plan was computed against.
|
|
79
|
+
* @param edits - the plan's edits.
|
|
80
|
+
* @returns the rewritten body (identical to the input when there is nothing to do).
|
|
81
|
+
*/
|
|
82
|
+
export declare function applyReferenceRewrite(content: string, edits: readonly ReferenceRewriteEdit[]): string;
|
|
83
|
+
/**
|
|
84
|
+
* One-line, model-facing summary of a plan, for the consolidation refusal and
|
|
85
|
+
* for the V2 apply path's audit text.
|
|
86
|
+
* @param plan - the plan to describe.
|
|
87
|
+
* @returns a compact sentence naming counts, the moves and any residual risk.
|
|
88
|
+
*/
|
|
89
|
+
export declare function describeReferenceRewrite(plan: ReferenceRewritePlan): string;
|
|
90
|
+
//# sourceMappingURL=reference-rewrite.d.ts.map
|
|
@@ -67,6 +67,8 @@ interface SkillHealthSnapshot {
|
|
|
67
67
|
}
|
|
68
68
|
interface SkillHealthDim {
|
|
69
69
|
bodyChars: number;
|
|
70
|
+
/** The same body on the token scale (limit basis) — the judged quantity. */
|
|
71
|
+
bodyTokens: number;
|
|
70
72
|
stampDensityPerKb: number | null;
|
|
71
73
|
supportGroups: number;
|
|
72
74
|
/** Usage churn facts; null when the caller supplied no counts. */
|
|
@@ -368,11 +368,27 @@ export declare class SkillLibrary {
|
|
|
368
368
|
* promises the present branch is complete (011 §7 enrichment, probe reads).
|
|
369
369
|
*/
|
|
370
370
|
listSupportFiles(rawName: string): Promise<Probe<string[]>>;
|
|
371
|
+
/**
|
|
372
|
+
* V4 (design §16.6): exact character counts of the support files that can
|
|
373
|
+
* POSSIBLY exceed the content cap. The pre-filter is BYTE size, and that is
|
|
374
|
+
* sound rather than approximate: a UTF-16 code unit never costs less than one
|
|
375
|
+
* UTF-8 byte, so a file whose bytes are within the cap provably cannot exceed
|
|
376
|
+
* the cap in characters. The returned map is therefore COMPLETE for the
|
|
377
|
+
* oversize question without reading the small files.
|
|
378
|
+
* @param rawName - the skill's name.
|
|
379
|
+
* @returns path -> character count (possibly empty), or null when the listing or
|
|
380
|
+
* a size probe cannot answer — unknown is never an empty map.
|
|
381
|
+
*/
|
|
382
|
+
supportFileChars(rawName: string): Promise<Record<string, number> | null>;
|
|
371
383
|
/**
|
|
372
384
|
* Structure-health facts for one skill (rc.73 A1, 008 design): body
|
|
373
385
|
* chars/density from SKILL.md, support groups from countSupportDirs, plus
|
|
374
386
|
* optional usage counts (A2 churn dimension) when the caller has them.
|
|
375
|
-
* Derived, never persisted
|
|
387
|
+
* Derived, never persisted. CONTRACT: `null` whenever the skill cannot be
|
|
388
|
+
* read — a missing file AND any read failure (EACCES/EIO/…) both answer
|
|
389
|
+
* null, so a whole health view degrades one ROW instead of throwing out of
|
|
390
|
+
* its per-skill loop (A6, audit P2-10: the former code absorbed only
|
|
391
|
+
* missing/EISDIR and let a transient win32 hold kill the entire view).
|
|
376
392
|
*/
|
|
377
393
|
assessHealth(rawName: string, thresholds?: SkillHealthThresholds, counts?: {
|
|
378
394
|
patchCount?: number;
|
|
@@ -436,9 +452,19 @@ export declare class SkillLibrary {
|
|
|
436
452
|
* stolen by the sweep. A dead-pid residue would otherwise permanently
|
|
437
453
|
* refuse archive/restore. */
|
|
438
454
|
private deleteStrandedLocks;
|
|
455
|
+
/** R2 follow-up (audit of A2): the recycled-pid window from io's
|
|
456
|
+
* `ALIVE_LOCK_TAKEOVER_MS`, applied to the stranded-lock sweepers. A lock
|
|
457
|
+
* whose holder pid probes alive but whose mtime is older than the alive
|
|
458
|
+
* window is a recycled pid, not a live writer (no write in this family
|
|
459
|
+
* holds a lock for more than minutes) — without this, one recycled pid
|
|
460
|
+
* blocked whole-tree snapshot recovery forever with a "retry once the
|
|
461
|
+
* write completes" message that could never become true. A backend without
|
|
462
|
+
* `mtime` keeps the conservative refuse-on-alive posture. */
|
|
463
|
+
private lockHolderAgedOut;
|
|
439
464
|
/** Remove `lockPath` only when its body has the writer-lock `pid:token`
|
|
440
|
-
* shape AND the holder pid is not alive
|
|
441
|
-
* or a live writer's
|
|
465
|
+
* shape AND the holder pid is not alive (or is an aged-out recycled pid —
|
|
466
|
+
* R2 follow-up); anything else (a user support file or a live writer's
|
|
467
|
+
* lock) is left untouched. */
|
|
442
468
|
private sweepLockIfStranded;
|
|
443
469
|
/** A1-4 (v18): a manifest-declared name is copied with `join(root, name)`;
|
|
444
470
|
* only a single, non-traversing path component is safe. Dotfiles
|
|
@@ -451,6 +477,17 @@ export declare class SkillLibrary {
|
|
|
451
477
|
* recovery. A non-lock body shape is left alone (user file). */
|
|
452
478
|
private refuseLiveLockOrSweep;
|
|
453
479
|
archive(rawName: string, options?: ArchiveOptions): Promise<SkillActionResult>;
|
|
480
|
+
/**
|
|
481
|
+
* V2 (design §16.7): what an apply-mode consolidation would do for ONE source,
|
|
482
|
+
* computed BEFORE any side effect. `blocked` means behaviour is exactly the
|
|
483
|
+
* plan-mode refusal (with the reason named); `apply` carries the rewritten body
|
|
484
|
+
* and the support files that must be copied into the target.
|
|
485
|
+
* @param source - the moving skill's name.
|
|
486
|
+
* @param targetName - the destination skill's name.
|
|
487
|
+
* @param body - the moving body (frontmatter stripped).
|
|
488
|
+
* @returns the decision plus the note to append to a refusal.
|
|
489
|
+
*/
|
|
490
|
+
private planSourceRehoming;
|
|
454
491
|
/**
|
|
455
492
|
* Merge the bodies of `sources` into `target` and archive the sources with
|
|
456
493
|
* an absorbed-into marker. Hermes-style consolidation: overlapping skills
|
|
@@ -468,6 +505,14 @@ export declare class SkillLibrary {
|
|
|
468
505
|
* `target/references/<source>.md` and archives the source — the demote path
|
|
469
506
|
* (009-II). A source body with support-directory links is refused there too
|
|
470
507
|
* (the references file would carry links whose files were archived).
|
|
508
|
+
*
|
|
509
|
+
* V2 (design §16.7) lifts that refusal when `referenceRewrite:'apply'` and the
|
|
510
|
+
* plan proves the move is safe: the support files the body NEEDS are copied into
|
|
511
|
+
* the target under their own paths (renamed only on a collision) and the body's
|
|
512
|
+
* references are rewritten to match, in the same commit. Anything the plan
|
|
513
|
+
* cannot place — a cited file the source no longer has, an unreadable listing —
|
|
514
|
+
* still refuses, and the refusal names it. The archived source keeps whatever
|
|
515
|
+
* the body did not need.
|
|
471
516
|
*/
|
|
472
517
|
consolidate(target: string, sources: string[], origin?: WriteOrigin, options?: {
|
|
473
518
|
mode?: 'append' | 'reference';
|
|
@@ -514,6 +559,19 @@ export declare class SkillLibrary {
|
|
|
514
559
|
* Backends without the mtime probe skip pruning (no false deletes on
|
|
515
560
|
* unknown age).
|
|
516
561
|
*/
|
|
562
|
+
/**
|
|
563
|
+
* 0.5.0 V1 (design §16.6-④): the retention window's READ half. Names every
|
|
564
|
+
* archived entry past the window without touching it — the report path the
|
|
565
|
+
* default policy uses, and the input the curator's run report carries.
|
|
566
|
+
* @returns the expired entry names ([] when the backend has no mtime probe,
|
|
567
|
+
* because an unknown age must never read as expired).
|
|
568
|
+
*/
|
|
569
|
+
expiredArchives(): Promise<string[] | null>;
|
|
570
|
+
/** V4 (design §16.6): support-file char policy, resolved at the call site. */
|
|
571
|
+
supportFileCharPolicy(): 'report' | 'enforce';
|
|
572
|
+
/** Retention policy resolved at the call site (absent limits object = report).
|
|
573
|
+
* Public so a run report can say WHICH policy produced its numbers. */
|
|
574
|
+
archiveRetentionPolicy(): 'report' | 'prune';
|
|
517
575
|
private pruneExpiredArchives;
|
|
518
576
|
/**
|
|
519
577
|
* Snapshot the recoverable skills state: active tree, usage/suppression
|
|
@@ -601,8 +659,8 @@ export interface NewSkillLibraryOptions {
|
|
|
601
659
|
threatExemptLabels?: readonly string[] | undefined;
|
|
602
660
|
}
|
|
603
661
|
export declare function newSkillLibrary(options: NewSkillLibraryOptions): SkillLibrary;
|
|
604
|
-
export { DEFAULT_SKILL_LIMITS } from './limits.ts';
|
|
605
|
-
export type { SkillLimits } from './limits.ts';
|
|
662
|
+
export { DEFAULT_SKILL_LIMITS, DEFAULT_CITATION_POLICY, DEFAULT_REFERENCE_REWRITE_POLICY, DEFAULT_ARCHIVE_RETENTION_POLICY, DEFAULT_SUPPORT_FILE_CHAR_POLICY, POLICY_STAGE_DEFAULTS, policyStageLimits, } from './limits.ts';
|
|
663
|
+
export type { SkillLimits, CitationPolicy, ReferenceRewritePolicy, ArchiveRetentionPolicy, SupportFileCharPolicy, PolicyStageFields, } from './limits.ts';
|
|
606
664
|
export { authoringFeedback, frontmatterBlock, frontmatterCatalogInvalid, normalizeFrontmatter, parseFrontmatter, relatedSkillNames, validateFrontmatter, yamlPlainScalarNeedsQuotes } from './frontmatter.ts';
|
|
607
665
|
export type { AuthoringFeedback, Frontmatter, FrontmatterNormalizeResult, FrontmatterRead, PlatformStringSplit } from './frontmatter.ts';
|
|
608
666
|
//# sourceMappingURL=skill-store.d.ts.map
|
|
@@ -186,6 +186,29 @@ export declare function isSkillReadToolName(name: string): boolean;
|
|
|
186
186
|
* counts as a read: the platform settles every started sub-dispatch, so pending
|
|
187
187
|
* is a live-window state, not a failure.
|
|
188
188
|
*/
|
|
189
|
+
/** Tool names whose dispatch reads one FILE — the deployment's file tool
|
|
190
|
+
* (default `read`). A differently named tool is configured, not guessed. */
|
|
191
|
+
export declare const DEFAULT_SUPPORT_READ_TOOL_NAMES: readonly string[];
|
|
192
|
+
/** One support-file read attributed to a skill (design §5.5). */
|
|
193
|
+
export interface SupportReadHit {
|
|
194
|
+
skill: string;
|
|
195
|
+
/** Skill-root-relative path, e.g. `references/design-x.md`. */
|
|
196
|
+
rel: string;
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Read one dispatch as a SUPPORT-FILE read attributed to a skill: a read of
|
|
200
|
+
* `<root>/<skill>/<support dir>/…` is demand evidence for that one file, the
|
|
201
|
+
* way `skillReadNameOf` is demand evidence for a whole body.
|
|
202
|
+
* @param signal - a settled dispatch.
|
|
203
|
+
* @param options - the deployment's file-tool names and its skills root; an
|
|
204
|
+
* empty root falls back to the `/skills/` marker in the path.
|
|
205
|
+
* @returns the skill name and the skill-root-relative path, or `null` when the
|
|
206
|
+
* dispatch is not a support-file read. Pure: no IO, no service lookup.
|
|
207
|
+
*/
|
|
208
|
+
export declare function supportFileReadOf(signal: ToolDispatchSignal, options: {
|
|
209
|
+
toolNames: readonly string[];
|
|
210
|
+
root: string;
|
|
211
|
+
}): SupportReadHit | null;
|
|
189
212
|
export declare function skillReadNameOf(signal: ToolDispatchSignal): string | undefined;
|
|
190
213
|
/**
|
|
191
214
|
* Fold one session log into its deduplicated dispatches.
|
package/lib/types/usage.d.ts
CHANGED
|
@@ -28,6 +28,11 @@ export interface UsageRecord {
|
|
|
28
28
|
* relevant again. */
|
|
29
29
|
feedback_score?: number | undefined;
|
|
30
30
|
feedback_warn?: boolean | undefined;
|
|
31
|
+
/** Demand evidence per SUPPORT FILE (design §5.5): how many observed reads
|
|
32
|
+
* landed on each `references/…`-style path of this skill. Absent on every
|
|
33
|
+
* record written before this field existed, and on skills whose support
|
|
34
|
+
* files were never read — absence means "no evidence", never "zero demand". */
|
|
35
|
+
support_reads?: Record<string, number> | undefined;
|
|
31
36
|
}
|
|
32
37
|
export type UsageMap = Map<string, UsageRecord>;
|
|
33
38
|
export declare function usageFile(root: string): string;
|
|
@@ -44,6 +49,8 @@ export declare function emptyRecord(): UsageRecord;
|
|
|
44
49
|
* Pure — exported for unit tests; `loadUsage` is the production caller.
|
|
45
50
|
*/
|
|
46
51
|
export declare function normalizeUsageRecord(record: unknown): UsageRecord;
|
|
52
|
+
/** Sidecar bound: a pathological path set must not grow the usage file forever. */
|
|
53
|
+
export declare const MAX_SUPPORT_READ_PATHS = 200;
|
|
47
54
|
export declare function loadUsage(root: string, io?: EvolutionIoLike): Promise<UsageMap>;
|
|
48
55
|
/**
|
|
49
56
|
* Atomic read-modify-write on the usage sidecar (rc.50 P2-2): `task` receives
|
|
@@ -100,8 +107,19 @@ export declare function getRecord(map: UsageMap, name: string): UsageRecord;
|
|
|
100
107
|
export declare function bumpView(map: UsageMap, name: string, when?: Date): void;
|
|
101
108
|
export declare function bumpUse(map: UsageMap, name: string, when?: Date): void;
|
|
102
109
|
export declare function bumpPatch(map: UsageMap, name: string, when?: Date): void;
|
|
110
|
+
/** Count one observed support-file read (design §5.5). The map is created on
|
|
111
|
+
* first evidence only — a skill with no support reads keeps the field absent. */
|
|
112
|
+
export declare function bumpSupportRead(map: UsageMap, name: string, rel: string, when?: Date): void;
|
|
103
113
|
export declare function markAgentCreated(map: UsageMap, name: string): void;
|
|
104
114
|
export declare function latestActivityAt(record: UsageRecord): string | null;
|
|
115
|
+
/** Days between an ISO timestamp (falling back to `created`) and `now`. An
|
|
116
|
+
* unparseable date counts as 0 days: NaN silently froze every age comparison
|
|
117
|
+
* (A2-9). */
|
|
118
|
+
export declare function daysSinceIso(iso: string | null, created: string, nowMs: number): number;
|
|
119
|
+
/** Idle days of one skill since its lifecycle age anchor (`last activity ??
|
|
120
|
+
* created_at`) — the SAME anchor the curator's transitions use, so the
|
|
121
|
+
* maintenance view and the lifecycle can never disagree about staleness. */
|
|
122
|
+
export declare function idleDays(record: UsageRecord, now?: Date): number;
|
|
105
123
|
/**
|
|
106
124
|
* Whether the library has ANY observed read evidence (C observation window):
|
|
107
125
|
* reads were invisible to the usage sidecar before A2, so `view_count` zero
|
package/package.json
CHANGED