byterover-cli 3.6.1 → 3.7.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/README.md +1 -1
- package/dist/agent/core/interfaces/cipher-services.d.ts +7 -0
- package/dist/agent/infra/agent/cipher-agent.js +4 -2
- package/dist/agent/infra/agent/service-initializer.js +28 -14
- package/dist/agent/infra/sandbox/curate-service.d.ts +4 -2
- package/dist/agent/infra/sandbox/curate-service.js +6 -4
- package/dist/agent/infra/tools/implementations/curate-tool.d.ts +4 -2
- package/dist/agent/infra/tools/implementations/curate-tool.js +169 -25
- package/dist/agent/infra/tools/implementations/expand-knowledge-tool.d.ts +2 -0
- package/dist/agent/infra/tools/implementations/expand-knowledge-tool.js +1 -1
- package/dist/agent/infra/tools/implementations/memory-symbol-tree.d.ts +0 -8
- package/dist/agent/infra/tools/implementations/memory-symbol-tree.js +3 -15
- package/dist/agent/infra/tools/implementations/search-knowledge-service.d.ts +49 -4
- package/dist/agent/infra/tools/implementations/search-knowledge-service.js +123 -53
- package/dist/agent/infra/tools/implementations/search-knowledge-tool.d.ts +2 -0
- package/dist/agent/infra/tools/tool-provider.js +1 -0
- package/dist/agent/infra/tools/tool-registry.d.ts +7 -0
- package/dist/agent/infra/tools/tool-registry.js +13 -6
- package/dist/oclif/commands/dream.d.ts +4 -0
- package/dist/oclif/commands/dream.js +31 -13
- package/dist/server/constants.d.ts +1 -1
- package/dist/server/constants.js +20 -1
- package/dist/server/core/domain/knowledge/markdown-writer.d.ts +12 -42
- package/dist/server/core/domain/knowledge/markdown-writer.js +55 -96
- package/dist/server/core/domain/knowledge/memory-scoring.d.ts +18 -37
- package/dist/server/core/domain/knowledge/memory-scoring.js +36 -85
- package/dist/server/core/domain/knowledge/runtime-signals-schema.d.ts +59 -0
- package/dist/server/core/domain/knowledge/runtime-signals-schema.js +46 -0
- package/dist/server/core/domain/knowledge/sidecar-logging.d.ts +14 -0
- package/dist/server/core/domain/knowledge/sidecar-logging.js +18 -0
- package/dist/server/core/interfaces/storage/i-runtime-signal-store.d.ts +111 -0
- package/dist/server/core/interfaces/storage/i-runtime-signal-store.js +38 -0
- package/dist/server/infra/context-tree/file-context-tree-archive-service.d.ts +16 -6
- package/dist/server/infra/context-tree/file-context-tree-archive-service.js +91 -32
- package/dist/server/infra/context-tree/file-context-tree-manifest-service.d.ts +14 -0
- package/dist/server/infra/context-tree/file-context-tree-manifest-service.js +20 -7
- package/dist/server/infra/context-tree/runtime-signal-store.d.ts +46 -0
- package/dist/server/infra/context-tree/runtime-signal-store.js +118 -0
- package/dist/server/infra/daemon/agent-process.js +25 -4
- package/dist/server/infra/dream/operations/consolidate.d.ts +17 -0
- package/dist/server/infra/dream/operations/consolidate.js +40 -19
- package/dist/server/infra/dream/operations/prune.d.ts +18 -0
- package/dist/server/infra/dream/operations/prune.js +31 -20
- package/dist/server/infra/dream/operations/synthesize.d.ts +13 -0
- package/dist/server/infra/dream/operations/synthesize.js +15 -3
- package/dist/server/infra/executor/dream-executor.d.ts +8 -0
- package/dist/server/infra/executor/dream-executor.js +3 -0
- package/dist/server/templates/skill/SKILL.md +79 -22
- package/oclif.manifest.json +429 -429
- package/package.json +1 -1
package/dist/server/constants.js
CHANGED
|
@@ -95,13 +95,32 @@ export const OVERVIEW_EXTENSION = '.overview.md';
|
|
|
95
95
|
export const MANIFEST_FILE = '_manifest.json';
|
|
96
96
|
export const ARCHIVE_IMPORTANCE_THRESHOLD = 35;
|
|
97
97
|
export const DEFAULT_GHOST_CUE_MAX_TOKENS = 220;
|
|
98
|
-
/** Patterns the context-tree .gitignore must contain
|
|
98
|
+
/** Patterns the context-tree .gitignore must contain. */
|
|
99
99
|
export const CONTEXT_TREE_GITIGNORE_PATTERNS = [
|
|
100
|
+
// Derived artifacts
|
|
100
101
|
'.gitignore',
|
|
101
102
|
'.snapshot.json',
|
|
102
103
|
'_manifest.json',
|
|
103
104
|
'_index.md',
|
|
104
105
|
'*.abstract.md',
|
|
105
106
|
'*.overview.md',
|
|
107
|
+
// macOS
|
|
108
|
+
'.DS_Store',
|
|
109
|
+
'._*',
|
|
110
|
+
// Windows
|
|
111
|
+
'Thumbs.db',
|
|
112
|
+
'ehthumbs.db',
|
|
113
|
+
'Desktop.ini',
|
|
114
|
+
// Linux
|
|
115
|
+
'.directory',
|
|
116
|
+
'.fuse_hidden*',
|
|
117
|
+
'.nfs*',
|
|
118
|
+
// Editor swap / backup / temp
|
|
119
|
+
'*.swp',
|
|
120
|
+
'*.swo',
|
|
121
|
+
'*~',
|
|
122
|
+
'.#*',
|
|
123
|
+
'*.bak',
|
|
124
|
+
'*.tmp',
|
|
106
125
|
];
|
|
107
126
|
export const CONTEXT_TREE_GITIGNORE_HEADER = '# Derived artifacts — do not track';
|
|
@@ -30,16 +30,14 @@ export interface Fact {
|
|
|
30
30
|
value?: string;
|
|
31
31
|
}
|
|
32
32
|
/**
|
|
33
|
-
*
|
|
34
|
-
*
|
|
33
|
+
* Content timestamps kept in markdown frontmatter. `createdAt` is the
|
|
34
|
+
* immutable creation time; `updatedAt` reflects the last content
|
|
35
|
+
* modification. Runtime ranking signals (importance, recency, maturity,
|
|
36
|
+
* accessCount, updateCount) live in the sidecar — see
|
|
37
|
+
* `features/runtime-signals/plan.md`.
|
|
35
38
|
*/
|
|
36
|
-
export interface
|
|
37
|
-
accessCount?: number;
|
|
39
|
+
export interface ContextTimestamps {
|
|
38
40
|
createdAt?: string;
|
|
39
|
-
importance?: number;
|
|
40
|
-
maturity?: 'core' | 'draft' | 'validated';
|
|
41
|
-
recency?: number;
|
|
42
|
-
updateCount?: number;
|
|
43
41
|
updatedAt?: string;
|
|
44
42
|
}
|
|
45
43
|
export interface ContextData {
|
|
@@ -50,48 +48,20 @@ export interface ContextData {
|
|
|
50
48
|
rawConcept?: RawConcept;
|
|
51
49
|
reason?: string;
|
|
52
50
|
relations?: string[];
|
|
53
|
-
scoring?: FrontmatterScoring;
|
|
54
51
|
snippets: string[];
|
|
55
52
|
summary?: string;
|
|
56
53
|
tags: string[];
|
|
54
|
+
timestamps?: ContextTimestamps;
|
|
57
55
|
}
|
|
58
|
-
interface Frontmatter {
|
|
59
|
-
accessCount?: number;
|
|
60
|
-
createdAt?: string;
|
|
61
|
-
importance?: number;
|
|
62
|
-
keywords: string[];
|
|
63
|
-
maturity?: 'core' | 'draft' | 'validated';
|
|
64
|
-
recency?: number;
|
|
65
|
-
related: string[];
|
|
66
|
-
summary?: string;
|
|
67
|
-
tags: string[];
|
|
68
|
-
title?: string;
|
|
69
|
-
updateCount?: number;
|
|
70
|
-
updatedAt?: string;
|
|
71
|
-
}
|
|
72
|
-
/**
|
|
73
|
-
* Parse content extracting relations from either frontmatter or legacy @ format.
|
|
74
|
-
* Returns parsed frontmatter metadata and the body content for further parsing.
|
|
75
|
-
*/
|
|
76
|
-
/**
|
|
77
|
-
* Extract scoring metadata from parsed frontmatter.
|
|
78
|
-
* Returns defaults for missing fields.
|
|
79
|
-
*/
|
|
80
|
-
export declare function extractScoring(fm: Frontmatter): FrontmatterScoring;
|
|
81
|
-
/**
|
|
82
|
-
* Parse frontmatter from raw markdown content and return scoring metadata.
|
|
83
|
-
* Exported for use by search-knowledge-service to extract scoring without
|
|
84
|
-
* going through the full parseContent path.
|
|
85
|
-
*/
|
|
86
|
-
export declare function parseFrontmatterScoring(content: string): FrontmatterScoring | undefined;
|
|
87
56
|
/**
|
|
88
|
-
*
|
|
89
|
-
*
|
|
57
|
+
* Extract the createdAt timestamp from a raw markdown file's frontmatter.
|
|
58
|
+
* Used by callers (e.g. curate UPDATE) that need to preserve the immutable
|
|
59
|
+
* creation time across a write without round-tripping through the full
|
|
60
|
+
* parsed-content shape.
|
|
90
61
|
*/
|
|
91
|
-
export declare function
|
|
62
|
+
export declare function parseCreatedAt(content: string): string | undefined;
|
|
92
63
|
export declare const MarkdownWriter: {
|
|
93
64
|
generateContext(data: ContextData): string;
|
|
94
65
|
mergeContexts(sourceContent: string, targetContent: string, reason?: string, summary?: string): string;
|
|
95
66
|
parseContent(content: string, name?: string): ContextData;
|
|
96
67
|
};
|
|
97
|
-
export {};
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import { dump as yamlDump, load as yamlLoad } from 'js-yaml';
|
|
2
|
-
import { determineTier, mergeScoring as mergeScoringFn } from './memory-scoring.js';
|
|
3
2
|
import { normalizeRelationPath, parseRelations } from './relation-parser.js';
|
|
4
3
|
/**
|
|
5
4
|
* Generate YAML frontmatter block from context data.
|
|
6
|
-
*
|
|
5
|
+
*
|
|
6
|
+
* Emits only semantic fields and content timestamps. Runtime ranking
|
|
7
|
+
* signals (importance, recency, maturity, accessCount, updateCount) are
|
|
8
|
+
* not written — they live in the sidecar store since commit 5 of the
|
|
9
|
+
* runtime-signals migration.
|
|
7
10
|
*/
|
|
8
|
-
function generateFrontmatter(title, relations, tags = [], keywords = [],
|
|
11
|
+
function generateFrontmatter(title, relations, tags = [], keywords = [], timestamps, summary) {
|
|
9
12
|
const normalizedRelations = (relations || []).map(rel => normalizeRelationPath(rel));
|
|
10
13
|
const fm = {};
|
|
11
14
|
if (title) {
|
|
@@ -19,29 +22,11 @@ function generateFrontmatter(title, relations, tags = [], keywords = [], scoring
|
|
|
19
22
|
fm.related = normalizedRelations;
|
|
20
23
|
}
|
|
21
24
|
fm.keywords = keywords;
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
if (scoring.recency !== undefined) {
|
|
28
|
-
fm.recency = Math.round(scoring.recency * 1000) / 1000;
|
|
29
|
-
}
|
|
30
|
-
if (scoring.maturity) {
|
|
31
|
-
fm.maturity = scoring.maturity;
|
|
32
|
-
}
|
|
33
|
-
if (scoring.accessCount !== undefined && scoring.accessCount > 0) {
|
|
34
|
-
fm.accessCount = scoring.accessCount;
|
|
35
|
-
}
|
|
36
|
-
if (scoring.updateCount !== undefined && scoring.updateCount > 0) {
|
|
37
|
-
fm.updateCount = scoring.updateCount;
|
|
38
|
-
}
|
|
39
|
-
if (scoring.createdAt) {
|
|
40
|
-
fm.createdAt = scoring.createdAt;
|
|
41
|
-
}
|
|
42
|
-
if (scoring.updatedAt) {
|
|
43
|
-
fm.updatedAt = scoring.updatedAt;
|
|
44
|
-
}
|
|
25
|
+
if (timestamps?.createdAt) {
|
|
26
|
+
fm.createdAt = timestamps.createdAt;
|
|
27
|
+
}
|
|
28
|
+
if (timestamps?.updatedAt) {
|
|
29
|
+
fm.updatedAt = timestamps.updatedAt;
|
|
45
30
|
}
|
|
46
31
|
// Always generate frontmatter since tags and keywords are required
|
|
47
32
|
const yamlContent = yamlDump(fm, { flowLevel: 1, lineWidth: -1, sortKeys: false }).trimEnd();
|
|
@@ -80,28 +65,17 @@ function parseFrontmatter(content) {
|
|
|
80
65
|
if (typeof parsed.summary === 'string') {
|
|
81
66
|
frontmatter.summary = parsed.summary;
|
|
82
67
|
}
|
|
83
|
-
//
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
frontmatter.recency = parsed.recency;
|
|
89
|
-
}
|
|
90
|
-
if (typeof parsed.accessCount === 'number') {
|
|
91
|
-
frontmatter.accessCount = parsed.accessCount;
|
|
92
|
-
}
|
|
93
|
-
if (typeof parsed.updateCount === 'number') {
|
|
94
|
-
frontmatter.updateCount = parsed.updateCount;
|
|
95
|
-
}
|
|
68
|
+
// Content timestamps (createdAt is immutable, updatedAt tracks real
|
|
69
|
+
// content modification). Pre-migration files may also carry legacy
|
|
70
|
+
// scoring fields (importance, recency, maturity, accessCount,
|
|
71
|
+
// updateCount) — those are silently ignored here; the runtime signals
|
|
72
|
+
// they represented now live in the sidecar.
|
|
96
73
|
if (typeof parsed.createdAt === 'string') {
|
|
97
74
|
frontmatter.createdAt = parsed.createdAt;
|
|
98
75
|
}
|
|
99
76
|
if (typeof parsed.updatedAt === 'string') {
|
|
100
77
|
frontmatter.updatedAt = parsed.updatedAt;
|
|
101
78
|
}
|
|
102
|
-
if (parsed.maturity === 'draft' || parsed.maturity === 'validated' || parsed.maturity === 'core') {
|
|
103
|
-
frontmatter.maturity = parsed.maturity;
|
|
104
|
-
}
|
|
105
79
|
return { body, frontmatter };
|
|
106
80
|
}
|
|
107
81
|
catch {
|
|
@@ -500,59 +474,29 @@ function mergeFacts(source, target) {
|
|
|
500
474
|
return merged.length > 0 ? merged : undefined;
|
|
501
475
|
}
|
|
502
476
|
/**
|
|
503
|
-
*
|
|
504
|
-
*
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
* Extract scoring metadata from parsed frontmatter.
|
|
508
|
-
* Returns defaults for missing fields.
|
|
509
|
-
*/
|
|
510
|
-
export function extractScoring(fm) {
|
|
511
|
-
return {
|
|
512
|
-
accessCount: fm.accessCount ?? 0,
|
|
513
|
-
createdAt: fm.createdAt,
|
|
514
|
-
importance: fm.importance ?? 50,
|
|
515
|
-
maturity: fm.maturity ?? 'draft',
|
|
516
|
-
recency: fm.recency ?? 1,
|
|
517
|
-
updateCount: fm.updateCount ?? 0,
|
|
518
|
-
updatedAt: fm.updatedAt,
|
|
519
|
-
};
|
|
520
|
-
}
|
|
521
|
-
/**
|
|
522
|
-
* Parse frontmatter from raw markdown content and return scoring metadata.
|
|
523
|
-
* Exported for use by search-knowledge-service to extract scoring without
|
|
524
|
-
* going through the full parseContent path.
|
|
525
|
-
*/
|
|
526
|
-
export function parseFrontmatterScoring(content) {
|
|
527
|
-
const parsed = parseFrontmatter(content);
|
|
528
|
-
if (!parsed) {
|
|
529
|
-
return undefined;
|
|
530
|
-
}
|
|
531
|
-
return extractScoring(parsed.frontmatter);
|
|
532
|
-
}
|
|
533
|
-
/**
|
|
534
|
-
* Replace scoring fields in existing markdown content without touching the body.
|
|
535
|
-
* If no frontmatter exists, returns the original content unchanged.
|
|
477
|
+
* Extract the createdAt timestamp from a raw markdown file's frontmatter.
|
|
478
|
+
* Used by callers (e.g. curate UPDATE) that need to preserve the immutable
|
|
479
|
+
* creation time across a write without round-tripping through the full
|
|
480
|
+
* parsed-content shape.
|
|
536
481
|
*/
|
|
537
|
-
export function
|
|
538
|
-
|
|
539
|
-
if (!parsed) {
|
|
540
|
-
return content;
|
|
541
|
-
}
|
|
542
|
-
const { body, frontmatter } = parsed;
|
|
543
|
-
const updatedFrontmatter = generateFrontmatter(frontmatter.title ?? '', frontmatter.related, frontmatter.tags, frontmatter.keywords, scoring, frontmatter.summary);
|
|
544
|
-
return updatedFrontmatter + body;
|
|
482
|
+
export function parseCreatedAt(content) {
|
|
483
|
+
return parseFrontmatter(content)?.frontmatter.createdAt;
|
|
545
484
|
}
|
|
546
485
|
function parseContentWithFrontmatter(content) {
|
|
547
486
|
const parsed = parseFrontmatter(content);
|
|
548
487
|
if (parsed) {
|
|
488
|
+
const timestamps = {};
|
|
489
|
+
if (parsed.frontmatter.createdAt)
|
|
490
|
+
timestamps.createdAt = parsed.frontmatter.createdAt;
|
|
491
|
+
if (parsed.frontmatter.updatedAt)
|
|
492
|
+
timestamps.updatedAt = parsed.frontmatter.updatedAt;
|
|
549
493
|
return {
|
|
550
494
|
body: parsed.body,
|
|
551
495
|
keywords: parsed.frontmatter.keywords,
|
|
552
496
|
relations: parsed.frontmatter.related,
|
|
553
|
-
scoring: extractScoring(parsed.frontmatter),
|
|
554
497
|
summary: parsed.frontmatter.summary,
|
|
555
498
|
tags: parsed.frontmatter.tags,
|
|
499
|
+
timestamps: Object.keys(timestamps).length > 0 ? timestamps : undefined,
|
|
556
500
|
title: parsed.frontmatter.title,
|
|
557
501
|
};
|
|
558
502
|
}
|
|
@@ -568,7 +512,7 @@ export const MarkdownWriter = {
|
|
|
568
512
|
generateContext(data) {
|
|
569
513
|
const snippets = (data.snippets || []).filter(s => s && s.trim());
|
|
570
514
|
const relations = data.relations || [];
|
|
571
|
-
const frontmatter = generateFrontmatter(data.name, relations, data.tags, data.keywords, data.
|
|
515
|
+
const frontmatter = generateFrontmatter(data.name, relations, data.tags, data.keywords, data.timestamps, data.summary);
|
|
572
516
|
const reasonSection = generateReasonSection(data.reason);
|
|
573
517
|
const rawConceptSection = generateRawConceptSection(data.rawConcept);
|
|
574
518
|
const narrativeSection = generateNarrativeSection(data.narrative);
|
|
@@ -602,14 +546,10 @@ export const MarkdownWriter = {
|
|
|
602
546
|
const mergedKeywords = [...new Set([...sourceParsed.keywords, ...targetParsed.keywords])];
|
|
603
547
|
// reason: explicit override wins, then source (newer), then target (older)
|
|
604
548
|
const mergedReason = reason ?? parseReasonSection(sourceParsed.body) ?? parseReasonSection(targetParsed.body);
|
|
605
|
-
// Merge
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
const merged = mergeScoringFn(sourceParsed.scoring ?? defaultScoring, targetParsed.scoring ?? defaultScoring);
|
|
610
|
-
const recalculatedTier = determineTier(merged.importance ?? 50, (merged.maturity ?? 'draft'));
|
|
611
|
-
mergedScoringData = { ...merged, maturity: recalculatedTier };
|
|
612
|
-
}
|
|
549
|
+
// Merge timestamps: preserve the earliest createdAt and stamp a fresh
|
|
550
|
+
// updatedAt. Scoring signals (importance/recency/maturity/counts) are
|
|
551
|
+
// merged at the sidecar layer by the merge caller — not here.
|
|
552
|
+
const mergedTimestamps = mergeTimestamps(sourceParsed.timestamps, targetParsed.timestamps);
|
|
613
553
|
const sourceRawConcept = parseRawConceptSection(sourceParsed.body);
|
|
614
554
|
const targetRawConcept = parseRawConceptSection(targetParsed.body);
|
|
615
555
|
const mergedRawConcept = mergeRawConcepts(sourceRawConcept, targetRawConcept);
|
|
@@ -637,14 +577,14 @@ export const MarkdownWriter = {
|
|
|
637
577
|
rawConcept: mergedRawConcept,
|
|
638
578
|
reason: mergedReason,
|
|
639
579
|
relations: mergedRelations,
|
|
640
|
-
scoring: mergedScoringData,
|
|
641
580
|
snippets: mergedSnippets,
|
|
642
581
|
summary: summary ?? sourceParsed.summary ?? targetParsed.summary,
|
|
643
582
|
tags: mergedTags,
|
|
583
|
+
timestamps: mergedTimestamps,
|
|
644
584
|
});
|
|
645
585
|
},
|
|
646
586
|
parseContent(content, name = '') {
|
|
647
|
-
const { body, keywords, relations,
|
|
587
|
+
const { body, keywords, relations, summary, tags, timestamps, title } = parseContentWithFrontmatter(content);
|
|
648
588
|
return {
|
|
649
589
|
facts: parseFactsSection(body),
|
|
650
590
|
keywords,
|
|
@@ -653,10 +593,29 @@ export const MarkdownWriter = {
|
|
|
653
593
|
rawConcept: parseRawConceptSection(body),
|
|
654
594
|
reason: parseReasonSection(body),
|
|
655
595
|
relations,
|
|
656
|
-
scoring,
|
|
657
596
|
snippets: extractSnippetsFromContent(body),
|
|
658
597
|
summary,
|
|
659
598
|
tags,
|
|
599
|
+
timestamps,
|
|
660
600
|
};
|
|
661
601
|
},
|
|
662
602
|
};
|
|
603
|
+
/**
|
|
604
|
+
* Merge two timestamp records: earliest createdAt, fresh updatedAt.
|
|
605
|
+
*
|
|
606
|
+
* Always stamps a fresh `updatedAt` — merge is a content modification, so
|
|
607
|
+
* the output always carries an updated timestamp regardless of input shape.
|
|
608
|
+
* `createdAt` only appears in the output when at least one input had it.
|
|
609
|
+
*/
|
|
610
|
+
function mergeTimestamps(a, b) {
|
|
611
|
+
const out = { updatedAt: new Date().toISOString() };
|
|
612
|
+
const aCreated = a?.createdAt;
|
|
613
|
+
const bCreated = b?.createdAt;
|
|
614
|
+
if (aCreated && bCreated) {
|
|
615
|
+
out.createdAt = new Date(aCreated).getTime() <= new Date(bCreated).getTime() ? aCreated : bCreated;
|
|
616
|
+
}
|
|
617
|
+
else if (aCreated ?? bCreated) {
|
|
618
|
+
out.createdAt = aCreated ?? bCreated;
|
|
619
|
+
}
|
|
620
|
+
return out;
|
|
621
|
+
}
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
*
|
|
10
10
|
* All functions are stateless and side-effect free.
|
|
11
11
|
*/
|
|
12
|
-
import type {
|
|
12
|
+
import type { RuntimeSignals } from './runtime-signals-schema.js';
|
|
13
13
|
/** Days for recency half-life (~21 days to halve) */
|
|
14
14
|
export declare const DECAY_RECENCY_FACTOR = 30;
|
|
15
15
|
/** Per-day importance multiplier (~78% after 50 days of non-use) */
|
|
@@ -41,23 +41,21 @@ export declare const TIER_BOOST: Record<string, number>;
|
|
|
41
41
|
* then applies a tier-based boost.
|
|
42
42
|
*
|
|
43
43
|
* @param bm25Normalized - Normalized BM25 score in [0, 1)
|
|
44
|
-
* @param
|
|
45
|
-
* @param recency - Recency score in [0, 1]
|
|
46
|
-
* @param maturity - Maturity tier ('draft' | 'validated' | 'core')
|
|
44
|
+
* @param signals - RuntimeSignals snapshot (importance, recency, maturity)
|
|
47
45
|
* @returns Compound score (typically in [0, ~1.15])
|
|
48
46
|
*/
|
|
49
|
-
export declare function compoundScore(bm25Normalized: number,
|
|
47
|
+
export declare function compoundScore(bm25Normalized: number, signals: RuntimeSignals): number;
|
|
50
48
|
/**
|
|
51
|
-
* Apply time-based exponential decay to
|
|
49
|
+
* Apply time-based exponential decay to a signals snapshot.
|
|
52
50
|
*
|
|
53
51
|
* Recency decays as exp(-days / DECAY_RECENCY_FACTOR).
|
|
54
52
|
* Importance decays as importance * DECAY_IMPORTANCE_FACTOR^days.
|
|
55
53
|
*
|
|
56
|
-
* @param
|
|
54
|
+
* @param signals - Current RuntimeSignals snapshot
|
|
57
55
|
* @param daysSinceLastUpdate - Days since the file was last updated
|
|
58
|
-
* @returns New
|
|
56
|
+
* @returns New signals with decayed values (original not mutated)
|
|
59
57
|
*/
|
|
60
|
-
export declare function applyDecay(
|
|
58
|
+
export declare function applyDecay(signals: RuntimeSignals, daysSinceLastUpdate: number): RuntimeSignals;
|
|
61
59
|
/**
|
|
62
60
|
* Determine the maturity tier based on importance score.
|
|
63
61
|
*
|
|
@@ -72,47 +70,30 @@ export declare function applyDecay(scoring: FrontmatterScoring, daysSinceLastUpd
|
|
|
72
70
|
* @returns The determined tier
|
|
73
71
|
*/
|
|
74
72
|
export declare function determineTier(importance: number, currentTier: 'core' | 'draft' | 'validated'): 'core' | 'draft' | 'validated';
|
|
75
|
-
/**
|
|
76
|
-
* Record a search access hit on a knowledge file.
|
|
77
|
-
*
|
|
78
|
-
* Increments access count and adds an importance bonus.
|
|
79
|
-
*
|
|
80
|
-
* @param scoring - Current scoring state
|
|
81
|
-
* @returns Updated scoring (original not mutated)
|
|
82
|
-
*/
|
|
83
|
-
export declare function recordAccessHit(scoring: FrontmatterScoring): FrontmatterScoring;
|
|
84
73
|
/**
|
|
85
74
|
* Record multiple accumulated access hits at once.
|
|
86
75
|
*
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
76
|
+
* Increments accessCount by `hitCount` and importance by
|
|
77
|
+
* `ACCESS_IMPORTANCE_BONUS * hitCount` (capped at 100). Caller is
|
|
78
|
+
* responsible for recomputing maturity via `determineTier` if the
|
|
79
|
+
* importance delta may cross a hysteresis threshold.
|
|
90
80
|
*/
|
|
91
|
-
export declare function recordAccessHits(
|
|
81
|
+
export declare function recordAccessHits(signals: RuntimeSignals, hitCount: number): RuntimeSignals;
|
|
92
82
|
/**
|
|
93
83
|
* Record a curate update on a knowledge file.
|
|
94
84
|
*
|
|
95
|
-
* Increments
|
|
96
|
-
*
|
|
97
|
-
*
|
|
98
|
-
* @param scoring - Current scoring state
|
|
99
|
-
* @returns Updated scoring (original not mutated)
|
|
100
|
-
*/
|
|
101
|
-
export declare function recordCurateUpdate(scoring: FrontmatterScoring): FrontmatterScoring;
|
|
102
|
-
/**
|
|
103
|
-
* Return default scoring values for a new or unscored knowledge file.
|
|
85
|
+
* Increments updateCount, adds an importance bonus, resets recency to 1.0.
|
|
86
|
+
* Caller is responsible for recomputing maturity via `determineTier`.
|
|
104
87
|
*/
|
|
105
|
-
export declare function
|
|
88
|
+
export declare function recordCurateUpdate(signals: RuntimeSignals): RuntimeSignals;
|
|
106
89
|
/**
|
|
107
|
-
* Merge two
|
|
90
|
+
* Merge two runtime-signal snapshots during a MERGE operation.
|
|
108
91
|
*
|
|
109
92
|
* Strategy:
|
|
110
93
|
* - importance: max of both
|
|
111
94
|
* - recency: max of both
|
|
112
95
|
* - accessCount: sum
|
|
113
96
|
* - updateCount: sum + 1 (for the merge itself)
|
|
114
|
-
* - maturity: higher tier
|
|
115
|
-
* - createdAt: earlier date
|
|
116
|
-
* - updatedAt: current time
|
|
97
|
+
* - maturity: higher tier (caller may refine via `determineTier`)
|
|
117
98
|
*/
|
|
118
|
-
export declare function mergeScoring(source:
|
|
99
|
+
export declare function mergeScoring(source: RuntimeSignals, target: RuntimeSignals): RuntimeSignals;
|
|
@@ -50,36 +50,33 @@ export const TIER_BOOST = {
|
|
|
50
50
|
* then applies a tier-based boost.
|
|
51
51
|
*
|
|
52
52
|
* @param bm25Normalized - Normalized BM25 score in [0, 1)
|
|
53
|
-
* @param
|
|
54
|
-
* @param recency - Recency score in [0, 1]
|
|
55
|
-
* @param maturity - Maturity tier ('draft' | 'validated' | 'core')
|
|
53
|
+
* @param signals - RuntimeSignals snapshot (importance, recency, maturity)
|
|
56
54
|
* @returns Compound score (typically in [0, ~1.15])
|
|
57
55
|
*/
|
|
58
|
-
export function compoundScore(bm25Normalized,
|
|
59
|
-
const normalizedImportance = Math.min(importance, 100) / 100;
|
|
60
|
-
const base = W_RELEVANCE * bm25Normalized + W_IMPORTANCE * normalizedImportance + W_RECENCY * recency;
|
|
61
|
-
const boost = TIER_BOOST[maturity] ?? TIER_BOOST.draft;
|
|
56
|
+
export function compoundScore(bm25Normalized, signals) {
|
|
57
|
+
const normalizedImportance = Math.min(signals.importance, 100) / 100;
|
|
58
|
+
const base = W_RELEVANCE * bm25Normalized + W_IMPORTANCE * normalizedImportance + W_RECENCY * signals.recency;
|
|
59
|
+
const boost = TIER_BOOST[signals.maturity] ?? TIER_BOOST.draft;
|
|
62
60
|
return base * boost;
|
|
63
61
|
}
|
|
64
62
|
/**
|
|
65
|
-
* Apply time-based exponential decay to
|
|
63
|
+
* Apply time-based exponential decay to a signals snapshot.
|
|
66
64
|
*
|
|
67
65
|
* Recency decays as exp(-days / DECAY_RECENCY_FACTOR).
|
|
68
66
|
* Importance decays as importance * DECAY_IMPORTANCE_FACTOR^days.
|
|
69
67
|
*
|
|
70
|
-
* @param
|
|
68
|
+
* @param signals - Current RuntimeSignals snapshot
|
|
71
69
|
* @param daysSinceLastUpdate - Days since the file was last updated
|
|
72
|
-
* @returns New
|
|
70
|
+
* @returns New signals with decayed values (original not mutated)
|
|
73
71
|
*/
|
|
74
|
-
export function applyDecay(
|
|
72
|
+
export function applyDecay(signals, daysSinceLastUpdate) {
|
|
75
73
|
if (daysSinceLastUpdate <= 0) {
|
|
76
|
-
return
|
|
74
|
+
return signals;
|
|
77
75
|
}
|
|
78
|
-
const currentImportance = scoring.importance ?? 50;
|
|
79
76
|
const newRecency = Math.exp(-daysSinceLastUpdate / DECAY_RECENCY_FACTOR);
|
|
80
|
-
const newImportance =
|
|
77
|
+
const newImportance = signals.importance * DECAY_IMPORTANCE_FACTOR ** daysSinceLastUpdate;
|
|
81
78
|
return {
|
|
82
|
-
...
|
|
79
|
+
...signals,
|
|
83
80
|
importance: Math.max(0, newImportance),
|
|
84
81
|
recency: newRecency,
|
|
85
82
|
};
|
|
@@ -114,104 +111,58 @@ export function determineTier(importance, currentTier) {
|
|
|
114
111
|
}
|
|
115
112
|
return currentTier;
|
|
116
113
|
}
|
|
117
|
-
/**
|
|
118
|
-
* Record a search access hit on a knowledge file.
|
|
119
|
-
*
|
|
120
|
-
* Increments access count and adds an importance bonus.
|
|
121
|
-
*
|
|
122
|
-
* @param scoring - Current scoring state
|
|
123
|
-
* @returns Updated scoring (original not mutated)
|
|
124
|
-
*/
|
|
125
|
-
export function recordAccessHit(scoring) {
|
|
126
|
-
const newAccessCount = (scoring.accessCount ?? 0) + 1;
|
|
127
|
-
const newImportance = Math.min(100, (scoring.importance ?? 50) + ACCESS_IMPORTANCE_BONUS);
|
|
128
|
-
return {
|
|
129
|
-
...scoring,
|
|
130
|
-
accessCount: newAccessCount,
|
|
131
|
-
importance: newImportance,
|
|
132
|
-
};
|
|
133
|
-
}
|
|
134
114
|
/**
|
|
135
115
|
* Record multiple accumulated access hits at once.
|
|
136
116
|
*
|
|
137
|
-
*
|
|
138
|
-
*
|
|
139
|
-
*
|
|
117
|
+
* Increments accessCount by `hitCount` and importance by
|
|
118
|
+
* `ACCESS_IMPORTANCE_BONUS * hitCount` (capped at 100). Caller is
|
|
119
|
+
* responsible for recomputing maturity via `determineTier` if the
|
|
120
|
+
* importance delta may cross a hysteresis threshold.
|
|
140
121
|
*/
|
|
141
|
-
export function recordAccessHits(
|
|
122
|
+
export function recordAccessHits(signals, hitCount) {
|
|
142
123
|
if (hitCount <= 0) {
|
|
143
|
-
return
|
|
124
|
+
return signals;
|
|
144
125
|
}
|
|
145
|
-
const newAccessCount = (scoring.accessCount ?? 0) + hitCount;
|
|
146
|
-
const newImportance = Math.min(100, (scoring.importance ?? 50) + ACCESS_IMPORTANCE_BONUS * hitCount);
|
|
147
126
|
return {
|
|
148
|
-
...
|
|
149
|
-
accessCount:
|
|
150
|
-
importance:
|
|
127
|
+
...signals,
|
|
128
|
+
accessCount: signals.accessCount + hitCount,
|
|
129
|
+
importance: Math.min(100, signals.importance + ACCESS_IMPORTANCE_BONUS * hitCount),
|
|
151
130
|
};
|
|
152
131
|
}
|
|
153
132
|
/**
|
|
154
133
|
* Record a curate update on a knowledge file.
|
|
155
134
|
*
|
|
156
|
-
* Increments
|
|
157
|
-
*
|
|
158
|
-
*
|
|
159
|
-
* @param scoring - Current scoring state
|
|
160
|
-
* @returns Updated scoring (original not mutated)
|
|
161
|
-
*/
|
|
162
|
-
export function recordCurateUpdate(scoring) {
|
|
163
|
-
const newUpdateCount = (scoring.updateCount ?? 0) + 1;
|
|
164
|
-
const newImportance = Math.min(100, (scoring.importance ?? 50) + UPDATE_IMPORTANCE_BONUS);
|
|
165
|
-
const now = new Date().toISOString();
|
|
166
|
-
return {
|
|
167
|
-
...scoring,
|
|
168
|
-
importance: newImportance,
|
|
169
|
-
recency: 1,
|
|
170
|
-
updateCount: newUpdateCount,
|
|
171
|
-
updatedAt: now,
|
|
172
|
-
};
|
|
173
|
-
}
|
|
174
|
-
/**
|
|
175
|
-
* Return default scoring values for a new or unscored knowledge file.
|
|
135
|
+
* Increments updateCount, adds an importance bonus, resets recency to 1.0.
|
|
136
|
+
* Caller is responsible for recomputing maturity via `determineTier`.
|
|
176
137
|
*/
|
|
177
|
-
export function
|
|
178
|
-
const now = new Date().toISOString();
|
|
138
|
+
export function recordCurateUpdate(signals) {
|
|
179
139
|
return {
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
importance: 50,
|
|
183
|
-
maturity: 'draft',
|
|
140
|
+
...signals,
|
|
141
|
+
importance: Math.min(100, signals.importance + UPDATE_IMPORTANCE_BONUS),
|
|
184
142
|
recency: 1,
|
|
185
|
-
updateCount:
|
|
186
|
-
updatedAt: now,
|
|
143
|
+
updateCount: signals.updateCount + 1,
|
|
187
144
|
};
|
|
188
145
|
}
|
|
189
146
|
/**
|
|
190
|
-
* Merge two
|
|
147
|
+
* Merge two runtime-signal snapshots during a MERGE operation.
|
|
191
148
|
*
|
|
192
149
|
* Strategy:
|
|
193
150
|
* - importance: max of both
|
|
194
151
|
* - recency: max of both
|
|
195
152
|
* - accessCount: sum
|
|
196
153
|
* - updateCount: sum + 1 (for the merge itself)
|
|
197
|
-
* - maturity: higher tier
|
|
198
|
-
* - createdAt: earlier date
|
|
199
|
-
* - updatedAt: current time
|
|
154
|
+
* - maturity: higher tier (caller may refine via `determineTier`)
|
|
200
155
|
*/
|
|
201
156
|
export function mergeScoring(source, target) {
|
|
202
157
|
const tierRank = { core: 3, draft: 1, validated: 2 };
|
|
203
|
-
const sourceRank = tierRank[source.maturity
|
|
204
|
-
const targetRank = tierRank[target.maturity
|
|
205
|
-
const higherTier = sourceRank >= targetRank ?
|
|
206
|
-
const sourceCreated = source.createdAt ? new Date(source.createdAt).getTime() : Date.now();
|
|
207
|
-
const targetCreated = target.createdAt ? new Date(target.createdAt).getTime() : Date.now();
|
|
158
|
+
const sourceRank = tierRank[source.maturity] ?? 1;
|
|
159
|
+
const targetRank = tierRank[target.maturity] ?? 1;
|
|
160
|
+
const higherTier = sourceRank >= targetRank ? source.maturity : target.maturity;
|
|
208
161
|
return {
|
|
209
|
-
accessCount:
|
|
210
|
-
|
|
211
|
-
importance: Math.max(source.importance ?? 50, target.importance ?? 50),
|
|
162
|
+
accessCount: source.accessCount + target.accessCount,
|
|
163
|
+
importance: Math.max(source.importance, target.importance),
|
|
212
164
|
maturity: higherTier,
|
|
213
|
-
recency: Math.max(source.recency
|
|
214
|
-
updateCount:
|
|
215
|
-
updatedAt: new Date().toISOString(),
|
|
165
|
+
recency: Math.max(source.recency, target.recency),
|
|
166
|
+
updateCount: source.updateCount + target.updateCount + 1,
|
|
216
167
|
};
|
|
217
168
|
}
|