@lmzhen/dsh-evolution-maintenance 0.26.15 → 0.27.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 CHANGED
@@ -1,6 +1,7 @@
1
1
  import { a as snapshotFromLibrary, i as enrichmentSnapshotOptions, n as computeProbe, r as buildEnrichment, t as PROBE_SIGNALS } from "./probe-Bojb0piw.js";
2
- import { AUTHORING_DESCRIPTION_BAR, DEFAULT_CURATOR_MODEL, DEFAULT_HEALTH_THRESHOLDS, DRIFT_MAX_LINE_CHARS, DRIFT_SIGNALS_VERSION, DRIFT_SIGNAL_NOUNS, LOW_QUALITY_THRESHOLD, MAINTAIN_OUTPUT_INSTRUCTION, MAINTAIN_PROMPT, MAX_TIMER_DELAY_MS, MIN_STAMP_BODY_CHARS, PROMPT_BUNDLE, PROMPT_BUNDLE_ID, computeDriftSignals, findDriftSignal, isRecord, redactSecrets, verifyPromptBundle } from "@lmzhen/dsh-evolution-core";
2
+ import { AUTHORING_DESCRIPTION_BAR, DEFAULT_CURATOR_MODEL, DEFAULT_HEALTH_THRESHOLDS, DRIFT_MAX_LINE_CHARS, DRIFT_SIGNALS_VERSION, DRIFT_SIGNAL_NOUNS, LOW_QUALITY_THRESHOLD, MAINTAIN_OUTPUT_INSTRUCTION, MAINTAIN_PROMPT, MAX_TIMER_DELAY_MS, MIN_STAMP_BODY_CHARS, PROMPT_BUNDLE, PROMPT_BUNDLE_ID, STORE_LEDGER, computeDriftSignals, evolutionHome, findDriftSignal, isRecord, redactSecrets, verifyPromptBundle } from "@lmzhen/dsh-evolution-core";
3
3
  import { createHash, randomUUID } from "node:crypto";
4
+ import { join } from "node:path";
4
5
  //#region lib/types/render-facts.js
5
6
  /**
6
7
  * Mechanical-facts rendering for the maintenance subagent (011 §6).
@@ -23,6 +24,19 @@ function renderFacts(report, options) {
23
24
  lines.push(`# skill=${sanitize(skill.name)} (${meta.join(" ")})`);
24
25
  for (const signal of skill.signals) lines.push(...renderSignal(signal, redact));
25
26
  }
27
+ for (const store of options.footprint ?? []) {
28
+ const parts = [
29
+ `${store.used === void 0 || store.skipped !== void 0 ? "[UNKNOWN]" : "[FACT]"} signal=store/${sanitize(store.id)}`,
30
+ `unit=${sanitize(store.unit)}`,
31
+ `bound=${sanitize(store.bound)}`,
32
+ `used=${store.used === void 0 ? "unknown" : String(store.used)}`,
33
+ `lastSweep=${sanitize(store.lastSweep ?? "never")}`,
34
+ `removed=${store.removed === void 0 ? "unknown" : String(store.removed)}`
35
+ ];
36
+ if (store.skipped !== void 0) parts.push(`skipped=${sanitize(store.skipped)}`);
37
+ parts.push(`verdict=${store.used === void 0 || store.skipped !== void 0 ? "unknown" : "fact"}`);
38
+ lines.push(parts.join(" "));
39
+ }
26
40
  lines.push("<<<END FACTS>>>");
27
41
  return lines.join("\n");
28
42
  }
@@ -285,6 +299,124 @@ function validateAndNormalizeMaintainPlan(raw, report, validSignals) {
285
299
  };
286
300
  }
287
301
  //#endregion
302
+ //#region lib/types/footprint.js
303
+ /**
304
+ * G3/3.3: the footprint reading for one maintenance run.
305
+ *
306
+ * One line per REGISTERED store (STORE_LEDGER), never a line per store that happened to be readable: the
307
+ * auditor must be able to tell "measured zero" from "not measured" (I3: not-done must never read as
308
+ * nothing-to-do). This first cut reads the stores whose artifact needs only the home-relative path the
309
+ * runtime already knows; everything else renders UNKNOWN with the reason, so the block is honest today
310
+ * and becomes informative as each reader lands.
311
+ */
312
+ /**
313
+ * Read what can be read, and say so for everything else.
314
+ * @param io - the runtime io, or undefined when the caller has none (every store then reads UNKNOWN).
315
+ * @returns one reading per registered store, in ledger order.
316
+ */
317
+ async function reclamationFootprint(io) {
318
+ const out = [];
319
+ for (const store of STORE_LEDGER) {
320
+ if (io === void 0) {
321
+ out.push({
322
+ id: store.id,
323
+ unit: store.unit,
324
+ bound: store.bound,
325
+ skipped: "the maintenance runtime has no io; nothing was measured"
326
+ });
327
+ continue;
328
+ }
329
+ if (store.id === "reports") {
330
+ try {
331
+ const names = await io.list(join(evolutionHome(), "reports"));
332
+ out.push({
333
+ id: store.id,
334
+ unit: store.unit,
335
+ bound: store.bound,
336
+ used: names.length,
337
+ lastSweep: "see the curator run end",
338
+ removed: void 0
339
+ });
340
+ } catch {
341
+ out.push({
342
+ id: store.id,
343
+ unit: store.unit,
344
+ bound: store.bound,
345
+ skipped: "the reports directory could not be listed"
346
+ });
347
+ }
348
+ continue;
349
+ }
350
+ out.push({
351
+ id: store.id,
352
+ unit: store.unit,
353
+ bound: store.bound,
354
+ skipped: "no reader yet for this store"
355
+ });
356
+ }
357
+ return out;
358
+ }
359
+ /**
360
+ * G3/3.4: the three reachability readings, folded into the footprint channel so the report has ONE shape.
361
+ *
362
+ * Paths come from the caller: the history root and the snapshot root live under the skills root, and the
363
+ * reports directory under the evolution home. A runtime that does not state its skills root gets UNKNOWN
364
+ * for the two rooted domains instead of a guess - the same three-state rule the rest of this file uses.
365
+ * @param io - the runtime io, or undefined when the caller has none.
366
+ * @param skillRoot - the configured skills root, or undefined when the runtime does not state one.
367
+ */
368
+ async function reachabilityFootprint(io, skillRoot) {
369
+ const domains = [
370
+ "history.blobs",
371
+ "reports.files",
372
+ "backups.trees"
373
+ ];
374
+ if (io === void 0) return domains.map((domain) => ({
375
+ id: domain,
376
+ unit: "files",
377
+ bound: "unmeasured",
378
+ skipped: "the maintenance runtime has no io; nothing was measured"
379
+ }));
380
+ const { historyReachability, reportsReachability, backupsReachability } = await import("@lmzhen/dsh-evolution-core");
381
+ const out = [];
382
+ if (skillRoot === void 0) for (const domain of ["history.blobs", "backups.trees"]) out.push({
383
+ id: domain,
384
+ unit: "files",
385
+ bound: "unmeasured",
386
+ skipped: "the runtime states no skills root; the reading was not attempted"
387
+ });
388
+ else {
389
+ const history = await historyReachability(io, join(skillRoot, ".history"));
390
+ out.push({
391
+ id: history.domain,
392
+ unit: history.unit,
393
+ bound: history.baseline,
394
+ used: history.unreachable,
395
+ lastSweep: "this run",
396
+ removed: void 0
397
+ });
398
+ const backups = await backupsReachability(io, join(skillRoot, ".backups"), []);
399
+ out.push({
400
+ id: backups.domain,
401
+ unit: backups.unit,
402
+ bound: backups.baseline,
403
+ used: backups.unreachable,
404
+ lastSweep: "this run",
405
+ removed: void 0
406
+ });
407
+ }
408
+ const reports = await reportsReachability(io, join(evolutionHome(), "reports"), [], []);
409
+ out.push({
410
+ id: reports.domain,
411
+ unit: reports.unit,
412
+ bound: reports.baseline,
413
+ used: reports.unreachable,
414
+ lastSweep: "this run",
415
+ removed: void 0
416
+ });
417
+ return out;
418
+ }
419
+ //#endregion
288
420
  //#region lib/types/orchestrate.js
289
421
  /**
290
422
  * Maintain orchestration (011 §3/§7): snapshot → drift signals → facts
@@ -336,7 +468,7 @@ function jointSignature(template, signalsVersion) {
336
468
  * rendered facts block (joint signature + redaction). Shared by `runMaintain`
337
469
  * and the `--facts` preview so the preview can never disagree with the scan.
338
470
  */
339
- function buildMaintainFacts(snapshots, usageObserved, redact) {
471
+ function buildMaintainFacts(snapshots, usageObserved, redact, footprint) {
340
472
  const report = computeDriftSignals(snapshots.map((snapshot) => ({
341
473
  ...snapshot,
342
474
  usageObserved
@@ -348,7 +480,8 @@ function buildMaintainFacts(snapshots, usageObserved, redact) {
348
480
  facts: renderFacts(report, {
349
481
  signalsVersion,
350
482
  signature,
351
- redact
483
+ redact,
484
+ footprint
352
485
  }),
353
486
  signalsVersion,
354
487
  signature
@@ -395,7 +528,6 @@ function formatPlan(validated, runId) {
395
528
  }
396
529
  /** Run one maintenance scan and return display text plus validation metadata. */
397
530
  async function runMaintain(runtime, options = {}) {
398
- let abortSignal;
399
531
  try {
400
532
  if (!verifyPromptBundle(PROMPT_BUNDLE)) return {
401
533
  ok: false,
@@ -430,7 +562,8 @@ async function runMaintain(runtime, options = {}) {
430
562
  text: "维护扫描:技能库为空,无需处理。"
431
563
  };
432
564
  }
433
- const { facts, report, signalsVersion, signature } = buildMaintainFacts(snapshots, enrichment?.usageObservedValue, options.redact);
565
+ const footprint = [...await reclamationFootprint(runtime.io), ...await reachabilityFootprint(runtime.io, runtime.skillRoot)];
566
+ const { facts, report, signalsVersion, signature } = buildMaintainFacts(snapshots, enrichment?.usageObservedValue, options.redact, footprint);
434
567
  const template = renderMaintainTemplate(MAINTAIN_PROMPT, PROMPT_BUNDLE_ID, signalsVersion, signature);
435
568
  const probeRegistered = runtime.tools?.get("maintenance_probe") != null;
436
569
  const probeDegraded = options.toolAllow === void 0 && !probeRegistered;
@@ -444,22 +577,18 @@ ${MAINTAIN_OUTPUT_INSTRUCTION}`;
444
577
  recommendationCount: 0,
445
578
  error: `maintain:--timeout 必须是以毫秒为单位的正整数,最大 ${MAX_TIMER_DELAY_MS};收到 ${String(timeoutMs)}`
446
579
  };
447
- const signal = options.signal !== void 0 ? AbortSignal.any([AbortSignal.timeout(timeoutMs), options.signal]) : AbortSignal.timeout(timeoutMs);
448
- abortSignal = signal;
449
580
  const agentOptions = { model: options.model ?? runtime.evolutionPolicy?.get()?.curatorModel ?? DEFAULT_CURATOR_MODEL };
450
581
  if (options.provider) agentOptions.provider = options.provider;
451
- const run = await runtime.subagents.start("spawn", {
452
- label: "dsh-evolution-maintain",
453
- prompt: [{
454
- type: "text",
455
- text: prompt
456
- }],
582
+ const outcome = await runtime.channel.run({
457
583
  parent: runtime.parent,
458
- signal,
584
+ label: "dsh-evolution-maintain",
585
+ promptText: template + "\n\n" + prompt,
586
+ kind: "maintainer",
587
+ allow: [...toolAllow],
459
588
  maxDepth: typeof options.maxDepth === "number" && Number.isFinite(options.maxDepth) && options.maxDepth >= 1 ? options.maxDepth : 1,
460
589
  agentOptions,
461
- persona: template,
462
- toolFilter: { allow: [...toolAllow] },
590
+ timeoutMs,
591
+ ...options.signal === void 0 ? {} : { signal: options.signal },
463
592
  outputSchema: {
464
593
  type: "object",
465
594
  additionalProperties: false,
@@ -530,53 +659,47 @@ ${MAINTAIN_OUTPUT_INSTRUCTION}`;
530
659
  }
531
660
  }
532
661
  });
533
- try {
534
- const runResult = await run.result;
535
- const raw = runResult.structured;
536
- if (raw === void 0) {
537
- if (runResult.stopReason === "aborted") return {
538
- ok: false,
539
- recommendationCount: 0,
540
- error: "维护扫描已中止(在子智能体产出计划之前运行被取消):请在会话空闲时重试;并发重交会取消上一次扫描。"
541
- };
542
- return {
543
- ok: false,
544
- recommendationCount: 0,
545
- error: "维护子智能体没有返回结构化计划(模型没有产出结构化计划,或运行在产出前结束):请重试;若反复出现,请提高 --timeout 或检查模型的输出结构。"
546
- };
547
- }
548
- const validated = validateAndNormalizeMaintainPlan(raw, report, existingSignalIds(report));
549
- if (!validated.ok) return {
550
- ok: false,
551
- recommendationCount: 0,
552
- error: `维护计划被校验器拒绝:${validated.errors.slice(0, 5).join("; ")}`
553
- };
554
- const runId = options.runId ?? randomUUID();
555
- return {
556
- ok: true,
557
- runId,
558
- verdict: validated.plan.verdict,
559
- forcedHuman: validated.forcedHuman,
560
- items: validated.plan.plan,
561
- notes: validated.plan.notes,
562
- needsHumanCount: validated.plan.plan.filter((item) => item.needs_human).length,
563
- recommendationCount: validated.plan.plan.length,
564
- text: formatPlan(validated, runId)
565
- };
566
- } finally {
567
- try {
568
- await run.dispose();
569
- } catch (disposeError) {
570
- runtime.logger?.warn(`dsh-evolution-maintenance: subagent dispose failed: ${disposeError instanceof Error ? disposeError.message : String(disposeError)}`);
571
- }
572
- }
662
+ if (outcome.kind === "unavailable") return {
663
+ ok: false,
664
+ recommendationCount: 0,
665
+ error: `维护扫描不可用(${outcome.code}):${outcome.reason}。请在已挂载 agentPresets/subagents 的宿主会话中重试。`
666
+ };
667
+ if (outcome.kind === "aborted") return {
668
+ ok: false,
669
+ recommendationCount: 0,
670
+ error: "维护扫描已中止(取消或超时),计划尚未产出 —— 在技能多、库大的场景下,请重试或提高超时(evolution-commands maintainTimeoutMs)。"
671
+ };
672
+ if (outcome.kind === "no-structured") return {
673
+ ok: false,
674
+ recommendationCount: 0,
675
+ error: "维护子智能体没有返回结构化计划(模型没有产出结构化计划,或运行在产出前结束):请重试;若反复出现,请提高 --timeout 或检查模型的输出结构。"
676
+ };
677
+ const raw = outcome.structured;
678
+ const validated = validateAndNormalizeMaintainPlan(raw, report, existingSignalIds(report));
679
+ if (!validated.ok) return {
680
+ ok: false,
681
+ recommendationCount: 0,
682
+ error: `维护计划被校验器拒绝:${validated.errors.slice(0, 5).join("; ")}`
683
+ };
684
+ const runId = options.runId ?? randomUUID();
685
+ return {
686
+ ok: true,
687
+ runId,
688
+ verdict: validated.plan.verdict,
689
+ forcedHuman: validated.forcedHuman,
690
+ items: validated.plan.plan,
691
+ notes: validated.plan.notes,
692
+ needsHumanCount: validated.plan.plan.filter((item) => item.needs_human).length,
693
+ recommendationCount: validated.plan.plan.length,
694
+ text: formatPlan(validated, runId)
695
+ };
573
696
  } catch (error) {
574
697
  const name = typeof error === "object" && error !== null ? error.name : void 0;
575
698
  const message = error instanceof Error ? error.message : String(error);
576
699
  return {
577
700
  ok: false,
578
701
  recommendationCount: 0,
579
- error: abortSignal?.aborted === true || name === "AbortError" || message === "This operation was aborted" ? "维护扫描已中止(取消或超时),计划尚未产出 —— 在技能多、库大的场景下,请重试或提高超时(evolution-commands maintainTimeoutMs)。" : `Maintenance scan failed: ${message}`
702
+ error: options.signal?.aborted === true || name === "AbortError" || message === "This operation was aborted" ? "维护扫描已中止(取消或超时),计划尚未产出 —— 在技能多、库大的场景下,请重试或提高超时(evolution-commands maintainTimeoutMs)。" : `Maintenance scan failed: ${message}`
580
703
  };
581
704
  }
582
705
  }
package/lib/tools.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { a as snapshotFromLibrary, i as enrichmentSnapshotOptions, n as computeProbe, r as buildEnrichment, t as PROBE_SIGNALS } from "./probe-Bojb0piw.js";
2
- import { DEFAULT_PROBE_TIMEOUT_MS, MAX_TIMER_DELAY_MS, assertSkillsRootAliasRetired, clampedNumber, newSkillLibrary, redactSecrets, resolveRootConfig } from "@lmzhen/dsh-evolution-core";
2
+ import { DEFAULT_PROBE_TIMEOUT_MS, MAX_TIMER_DELAY_MS, assertSkillsRootAliasRetired, authorityFrom, clampedNumber, newSkillLibrary, redactSecrets, resolveRootConfig } from "@lmzhen/dsh-evolution-core";
3
3
  import z from "@deepseek-ai/schemastery";
4
4
  import { defineTool } from "@deepseek-ai/dsh-tools";
5
5
  //#region lib/types/tools.js
@@ -69,6 +69,7 @@ function apply(ctx, rawConfig = {}) {
69
69
  ...target ? { target } : {}
70
70
  };
71
71
  const library = newSkillLibrary({
72
+ authority: authorityFrom({ origin: "foreground" }),
72
73
  config: rootConfig,
73
74
  io: ioRegistry.provider()
74
75
  });
@@ -0,0 +1,30 @@
1
+ import type { StoreFootprint } from './render-facts.ts';
2
+ import type { ReachabilityIo } from '@lmzhen/dsh-evolution-core';
3
+ /**
4
+ * The io surface this reading needs; a runtime without it yields UNKNOWN for every store.
5
+ *
6
+ * It EXTENDS the reachability read surface instead of restating it: this module drives those readings,
7
+ * so the reads they need are the reads it needs. Restating them here is what produced the same
8
+ * "argument not assignable" error three times - the surface now has ONE owner (`ReachabilityIo`) and this
9
+ * adds only the probe the report sweep uses on top.
10
+ */
11
+ export interface FootprintIo extends ReachabilityIo {
12
+ mtime?(path: string): Promise<number | null>;
13
+ }
14
+ /**
15
+ * Read what can be read, and say so for everything else.
16
+ * @param io - the runtime io, or undefined when the caller has none (every store then reads UNKNOWN).
17
+ * @returns one reading per registered store, in ledger order.
18
+ */
19
+ export declare function reclamationFootprint(io: FootprintIo | undefined): Promise<StoreFootprint[]>;
20
+ /**
21
+ * G3/3.4: the three reachability readings, folded into the footprint channel so the report has ONE shape.
22
+ *
23
+ * Paths come from the caller: the history root and the snapshot root live under the skills root, and the
24
+ * reports directory under the evolution home. A runtime that does not state its skills root gets UNKNOWN
25
+ * for the two rooted domains instead of a guess - the same three-state rule the rest of this file uses.
26
+ * @param io - the runtime io, or undefined when the caller has none.
27
+ * @param skillRoot - the configured skills root, or undefined when the runtime does not state one.
28
+ */
29
+ export declare function reachabilityFootprint(io: FootprintIo | undefined, skillRoot: string | undefined): Promise<StoreFootprint[]>;
30
+ //# sourceMappingURL=footprint.d.ts.map
@@ -6,54 +6,24 @@
6
6
  */
7
7
  import { type DriftReport, type DriftSkillSnapshot } from '@lmzhen/dsh-evolution-core';
8
8
  import type { Agent } from '@deepseek-ai/dsh-agent';
9
+ import type { SubagentChannel } from '@lmzhen/dsh-evolution-core';
9
10
  import { type SkillLibraryLike } from './drift-scan.ts';
10
11
  import { type Enrichment } from './enrichment.ts';
12
+ import { type StoreFootprint } from './render-facts.ts';
13
+ import { type FootprintIo } from './footprint.ts';
11
14
  import { type MaintainPlanItem } from './validate-plan.ts';
12
- /** I-5 (v37): the platform's `SubagentStartRequest` fields this orchestrator sends.
13
- * Typed locally (not imported) because the platform type lives behind the subagent row this
14
- * package does not depend on; the required `parent` and `signal` mirror the platform. */
15
- export interface SubagentStartRequestLike {
16
- readonly label: string;
17
- readonly prompt: readonly {
18
- readonly type: 'text';
19
- readonly text: string;
20
- }[];
21
- readonly parent: Agent;
22
- readonly signal: AbortSignal;
23
- readonly maxDepth: number;
24
- readonly agentOptions: Record<string, string>;
25
- readonly persona: string;
26
- readonly toolFilter: {
27
- readonly allow: readonly string[];
28
- };
29
- readonly outputSchema: Record<string, unknown>;
30
- }
31
- /** I-5 (v37): the platform's `SubagentResult` fields the outcome mapping reads. The
32
- * index signature keeps a REAL platform result assignable (the platform type is
33
- * wider than these three fields); the fields below are the ones this code relies on. */
34
- export interface SubagentResultLike {
35
- readonly structured?: unknown;
36
- readonly diagnostic?: string;
37
- readonly stopReason: string;
38
- }
39
- /** I-5 (v37): the platform's `SubagentRun` contract the orchestrator owns — `dispose` is
40
- * NOT optional (the platform declares it required and every exit path here awaits it). */
41
- export interface SubagentRunLike {
42
- readonly result: Promise<SubagentResultLike>;
43
- dispose(): Promise<void>;
44
- }
15
+ /**
16
+ * W5 G3-S4: execution goes through evolution-core's Channel (the 0.2.1 startActivation contract +
17
+ * restricted preset). The former local views of the platform subagent request/result are gone — a
18
+ * structural copy would be the second definition the contract freeze forbids.
19
+ */
45
20
  export interface MaintainRuntime {
46
21
  /** SkillLibrary-like reader for snapshot assembly. */
47
22
  library: SkillLibraryLike;
48
- /** Subagent spawner (platform `subagents` service — the request fields this
49
- * orchestrator sends, typed as the platform requires them: I-5 (v37) tightened the
50
- * request from `unknown` and `dispose` from optional to the platform's
51
- * always-present handle, so a future miss fails `tsc` instead of a runtime TypeError. */
52
- subagents: {
53
- start(kind: 'spawn', options: SubagentStartRequestLike): Promise<SubagentRunLike>;
54
- };
55
- /** The spawning agent (platform `SubagentStartRequest.parent`, required: in-process
56
- * providers derive workspace, lineage and delegation depth from its durable session). */
23
+ /** W5 G3-S4: the ONE execution path (capability probe, restricted preset, timeout/abort and
24
+ * exactly-one dispose live in the channel; this orchestrator only maps the outcome). */
25
+ channel: SubagentChannel;
26
+ /** The spawning agent (0.2.1 `SubagentStartRequest.parent`). */
57
27
  parent: Agent;
58
28
  /** Optional logger for dispose-failure traces; when absent the failure is
59
29
  * swallowed without surfacing (the orchestrator is a pure function). */
@@ -83,6 +53,9 @@ export interface MaintainRuntime {
83
53
  * probe the scan keeps the previous empty-library answer. */
84
54
  rootExists?: (() => Promise<boolean>) | undefined;
85
55
  /** V27 M-02: the root the probe above refers to, for the error message. */
56
+ /** G3/3.3: the runtime io the footprint reading needs. Optional: a caller without it gets UNKNOWN for
57
+ * every registered store instead of an invented zero. */
58
+ io?: FootprintIo | undefined;
86
59
  skillRoot?: string | undefined;
87
60
  }
88
61
  export interface MaintainOptions {
@@ -138,7 +111,10 @@ export interface FactsBundle {
138
111
  * rendered facts block (joint signature + redaction). Shared by `runMaintain`
139
112
  * and the `--facts` preview so the preview can never disagree with the scan.
140
113
  */
141
- export declare function buildMaintainFacts(snapshots: ReadonlyArray<DriftSkillSnapshot>, usageObserved: boolean | undefined, redact: ((text: string) => string) | undefined): FactsBundle;
114
+ export declare function buildMaintainFacts(snapshots: ReadonlyArray<DriftSkillSnapshot>, usageObserved: boolean | undefined, redact: ((text: string) => string) | undefined,
115
+ /** G3/3.3: one reading per registered bounded store, produced at this run's reclamation point.
116
+ * Optional so the preview and any older caller keep the exact block they had. */
117
+ footprint?: readonly StoreFootprint[]): FactsBundle;
142
118
  /** Run one maintenance scan and return display text plus validation metadata. */
143
119
  export declare function runMaintain(runtime: MaintainRuntime, options?: MaintainOptions): Promise<MaintainOutcome>;
144
120
  //# sourceMappingURL=orchestrate.d.ts.map
@@ -9,11 +9,27 @@
9
9
  * redactor and the delimiter/newline escaping before it leaves the session.
10
10
  */
11
11
  import { type DriftReport } from '@lmzhen/dsh-evolution-core';
12
+ /** G3/3.3: what one footprint line reports about one registered store. */
13
+ export interface StoreFootprint {
14
+ readonly id: string;
15
+ readonly unit: string;
16
+ /** The reading, when one exists; undefined renders as UNKNOWN instead of an invented zero. */
17
+ readonly used?: number | undefined;
18
+ readonly bound: string;
19
+ readonly lastSweep?: string | undefined;
20
+ readonly removed?: number | undefined;
21
+ /** Set for a `manual` store that did not run: stated, never implied. */
22
+ readonly skipped?: string | undefined;
23
+ }
12
24
  export interface RenderFactsOptions {
13
25
  /** signals_version — changes whenever the signal set/thresholds change. */
14
26
  signalsVersion: string;
15
27
  /** Joint signature: sha256(template-text + signal-definitions) — shared with MAINTAIN_PROMPT head. */
16
28
  signature: string;
29
+ /** G3/3.3: one reading per registered bounded store (STORE_LEDGER). Absent ⇒ no footprint lines at
30
+ * all, so an older caller keeps the exact block it had; a store with no reading renders as UNKNOWN
31
+ * and a manual-trigger store that did not run says so — "not done" must never read as "nothing to do". */
32
+ footprint?: readonly StoreFootprint[] | undefined;
17
33
  /** Optional redactor; defaults to core `redactSecrets`. */
18
34
  redact?: ((text: string) => string) | undefined;
19
35
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@lmzhen/dsh-evolution-maintenance",
3
3
  "description": "Deterministic maintenance-scan surface: skill-library snapshot assembly, drift signals and mechanical-facts rendering (design 011) (community build)",
4
- "version": "0.26.15",
4
+ "version": "0.27.0",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -30,16 +30,19 @@
30
30
  ],
31
31
  "license": "MIT",
32
32
  "dependencies": {
33
- "@deepseek-ai/schemastery": "^3.18.4",
34
- "@lmzhen/dsh-evolution-core": "^0.26.15"
33
+ "@deepseek-ai/schemastery": "^3.18.5",
34
+ "@lmzhen/dsh-evolution-core": "^0.27.0"
35
35
  },
36
36
  "peerDependencies": {
37
- "@deepseek-ai/dsh-agent": "^0.2.0-rc.1",
38
- "@deepseek-ai/cordis": "^4.0.1",
39
- "@deepseek-ai/dsh-tools": "^0.2.0-rc.1"
37
+ "@deepseek-ai/cordis": "^4.0.5",
38
+ "@deepseek-ai/dsh-agent": "^0.2.1-rc.1",
39
+ "@deepseek-ai/dsh-tools": "^0.2.1-rc.1"
40
40
  },
41
41
  "devDependencies": {
42
- "@deepseek-ai/dsh-agent": "^0.2.0-rc.1",
43
- "@deepseek-ai/dsh-tools": "^0.2.0-rc.1"
42
+ "@deepseek-ai/dsh-agent": "^0.2.1-rc.1",
43
+ "@deepseek-ai/dsh-tools": "^0.2.1-rc.1"
44
+ },
45
+ "engines": {
46
+ "dsh": ">=0.2.1-rc.1 <0.2.2-0"
44
47
  }
45
48
  }