@hiveai/core 0.21.0 → 0.24.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/dist/index.d.ts CHANGED
@@ -593,6 +593,19 @@ interface PreventionEvent {
593
593
  declare function preventionLogPath(paths: HaivePaths): string;
594
594
  /** Append one catch to the log. Best-effort, creates the dir on demand. */
595
595
  declare function appendPreventionEvent(paths: HaivePaths, event: PreventionEvent): Promise<void>;
596
+ /**
597
+ * THE single recorder for "a documented lesson intercepted a real mistake". Every gate path —
598
+ * the installed git-hook gate (`enforce check`), the standalone `haive sensors check`, and the
599
+ * `anti_patterns_check` MCP tool — funnels its fired memory ids through here so prevention is
600
+ * recorded once and identically, not bolted onto each entry point (it used to leak: the git-hook
601
+ * gate blocked but never recorded — see the harness-positioning gotcha).
602
+ *
603
+ * Bumps `prevented_count` in usage.json (debounced per memory via {@link recordPrevention}) AND
604
+ * appends one timestamped event per NEW catch to the prevention log. Best-effort: a telemetry
605
+ * write must never break a commit, so failures are swallowed. Returns the ids actually recorded
606
+ * (i.e. not debounced), so callers can report "caught for you" without re-counting.
607
+ */
608
+ declare function recordPreventionHits(paths: HaivePaths, firedIds: string[], source: PreventionSource, now?: Date): Promise<string[]>;
596
609
  /** Read all catch events (skips malformed lines). */
597
610
  declare function loadPreventionEvents(paths: HaivePaths): Promise<PreventionEvent[]>;
598
611
  interface PreventionTrend {
@@ -2021,14 +2034,20 @@ declare function findUncapturedFailures(failures: FailureObservation[], captureT
2021
2034
  * Pure: the caller supplies hot files (from git history / briefing-radar) and the loaded corpus.
2022
2035
  */
2023
2036
 
2037
+ /** Where a file's "heat" came from: committed git churn, agent edits this/recent sessions, or both. */
2038
+ type HotFileSource = "git" | "agent" | "both";
2024
2039
  interface HotFile {
2025
2040
  path: string;
2026
2041
  /** Number of times the file changed in the lookback window (the "heat"). */
2027
2042
  changes: number;
2043
+ /** Provenance of the heat. Optional for back-compat with git-only callers. */
2044
+ source?: HotFileSource;
2028
2045
  }
2029
2046
  interface CoverageGap {
2030
2047
  path: string;
2031
2048
  changes: number;
2049
+ /** Provenance of the heat that made this file a blind spot. */
2050
+ source?: HotFileSource;
2032
2051
  }
2033
2052
  interface CoverageOptions {
2034
2053
  /** Only flag files with at least this many changes. Default 3. */
@@ -2050,6 +2069,17 @@ declare function isCovered(file: string, coverage: Set<string>): boolean;
2050
2069
  * Highest heat first. These are the highest-value places to add a memory or sensor.
2051
2070
  */
2052
2071
  declare function findCoverageGaps(hotFiles: HotFile[], memories: LoadedMemory[], options?: CoverageOptions): CoverageGap[];
2072
+ /**
2073
+ * Tally a flat list of edited file paths into HotFiles — the agent-edit heat signal from the
2074
+ * PostToolUse observation log (files agents actually touch), complementary to committed git churn.
2075
+ * Pure: the caller reads/normalizes the observation paths.
2076
+ */
2077
+ declare function tallyHotFiles(paths: string[], source?: HotFileSource): HotFile[];
2078
+ /**
2079
+ * Merge two HotFile lists, summing heat per path. A file hot in both lists is tagged `both` so the
2080
+ * report can show that agents AND git churn both point at the same uncovered file (the strongest gap).
2081
+ */
2082
+ declare function mergeHotFiles(a: HotFile[], b: HotFile[]): HotFile[];
2053
2083
 
2054
2084
  interface EvalHistoryEntry {
2055
2085
  /** ISO timestamp of the eval run. */
@@ -2114,6 +2144,26 @@ interface ConflictResolution {
2114
2144
  }
2115
2145
  /** Compare two memories; returns the one that should WIN plus the reason. Pure. */
2116
2146
  declare function planConflictResolution(a: LoadedMemory, b: LoadedMemory): ConflictResolution;
2147
+ interface AppliedConflictResolution {
2148
+ /** Updated frontmatter for the memory to keep (promoted). */
2149
+ winner: MemoryFrontmatter;
2150
+ /** Updated frontmatter for the memory to supersede (deprecated). */
2151
+ loser: MemoryFrontmatter;
2152
+ /** Topic the winner now carries — the consolidation target for future `mem_save` upserts. Null when neither carried one. */
2153
+ topic: string | null;
2154
+ /** True when the winner adopted the loser's topic because it had none. */
2155
+ topic_adopted: boolean;
2156
+ }
2157
+ /**
2158
+ * Turn a {@link ConflictResolution} plan into the two concrete frontmatter updates — the guided
2159
+ * supersede the backlog called for, wired into topic-upsert/revision_count:
2160
+ * - loser → deprecated, stamped with stale_reason + a related_ids link to the winner.
2161
+ * - winner → revision_count++ (it absorbed a contradiction), verified now, linked to the loser,
2162
+ * and it ADOPTS the loser's topic when it had none — so the next `mem_save` on this subject
2163
+ * upserts into the winner instead of spawning a third conflicting memory. An existing winner
2164
+ * topic is never overwritten. Pure: the caller persists both.
2165
+ */
2166
+ declare function applyConflictResolution(winner: LoadedMemory, loser: LoadedMemory, plan: ConflictResolution, now?: Date): AppliedConflictResolution;
2117
2167
 
2118
2168
  /**
2119
2169
  * Cold-start seeding from git history — the harness has value only once the corpus is populated,
@@ -2343,4 +2393,4 @@ declare function prepareBridgeData(memories: Memory[], sensors: BridgeSensor[],
2343
2393
  */
2344
2394
  declare function generateBridges(memories: Memory[], sensors: BridgeSensor[], opts?: GenerateBridgesOptions): BridgeFileOutput[];
2345
2395
 
2346
- export { AUTOPILOT_DEFAULTS, type Activation, type ActivationContext, ActivationSchema, type Anchor, AnchorSchema, type AntiPatternGate, type AutoPromoteRule, BRIDGE_MARKERS, BRIDGE_TARGETS, BRIDGE_TARGET_PATH, BRIEFING_MARKER_TTL_MS, BRIEFING_PRESET_DEFAULTS, type BreakingChange, type BridgeFileOutput, type BridgeMemoryEntry, type BridgeSensor, type BridgeTarget, type BriefingBudgetNumbers, type BriefingBudgetPreset, type BriefingMarker, type BriefingProofLineOptions, type BudgetPart, type BudgetSlice, type BuildCodeMapOptions, CHARS_PER_TOKEN, CODE_MAP_FILE, CODE_STOPWORDS, CONFIG_FILE, type CaughtForYouOptions, type CaughtForYouRow, type CaughtForYouSummary, type CodeExport, type CodeExportKind, type CodeFileEntry, type CodeMap, type CodeMapQueryOptions, type CollectTimelineOpts, type CommandSensorSpec, type ConfidenceLevel, type ConfidenceThresholds, type ConflictCandidatePair, type ConflictCandidatesOpts, type ConflictResolution, type ContractDiffResult, type ContractFile, type ContractSnapshot, type CoverageGap, type CoverageOptions, CrossRepoProvenanceSchema, type CrossRepoReport, type CrossRepoSource, DECAY_DAYS, DEFAULT_AUTO_PROMOTE_RULE, DEFAULT_CONFIDENCE_THRESHOLDS, DEFAULT_CONFIG, DEFAULT_DORMANT_DAYS, DEFAULT_PRIORITY_SIGNALS, type DashboardOptions, type DashboardReport, type DepChange, type DepTrackResult, type DependencySnapshot, type DetectStacksInput, type DetectableStack, type DocFrequency, type DormantRow, type DraftOptions, type DraftsOptions, ENV_WORKAROUND_TAGS, type EvalDelta, type EvalHistoryEntry, type EvalReport, type EvalSpec, type EvalTrend, type FailureCoverageOptions, type FailureObservation, type FeedbackAdjustment, type FeedbackAdjustmentAction, type FeedbackAdjustmentOptions, type Finding, type FindingFormat, type FindingSeverity, GUESSABLE_THRESHOLD, type GatePrecision, type GatePrecisionDelta, type GatePrecisionMetricDelta, type GateTuningSuggestion, type GenerateBridgesOptions, type GitCommit, HAIVE_DIR, type HaiveConfig, type HaivePaths, type HotFile, type ImpactOptions, type ImpactRow, type ImpactScore, type ImpactSummary, type ImpactTier, type LexicalRankResult, type LoadedMemory, MEMORIES_DIR, MIN_WORD_LEN, type Memory, type MemoryDraft, type MemoryFrontmatter, MemoryFrontmatterSchema, type MemoryPriority, type MemoryScope, MemoryScopeSchema, type MemoryStatus, MemoryStatusSchema, type MemoryType, MemoryTypeSchema, type MemoryUsage, type MergeResult, type MetricDelta, PREVENTION_DEBOUNCE_MS, PROJECT_CONTEXT_FILE, PROJECT_CONTEXT_THROTTLE_MS, type PreventionEvent, type PreventionRow, type PreventionSource, type PreventionTrend, type PrioritySignals, RUNTIME_JOURNAL_FILENAME, type RecurrenceReport, type RecurrenceRow, type ResolveProjectInfo, type RetirementSignal, type RetrievalAggregate, type RetrievalCase, type RetrievalCaseResult, type RuntimeJournalEntry, SESSION_RECAP_TTL_MS, STACK_PACK_TAG, type SeedProposal, type SelfEvalOptions, type Sensor, type SensorAggregate, type SensorCase, type SensorCaseResult, type SensorHit, type SensorRow, SensorSchema, type SensorSuggestionOptions, type SensorTarget, type SkillActivation, type TimelineEntry, type TopicStatusPair, type TruncateOptions, type TruncateResult, USAGE_FILE, USAGE_LOG_DIR, USAGE_LOG_FILE, type UncapturedFailure, type UsageAggregate, type UsageEvent, type UsageIndex, type VerifyOptions, type VerifyResult, addedLinesFromDiff, aggregateRetrieval, aggregateSensors, aggregateUsage, allocateBudget, antiPatternGateParams, appendEvalHistory, appendPreventionEvent, appendRuntimeJournalEntry, appendUsageEvent, applyFeedbackAdjustment, bridgeMemorySummary, briefingMarkerPath, briefingMarkersDir, briefingProofLine, buildCodeMap, buildCoverageIndex, buildDashboard, buildDocFrequency, buildFrontmatter, buildReport, bumpRead, classifyMemoryPriority, codeMapPath, collectTimelineEntries, compactAutoRecapBody, compareEvalReports, compareGatePrecision, compareImpact, compileRegexSensor, computeEvalTrend, computeGatePrecision, computeImpact, computePreventionTrend, computeRecurrence, configPath, contractLockPath, deriveConfidence, detectStacksFromManifests, diffContract, diffHasDistinctiveOverlap, distinctiveCap, draftsFromFindings, emptyUsage, emptyUsageIndex, enforcementDir, estimateTokens, evalHistoryPath, evaluateSkillActivation, extractActionsBriefBody, extractSnippet, filterNewDrafts, findCoverageGaps, findLexicalConflictPairs, findProjectRoot, findTopicStatusConflictPairs, findUncapturedFailures, findingBody, findingToDraft, firstMemoryOneLine, generateBridges, getUsage, globToRegExp, hasRecentBriefingMarker, hashProjectContext, inferModulesFromPaths, isAutoPromoteEligible, isAutoRecap, isCovered, isDecaying, isDistinctiveToken, isEnvWorkaroundMemory, isFreshIsoDate, isGlobPath, isLikelyGuessable, isRetiredMemory, isSkill, isSkillSuppressed, isStackPackSeed, listMarkdownFilesRecursive, literalMatchesAllTokens, literalMatchesAnyToken, loadCodeMap, loadConfig, loadConfigSync, loadEvalHistory, loadMemoriesFromDir, loadMemory, loadPreventionEvents, loadUsageIndex, looksLikeGenericAdvice, memoryFilePath, memoryMatchesAnchorPaths, mergeMemoryVersions, newMemoryId, normalizeFindingSeverity, normalizeSessionId, overallScore, parseEslintJson, parseFindings, parseMemory, parseNpmAudit, parseSarif, parseSince, parseSonar, pathsOverlap, pickSnippetNeedle, planConflictResolution, prepareBridgeData, preventionLogPath, priorityRank, prioritySignals, projectContextRecentlyEmitted, proposeSeedsFromCommits, pullCrossRepoSources, queryCodeMap, rankMemoriesLexical, readRecentBriefingMarker, readRuntimeJournalTail, readUsageEvents, recommendFeedbackAdjustment, recordApplied, recordPrevention, recordProjectContextEmission, recordRejection, relPathFrom, renderCaughtForYou, resolveBriefingBudget, resolveHaivePaths, resolveManifestFiles, resolveProjectInfo, retirementSignal, runRegexSensor, runSensors, runtimeJournalPath, saveCodeMap, saveConfig, saveUsageIndex, scoreRetrievalCase, scoreSensorCase, selectCommandSensors, sensorAppliesToPath, sensorTargetsFromDiff, serializeMemory, snapshotContract, specificityScore, stripPrivate, suggestGate, suggestSensorFromMemory, suggestTopicKey, summarizeCaughtForYou, summarizeImpact, synthesizeSelfEvalCases, titleFromBody, tokenizeQuery, tokenizeWords, trackDependencies, trackReads, truncateToTokens, usageLogPath, usageLogSize, usagePath, verifyAnchor, watchContracts, writeBriefingMarker };
2396
+ export { AUTOPILOT_DEFAULTS, type Activation, type ActivationContext, ActivationSchema, type Anchor, AnchorSchema, type AntiPatternGate, type AppliedConflictResolution, type AutoPromoteRule, BRIDGE_MARKERS, BRIDGE_TARGETS, BRIDGE_TARGET_PATH, BRIEFING_MARKER_TTL_MS, BRIEFING_PRESET_DEFAULTS, type BreakingChange, type BridgeFileOutput, type BridgeMemoryEntry, type BridgeSensor, type BridgeTarget, type BriefingBudgetNumbers, type BriefingBudgetPreset, type BriefingMarker, type BriefingProofLineOptions, type BudgetPart, type BudgetSlice, type BuildCodeMapOptions, CHARS_PER_TOKEN, CODE_MAP_FILE, CODE_STOPWORDS, CONFIG_FILE, type CaughtForYouOptions, type CaughtForYouRow, type CaughtForYouSummary, type CodeExport, type CodeExportKind, type CodeFileEntry, type CodeMap, type CodeMapQueryOptions, type CollectTimelineOpts, type CommandSensorSpec, type ConfidenceLevel, type ConfidenceThresholds, type ConflictCandidatePair, type ConflictCandidatesOpts, type ConflictResolution, type ContractDiffResult, type ContractFile, type ContractSnapshot, type CoverageGap, type CoverageOptions, CrossRepoProvenanceSchema, type CrossRepoReport, type CrossRepoSource, DECAY_DAYS, DEFAULT_AUTO_PROMOTE_RULE, DEFAULT_CONFIDENCE_THRESHOLDS, DEFAULT_CONFIG, DEFAULT_DORMANT_DAYS, DEFAULT_PRIORITY_SIGNALS, type DashboardOptions, type DashboardReport, type DepChange, type DepTrackResult, type DependencySnapshot, type DetectStacksInput, type DetectableStack, type DocFrequency, type DormantRow, type DraftOptions, type DraftsOptions, ENV_WORKAROUND_TAGS, type EvalDelta, type EvalHistoryEntry, type EvalReport, type EvalSpec, type EvalTrend, type FailureCoverageOptions, type FailureObservation, type FeedbackAdjustment, type FeedbackAdjustmentAction, type FeedbackAdjustmentOptions, type Finding, type FindingFormat, type FindingSeverity, GUESSABLE_THRESHOLD, type GatePrecision, type GatePrecisionDelta, type GatePrecisionMetricDelta, type GateTuningSuggestion, type GenerateBridgesOptions, type GitCommit, HAIVE_DIR, type HaiveConfig, type HaivePaths, type HotFile, type HotFileSource, type ImpactOptions, type ImpactRow, type ImpactScore, type ImpactSummary, type ImpactTier, type LexicalRankResult, type LoadedMemory, MEMORIES_DIR, MIN_WORD_LEN, type Memory, type MemoryDraft, type MemoryFrontmatter, MemoryFrontmatterSchema, type MemoryPriority, type MemoryScope, MemoryScopeSchema, type MemoryStatus, MemoryStatusSchema, type MemoryType, MemoryTypeSchema, type MemoryUsage, type MergeResult, type MetricDelta, PREVENTION_DEBOUNCE_MS, PROJECT_CONTEXT_FILE, PROJECT_CONTEXT_THROTTLE_MS, type PreventionEvent, type PreventionRow, type PreventionSource, type PreventionTrend, type PrioritySignals, RUNTIME_JOURNAL_FILENAME, type RecurrenceReport, type RecurrenceRow, type ResolveProjectInfo, type RetirementSignal, type RetrievalAggregate, type RetrievalCase, type RetrievalCaseResult, type RuntimeJournalEntry, SESSION_RECAP_TTL_MS, STACK_PACK_TAG, type SeedProposal, type SelfEvalOptions, type Sensor, type SensorAggregate, type SensorCase, type SensorCaseResult, type SensorHit, type SensorRow, SensorSchema, type SensorSuggestionOptions, type SensorTarget, type SkillActivation, type TimelineEntry, type TopicStatusPair, type TruncateOptions, type TruncateResult, USAGE_FILE, USAGE_LOG_DIR, USAGE_LOG_FILE, type UncapturedFailure, type UsageAggregate, type UsageEvent, type UsageIndex, type VerifyOptions, type VerifyResult, addedLinesFromDiff, aggregateRetrieval, aggregateSensors, aggregateUsage, allocateBudget, antiPatternGateParams, appendEvalHistory, appendPreventionEvent, appendRuntimeJournalEntry, appendUsageEvent, applyConflictResolution, applyFeedbackAdjustment, bridgeMemorySummary, briefingMarkerPath, briefingMarkersDir, briefingProofLine, buildCodeMap, buildCoverageIndex, buildDashboard, buildDocFrequency, buildFrontmatter, buildReport, bumpRead, classifyMemoryPriority, codeMapPath, collectTimelineEntries, compactAutoRecapBody, compareEvalReports, compareGatePrecision, compareImpact, compileRegexSensor, computeEvalTrend, computeGatePrecision, computeImpact, computePreventionTrend, computeRecurrence, configPath, contractLockPath, deriveConfidence, detectStacksFromManifests, diffContract, diffHasDistinctiveOverlap, distinctiveCap, draftsFromFindings, emptyUsage, emptyUsageIndex, enforcementDir, estimateTokens, evalHistoryPath, evaluateSkillActivation, extractActionsBriefBody, extractSnippet, filterNewDrafts, findCoverageGaps, findLexicalConflictPairs, findProjectRoot, findTopicStatusConflictPairs, findUncapturedFailures, findingBody, findingToDraft, firstMemoryOneLine, generateBridges, getUsage, globToRegExp, hasRecentBriefingMarker, hashProjectContext, inferModulesFromPaths, isAutoPromoteEligible, isAutoRecap, isCovered, isDecaying, isDistinctiveToken, isEnvWorkaroundMemory, isFreshIsoDate, isGlobPath, isLikelyGuessable, isRetiredMemory, isSkill, isSkillSuppressed, isStackPackSeed, listMarkdownFilesRecursive, literalMatchesAllTokens, literalMatchesAnyToken, loadCodeMap, loadConfig, loadConfigSync, loadEvalHistory, loadMemoriesFromDir, loadMemory, loadPreventionEvents, loadUsageIndex, looksLikeGenericAdvice, memoryFilePath, memoryMatchesAnchorPaths, mergeHotFiles, mergeMemoryVersions, newMemoryId, normalizeFindingSeverity, normalizeSessionId, overallScore, parseEslintJson, parseFindings, parseMemory, parseNpmAudit, parseSarif, parseSince, parseSonar, pathsOverlap, pickSnippetNeedle, planConflictResolution, prepareBridgeData, preventionLogPath, priorityRank, prioritySignals, projectContextRecentlyEmitted, proposeSeedsFromCommits, pullCrossRepoSources, queryCodeMap, rankMemoriesLexical, readRecentBriefingMarker, readRuntimeJournalTail, readUsageEvents, recommendFeedbackAdjustment, recordApplied, recordPrevention, recordPreventionHits, recordProjectContextEmission, recordRejection, relPathFrom, renderCaughtForYou, resolveBriefingBudget, resolveHaivePaths, resolveManifestFiles, resolveProjectInfo, retirementSignal, runRegexSensor, runSensors, runtimeJournalPath, saveCodeMap, saveConfig, saveUsageIndex, scoreRetrievalCase, scoreSensorCase, selectCommandSensors, sensorAppliesToPath, sensorTargetsFromDiff, serializeMemory, snapshotContract, specificityScore, stripPrivate, suggestGate, suggestSensorFromMemory, suggestTopicKey, summarizeCaughtForYou, summarizeImpact, synthesizeSelfEvalCases, tallyHotFiles, titleFromBody, tokenizeQuery, tokenizeWords, trackDependencies, trackReads, truncateToTokens, usageLogPath, usageLogSize, usagePath, verifyAnchor, watchContracts, writeBriefingMarker };
package/dist/index.js CHANGED
@@ -852,6 +852,25 @@ async function appendPreventionEvent(paths, event) {
852
852
  await mkdir2(path6.dirname(file), { recursive: true });
853
853
  await appendFile(file, JSON.stringify(event) + "\n", "utf8");
854
854
  }
855
+ async function recordPreventionHits(paths, firedIds, source, now = /* @__PURE__ */ new Date()) {
856
+ const unique = [...new Set(firedIds)].filter(Boolean);
857
+ if (unique.length === 0) return [];
858
+ const usage = await loadUsageIndex(paths).catch(() => null);
859
+ if (!usage) return [];
860
+ const recordedIds = [];
861
+ for (const id of unique) {
862
+ if (recordPrevention(usage, id, now.getTime())) recordedIds.push(id);
863
+ }
864
+ if (recordedIds.length === 0) return [];
865
+ await saveUsageIndex(paths, usage).catch(() => {
866
+ });
867
+ const at = now.toISOString();
868
+ for (const id of recordedIds) {
869
+ await appendPreventionEvent(paths, { at, id, source }).catch(() => {
870
+ });
871
+ }
872
+ return recordedIds;
873
+ }
855
874
  async function loadPreventionEvents(paths) {
856
875
  const file = preventionLogPath(paths);
857
876
  if (!existsSync4(file)) return [];
@@ -3343,10 +3362,21 @@ function pickDistinctiveToken(text) {
3343
3362
  const best = [...candidates.values()].sort((a, b) => b.score - a.score)[0];
3344
3363
  return best?.raw ?? null;
3345
3364
  }
3365
+ var FILE_EXT_REF = /\.(ts|tsx|js|jsx|mjs|cjs|py|go|rs|java|kt|swift|rb|php|cs|cpp|c|h|md|json|ya?ml|toml|lock)\b/i;
3366
+ function isDegenerateToken(token) {
3367
+ if (/^[\d.\-:_]+$/.test(token)) return true;
3368
+ if (/\d+\s*-\s*\d+/.test(token)) return true;
3369
+ if (FILE_EXT_REF.test(token)) return true;
3370
+ const letters = (token.match(/[A-Za-z]/g) ?? []).length;
3371
+ const digits = (token.match(/\d/g) ?? []).length;
3372
+ if (digits >= 3 && letters <= 1) return true;
3373
+ return false;
3374
+ }
3346
3375
  function isDistinctiveToken2(token, isCodeLike) {
3347
3376
  if (token.length < 4 || token.length > 80) return false;
3348
3377
  if (/^https?:\/\//i.test(token)) return false;
3349
3378
  if (/^\d+$/.test(token)) return false;
3379
+ if (isDegenerateToken(token)) return false;
3350
3380
  const lower = token.toLowerCase();
3351
3381
  if (SENSOR_STOPWORDS.has(lower)) return false;
3352
3382
  if (!/[A-Za-z]/.test(token)) return false;
@@ -3357,6 +3387,7 @@ function isBoringValue(value) {
3357
3387
  if (!value || value.length > 80) return true;
3358
3388
  const lower = value.toLowerCase();
3359
3389
  if (lower === "true" || lower === "false") return false;
3390
+ if (isDegenerateToken(value)) return true;
3360
3391
  return SENSOR_STOPWORDS.has(lower);
3361
3392
  }
3362
3393
  function sensorMessageFromBody(body, token) {
@@ -3906,11 +3937,36 @@ function findCoverageGaps(hotFiles, memories, options = {}) {
3906
3937
  for (const hot of hotFiles) {
3907
3938
  if (hot.changes < minChanges) continue;
3908
3939
  if (isCovered(hot.path, coverage)) continue;
3909
- gaps.push({ path: normalizePath(hot.path), changes: hot.changes });
3940
+ gaps.push({ path: normalizePath(hot.path), changes: hot.changes, ...hot.source ? { source: hot.source } : {} });
3910
3941
  }
3911
3942
  gaps.sort((a, b) => b.changes - a.changes);
3912
3943
  return gaps.slice(0, limit);
3913
3944
  }
3945
+ function tallyHotFiles(paths, source = "agent") {
3946
+ const counts = /* @__PURE__ */ new Map();
3947
+ for (const raw of paths) {
3948
+ const norm = normalizePath(raw);
3949
+ if (!norm) continue;
3950
+ counts.set(norm, (counts.get(norm) ?? 0) + 1);
3951
+ }
3952
+ return [...counts.entries()].map(([path18, changes]) => ({ path: path18, changes, source })).sort((a, b) => b.changes - a.changes);
3953
+ }
3954
+ function mergeHotFiles(a, b) {
3955
+ const merged = /* @__PURE__ */ new Map();
3956
+ for (const hot of [...a, ...b]) {
3957
+ const norm = normalizePath(hot.path);
3958
+ if (!norm) continue;
3959
+ const existing = merged.get(norm);
3960
+ if (!existing) {
3961
+ merged.set(norm, { path: norm, changes: hot.changes, ...hot.source ? { source: hot.source } : {} });
3962
+ continue;
3963
+ }
3964
+ existing.changes += hot.changes;
3965
+ if (hot.source && existing.source && hot.source !== existing.source) existing.source = "both";
3966
+ else existing.source = existing.source ?? hot.source;
3967
+ }
3968
+ return [...merged.values()].sort((x, y) => y.changes - x.changes);
3969
+ }
3914
3970
 
3915
3971
  // src/eval-history.ts
3916
3972
  import { appendFile as appendFile4, mkdir as mkdir11, readFile as readFile14 } from "fs/promises";
@@ -3998,6 +4054,30 @@ function planConflictResolution(a, b) {
3998
4054
  stale_reason: `Superseded by ${keepId} (conflict resolved on ${reason}).`
3999
4055
  };
4000
4056
  }
4057
+ function applyConflictResolution(winner, loser, plan, now = /* @__PURE__ */ new Date()) {
4058
+ const ts = now.toISOString();
4059
+ const wf = winner.memory.frontmatter;
4060
+ const lf = loser.memory.frontmatter;
4061
+ const winnerHasTopic = Boolean(wf.topic && wf.topic.trim() !== "");
4062
+ const loserHasTopic = Boolean(lf.topic && lf.topic.trim() !== "");
4063
+ const topicAdopted = !winnerHasTopic && loserHasTopic;
4064
+ const topic = winnerHasTopic ? wf.topic : topicAdopted ? lf.topic : null;
4065
+ const winnerFm = {
4066
+ ...wf,
4067
+ revision_count: wf.revision_count + 1,
4068
+ verified_at: ts,
4069
+ related_ids: [.../* @__PURE__ */ new Set([...wf.related_ids, plan.supersede_id])],
4070
+ ...topic ? { topic } : {}
4071
+ };
4072
+ const loserFm = {
4073
+ ...lf,
4074
+ status: "deprecated",
4075
+ stale_reason: plan.stale_reason,
4076
+ verified_at: ts,
4077
+ related_ids: [.../* @__PURE__ */ new Set([...lf.related_ids, plan.keep_id])]
4078
+ };
4079
+ return { winner: winnerFm, loser: loserFm, topic, topic_adopted: topicAdopted };
4080
+ }
4001
4081
 
4002
4082
  // src/seed-git.ts
4003
4083
  var REVERT_RE = /^Revert\s+"(.+)"\s*$/i;
@@ -4383,6 +4463,7 @@ export {
4383
4463
  appendPreventionEvent,
4384
4464
  appendRuntimeJournalEntry,
4385
4465
  appendUsageEvent,
4466
+ applyConflictResolution,
4386
4467
  applyFeedbackAdjustment,
4387
4468
  bridgeMemorySummary,
4388
4469
  briefingMarkerPath,
@@ -4466,6 +4547,7 @@ export {
4466
4547
  looksLikeGenericAdvice,
4467
4548
  memoryFilePath,
4468
4549
  memoryMatchesAnchorPaths,
4550
+ mergeHotFiles,
4469
4551
  mergeMemoryVersions,
4470
4552
  newMemoryId,
4471
4553
  normalizeFindingSeverity,
@@ -4496,6 +4578,7 @@ export {
4496
4578
  recommendFeedbackAdjustment,
4497
4579
  recordApplied,
4498
4580
  recordPrevention,
4581
+ recordPreventionHits,
4499
4582
  recordProjectContextEmission,
4500
4583
  recordRejection,
4501
4584
  relPathFrom,
@@ -4526,6 +4609,7 @@ export {
4526
4609
  summarizeCaughtForYou,
4527
4610
  summarizeImpact,
4528
4611
  synthesizeSelfEvalCases,
4612
+ tallyHotFiles,
4529
4613
  titleFromBody,
4530
4614
  tokenizeQuery,
4531
4615
  tokenizeWords,