@hiveai/core 0.10.3 → 0.10.4
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 +19 -3
- package/dist/index.js +150 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -326,6 +326,7 @@ declare function buildFrontmatter(input: {
|
|
|
326
326
|
topic?: string;
|
|
327
327
|
status?: MemoryFrontmatter["status"];
|
|
328
328
|
relatedIds?: string[];
|
|
329
|
+
sensor?: Sensor;
|
|
329
330
|
}): MemoryFrontmatter;
|
|
330
331
|
|
|
331
332
|
declare const HAIVE_DIR = ".ai";
|
|
@@ -1109,8 +1110,9 @@ interface SensorTarget {
|
|
|
1109
1110
|
}
|
|
1110
1111
|
/**
|
|
1111
1112
|
* Does this sensor apply to `path`? A sensor with no explicit `paths` (and whose
|
|
1112
|
-
* memory has no anchor paths) applies everywhere. Otherwise it applies
|
|
1113
|
-
*
|
|
1113
|
+
* memory has no anchor paths) applies everywhere. Otherwise it applies only to the
|
|
1114
|
+
* exact file or directory prefix. Use an explicit directory path (`src/foo/`) when a
|
|
1115
|
+
* sensor should cover a whole subtree.
|
|
1114
1116
|
*/
|
|
1115
1117
|
declare function sensorAppliesToPath(sensor: Sensor, anchorPaths: string[], path: string): boolean;
|
|
1116
1118
|
/**
|
|
@@ -1130,6 +1132,8 @@ declare function runRegexSensor(memoryId: string, sensor: Sensor, target: Sensor
|
|
|
1130
1132
|
* are the CLI's responsibility). At most one hit per (memory, file) pair is returned.
|
|
1131
1133
|
*/
|
|
1132
1134
|
declare function runSensors(memories: Memory[], targets: SensorTarget[]): SensorHit[];
|
|
1135
|
+
/** Split a unified diff into per-file targets containing only added lines. */
|
|
1136
|
+
declare function sensorTargetsFromDiff(diff: string): SensorTarget[];
|
|
1133
1137
|
/**
|
|
1134
1138
|
* Extract the added lines from a unified diff (lines starting with a single `+`,
|
|
1135
1139
|
* excluding the `+++` file header). Mirrors the diff-handling already used by the
|
|
@@ -1137,4 +1141,16 @@ declare function runSensors(memories: Memory[], targets: SensorTarget[]): Sensor
|
|
|
1137
1141
|
*/
|
|
1138
1142
|
declare function addedLinesFromDiff(diff: string): string;
|
|
1139
1143
|
|
|
1140
|
-
|
|
1144
|
+
interface SensorSuggestionOptions {
|
|
1145
|
+
/** Extra paths to put on the sensor. Defaults to the memory anchor paths. */
|
|
1146
|
+
paths?: string[];
|
|
1147
|
+
}
|
|
1148
|
+
/**
|
|
1149
|
+
* Conservatively suggest a regex sensor from a gotcha/attempt body.
|
|
1150
|
+
*
|
|
1151
|
+
* This helper intentionally returns null more often than it guesses. Autogenerated
|
|
1152
|
+
* sensors start as `warn`, never `block`; humans can promote them after seeing them fire.
|
|
1153
|
+
*/
|
|
1154
|
+
declare function suggestSensorFromMemory(body: string, anchorPaths: string[], options?: SensorSuggestionOptions): Sensor | null;
|
|
1155
|
+
|
|
1156
|
+
export { AUTOPILOT_DEFAULTS, type Anchor, AnchorSchema, type AntiPatternGate, type AutoPromoteRule, BRIEFING_MARKER_TTL_MS, BRIEFING_PRESET_DEFAULTS, type BreakingChange, type BriefingBudgetNumbers, type BriefingBudgetPreset, type BriefingMarker, type BudgetPart, type BudgetSlice, type BuildCodeMapOptions, CHARS_PER_TOKEN, CODE_MAP_FILE, CONFIG_FILE, type CodeExport, type CodeExportKind, type CodeFileEntry, type CodeMap, type CodeMapQueryOptions, type CollectTimelineOpts, type ConfidenceLevel, type ConfidenceThresholds, type ConflictCandidatePair, type ConflictCandidatesOpts, type ContractDiffResult, type ContractFile, type ContractSnapshot, CrossRepoProvenanceSchema, type CrossRepoReport, type CrossRepoSource, DECAY_DAYS, DEFAULT_AUTO_PROMOTE_RULE, DEFAULT_CONFIDENCE_THRESHOLDS, DEFAULT_CONFIG, type DepChange, type DepTrackResult, type DependencySnapshot, GUESSABLE_THRESHOLD, HAIVE_DIR, type HaiveConfig, type HaivePaths, type LexicalRankResult, type LoadedMemory, MEMORIES_DIR, type Memory, type MemoryFrontmatter, MemoryFrontmatterSchema, type MemoryScope, MemoryScopeSchema, type MemoryStatus, MemoryStatusSchema, type MemoryType, MemoryTypeSchema, type MemoryUsage, PROJECT_CONTEXT_FILE, RUNTIME_JOURNAL_FILENAME, type ResolveProjectInfo, type RetirementSignal, type RuntimeJournalEntry, SESSION_RECAP_TTL_MS, STACK_PACK_TAG, type Sensor, type SensorHit, SensorSchema, type SensorSuggestionOptions, type SensorTarget, type TimelineEntry, type TopicStatusPair, type TruncateOptions, type TruncateResult, USAGE_FILE, USAGE_LOG_DIR, USAGE_LOG_FILE, type UsageAggregate, type UsageEvent, type UsageIndex, type VerifyOptions, type VerifyResult, addedLinesFromDiff, aggregateUsage, allocateBudget, antiPatternGateParams, appendRuntimeJournalEntry, appendUsageEvent, briefingMarkerPath, briefingMarkersDir, buildCodeMap, buildFrontmatter, bumpRead, codeMapPath, collectTimelineEntries, compileRegexSensor, configPath, contractLockPath, deriveConfidence, diffContract, emptyUsage, emptyUsageIndex, enforcementDir, estimateTokens, extractActionsBriefBody, extractSnippet, findLexicalConflictPairs, findProjectRoot, findTopicStatusConflictPairs, firstMemoryOneLine, getUsage, globToRegExp, hasRecentBriefingMarker, inferModulesFromPaths, isAutoPromoteEligible, isDecaying, isFreshIsoDate, isGlobPath, isLikelyGuessable, isRetiredMemory, isStackPackSeed, listMarkdownFilesRecursive, literalMatchesAllTokens, literalMatchesAnyToken, loadCodeMap, loadConfig, loadConfigSync, loadMemoriesFromDir, loadMemory, loadUsageIndex, memoryFilePath, memoryMatchesAnchorPaths, newMemoryId, normalizeSessionId, parseMemory, parseSince, pathsOverlap, pickSnippetNeedle, pullCrossRepoSources, queryCodeMap, rankMemoriesLexical, readRecentBriefingMarker, readRuntimeJournalTail, readUsageEvents, recordRejection, relPathFrom, resolveBriefingBudget, resolveHaivePaths, resolveManifestFiles, resolveProjectInfo, retirementSignal, runRegexSensor, runSensors, runtimeJournalPath, saveCodeMap, saveConfig, saveUsageIndex, sensorAppliesToPath, sensorTargetsFromDiff, serializeMemory, snapshotContract, specificityScore, stripPrivate, suggestSensorFromMemory, suggestTopicKey, tokenizeQuery, trackDependencies, trackReads, truncateToTokens, usageLogPath, usageLogSize, usagePath, verifyAnchor, watchContracts, writeBriefingMarker };
|
package/dist/index.js
CHANGED
|
@@ -148,6 +148,7 @@ function buildFrontmatter(input) {
|
|
|
148
148
|
created_at: now.toISOString(),
|
|
149
149
|
expires_when: null,
|
|
150
150
|
topic: input.topic,
|
|
151
|
+
sensor: input.sensor,
|
|
151
152
|
revision_count: 0,
|
|
152
153
|
related_ids: input.relatedIds ?? []
|
|
153
154
|
});
|
|
@@ -2465,10 +2466,18 @@ function isRetiredMemory(fm, body = "", now = /* @__PURE__ */ new Date()) {
|
|
|
2465
2466
|
}
|
|
2466
2467
|
|
|
2467
2468
|
// src/sensors.ts
|
|
2469
|
+
function normalizeProjectPath(value) {
|
|
2470
|
+
return value.replace(/\\/g, "/").replace(/^\.\//, "").replace(/^[ab]\//, "").replace(/\/+$/g, "");
|
|
2471
|
+
}
|
|
2468
2472
|
function sensorAppliesToPath(sensor, anchorPaths, path15) {
|
|
2469
2473
|
const scopes = sensor.paths.length > 0 ? sensor.paths : anchorPaths;
|
|
2470
2474
|
if (scopes.length === 0) return true;
|
|
2471
|
-
|
|
2475
|
+
const target = normalizeProjectPath(path15);
|
|
2476
|
+
return scopes.some((rawScope) => {
|
|
2477
|
+
const scope = normalizeProjectPath(rawScope);
|
|
2478
|
+
if (!scope) return false;
|
|
2479
|
+
return target === scope || target.startsWith(`${scope}/`);
|
|
2480
|
+
});
|
|
2472
2481
|
}
|
|
2473
2482
|
function compileRegexSensor(sensor) {
|
|
2474
2483
|
if (sensor.kind !== "regex" || !sensor.pattern) return null;
|
|
@@ -2511,9 +2520,147 @@ function runSensors(memories, targets) {
|
|
|
2511
2520
|
}
|
|
2512
2521
|
return hits;
|
|
2513
2522
|
}
|
|
2523
|
+
function sensorTargetsFromDiff(diff) {
|
|
2524
|
+
const targets = [];
|
|
2525
|
+
let currentPath = null;
|
|
2526
|
+
let added = [];
|
|
2527
|
+
const flush = () => {
|
|
2528
|
+
if (!currentPath || added.length === 0) return;
|
|
2529
|
+
targets.push({ path: currentPath, content: added.join("\n") });
|
|
2530
|
+
added = [];
|
|
2531
|
+
};
|
|
2532
|
+
for (const line of diff.split("\n")) {
|
|
2533
|
+
if (line.startsWith("diff --git ")) {
|
|
2534
|
+
flush();
|
|
2535
|
+
currentPath = null;
|
|
2536
|
+
continue;
|
|
2537
|
+
}
|
|
2538
|
+
if (line.startsWith("+++ ")) {
|
|
2539
|
+
flush();
|
|
2540
|
+
const raw = line.slice(4).trim();
|
|
2541
|
+
currentPath = raw === "/dev/null" ? null : normalizeProjectPath(raw);
|
|
2542
|
+
continue;
|
|
2543
|
+
}
|
|
2544
|
+
if (line.startsWith("+") && !line.startsWith("+++")) {
|
|
2545
|
+
if (!currentPath) currentPath = "";
|
|
2546
|
+
added.push(line.slice(1));
|
|
2547
|
+
}
|
|
2548
|
+
}
|
|
2549
|
+
flush();
|
|
2550
|
+
return targets;
|
|
2551
|
+
}
|
|
2514
2552
|
function addedLinesFromDiff(diff) {
|
|
2553
|
+
const targets = sensorTargetsFromDiff(diff);
|
|
2554
|
+
if (targets.length > 0) return targets.map((target) => target.content).join("\n");
|
|
2515
2555
|
return diff.split("\n").filter((l) => l.startsWith("+") && !l.startsWith("+++")).map((l) => l.slice(1)).join("\n");
|
|
2516
2556
|
}
|
|
2557
|
+
|
|
2558
|
+
// src/sensor-suggest.ts
|
|
2559
|
+
var CODE_TOKEN_RE = /`([^`\n]{3,80})`|["']([A-Za-z0-9_.:-]{3,80})["']|\b([A-Za-z][A-Za-z0-9_.:-]{2,79})\b/g;
|
|
2560
|
+
var SENSOR_STOPWORDS = /* @__PURE__ */ new Set([
|
|
2561
|
+
"about",
|
|
2562
|
+
"after",
|
|
2563
|
+
"again",
|
|
2564
|
+
"agent",
|
|
2565
|
+
"always",
|
|
2566
|
+
"anchor",
|
|
2567
|
+
"approach",
|
|
2568
|
+
"because",
|
|
2569
|
+
"before",
|
|
2570
|
+
"break",
|
|
2571
|
+
"broken",
|
|
2572
|
+
"cannot",
|
|
2573
|
+
"change",
|
|
2574
|
+
"code",
|
|
2575
|
+
"commit",
|
|
2576
|
+
"correct",
|
|
2577
|
+
"could",
|
|
2578
|
+
"default",
|
|
2579
|
+
"detect",
|
|
2580
|
+
"direct",
|
|
2581
|
+
"directory",
|
|
2582
|
+
"does",
|
|
2583
|
+
"error",
|
|
2584
|
+
"failed",
|
|
2585
|
+
"fails",
|
|
2586
|
+
"file",
|
|
2587
|
+
"files",
|
|
2588
|
+
"future",
|
|
2589
|
+
"instead",
|
|
2590
|
+
"memory",
|
|
2591
|
+
"never",
|
|
2592
|
+
"project",
|
|
2593
|
+
"recorded",
|
|
2594
|
+
"repo",
|
|
2595
|
+
"return",
|
|
2596
|
+
"should",
|
|
2597
|
+
"source",
|
|
2598
|
+
"status",
|
|
2599
|
+
"string",
|
|
2600
|
+
"this",
|
|
2601
|
+
"tried",
|
|
2602
|
+
"true",
|
|
2603
|
+
"type",
|
|
2604
|
+
"undefined",
|
|
2605
|
+
"value",
|
|
2606
|
+
"when",
|
|
2607
|
+
"where",
|
|
2608
|
+
"which",
|
|
2609
|
+
"with",
|
|
2610
|
+
"without"
|
|
2611
|
+
]);
|
|
2612
|
+
function suggestSensorFromMemory(body, anchorPaths, options = {}) {
|
|
2613
|
+
const paths = options.paths ?? anchorPaths;
|
|
2614
|
+
if (paths.length === 0) return null;
|
|
2615
|
+
const negativeText = body.split(/\*\*Instead,\s*use:\*\*|^##\s+Instead\b/im)[0] ?? body;
|
|
2616
|
+
const token = pickDistinctiveToken(negativeText);
|
|
2617
|
+
if (!token) return null;
|
|
2618
|
+
return {
|
|
2619
|
+
kind: "regex",
|
|
2620
|
+
pattern: escapeRegExp(token),
|
|
2621
|
+
paths,
|
|
2622
|
+
message: sensorMessageFromBody(body, token),
|
|
2623
|
+
severity: "warn",
|
|
2624
|
+
autogen: true,
|
|
2625
|
+
last_fired: null
|
|
2626
|
+
};
|
|
2627
|
+
}
|
|
2628
|
+
function pickDistinctiveToken(text) {
|
|
2629
|
+
const candidates = /* @__PURE__ */ new Map();
|
|
2630
|
+
for (const match of text.matchAll(CODE_TOKEN_RE)) {
|
|
2631
|
+
const raw = (match[1] ?? match[2] ?? match[3] ?? "").trim();
|
|
2632
|
+
const token = raw.replace(/^[^\w.-]+|[^\w.-]+$/g, "");
|
|
2633
|
+
const isCodeLike = Boolean(match[1] ?? match[2]);
|
|
2634
|
+
if (!isDistinctiveToken(token, isCodeLike)) continue;
|
|
2635
|
+
const key = token.toLowerCase();
|
|
2636
|
+
const codeSpanBonus = match[1] ? 20 : match[2] ? 8 : 0;
|
|
2637
|
+
const shapeBonus = /[-_.:]/.test(token) ? 3 : /[A-Z]/.test(token.slice(1)) ? 2 : /\d/.test(token) ? 1 : 0;
|
|
2638
|
+
const score = token.length + codeSpanBonus + shapeBonus;
|
|
2639
|
+
const existing = candidates.get(key);
|
|
2640
|
+
if (!existing || score > existing.score) candidates.set(key, { raw: token, score });
|
|
2641
|
+
}
|
|
2642
|
+
const best = [...candidates.values()].sort((a, b) => b.score - a.score)[0];
|
|
2643
|
+
return best?.raw ?? null;
|
|
2644
|
+
}
|
|
2645
|
+
function isDistinctiveToken(token, isCodeLike) {
|
|
2646
|
+
if (token.length < 4 || token.length > 80) return false;
|
|
2647
|
+
if (/^https?:\/\//i.test(token)) return false;
|
|
2648
|
+
if (/^\d+$/.test(token)) return false;
|
|
2649
|
+
const lower = token.toLowerCase();
|
|
2650
|
+
if (SENSOR_STOPWORDS.has(lower)) return false;
|
|
2651
|
+
if (!/[A-Za-z]/.test(token)) return false;
|
|
2652
|
+
const shaped = /[-_.:\d]/.test(token) || /[A-Z]/.test(token.slice(1));
|
|
2653
|
+
return shaped || isCodeLike;
|
|
2654
|
+
}
|
|
2655
|
+
function sensorMessageFromBody(body, token) {
|
|
2656
|
+
const instead = body.match(/\*\*Instead,\s*use:\*\*\s*([^\n]+)/i)?.[1]?.trim();
|
|
2657
|
+
if (instead) return `Avoid ${token}; ${instead}`;
|
|
2658
|
+
const firstGuidance = body.split("\n").map((line) => line.replace(/^#+\s*/, "").trim()).find((line) => line.length > 0 && !line.startsWith("---"));
|
|
2659
|
+
return firstGuidance?.slice(0, 180) || `Avoid ${token}; this matched an autogenerated hAIve sensor.`;
|
|
2660
|
+
}
|
|
2661
|
+
function escapeRegExp(value) {
|
|
2662
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2663
|
+
}
|
|
2517
2664
|
export {
|
|
2518
2665
|
AUTOPILOT_DEFAULTS,
|
|
2519
2666
|
AnchorSchema,
|
|
@@ -2618,10 +2765,12 @@ export {
|
|
|
2618
2765
|
saveConfig,
|
|
2619
2766
|
saveUsageIndex,
|
|
2620
2767
|
sensorAppliesToPath,
|
|
2768
|
+
sensorTargetsFromDiff,
|
|
2621
2769
|
serializeMemory,
|
|
2622
2770
|
snapshotContract,
|
|
2623
2771
|
specificityScore,
|
|
2624
2772
|
stripPrivate,
|
|
2773
|
+
suggestSensorFromMemory,
|
|
2625
2774
|
suggestTopicKey,
|
|
2626
2775
|
tokenizeQuery,
|
|
2627
2776
|
trackDependencies,
|