@lmzhen/dsh-evolution-core 0.12.0 → 0.13.1

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
@@ -6534,16 +6534,22 @@ function readVersionEntry(entry) {
6534
6534
  if (typeof candidate.at !== "string") return null;
6535
6535
  if (typeof candidate.action !== "string") return null;
6536
6536
  if (typeof candidate.chars !== "number") return null;
6537
- return typeof candidate.beforeHash === "string" && candidate.beforeHash !== "" ? {
6538
- ...candidate,
6539
- beforeHash: candidate.beforeHash
6540
- } : {
6537
+ const summary = typeof candidate.summary === "string" && candidate.summary !== "" ? candidate.summary : void 0;
6538
+ const base = {
6541
6539
  v: candidate.v,
6542
6540
  at: candidate.at,
6543
6541
  action: candidate.action,
6544
6542
  hash: candidate.hash,
6545
6543
  chars: candidate.chars
6546
6544
  };
6545
+ return typeof candidate.beforeHash === "string" && candidate.beforeHash !== "" ? {
6546
+ ...base,
6547
+ beforeHash: candidate.beforeHash,
6548
+ ...summary === void 0 ? {} : { summary }
6549
+ } : {
6550
+ ...base,
6551
+ ...summary === void 0 ? {} : { summary }
6552
+ };
6547
6553
  }
6548
6554
  /**
6549
6555
  * Read one index body for a RECORDER: "understood", "no file", or "cannot be understood".
@@ -6617,6 +6623,7 @@ function nextHistoryIndex(versions, input, keep) {
6617
6623
  action: input.action,
6618
6624
  hash: afterHash,
6619
6625
  chars: input.after?.length ?? 0,
6626
+ ...input.summary === void 0 ? {} : { summary: input.summary },
6620
6627
  ...beforeHash === null ? {} : { beforeHash }
6621
6628
  });
6622
6629
  }
@@ -6722,6 +6729,172 @@ async function recordVersions(root, io, input, keep) {
6722
6729
  });
6723
6730
  return recorded;
6724
6731
  }
6732
+ /**
6733
+ * Classify one version action for display. Fail-closed like {@link versionTarget}: an action this
6734
+ * module does not know reads as `other` rather than being guessed, and `tests/skill-history.spec.ts`
6735
+ * pins every action the library can write, so a new one cannot land unclassified.
6736
+ * @param action - the entry’s action label.
6737
+ * @returns the kind the faces switch on.
6738
+ */
6739
+ function versionActionKind(action) {
6740
+ switch (action) {
6741
+ case BASELINE_ACTION: return "baseline";
6742
+ case "create": return "create";
6743
+ case "patch":
6744
+ case "edit": return "patch";
6745
+ case "update": return "update";
6746
+ case "restore": return "restore";
6747
+ case "delete": return "delete";
6748
+ case "archive": return "archive";
6749
+ case "consolidate": return "consolidate";
6750
+ case "restructure": return "restructure";
6751
+ case "write_file": return "support-write";
6752
+ case "remove_file": return "support-remove";
6753
+ default: return "other";
6754
+ }
6755
+ }
6756
+ /** Lines one changed region may carry before it is windowed and flagged. */
6757
+ const TEXT_DIFF_MAX_LINES = 40;
6758
+ /** Characters one windowed side may carry, so a single enormous line cannot travel whole. */
6759
+ const TEXT_DIFF_MAX_CHARS = 4e3;
6760
+ /**
6761
+ * The facts of one whole-body replacement: how many lines the changed region gained and lost, and
6762
+ * that region itself as one hunk.
6763
+ *
6764
+ * The algorithm is the honest one for "what did this edit touch": common leading and trailing lines
6765
+ * are trimmed away and what remains is ONE contiguous region. It is not a minimal edit script — a
6766
+ * body edited in two distant places reports their span as one region — and that limitation is the
6767
+ * reason the faces show the region as context rather than as a line-by-line proof.
6768
+ * @param before - the body the write replaced.
6769
+ * @param after - the body the write stored.
6770
+ * @param path - the path the hunk names (the platform’s diff card requires one).
6771
+ * @param maxLines - the window a region’s text is capped to.
6772
+ * @returns the counts, the windowed hunk, and whether the window cut anything.
6773
+ */
6774
+ function textDiffFacts(before, after, path, maxLines = 40) {
6775
+ const oldLines = before === "" ? [] : before.split("\n");
6776
+ const newLines = after === "" ? [] : after.split("\n");
6777
+ let head = 0;
6778
+ while (head < oldLines.length && head < newLines.length && oldLines[head] === newLines[head]) head += 1;
6779
+ let tail = 0;
6780
+ while (tail < oldLines.length - head && tail < newLines.length - head && oldLines[oldLines.length - 1 - tail] === newLines[newLines.length - 1 - tail]) tail += 1;
6781
+ const oldRegion = oldLines.slice(head, oldLines.length - tail);
6782
+ const newRegion = newLines.slice(head, newLines.length - tail);
6783
+ if (oldRegion.length === 0 && newRegion.length === 0) return {
6784
+ linesAdded: 0,
6785
+ linesRemoved: 0,
6786
+ hunks: [],
6787
+ truncated: false
6788
+ };
6789
+ const window = (lines) => {
6790
+ const joined = lines.slice(0, maxLines).join("\n");
6791
+ if (joined.length <= 4e3) return {
6792
+ text: joined,
6793
+ cut: lines.length > maxLines
6794
+ };
6795
+ return {
6796
+ text: joined.slice(0, TEXT_DIFF_MAX_CHARS),
6797
+ cut: true
6798
+ };
6799
+ };
6800
+ const oldWindow = window(oldRegion);
6801
+ const newWindow = window(newRegion);
6802
+ return {
6803
+ linesAdded: newRegion.length,
6804
+ linesRemoved: oldRegion.length,
6805
+ hunks: [{
6806
+ path,
6807
+ oldText: oldWindow.text,
6808
+ newText: newWindow.text
6809
+ }],
6810
+ truncated: oldWindow.cut || newWindow.cut
6811
+ };
6812
+ }
6813
+ /** Characters one generated summary may carry. */
6814
+ const VERSION_SUMMARY_MAX_CHARS = 80;
6815
+ /**
6816
+ * The fixed prompt the optional per-write summarizer runs (registry `skillVersionSummary`).
6817
+ *
6818
+ * Model-visible, so it is written from the model’s perspective and pinned: the tests assert that it
6819
+ * carries the skill name, the character cap and both bodies, because a silent change to a prompt is a
6820
+ * silent change to what every recorded version says.
6821
+ * @param input - the skill, the action, and the two bodies (null when absent).
6822
+ * @returns the prompt text.
6823
+ */
6824
+ function versionSummaryPrompt(input) {
6825
+ return [
6826
+ "You are writing ONE line that tells a reader what this single edit to the agent skill",
6827
+ "\"" + input.name + "\" changed. The edit action was \"" + input.action + "\".",
6828
+ "Rules: at most " + String(80) + " characters; the same language as the body;",
6829
+ "name the decision, number or section that moved (for example \"keeps 30 days instead of 90 days\");",
6830
+ "never say merely that something changed; no preamble, quotes, markdown or trailing period.",
6831
+ "",
6832
+ "Body BEFORE the edit:",
6833
+ input.before ?? "(the skill did not exist before this edit)",
6834
+ "",
6835
+ "Body AFTER the edit:",
6836
+ input.after ?? "(the skill does not exist after this edit)"
6837
+ ].join("\n");
6838
+ }
6839
+ /**
6840
+ * Reduce a model answer to the one line the index stores.
6841
+ * @param raw - the model’s answer, whatever shape it came in.
6842
+ * @param maxChars - the character cap.
6843
+ * @returns the one line, or undefined when there is nothing usable (callers store no summary).
6844
+ */
6845
+ function normalizeVersionSummary(raw, maxChars = 80) {
6846
+ const firstLine = raw.split("\n").map((line) => line.trim()).find((line) => line !== "");
6847
+ if (firstLine === void 0) return void 0;
6848
+ const stripped = firstLine.replace(/^["'`*•s]+/u, "").replace(/["'`*s]+$/u, "").replace(/[。.]$/u, "").trim();
6849
+ if (stripped === "") return void 0;
6850
+ return stripped.length > maxChars ? stripped.slice(0, maxChars - 1) + "…" : stripped;
6851
+ }
6852
+ /**
6853
+ * How long ago an ISO timestamp was, as a bucket plus a count — never as a sentence.
6854
+ *
6855
+ * The shape deliberately mirrors the platform’s `relativeTime` (`ui-primitives`), so a face that later
6856
+ * may import the platform helper swaps one call and keeps its dictionary; the WORDS live in the
6857
+ * faces (the panel’s locale dictionary, the command surface’s English), which is why this module
6858
+ * returns numbers.
6859
+ * @param at - an ISO timestamp, or any string `Date.parse` understands.
6860
+ * @param nowMs - the reference instant.
6861
+ * @returns the bucket and its count; an unparsable timestamp reads as `now` with 0.
6862
+ */
6863
+ function elapsedSince(at, nowMs) {
6864
+ const then = Date.parse(at);
6865
+ if (Number.isNaN(then)) return {
6866
+ unit: "now",
6867
+ n: 0
6868
+ };
6869
+ const minutes = Math.floor(Math.max(0, nowMs - then) / 6e4);
6870
+ if (minutes < 1) return {
6871
+ unit: "now",
6872
+ n: 0
6873
+ };
6874
+ if (minutes < 60) return {
6875
+ unit: "minutes",
6876
+ n: minutes
6877
+ };
6878
+ const hours = Math.floor(minutes / 60);
6879
+ if (hours < 24) return {
6880
+ unit: "hours",
6881
+ n: hours
6882
+ };
6883
+ const days = Math.floor(hours / 24);
6884
+ if (days < 30) return {
6885
+ unit: "days",
6886
+ n: days
6887
+ };
6888
+ const months = Math.floor(days / 30);
6889
+ if (months < 12) return {
6890
+ unit: "months",
6891
+ n: months
6892
+ };
6893
+ return {
6894
+ unit: "years",
6895
+ n: Math.max(1, Math.floor(days / 365))
6896
+ };
6897
+ }
6725
6898
  //#endregion
6726
6899
  //#region lib/types/fuzzy-match.js
6727
6900
  /**
@@ -10986,4 +11159,4 @@ function sessionAudited(ctx, sessionId, sessionScoped) {
10986
11159
  return false;
10987
11160
  }
10988
11161
  //#endregion
10989
- export { ALIVE_LOCK_TAKEOVER_MS, AUTHORING_DESCRIPTION_BAR, AUTHORING_SPLIT_LINE_CHARS, BASELINE_ACTION, CITATION_SUPPORT_DIRS, COMBINED_REVIEW_PLAN_PROMPT, COMBINED_REVIEW_PROMPT, COMPLETION_SKILL_REVIEW_PROMPT, CONTENT_SPLIT_HINT, COST_ASCII_TOKEN_HIGH, COST_ASCII_TOKEN_LOW, COST_CJK_TOKEN_HIGH, COST_CJK_TOKEN_LOW, CURATOR_DRY_RUN_BANNER, CURATOR_PROMPT, DEAD_LOCK_TAKEOVER_MS, DEDUP_MAX_PAIR_COMPARISONS, DEDUP_SIMILARITY_THRESHOLD, DEFAULT_ARCHIVE_AFTER_DAYS, DEFAULT_ARCHIVE_RETENTION_POLICY, DEFAULT_CITATION_POLICY, DEFAULT_CITATION_REF_BUDGET, DEFAULT_CONSOLIDATION_FAILURES, DEFAULT_CURATOR_BOOT_GRACE_SECONDS, DEFAULT_CURATOR_INTERVAL_HOURS, DEFAULT_CURATOR_MODEL, DEFAULT_CURATOR_REVIEW_MAX_TOKENS, DEFAULT_HEALTH_THRESHOLDS, DEFAULT_MAX_OPS_PER_PLAN, DEFAULT_MEMORY_CHAR_LIMIT, DEFAULT_MEMORY_REVIEW_MODEL, DEFAULT_MIN_IDLE_HOURS, DEFAULT_MUTATION_CAP, DEFAULT_REFERENCE_REWRITE_POLICY, DEFAULT_REVIEW_CONTEXT_MESSAGES, DEFAULT_REVIEW_MEMORY_INTERVAL, DEFAULT_REVIEW_MESSAGE_CHARS, DEFAULT_REVIEW_SKILL_INTERVAL, DEFAULT_REVIEW_TIMEOUT_MS, DEFAULT_SKILL_CONTENT_CHARS, DEFAULT_SKILL_LIMITS, DEFAULT_SKILL_REVIEW_COMPLETION_MIN_TOOL_CALLS, DEFAULT_SKILL_REVIEW_MODEL, DEFAULT_SKILL_REVIEW_TRIGGER, DEFAULT_SKILL_VERSION_KEEP, DEFAULT_STALE_AFTER_DAYS, DEFAULT_SUBSTANTIVE_MIN_AGENT_CHARS, DEFAULT_SUBSTANTIVE_MIN_TOOL_CALLS, DEFAULT_SUBSTANTIVE_MIN_USER_CHARS, DEFAULT_SUPPORT_FILE_CHAR_POLICY, DEFAULT_SUPPORT_READ_TOOL_NAMES, DEFAULT_THREAT_MAX_SCAN_CHARS, DEFAULT_USER_CHAR_LIMIT, DISPATCH_EVENT_TYPES, DRIFT_MAX_LINE_CHARS, DRIFT_SIGNALS_VERSION, DRIFT_SIGNAL_NOUNS, DSH_AUTHORING_STANDARDS, EMPTY_LOCK_TAKEOVER_MS, ENTRY_DELIMITER, EVENT_ARCHIVE_PREFIX, EVENT_LOG_RETAIN_ARCHIVES, EVENT_LOG_ROTATE_AT, EVENT_LOG_VERSION, EVOLUTION_ERRORS, EVOLUTION_WRITE_TOOLS, EvolutionGateSet, FAMILY_SESSION_TOOL_NAMES, FORBIDDEN_CONTROL_KEYS, HEALTH_STAMP_RE, HISTORY_DIR, HISTORY_INDEX_VERSION, HOOK_LINE_RE, INSTANCE_KEYS, KEEP_LINE_RE, LOCK_BODY_RE, LOCK_SUFFIX, LOCK_TEAR_TAKEOVER_MS, LOW_QUALITY_THRESHOLD, LostWriteLock, MAINTAIN_OUTPUT_INSTRUCTION, MAINTAIN_PROMPT, MAX_DESCRIPTION_LENGTH, MAX_RESTRUCTURE_MOVES, MAX_SKILL_CONTENT_CHARS, MAX_SKILL_FILE_BYTES, MAX_SKILL_NAME_LENGTH, MAX_SUPPORT_READ_PATHS, MAX_TIMER_DELAY_MS, MEMORY_GUIDANCE_SECTION_ORDER, MEMORY_REVIEW_PROMPT, MIN_STAMP_BODY_CHARS, MUTATIONS_FILE_VERSION, MemoryStore, NATIVE_CALL_EVENT, NATIVE_RESULT_EVENT, PARAM_ALIASES, PARAM_EXPOSURE, PARAM_GROUP_LABELS, PARAM_NAMESPACES, PATTERN_OVERLAP, PLATFORM_CATALOG_DESCRIPTION_DEFAULT, PLATFORM_ESTIMATE_CHARS_PER_TOKEN, POLICY_STAGE_DEFAULTS, PROMPT_BUNDLE, PROMPT_BUNDLE_ID, PROMPT_BUNDLE_VERSION, PROTECTED_BUILTIN_SKILLS, PTC_DISPATCH_EVENT, PTC_DISPATCH_START_EVENT, QUALITY_WEIGHTS, READ_REQUIRED_ACTIONS, RESTRUCTURE_TARGET_RE, RETENTION_FEEDBACK_KEEP_RATIO, RETENTION_FEEDBACK_MIN_CHARS, SKILLS_GUIDANCE, SKILLS_GUIDANCE_SECTION_ORDER, SKILL_ACTION_REQUIRED_FIELDS, SKILL_NAME_RE, SKILL_REVIEW_PLAN_PROMPT, SKILL_REVIEW_PROMPT, SNAPSHOT_EXTRA_NAME_RE, SUMMARY_DUPLICATE_HINT_THRESHOLD, SUPPORT_DIRS, SUPPRESSED_FILE_VERSION, SkillLibrary, THREAT_EXEMPTION_HINT, TOKEN_ASCII_WEIGHT, TOKEN_CJK_WEIGHT, ToolDispatchNormalizer, ToolDispatchPayloadError, UPSTREAM_LIMIT_CHARS_PER_TOKEN, advanceReview, affinity, allowRowCollisions, appendEvolutionEvent, applyCuratorLifecycleFields, applyCuratorMetaFields, applyReferenceRewrite, assertDispatchPayload, assertSkillsRootAliasRetired, assessStructureHealth, authoringFeedback, blobPath, bodyCost, buildCuratorRunReport, buildLearnPrompt, bumpPatch, bumpSupportRead, bumpUse, bumpView, callingScope, canonicalWriteId, claimInstance, clampedNumber, clearReviewChannel, collectReadSkillNames, composePresetComposition, computeDedupGroups, computeDriftSignals, computeLifecycleTransitions, computePrefixClusters, computeQualityScores, computeScopeView, contentHash, contentRetentionFeedback, countDispatches, createGateSet, daysSinceIso, decideTakeover, describeReferenceRewrite, duplicateHeadings, emptyRecord, errorText, evaluateThreat, eventsFile, evidenceKindIndex, evolutionEventPayloadIssue, evolutionHome, evolutionIoAdapter, evolutionRoot, filterUnreadSkillOps, findDriftSignal, foldCuratorFields, foldToolDispatches, foldTurn, frontmatterBlock, frontmatterCatalogInvalid, getRecord, historyIndexFile, historyRoot, identity, idleDays, installParamSection, instanceClaimKey, instanceClaimedWriteSites, instanceHolder, isAbsent, isCommittedWarning, isDeprecatedParamId, isFileShapedPath, isMissingPath, isPresent, isProcessAlive, isProgramToolName, isReviewChannelSession, isSkillReadToolName, isSkillToolName, isUnknown, isUnreadWrite, latestActivityAt, lifecycleCandidate, listEventArchives, loadMutations, loadRowOverrides, loadSuppressedNames, loadUsage, loadVersionContent, loadVersions, makeSerialQueue, mapProbe, markAgentCreated, markReviewChannel, markerEntryName, memoryRoot, missingSupportPointers, mutateUsage, mutationsFile, narrowNameMatches, nearDuplicateSummaries, neutralizePromptVariables, newSkillLibrary, nextHistoryIndex, nodeEvolutionIo, normalizeFrontmatter, normalizeUsageRecord, observeEvent, orderVersions, overlongLines, paramNamespace, paramSectionOverrides, parseCuratorNominations, parseEvolutionEvents, parseFrontmatter, parseHistoryIndex, parseLockBody, partitionVersions, pendingSelfCleanup, persistedWriteSite, persistedWriteSites, planReferenceRewrite, planRehoming, policyStageLimits, probeAbsent, probeList, probeMtime, probePresent, probeReason, probeUnknown, projectionText, readDispatchSignal, readEvolutionEvents, readEvolutionTimeline, readHistoryIndex, readNumberParam, readParam, recordMutation, recordVersions, redactSecrets, relatedSkillNames, releaseInstance, renameWithRetry, renderCuratorReportMarkdown, resolveCitations, resolveExecOrigins, resolveOrigins, resolveParamId, resolveRootConfig, resolveSkillsRoot, retainEventArchives, retirementReport, reviewPrompt, saveSuppressedNames, saveUsage, scanBodyHooks, scanContentThreats, scanMemoryThreats, scanThreats, scopedProbeReport, sessionAudited, sessionReadSkillNames, sessionSeesFamilyTools, skillReadNameOf, skillsRoot, supportFileReadOf, suppressedFile, sweepReviewChannelSessions, tokenLineFor, transactIo, transactTaskGuard, unhookedSupportPointers, updateSuppressedNames, usageFile, usageObserved, validateFrontmatter, validateRestructureTarget, valueOr, verifyPromptBundle, versionTarget, vocabulary, writeDurableTmp, yamlPlainScalarNeedsQuotes };
11162
+ export { ALIVE_LOCK_TAKEOVER_MS, AUTHORING_DESCRIPTION_BAR, AUTHORING_SPLIT_LINE_CHARS, BASELINE_ACTION, CITATION_SUPPORT_DIRS, COMBINED_REVIEW_PLAN_PROMPT, COMBINED_REVIEW_PROMPT, COMPLETION_SKILL_REVIEW_PROMPT, CONTENT_SPLIT_HINT, COST_ASCII_TOKEN_HIGH, COST_ASCII_TOKEN_LOW, COST_CJK_TOKEN_HIGH, COST_CJK_TOKEN_LOW, CURATOR_DRY_RUN_BANNER, CURATOR_PROMPT, DEAD_LOCK_TAKEOVER_MS, DEDUP_MAX_PAIR_COMPARISONS, DEDUP_SIMILARITY_THRESHOLD, DEFAULT_ARCHIVE_AFTER_DAYS, DEFAULT_ARCHIVE_RETENTION_POLICY, DEFAULT_CITATION_POLICY, DEFAULT_CITATION_REF_BUDGET, DEFAULT_CONSOLIDATION_FAILURES, DEFAULT_CURATOR_BOOT_GRACE_SECONDS, DEFAULT_CURATOR_INTERVAL_HOURS, DEFAULT_CURATOR_MODEL, DEFAULT_CURATOR_REVIEW_MAX_TOKENS, DEFAULT_HEALTH_THRESHOLDS, DEFAULT_MAX_OPS_PER_PLAN, DEFAULT_MEMORY_CHAR_LIMIT, DEFAULT_MEMORY_REVIEW_MODEL, DEFAULT_MIN_IDLE_HOURS, DEFAULT_MUTATION_CAP, DEFAULT_REFERENCE_REWRITE_POLICY, DEFAULT_REVIEW_CONTEXT_MESSAGES, DEFAULT_REVIEW_MEMORY_INTERVAL, DEFAULT_REVIEW_MESSAGE_CHARS, DEFAULT_REVIEW_SKILL_INTERVAL, DEFAULT_REVIEW_TIMEOUT_MS, DEFAULT_SKILL_CONTENT_CHARS, DEFAULT_SKILL_LIMITS, DEFAULT_SKILL_REVIEW_COMPLETION_MIN_TOOL_CALLS, DEFAULT_SKILL_REVIEW_MODEL, DEFAULT_SKILL_REVIEW_TRIGGER, DEFAULT_SKILL_VERSION_KEEP, DEFAULT_STALE_AFTER_DAYS, DEFAULT_SUBSTANTIVE_MIN_AGENT_CHARS, DEFAULT_SUBSTANTIVE_MIN_TOOL_CALLS, DEFAULT_SUBSTANTIVE_MIN_USER_CHARS, DEFAULT_SUPPORT_FILE_CHAR_POLICY, DEFAULT_SUPPORT_READ_TOOL_NAMES, DEFAULT_THREAT_MAX_SCAN_CHARS, DEFAULT_USER_CHAR_LIMIT, DISPATCH_EVENT_TYPES, DRIFT_MAX_LINE_CHARS, DRIFT_SIGNALS_VERSION, DRIFT_SIGNAL_NOUNS, DSH_AUTHORING_STANDARDS, EMPTY_LOCK_TAKEOVER_MS, ENTRY_DELIMITER, EVENT_ARCHIVE_PREFIX, EVENT_LOG_RETAIN_ARCHIVES, EVENT_LOG_ROTATE_AT, EVENT_LOG_VERSION, EVOLUTION_ERRORS, EVOLUTION_WRITE_TOOLS, EvolutionGateSet, FAMILY_SESSION_TOOL_NAMES, FORBIDDEN_CONTROL_KEYS, HEALTH_STAMP_RE, HISTORY_DIR, HISTORY_INDEX_VERSION, HOOK_LINE_RE, INSTANCE_KEYS, KEEP_LINE_RE, LOCK_BODY_RE, LOCK_SUFFIX, LOCK_TEAR_TAKEOVER_MS, LOW_QUALITY_THRESHOLD, LostWriteLock, MAINTAIN_OUTPUT_INSTRUCTION, MAINTAIN_PROMPT, MAX_DESCRIPTION_LENGTH, MAX_RESTRUCTURE_MOVES, MAX_SKILL_CONTENT_CHARS, MAX_SKILL_FILE_BYTES, MAX_SKILL_NAME_LENGTH, MAX_SUPPORT_READ_PATHS, MAX_TIMER_DELAY_MS, MEMORY_GUIDANCE_SECTION_ORDER, MEMORY_REVIEW_PROMPT, MIN_STAMP_BODY_CHARS, MUTATIONS_FILE_VERSION, MemoryStore, NATIVE_CALL_EVENT, NATIVE_RESULT_EVENT, PARAM_ALIASES, PARAM_EXPOSURE, PARAM_GROUP_LABELS, PARAM_NAMESPACES, PATTERN_OVERLAP, PLATFORM_CATALOG_DESCRIPTION_DEFAULT, PLATFORM_ESTIMATE_CHARS_PER_TOKEN, POLICY_STAGE_DEFAULTS, PROMPT_BUNDLE, PROMPT_BUNDLE_ID, PROMPT_BUNDLE_VERSION, PROTECTED_BUILTIN_SKILLS, PTC_DISPATCH_EVENT, PTC_DISPATCH_START_EVENT, QUALITY_WEIGHTS, READ_REQUIRED_ACTIONS, RESTRUCTURE_TARGET_RE, RETENTION_FEEDBACK_KEEP_RATIO, RETENTION_FEEDBACK_MIN_CHARS, SKILLS_GUIDANCE, SKILLS_GUIDANCE_SECTION_ORDER, SKILL_ACTION_REQUIRED_FIELDS, SKILL_NAME_RE, SKILL_REVIEW_PLAN_PROMPT, SKILL_REVIEW_PROMPT, SNAPSHOT_EXTRA_NAME_RE, SUMMARY_DUPLICATE_HINT_THRESHOLD, SUPPORT_DIRS, SUPPRESSED_FILE_VERSION, SkillLibrary, TEXT_DIFF_MAX_CHARS, TEXT_DIFF_MAX_LINES, THREAT_EXEMPTION_HINT, TOKEN_ASCII_WEIGHT, TOKEN_CJK_WEIGHT, ToolDispatchNormalizer, ToolDispatchPayloadError, UPSTREAM_LIMIT_CHARS_PER_TOKEN, VERSION_SUMMARY_MAX_CHARS, advanceReview, affinity, allowRowCollisions, appendEvolutionEvent, applyCuratorLifecycleFields, applyCuratorMetaFields, applyReferenceRewrite, assertDispatchPayload, assertSkillsRootAliasRetired, assessStructureHealth, authoringFeedback, blobPath, bodyCost, buildCuratorRunReport, buildLearnPrompt, bumpPatch, bumpSupportRead, bumpUse, bumpView, callingScope, canonicalWriteId, claimInstance, clampedNumber, clearReviewChannel, collectReadSkillNames, composePresetComposition, computeDedupGroups, computeDriftSignals, computeLifecycleTransitions, computePrefixClusters, computeQualityScores, computeScopeView, contentHash, contentRetentionFeedback, countDispatches, createGateSet, daysSinceIso, decideTakeover, describeReferenceRewrite, duplicateHeadings, elapsedSince, emptyRecord, errorText, evaluateThreat, eventsFile, evidenceKindIndex, evolutionEventPayloadIssue, evolutionHome, evolutionIoAdapter, evolutionRoot, filterUnreadSkillOps, findDriftSignal, foldCuratorFields, foldToolDispatches, foldTurn, frontmatterBlock, frontmatterCatalogInvalid, getRecord, historyIndexFile, historyRoot, identity, idleDays, installParamSection, instanceClaimKey, instanceClaimedWriteSites, instanceHolder, isAbsent, isCommittedWarning, isDeprecatedParamId, isFileShapedPath, isMissingPath, isPresent, isProcessAlive, isProgramToolName, isReviewChannelSession, isSkillReadToolName, isSkillToolName, isUnknown, isUnreadWrite, latestActivityAt, lifecycleCandidate, listEventArchives, loadMutations, loadRowOverrides, loadSuppressedNames, loadUsage, loadVersionContent, loadVersions, makeSerialQueue, mapProbe, markAgentCreated, markReviewChannel, markerEntryName, memoryRoot, missingSupportPointers, mutateUsage, mutationsFile, narrowNameMatches, nearDuplicateSummaries, neutralizePromptVariables, newSkillLibrary, nextHistoryIndex, nodeEvolutionIo, normalizeFrontmatter, normalizeUsageRecord, normalizeVersionSummary, observeEvent, orderVersions, overlongLines, paramNamespace, paramSectionOverrides, parseCuratorNominations, parseEvolutionEvents, parseFrontmatter, parseHistoryIndex, parseLockBody, partitionVersions, pendingSelfCleanup, persistedWriteSite, persistedWriteSites, planReferenceRewrite, planRehoming, policyStageLimits, probeAbsent, probeList, probeMtime, probePresent, probeReason, probeUnknown, projectionText, readDispatchSignal, readEvolutionEvents, readEvolutionTimeline, readHistoryIndex, readNumberParam, readParam, recordMutation, recordVersions, redactSecrets, relatedSkillNames, releaseInstance, renameWithRetry, renderCuratorReportMarkdown, resolveCitations, resolveExecOrigins, resolveOrigins, resolveParamId, resolveRootConfig, resolveSkillsRoot, retainEventArchives, retirementReport, reviewPrompt, saveSuppressedNames, saveUsage, scanBodyHooks, scanContentThreats, scanMemoryThreats, scanThreats, scopedProbeReport, sessionAudited, sessionReadSkillNames, sessionSeesFamilyTools, skillReadNameOf, skillsRoot, supportFileReadOf, suppressedFile, sweepReviewChannelSessions, textDiffFacts, tokenLineFor, transactIo, transactTaskGuard, unhookedSupportPointers, updateSuppressedNames, usageFile, usageObserved, validateFrontmatter, validateRestructureTarget, valueOr, verifyPromptBundle, versionActionKind, versionSummaryPrompt, versionTarget, vocabulary, writeDurableTmp, yamlPlainScalarNeedsQuotes };
@@ -50,6 +50,10 @@ export interface SkillVersion {
50
50
  * can interleave (the index is appended after the content lock is released); this link is computed
51
51
  * from the bytes a write lock actually read, so it is the authoritative predecessor. */
52
52
  beforeHash?: string;
53
+ /** One line saying what this write changed, when the deployment turned the summarizer on
54
+ * (registry `skillVersionSummary`). Absent is a first-class state: the faces say so instead of
55
+ * inventing a sentence, and a version recorded before the feature existed simply has none. */
56
+ summary?: string;
53
57
  }
54
58
  /** One history write, carrying the content the caller already holds. */
55
59
  export interface VersionRecordInput {
@@ -60,6 +64,8 @@ export interface VersionRecordInput {
60
64
  /** Content after the mutation, or null when there is none (archive/remove_file). */
61
65
  after: string | null;
62
66
  at: string;
67
+ /** The one-line summary of THIS write, or undefined when the deployment does not summarize. */
68
+ summary?: string;
63
69
  }
64
70
  /** The version numbers one write produced; a side with no content contributes nothing. */
65
71
  export interface RecordedVersions {
@@ -223,4 +229,88 @@ export declare function loadVersionContent(root: string, io: EvolutionIoLike, na
223
229
  * @returns the version numbers, or null when there was no content to record.
224
230
  */
225
231
  export declare function recordVersions(root: string, io: EvolutionIoLike, input: VersionRecordInput, keep: number): Promise<RecordedVersions | null>;
232
+ /** What kind of change one recorded version carries, as a CLOSED set. */
233
+ export type VersionActionKind = 'baseline' | 'create' | 'patch' | 'update' | 'restore' | 'delete' | 'archive' | 'consolidate' | 'restructure' | 'support-write' | 'support-remove' | 'other';
234
+ /**
235
+ * Classify one version action for display. Fail-closed like {@link versionTarget}: an action this
236
+ * module does not know reads as `other` rather than being guessed, and `tests/skill-history.spec.ts`
237
+ * pins every action the library can write, so a new one cannot land unclassified.
238
+ * @param action - the entry’s action label.
239
+ * @returns the kind the faces switch on.
240
+ */
241
+ export declare function versionActionKind(action: string): VersionActionKind;
242
+ /** One changed region between two bodies — the shape the platform’s diff card eats. */
243
+ export interface TextDiffHunk {
244
+ readonly path: string;
245
+ readonly oldText: string;
246
+ readonly newText: string;
247
+ }
248
+ /** What one whole-body replacement changed, as facts. */
249
+ export interface TextDiffFacts {
250
+ readonly linesAdded: number;
251
+ readonly linesRemoved: number;
252
+ readonly hunks: readonly TextDiffHunk[];
253
+ /** True when a changed region was longer than the window, so its text is a prefix. */
254
+ readonly truncated: boolean;
255
+ }
256
+ /** Lines one changed region may carry before it is windowed and flagged. */
257
+ export declare const TEXT_DIFF_MAX_LINES = 40;
258
+ /** Characters one windowed side may carry, so a single enormous line cannot travel whole. */
259
+ export declare const TEXT_DIFF_MAX_CHARS = 4000;
260
+ /**
261
+ * The facts of one whole-body replacement: how many lines the changed region gained and lost, and
262
+ * that region itself as one hunk.
263
+ *
264
+ * The algorithm is the honest one for "what did this edit touch": common leading and trailing lines
265
+ * are trimmed away and what remains is ONE contiguous region. It is not a minimal edit script — a
266
+ * body edited in two distant places reports their span as one region — and that limitation is the
267
+ * reason the faces show the region as context rather than as a line-by-line proof.
268
+ * @param before - the body the write replaced.
269
+ * @param after - the body the write stored.
270
+ * @param path - the path the hunk names (the platform’s diff card requires one).
271
+ * @param maxLines - the window a region’s text is capped to.
272
+ * @returns the counts, the windowed hunk, and whether the window cut anything.
273
+ */
274
+ export declare function textDiffFacts(before: string, after: string, path: string, maxLines?: number): TextDiffFacts;
275
+ /** Characters one generated summary may carry. */
276
+ export declare const VERSION_SUMMARY_MAX_CHARS = 80;
277
+ /**
278
+ * The fixed prompt the optional per-write summarizer runs (registry `skillVersionSummary`).
279
+ *
280
+ * Model-visible, so it is written from the model’s perspective and pinned: the tests assert that it
281
+ * carries the skill name, the character cap and both bodies, because a silent change to a prompt is a
282
+ * silent change to what every recorded version says.
283
+ * @param input - the skill, the action, and the two bodies (null when absent).
284
+ * @returns the prompt text.
285
+ */
286
+ export declare function versionSummaryPrompt(input: {
287
+ name: string;
288
+ action: string;
289
+ before: string | null;
290
+ after: string | null;
291
+ }): string;
292
+ /**
293
+ * Reduce a model answer to the one line the index stores.
294
+ * @param raw - the model’s answer, whatever shape it came in.
295
+ * @param maxChars - the character cap.
296
+ * @returns the one line, or undefined when there is nothing usable (callers store no summary).
297
+ */
298
+ export declare function normalizeVersionSummary(raw: string, maxChars?: number): string | undefined;
299
+ /** A relative age, in the shape the platform’s own `relativeTime(at, now)` returns. */
300
+ export interface RelativeAge {
301
+ readonly unit: 'now' | 'minutes' | 'hours' | 'days' | 'months' | 'years';
302
+ readonly n: number;
303
+ }
304
+ /**
305
+ * How long ago an ISO timestamp was, as a bucket plus a count — never as a sentence.
306
+ *
307
+ * The shape deliberately mirrors the platform’s `relativeTime` (`ui-primitives`), so a face that later
308
+ * may import the platform helper swaps one call and keeps its dictionary; the WORDS live in the
309
+ * faces (the panel’s locale dictionary, the command surface’s English), which is why this module
310
+ * returns numbers.
311
+ * @param at - an ISO timestamp, or any string `Date.parse` understands.
312
+ * @param nowMs - the reference instant.
313
+ * @returns the bucket and its count; an unparsable timestamp reads as `now` with 0.
314
+ */
315
+ export declare function elapsedSince(at: string, nowMs: number): RelativeAge;
226
316
  //# sourceMappingURL=skill-history.d.ts.map
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@lmzhen/dsh-evolution-core",
3
3
  "description": "Shared stores, prompts, signals and lifecycle logic for the dsh-evolution plugin family (community build)",
4
- "version": "0.12.0",
4
+ "version": "0.13.1",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },