bikky 0.3.3 → 0.3.5
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 +16 -3
- package/dist/config.d.ts +1 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +6 -1
- package/dist/config.js.map +1 -1
- package/dist/config.test.d.ts +3 -2
- package/dist/config.test.d.ts.map +1 -1
- package/dist/config.test.js +12 -6
- package/dist/config.test.js.map +1 -1
- package/dist/daemon/capture-policy.d.ts +4 -4
- package/dist/daemon/capture-policy.d.ts.map +1 -1
- package/dist/daemon/capture-policy.js +8 -17
- package/dist/daemon/capture-policy.js.map +1 -1
- package/dist/daemon/capture-policy.test.js +2 -2
- package/dist/daemon/capture-policy.test.js.map +1 -1
- package/dist/daemon/entity-typing.d.ts +20 -0
- package/dist/daemon/entity-typing.d.ts.map +1 -0
- package/dist/daemon/entity-typing.js +166 -0
- package/dist/daemon/entity-typing.js.map +1 -0
- package/dist/daemon/episode-summary.d.ts +3 -0
- package/dist/daemon/episode-summary.d.ts.map +1 -1
- package/dist/daemon/episode-summary.js +19 -2
- package/dist/daemon/episode-summary.js.map +1 -1
- package/dist/daemon/episode-summary.test.js +5 -5
- package/dist/daemon/episode-summary.test.js.map +1 -1
- package/dist/daemon/extraction-quality.test.d.ts +2 -0
- package/dist/daemon/extraction-quality.test.d.ts.map +1 -0
- package/dist/daemon/extraction-quality.test.js +283 -0
- package/dist/daemon/extraction-quality.test.js.map +1 -0
- package/dist/daemon/extraction-rules.d.ts +131 -0
- package/dist/daemon/extraction-rules.d.ts.map +1 -0
- package/dist/daemon/extraction-rules.js +321 -0
- package/dist/daemon/extraction-rules.js.map +1 -0
- package/dist/daemon/extraction-rules.test.d.ts +2 -0
- package/dist/daemon/extraction-rules.test.d.ts.map +1 -0
- package/dist/daemon/extraction-rules.test.js +183 -0
- package/dist/daemon/extraction-rules.test.js.map +1 -0
- package/dist/daemon/extraction.d.ts +19 -1
- package/dist/daemon/extraction.d.ts.map +1 -1
- package/dist/daemon/extraction.js +169 -21
- package/dist/daemon/extraction.js.map +1 -1
- package/dist/daemon/extraction.test.js +96 -2
- package/dist/daemon/extraction.test.js.map +1 -1
- package/dist/daemon/loop.d.ts.map +1 -1
- package/dist/daemon/loop.js +14 -0
- package/dist/daemon/loop.js.map +1 -1
- package/dist/daemon/qdrant.d.ts +15 -1
- package/dist/daemon/qdrant.d.ts.map +1 -1
- package/dist/daemon/qdrant.js +45 -2
- package/dist/daemon/qdrant.js.map +1 -1
- package/dist/daemon/relations-vocab.d.ts +44 -0
- package/dist/daemon/relations-vocab.d.ts.map +1 -0
- package/dist/daemon/relations-vocab.js +168 -0
- package/dist/daemon/relations-vocab.js.map +1 -0
- package/dist/daemon/relations-vocab.test.d.ts +2 -0
- package/dist/daemon/relations-vocab.test.d.ts.map +1 -0
- package/dist/daemon/relations-vocab.test.js +69 -0
- package/dist/daemon/relations-vocab.test.js.map +1 -0
- package/dist/daemon/relations.d.ts +2 -0
- package/dist/daemon/relations.d.ts.map +1 -1
- package/dist/daemon/relations.js +15 -5
- package/dist/daemon/relations.js.map +1 -1
- package/dist/daemon/session-index.test.js +1 -1
- package/dist/daemon/session-index.test.js.map +1 -1
- package/dist/daemon/watcher-health.d.ts +20 -0
- package/dist/daemon/watcher-health.d.ts.map +1 -0
- package/dist/daemon/watcher-health.js +78 -0
- package/dist/daemon/watcher-health.js.map +1 -0
- package/dist/daemon/watcher-health.test.d.ts +5 -0
- package/dist/daemon/watcher-health.test.d.ts.map +1 -0
- package/dist/daemon/watcher-health.test.js +96 -0
- package/dist/daemon/watcher-health.test.js.map +1 -0
- package/dist/daemon/watcher.test.d.ts +3 -2
- package/dist/daemon/watcher.test.d.ts.map +1 -1
- package/dist/daemon/watcher.test.js +9 -19
- package/dist/daemon/watcher.test.js.map +1 -1
- package/dist/daemon/workstream-resolver.d.ts +76 -0
- package/dist/daemon/workstream-resolver.d.ts.map +1 -0
- package/dist/daemon/workstream-resolver.js +180 -0
- package/dist/daemon/workstream-resolver.js.map +1 -0
- package/dist/daemon/workstream-resolver.test.d.ts +2 -0
- package/dist/daemon/workstream-resolver.test.d.ts.map +1 -0
- package/dist/daemon/workstream-resolver.test.js +128 -0
- package/dist/daemon/workstream-resolver.test.js.map +1 -0
- package/dist/daemon/workstream-summary.test.js +4 -4
- package/dist/daemon/workstream-summary.test.js.map +1 -1
- package/dist/mcp/helpers.d.ts.map +1 -1
- package/dist/mcp/helpers.js +17 -2
- package/dist/mcp/helpers.js.map +1 -1
- package/dist/mcp/taxonomy.d.ts +6 -18
- package/dist/mcp/taxonomy.d.ts.map +1 -1
- package/dist/mcp/taxonomy.js +5 -25
- package/dist/mcp/taxonomy.js.map +1 -1
- package/dist/mcp/taxonomy.test.js +10 -5
- package/dist/mcp/taxonomy.test.js.map +1 -1
- package/dist/mcp/tools.d.ts.map +1 -1
- package/dist/mcp/tools.js +337 -2
- package/dist/mcp/tools.js.map +1 -1
- package/dist/mcp/tools.test.js +209 -0
- package/dist/mcp/tools.test.js.map +1 -1
- package/dist/prompts/distill.d.ts.map +1 -1
- package/dist/prompts/distill.js +3 -2
- package/dist/prompts/distill.js.map +1 -1
- package/dist/prompts/entity-typing.d.ts +18 -0
- package/dist/prompts/entity-typing.d.ts.map +1 -0
- package/dist/prompts/entity-typing.js +60 -0
- package/dist/prompts/entity-typing.js.map +1 -0
- package/dist/prompts/episode-summary.d.ts.map +1 -1
- package/dist/prompts/episode-summary.js +17 -3
- package/dist/prompts/episode-summary.js.map +1 -1
- package/dist/prompts/extraction.d.ts.map +1 -1
- package/dist/prompts/extraction.js +114 -5
- package/dist/prompts/extraction.js.map +1 -1
- package/dist/prompts/index.d.ts +1 -0
- package/dist/prompts/index.d.ts.map +1 -1
- package/dist/prompts/index.js +1 -0
- package/dist/prompts/index.js.map +1 -1
- package/dist/prompts/relations.d.ts.map +1 -1
- package/dist/prompts/relations.js +26 -4
- package/dist/prompts/relations.js.map +1 -1
- package/dist/render.d.ts.map +1 -1
- package/dist/render.js +7 -1
- package/dist/render.js.map +1 -1
- package/dist/render.test.js +3 -2
- package/dist/render.test.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Subtype rule table.
|
|
3
|
+
*
|
|
4
|
+
* The extraction LLM picks a `memory_subtype` from 8 fact subtypes. A few
|
|
5
|
+
* subtype pairs are easy to confuse:
|
|
6
|
+
*
|
|
7
|
+
* - operational_procedure vs troubleshooting_gotcha
|
|
8
|
+
* - codebase_map vs infra_topology
|
|
9
|
+
* - domain_rule vs preference
|
|
10
|
+
*
|
|
11
|
+
* This module backstops the LLM with a lightweight keyword scorer. It returns a
|
|
12
|
+
* deterministic top-1 subtype + score for a fact's content, so the daemon can:
|
|
13
|
+
*
|
|
14
|
+
* - flag facts where the LLM and the rule table strongly disagree
|
|
15
|
+
* (set `review_status: candidate` so a human can verify)
|
|
16
|
+
* - downgrade confidence when nothing scores above a floor
|
|
17
|
+
*
|
|
18
|
+
* The table is intentionally small. It is NOT the source of truth — the LLM is.
|
|
19
|
+
* The rule table is a cheap consistency check.
|
|
20
|
+
*/
|
|
21
|
+
const RULES = {
|
|
22
|
+
operational_procedure: [
|
|
23
|
+
{ re: /\b(?:rollout|rollback|deploy(?:ment|ed|ing)?|release|cron|schedul(?:e|ed))\b/i, weight: 1.0 },
|
|
24
|
+
{ re: /\b(?:helm|kubectl|terraform|ansible|argo|flux|aws\b)/i, weight: 0.8 },
|
|
25
|
+
{ re: /\b(?:procedure|runbook|playbook|step\s*\d|first[\s,]+then)\b/i, weight: 0.9 },
|
|
26
|
+
{ re: /\b(?:maintenance|incident|on-call|oncall|backfill|migration|cutover)\b/i, weight: 0.8 },
|
|
27
|
+
],
|
|
28
|
+
troubleshooting_gotcha: [
|
|
29
|
+
{ re: /\b(?:gotcha|caveat|watch\s+out|beware|warning|broken|breaks?)\b/i, weight: 1.0 },
|
|
30
|
+
{ re: /\b(?:fails?|failing|error|exception|stack\s*trace|crash(?:es|ed|ing)?)\b/i, weight: 0.7 },
|
|
31
|
+
{ re: /\b(?:workaround|hack|fix|patch|bug)\b/i, weight: 0.7 },
|
|
32
|
+
{ re: /\b(?:silently|unexpected|surprising|tricky|subtle)\b/i, weight: 0.8 },
|
|
33
|
+
],
|
|
34
|
+
codebase_map: [
|
|
35
|
+
{ re: /\b[\w.-]+\.(?:ts|tsx|js|jsx|mjs|cjs|py|go|rs|java|kt|rb|php|sql|sh|md|ya?ml|toml|json)\b/i, weight: 1.0 },
|
|
36
|
+
{ re: /\b(?:module|package|class|function|method|symbol|interface|export|import)\b/i, weight: 0.7 },
|
|
37
|
+
{ re: /\b(?:src|lib|tests?|packages|apps|services|cmd|internal|pkg)\/[\w./-]+/i, weight: 0.9 },
|
|
38
|
+
{ re: /\b(?:repo|repository|monorepo|workspace)\b/i, weight: 0.5 },
|
|
39
|
+
],
|
|
40
|
+
infra_topology: [
|
|
41
|
+
{ re: /\b(?:cluster|namespace|pod|deployment|service|ingress|node|vpc|subnet|region|az)\b/i, weight: 0.9 },
|
|
42
|
+
{ re: /\b(?:s3|rds|sqs|sns|ec2|eks|ecs|lambda|fargate|kinesis|dynamodb|cloudfront)\b/i, weight: 0.9 },
|
|
43
|
+
{ re: /\b(?:postgres(?:ql)?|mysql|redis|kafka|rabbitmq|clickhouse|mongo)\b/i, weight: 0.8 },
|
|
44
|
+
{ re: /\b(?:queue|topic|database|cache|broker|gateway|load\s*balancer)\b/i, weight: 0.5 },
|
|
45
|
+
],
|
|
46
|
+
architecture_decision: [
|
|
47
|
+
{ re: /\b(?:we\s+(?:chose|decided|picked|selected)|chosen\s+over|preferred\s+over)\b/i, weight: 1.0 },
|
|
48
|
+
{ re: /\b(?:rationale|because|trade[\s-]*off|alternative|considered)\b/i, weight: 0.7 },
|
|
49
|
+
{ re: /\b(?:adr|design\s+decision|architectural\s+decision|policy\s+decision)\b/i, weight: 1.0 },
|
|
50
|
+
{ re: /\b(?:standard(?:ise|ize)?d?|convention|approach|strategy)\b/i, weight: 0.5 },
|
|
51
|
+
],
|
|
52
|
+
access_pattern: [
|
|
53
|
+
{ re: /\b(?:auth(?:n|z|entication|orization)?|oauth|jwt|saml|sso|api[\s-]*key|token)\b/i, weight: 1.0 },
|
|
54
|
+
{ re: /\b(?:role|permission|grant|policy|iam|rbac|acl)\b/i, weight: 0.9 },
|
|
55
|
+
{ re: /\b(?:approval|approver|review(?:er)?|gate|sign[\s-]*off)\b/i, weight: 0.6 },
|
|
56
|
+
{ re: /\b(?:credential|secret|vault|1password|op\s+read)\b/i, weight: 0.7 },
|
|
57
|
+
],
|
|
58
|
+
domain_rule: [
|
|
59
|
+
{ re: /\b(?:must|shall|required|mandatory|invalid\s+if|only\s+if|unless)\b/i, weight: 0.8 },
|
|
60
|
+
{ re: /\b(?:business\s+rule|policy|regulation|sla|slo|kpi|metric)\b/i, weight: 1.0 },
|
|
61
|
+
{ re: /\b(?:workflow|lifecycle|stage|status\s+transition)\b/i, weight: 0.6 },
|
|
62
|
+
{ re: /\b(?:eligible|ineligible|allowed|forbidden|prohibited)\b/i, weight: 0.7 },
|
|
63
|
+
],
|
|
64
|
+
preference: [
|
|
65
|
+
{ re: /\b(?:prefer(?:s|red|ence)?|like(?:s|d)?\s+to|tend(?:s|ed)?\s+to)\b/i, weight: 1.0 },
|
|
66
|
+
{ re: /\b(?:my|our|team['']?s|user['']?s)\s+(?:style|convention|habit)\b/i, weight: 0.8 },
|
|
67
|
+
{ re: /\b(?:always|never|usually|by\s+default)\b/i, weight: 0.4 },
|
|
68
|
+
{ re: /\b(?:opinion|taste|personal)\b/i, weight: 0.6 },
|
|
69
|
+
],
|
|
70
|
+
};
|
|
71
|
+
const SUBTYPES = Object.keys(RULES);
|
|
72
|
+
/**
|
|
73
|
+
* Score `content` against every subtype's rule terms. Returns the top-1
|
|
74
|
+
* subtype + raw score, plus all scores for inspection.
|
|
75
|
+
*/
|
|
76
|
+
export const scoreSubtype = (content) => {
|
|
77
|
+
const text = content || "";
|
|
78
|
+
const scores = {};
|
|
79
|
+
let topSubtype = null;
|
|
80
|
+
let topScore = 0;
|
|
81
|
+
for (const subtype of SUBTYPES) {
|
|
82
|
+
let total = 0;
|
|
83
|
+
for (const term of RULES[subtype]) {
|
|
84
|
+
if (term.re.test(text))
|
|
85
|
+
total += term.weight;
|
|
86
|
+
}
|
|
87
|
+
scores[subtype] = total;
|
|
88
|
+
if (total > topScore) {
|
|
89
|
+
topScore = total;
|
|
90
|
+
topSubtype = subtype;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
// Require at least one strong term hit (weight >= 1.0 → score >= 1.0) before
|
|
94
|
+
// we trust the rule table. Below that threshold we yield no opinion.
|
|
95
|
+
if (topScore < 1.0) {
|
|
96
|
+
return { subtype: null, score: topScore, scores };
|
|
97
|
+
}
|
|
98
|
+
return { subtype: topSubtype, score: topScore, scores };
|
|
99
|
+
};
|
|
100
|
+
const DISAGREEMENT_MARGIN = 0.6;
|
|
101
|
+
export const compareSubtype = (content, llmSubtype) => {
|
|
102
|
+
const score = scoreSubtype(content);
|
|
103
|
+
if (!score.subtype) {
|
|
104
|
+
return { verdict: "agree", ruleSubtype: null, ruleScore: score.score, margin: 0 };
|
|
105
|
+
}
|
|
106
|
+
if (!llmSubtype || score.subtype === llmSubtype) {
|
|
107
|
+
return { verdict: "agree", ruleSubtype: score.subtype, ruleScore: score.score, margin: 0 };
|
|
108
|
+
}
|
|
109
|
+
const llmScore = score.scores[llmSubtype] ?? 0;
|
|
110
|
+
const margin = score.score - llmScore;
|
|
111
|
+
if (margin >= DISAGREEMENT_MARGIN) {
|
|
112
|
+
return { verdict: "disagree", ruleSubtype: score.subtype, ruleScore: score.score, margin };
|
|
113
|
+
}
|
|
114
|
+
return { verdict: "agree", ruleSubtype: score.subtype, ruleScore: score.score, margin };
|
|
115
|
+
};
|
|
116
|
+
/**
|
|
117
|
+
* Shape-based typed-token detector. A "typed token" is any substring that an
|
|
118
|
+
* engineer could plausibly grep for and find a unique referent: file paths,
|
|
119
|
+
* URLs, code-formatted spans, version strings, issue/PR refs, identifier-shaped
|
|
120
|
+
* names (kebab-case / snake_case / dotted / camelCase ≥ 3 chars).
|
|
121
|
+
*
|
|
122
|
+
* Intentionally shape-only — no hardcoded tool/service vocabulary.
|
|
123
|
+
*/
|
|
124
|
+
export const hasTypedToken = (content) => {
|
|
125
|
+
const text = content || "";
|
|
126
|
+
return Boolean(
|
|
127
|
+
// Backtick-quoted code spans
|
|
128
|
+
/`[^`\n]{2,}`/.test(text) ||
|
|
129
|
+
// URLs
|
|
130
|
+
/\bhttps?:\/\/\S+/.test(text) ||
|
|
131
|
+
// File paths (slash-separated with at least one segment containing a dot OR ≥ 3 segments)
|
|
132
|
+
/(?:^|[\s(])(?:[\w.-]+\/){1,}[\w.-]+\.[a-z0-9]{1,8}(?=[\s),.;:]|$)/i.test(text) ||
|
|
133
|
+
/(?:^|[\s(])(?:[\w.-]+\/){2,}[\w.-]+(?=[\s),.;:]|$)/.test(text) ||
|
|
134
|
+
// Filenames with extension (no slash)
|
|
135
|
+
/\b[\w.-]+\.(?:[a-z]{1,4}|ya?ml|toml|json|sql|md)\b/i.test(text) ||
|
|
136
|
+
// Issue / PR refs
|
|
137
|
+
/(?:^|[\s(])#\d{2,}\b/.test(text) ||
|
|
138
|
+
/\b(?:issue|pr|pull[- ]request)\s*#?\d+\b/i.test(text) ||
|
|
139
|
+
// Version strings (semver-ish or hash-ish)
|
|
140
|
+
/\bv?\d+\.\d+(?:\.\d+)?(?:[-+][\w.]+)?\b/.test(text) ||
|
|
141
|
+
/\b[0-9a-f]{7,40}\b/.test(text) ||
|
|
142
|
+
// Constant-case identifiers (≥ 3 chars)
|
|
143
|
+
/\b[A-Z][A-Z0-9_]{2,}\b/.test(text) ||
|
|
144
|
+
// Dotted identifiers (a.b.c) — service names, package paths
|
|
145
|
+
/\b[a-z][\w-]*(?:\.[\w-]+){2,}\b/.test(text) ||
|
|
146
|
+
// Kebab/snake identifiers ≥ 8 chars containing a digit OR ≥ 2 separators
|
|
147
|
+
// (filters out plain English compounds like "pre-built", "high-quality")
|
|
148
|
+
/\b[a-z][a-z0-9]*[-_][a-z0-9-_]{4,}\b(?=.*\d|.*[-_].*[-_])/.test(text) ||
|
|
149
|
+
// CamelCase identifiers (≥ 2 humps)
|
|
150
|
+
/\b[A-Z][a-z0-9]+[A-Z][A-Za-z0-9]+\b/.test(text));
|
|
151
|
+
};
|
|
152
|
+
/**
|
|
153
|
+
* Returns true iff `subject` either:
|
|
154
|
+
* - looks like a typed token itself, OR
|
|
155
|
+
* - matches (case-insensitively) one of the entities the LLM extracted, OR
|
|
156
|
+
* - is a substring of one of the entities (or vice versa) — handles
|
|
157
|
+
* "the foo cronjob" vs entity "foo".
|
|
158
|
+
*
|
|
159
|
+
* Intentionally permissive — we want to catch the ungrounded cases, not
|
|
160
|
+
* second-guess every wording choice.
|
|
161
|
+
*/
|
|
162
|
+
export const subjectResolves = (subject, entities) => {
|
|
163
|
+
const s = (subject || "").trim();
|
|
164
|
+
if (!s)
|
|
165
|
+
return false;
|
|
166
|
+
if (hasTypedToken(s))
|
|
167
|
+
return true;
|
|
168
|
+
const sLower = s.toLowerCase();
|
|
169
|
+
for (const entity of entities) {
|
|
170
|
+
const e = entity.toLowerCase();
|
|
171
|
+
if (!e)
|
|
172
|
+
continue;
|
|
173
|
+
if (sLower === e)
|
|
174
|
+
return true;
|
|
175
|
+
if (sLower.includes(e) && e.length >= 3)
|
|
176
|
+
return true;
|
|
177
|
+
if (e.includes(sLower) && sLower.length >= 3)
|
|
178
|
+
return true;
|
|
179
|
+
}
|
|
180
|
+
return false;
|
|
181
|
+
};
|
|
182
|
+
/**
|
|
183
|
+
* Structural grounding gate. Combines:
|
|
184
|
+
* - typed-token presence in `content`
|
|
185
|
+
* - subject resolution against typed tokens or entities
|
|
186
|
+
* - LLM's own subject_specificity self-grade
|
|
187
|
+
* - LLM's own self_contained self-grade
|
|
188
|
+
*
|
|
189
|
+
* Verdict semantics (consumed by storeFacts):
|
|
190
|
+
* - grounded → no action, store as-is
|
|
191
|
+
* - ambiguous → keep, but lower confidence and flag in metadata
|
|
192
|
+
* - ungrounded → drop entirely
|
|
193
|
+
*/
|
|
194
|
+
export const verifyGrounding = (input) => {
|
|
195
|
+
const typed = hasTypedToken(input.content);
|
|
196
|
+
const subjectOk = subjectResolves(input.subject, input.entities);
|
|
197
|
+
const specificity = typeof input.subject_specificity === "number"
|
|
198
|
+
? input.subject_specificity
|
|
199
|
+
: null;
|
|
200
|
+
const selfContained = input.self_contained;
|
|
201
|
+
// Hard reject conditions:
|
|
202
|
+
// 1. The fact carries no typed token AND its subject does not resolve.
|
|
203
|
+
// 2. The LLM rated subject_specificity below 0.3 AND the subject does not
|
|
204
|
+
// resolve to entities — a typed token elsewhere in `content` is NOT
|
|
205
|
+
// enough to rescue a vague subject (the subject is what the fact is
|
|
206
|
+
// ABOUT, the rest of the content is supporting detail).
|
|
207
|
+
// 3. The LLM said the fact is not self-contained AND nothing rescues it.
|
|
208
|
+
if (!typed && !subjectOk) {
|
|
209
|
+
return {
|
|
210
|
+
verdict: "ungrounded",
|
|
211
|
+
reason: "no typed token and subject does not resolve to entities",
|
|
212
|
+
hasTypedToken: typed,
|
|
213
|
+
subjectResolves: subjectOk,
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
if (specificity !== null && specificity < 0.3 && !subjectOk) {
|
|
217
|
+
return {
|
|
218
|
+
verdict: "ungrounded",
|
|
219
|
+
reason: `LLM self-rated subject_specificity=${specificity}; subject does not resolve to entities`,
|
|
220
|
+
hasTypedToken: typed,
|
|
221
|
+
subjectResolves: subjectOk,
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
if (selfContained === false && !subjectOk) {
|
|
225
|
+
return {
|
|
226
|
+
verdict: "ungrounded",
|
|
227
|
+
reason: "LLM marked self_contained=false and subject does not resolve to entities",
|
|
228
|
+
hasTypedToken: typed,
|
|
229
|
+
subjectResolves: subjectOk,
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
// Soft downgrade: subject resolves but LLM is not confident.
|
|
233
|
+
if (specificity !== null && specificity < 0.5) {
|
|
234
|
+
return {
|
|
235
|
+
verdict: "ambiguous",
|
|
236
|
+
reason: `LLM self-rated subject_specificity=${specificity}`,
|
|
237
|
+
hasTypedToken: typed,
|
|
238
|
+
subjectResolves: subjectOk,
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
if (selfContained === false) {
|
|
242
|
+
return {
|
|
243
|
+
verdict: "ambiguous",
|
|
244
|
+
reason: "LLM marked self_contained=false but subject resolves to entities",
|
|
245
|
+
hasTypedToken: typed,
|
|
246
|
+
subjectResolves: subjectOk,
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
return {
|
|
250
|
+
verdict: "grounded",
|
|
251
|
+
reason: "typed token present and/or subject resolves",
|
|
252
|
+
hasTypedToken: typed,
|
|
253
|
+
subjectResolves: subjectOk,
|
|
254
|
+
};
|
|
255
|
+
};
|
|
256
|
+
const ISO_DATE_RE = /^\d{4}-\d{2}-\d{2}$/;
|
|
257
|
+
const DAY_MS = 24 * 60 * 60 * 1000;
|
|
258
|
+
const isoDateOnly = (d) => d.toISOString().slice(0, 10);
|
|
259
|
+
const addDaysIso = (anchor, days) => {
|
|
260
|
+
const base = ISO_DATE_RE.test(anchor) ? new Date(`${anchor}T00:00:00Z`) : new Date();
|
|
261
|
+
return new Date(base.getTime() + days * DAY_MS).toISOString();
|
|
262
|
+
};
|
|
263
|
+
/**
|
|
264
|
+
* Translate the LLM's self-judged volatility into structural decisions:
|
|
265
|
+
* - transient → expires_at = as_of + 30d, force category=observations,
|
|
266
|
+
* half-life × 0.25
|
|
267
|
+
* - ephemeral → expires_at = as_of + 7d, force category=observations,
|
|
268
|
+
* half-life × 0.1
|
|
269
|
+
* - stable → no expiry, half-life × 1.0
|
|
270
|
+
* - evolving → no expiry, half-life × 1.0 (default)
|
|
271
|
+
*
|
|
272
|
+
* If the LLM emitted no volatility, defaults to "evolving" (NOT "stable") —
|
|
273
|
+
* we err on the side of letting decay do its job.
|
|
274
|
+
*
|
|
275
|
+
* If volatility >= transient and `as_of` is missing, we fall back to today
|
|
276
|
+
* and note the synthesis in the audit metadata.
|
|
277
|
+
*/
|
|
278
|
+
export const verifyVolatilityCoherence = (input) => {
|
|
279
|
+
const notes = [];
|
|
280
|
+
const effective = input.volatility ?? "evolving";
|
|
281
|
+
if (!input.volatility)
|
|
282
|
+
notes.push("volatility defaulted to 'evolving' (LLM omitted)");
|
|
283
|
+
let asOf = input.as_of && ISO_DATE_RE.test(input.as_of) ? input.as_of : null;
|
|
284
|
+
if ((effective === "transient" || effective === "ephemeral") && !asOf) {
|
|
285
|
+
asOf = isoDateOnly(new Date());
|
|
286
|
+
notes.push(`as_of synthesised to ${asOf} (LLM omitted but volatility=${effective})`);
|
|
287
|
+
}
|
|
288
|
+
const validFrom = asOf
|
|
289
|
+
? new Date(`${asOf}T00:00:00Z`).toISOString()
|
|
290
|
+
: new Date().toISOString();
|
|
291
|
+
let expiresAt = null;
|
|
292
|
+
let halfLifeMultiplier = 1.0;
|
|
293
|
+
let forcedCategory = null;
|
|
294
|
+
if (effective === "transient") {
|
|
295
|
+
expiresAt = addDaysIso(asOf ?? isoDateOnly(new Date()), 30);
|
|
296
|
+
halfLifeMultiplier = 0.25;
|
|
297
|
+
if (input.category !== "observations") {
|
|
298
|
+
forcedCategory = "observations";
|
|
299
|
+
const wasNote = input.category ? `was ${input.category}` : "category was unset";
|
|
300
|
+
notes.push(`category forced to observations (${wasNote}) — volatility=transient`);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
else if (effective === "ephemeral") {
|
|
304
|
+
expiresAt = addDaysIso(asOf ?? isoDateOnly(new Date()), 7);
|
|
305
|
+
halfLifeMultiplier = 0.1;
|
|
306
|
+
if (input.category !== "observations") {
|
|
307
|
+
forcedCategory = "observations";
|
|
308
|
+
const wasNote = input.category ? `was ${input.category}` : "category was unset";
|
|
309
|
+
notes.push(`category forced to observations (${wasNote}) — volatility=ephemeral`);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
return {
|
|
313
|
+
effective,
|
|
314
|
+
forcedCategory,
|
|
315
|
+
expiresAt,
|
|
316
|
+
validFrom,
|
|
317
|
+
halfLifeMultiplier,
|
|
318
|
+
notes,
|
|
319
|
+
};
|
|
320
|
+
};
|
|
321
|
+
//# sourceMappingURL=extraction-rules.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extraction-rules.js","sourceRoot":"","sources":["../../src/daemon/extraction-rules.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extraction-rules.test.d.ts","sourceRoot":"","sources":["../../src/daemon/extraction-rules.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { test } from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { compareSubtype, scoreSubtype } from "./extraction-rules.js";
|
|
4
|
+
test("scoreSubtype: operational_procedure for deploy/rollout content", () => {
|
|
5
|
+
const r = scoreSubtype("Run helm upgrade --install bikky and wait for rollout to complete.");
|
|
6
|
+
assert.equal(r.subtype, "operational_procedure");
|
|
7
|
+
assert.ok(r.score >= 1.0);
|
|
8
|
+
});
|
|
9
|
+
test("scoreSubtype: troubleshooting_gotcha for failure-mode content", () => {
|
|
10
|
+
const r = scoreSubtype("Watch out: the WA cron silently fails when the suspended flag is set.");
|
|
11
|
+
assert.equal(r.subtype, "troubleshooting_gotcha");
|
|
12
|
+
assert.ok(r.score >= 1.0);
|
|
13
|
+
});
|
|
14
|
+
test("scoreSubtype: codebase_map for file-path content", () => {
|
|
15
|
+
const r = scoreSubtype("Smoke tests live in packages/ui/tests/smoke.spec.ts.");
|
|
16
|
+
assert.equal(r.subtype, "codebase_map");
|
|
17
|
+
});
|
|
18
|
+
test("scoreSubtype: infra_topology for cluster/service content", () => {
|
|
19
|
+
const r = scoreSubtype("The lloyds cluster runs in eu-west-2 with an EKS node group and an RDS postgres.");
|
|
20
|
+
assert.equal(r.subtype, "infra_topology");
|
|
21
|
+
});
|
|
22
|
+
test("scoreSubtype: architecture_decision for explicit-choice content", () => {
|
|
23
|
+
const r = scoreSubtype("We chose Qdrant over Pinecone because of the ADR on self-hostable stores.");
|
|
24
|
+
assert.equal(r.subtype, "architecture_decision");
|
|
25
|
+
});
|
|
26
|
+
test("scoreSubtype: access_pattern for auth content", () => {
|
|
27
|
+
const r = scoreSubtype("Use the API key stored in 1Password at op://agent00/notion-api-key/credential for auth.");
|
|
28
|
+
assert.equal(r.subtype, "access_pattern");
|
|
29
|
+
});
|
|
30
|
+
test("scoreSubtype: domain_rule for business-rule content", () => {
|
|
31
|
+
const r = scoreSubtype("A scam session must reach an SLA of 30s before alerting; otherwise it is ineligible.");
|
|
32
|
+
assert.equal(r.subtype, "domain_rule");
|
|
33
|
+
});
|
|
34
|
+
test("scoreSubtype: preference for personal-style content", () => {
|
|
35
|
+
const r = scoreSubtype("I prefer kebab-case branch names by convention; that is my team's style.");
|
|
36
|
+
assert.equal(r.subtype, "preference");
|
|
37
|
+
});
|
|
38
|
+
test("scoreSubtype: returns null subtype when no strong term hits", () => {
|
|
39
|
+
const r = scoreSubtype("Random sentence with nothing distinctive in it at all.");
|
|
40
|
+
assert.equal(r.subtype, null);
|
|
41
|
+
assert.ok(r.score < 1.0);
|
|
42
|
+
});
|
|
43
|
+
test("compareSubtype: agree when LLM matches rule top-1", () => {
|
|
44
|
+
const a = compareSubtype("Watch out: the cron silently fails on a certain edge case.", "troubleshooting_gotcha");
|
|
45
|
+
assert.equal(a.verdict, "agree");
|
|
46
|
+
});
|
|
47
|
+
test("compareSubtype: agree when rule table has no opinion", () => {
|
|
48
|
+
const a = compareSubtype("Bland and uninformative content.", "preference");
|
|
49
|
+
assert.equal(a.verdict, "agree");
|
|
50
|
+
});
|
|
51
|
+
test("compareSubtype: disagree when LLM picks operational_procedure but content is a gotcha", () => {
|
|
52
|
+
const a = compareSubtype("Watch out: the cron silently fails when the suspended flag is set; the workaround is to clear it manually.", "operational_procedure");
|
|
53
|
+
assert.equal(a.verdict, "disagree");
|
|
54
|
+
assert.equal(a.ruleSubtype, "troubleshooting_gotcha");
|
|
55
|
+
});
|
|
56
|
+
test("compareSubtype: agree (no flip) when scores are close — small margin", () => {
|
|
57
|
+
// Mixed content where both subtypes have hits and the margin is small.
|
|
58
|
+
const a = compareSubtype("Run kubectl rollout restart; if it fails, watch for the wedged-pod gotcha.", "operational_procedure");
|
|
59
|
+
// Rule top-1 may be operational_procedure or troubleshooting_gotcha here;
|
|
60
|
+
// the contract is: if scores are close (margin < 0.6), the verdict is agree.
|
|
61
|
+
if (a.verdict === "disagree") {
|
|
62
|
+
assert.ok(a.margin >= 0.6, `margin ${a.margin} should be >= 0.6 to disagree`);
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
// ── Phase 2: grounding + volatility coherence verifiers ─────────────────────
|
|
66
|
+
import { hasTypedToken, subjectResolves, verifyGrounding, verifyVolatilityCoherence, } from "./extraction-rules.js";
|
|
67
|
+
test("hasTypedToken: detects file paths with extensions", () => {
|
|
68
|
+
assert.ok(hasTypedToken("Smoke tests live in packages/ui/tests/smoke.spec.ts."));
|
|
69
|
+
});
|
|
70
|
+
test("hasTypedToken: detects URLs", () => {
|
|
71
|
+
assert.ok(hasTypedToken("See https://example.com/docs for more."));
|
|
72
|
+
});
|
|
73
|
+
test("hasTypedToken: detects backtick code spans", () => {
|
|
74
|
+
assert.ok(hasTypedToken("Use the `BIKKY_COLLECTION` env var."));
|
|
75
|
+
});
|
|
76
|
+
test("hasTypedToken: detects kebab-case identifiers", () => {
|
|
77
|
+
assert.ok(hasTypedToken("The dbt-run-cronjob-v100 schedules nightly runs."));
|
|
78
|
+
});
|
|
79
|
+
test("hasTypedToken: detects camelCase identifiers", () => {
|
|
80
|
+
assert.ok(hasTypedToken("The factQualitySignals function lives in extraction.ts."));
|
|
81
|
+
});
|
|
82
|
+
test("hasTypedToken: rejects pure prose", () => {
|
|
83
|
+
assert.equal(hasTypedToken("the pipeline uses pre-built images for deployment"), false);
|
|
84
|
+
});
|
|
85
|
+
test("subjectResolves: matches by entity inclusion", () => {
|
|
86
|
+
assert.ok(subjectResolves("the foo cronjob", ["foo"]));
|
|
87
|
+
});
|
|
88
|
+
test("subjectResolves: matches when subject is a typed token", () => {
|
|
89
|
+
assert.ok(subjectResolves("packages/ui/tests/smoke.spec.ts", []));
|
|
90
|
+
});
|
|
91
|
+
test("subjectResolves: rejects bare common nouns with no entity match", () => {
|
|
92
|
+
assert.equal(subjectResolves("the pipeline", ["bikky"]), false);
|
|
93
|
+
});
|
|
94
|
+
test("verifyGrounding: rejects ungrounded prose with vague subject", () => {
|
|
95
|
+
// EX1 from prompt: "The pipeline uses pre-built Docker images pulled from ECR."
|
|
96
|
+
// No typed token, generic subject — should be REJECTED.
|
|
97
|
+
const r = verifyGrounding({
|
|
98
|
+
content: "The pipeline uses pre-built Docker images pulled from ECR.",
|
|
99
|
+
subject: "the pipeline",
|
|
100
|
+
subject_specificity: 0.2,
|
|
101
|
+
self_contained: false,
|
|
102
|
+
entities: ["docker", "ecr"],
|
|
103
|
+
});
|
|
104
|
+
assert.equal(r.verdict, "ungrounded");
|
|
105
|
+
});
|
|
106
|
+
test("verifyGrounding: accepts well-grounded fact with typed subject", () => {
|
|
107
|
+
const r = verifyGrounding({
|
|
108
|
+
content: "The CI workflow .github/workflows/release.yml builds Docker images.",
|
|
109
|
+
subject: ".github/workflows/release.yml",
|
|
110
|
+
subject_specificity: 0.95,
|
|
111
|
+
self_contained: true,
|
|
112
|
+
entities: ["bikky-dev/bikky"],
|
|
113
|
+
});
|
|
114
|
+
assert.equal(r.verdict, "grounded");
|
|
115
|
+
});
|
|
116
|
+
test("verifyGrounding: rejects 'Step 2' style episode-relative subject", () => {
|
|
117
|
+
// EX3 from prompt: "Step 2 is part of the dbt cronjob."
|
|
118
|
+
const r = verifyGrounding({
|
|
119
|
+
content: "Step 2 is part of the dbt cronjob.",
|
|
120
|
+
subject: "Step 2",
|
|
121
|
+
subject_specificity: 0.0,
|
|
122
|
+
self_contained: false,
|
|
123
|
+
entities: ["dbt"],
|
|
124
|
+
});
|
|
125
|
+
assert.equal(r.verdict, "ungrounded");
|
|
126
|
+
});
|
|
127
|
+
test("verifyGrounding: downgrades to ambiguous when LLM is mid-confident", () => {
|
|
128
|
+
// typed token rescues, but LLM only rates 0.4 on subject_specificity.
|
|
129
|
+
const r = verifyGrounding({
|
|
130
|
+
content: "The deploy.sh script is the entry point.",
|
|
131
|
+
subject: "deploy.sh",
|
|
132
|
+
subject_specificity: 0.4,
|
|
133
|
+
self_contained: false,
|
|
134
|
+
entities: [],
|
|
135
|
+
});
|
|
136
|
+
// Has typed token (deploy.sh) so not ungrounded; self_contained=false → ambiguous.
|
|
137
|
+
assert.equal(r.verdict, "ambiguous");
|
|
138
|
+
});
|
|
139
|
+
test("verifyVolatilityCoherence: transient gets 30d expiry and observations category", () => {
|
|
140
|
+
const r = verifyVolatilityCoherence({
|
|
141
|
+
volatility: "transient",
|
|
142
|
+
as_of: "2026-04-28",
|
|
143
|
+
category: "infrastructure",
|
|
144
|
+
});
|
|
145
|
+
assert.equal(r.effective, "transient");
|
|
146
|
+
assert.equal(r.forcedCategory, "observations");
|
|
147
|
+
assert.equal(r.halfLifeMultiplier, 0.25);
|
|
148
|
+
assert.ok(r.expiresAt);
|
|
149
|
+
// 30 days after 2026-04-28 is 2026-05-28
|
|
150
|
+
assert.ok(r.expiresAt.startsWith("2026-05-28"));
|
|
151
|
+
});
|
|
152
|
+
test("verifyVolatilityCoherence: ephemeral gets 7d expiry and observations category", () => {
|
|
153
|
+
const r = verifyVolatilityCoherence({
|
|
154
|
+
volatility: "ephemeral",
|
|
155
|
+
as_of: "2026-04-28",
|
|
156
|
+
category: "operations",
|
|
157
|
+
});
|
|
158
|
+
assert.equal(r.effective, "ephemeral");
|
|
159
|
+
assert.equal(r.forcedCategory, "observations");
|
|
160
|
+
assert.equal(r.halfLifeMultiplier, 0.1);
|
|
161
|
+
assert.ok(r.expiresAt.startsWith("2026-05-05"));
|
|
162
|
+
});
|
|
163
|
+
test("verifyVolatilityCoherence: stable / evolving leave category alone with no expiry", () => {
|
|
164
|
+
const stable = verifyVolatilityCoherence({ volatility: "stable", category: "codebase" });
|
|
165
|
+
assert.equal(stable.expiresAt, null);
|
|
166
|
+
assert.equal(stable.forcedCategory, null);
|
|
167
|
+
assert.equal(stable.halfLifeMultiplier, 1.0);
|
|
168
|
+
const evolving = verifyVolatilityCoherence({ volatility: "evolving", category: "infrastructure" });
|
|
169
|
+
assert.equal(evolving.expiresAt, null);
|
|
170
|
+
assert.equal(evolving.forcedCategory, null);
|
|
171
|
+
});
|
|
172
|
+
test("verifyVolatilityCoherence: synthesises as_of when missing for transient", () => {
|
|
173
|
+
const r = verifyVolatilityCoherence({ volatility: "transient", category: "observations" });
|
|
174
|
+
assert.equal(r.effective, "transient");
|
|
175
|
+
assert.ok(r.expiresAt);
|
|
176
|
+
assert.ok(r.notes.some((n) => n.includes("as_of synthesised")));
|
|
177
|
+
});
|
|
178
|
+
test("verifyVolatilityCoherence: missing volatility defaults to evolving (not stable)", () => {
|
|
179
|
+
const r = verifyVolatilityCoherence({ category: "codebase" });
|
|
180
|
+
assert.equal(r.effective, "evolving");
|
|
181
|
+
assert.ok(r.notes.some((n) => n.includes("defaulted to 'evolving'")));
|
|
182
|
+
});
|
|
183
|
+
//# sourceMappingURL=extraction-rules.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extraction-rules.test.js","sourceRoot":"","sources":["../../src/daemon/extraction-rules.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -8,11 +8,13 @@
|
|
|
8
8
|
import type { BikkyConfig } from "../config.js";
|
|
9
9
|
import type { LogFn } from "./qdrant.js";
|
|
10
10
|
export declare const setLogger: (fn: LogFn) => void;
|
|
11
|
-
export declare const DEFAULT_EXTRACTION_PROMPT = "You are Bikky's memory extraction agent for open-source coding agents. Extract durable, reusable facts that help a future agent continue work without rereading the whole transcript.\n\n## Core rule\nExtract fewer, sharper memories. A candidate fact must be independently useful after the session is gone.\n\n## Quality gate\nEvery fact must pass at least one gate:\n1. GREPPABLE: names a file path, package, symbol, config key, CLI flag, issue/PR, service, or API a future agent can search for.\n2. RUNNABLE: contains a command, URL, setting, port, or procedure that can be executed or checked.\n3. NAVIGABLE: tells a future agent where to look and what that location means.\n4. DECISIVE: records a durable decision, rationale, constraint, convention,
|
|
11
|
+
export declare const DEFAULT_EXTRACTION_PROMPT = "You are Bikky's memory extraction agent for open-source coding agents. Extract durable, reusable facts that help a future agent continue work without rereading the whole transcript.\n\n## Core rule\nExtract fewer, sharper memories. A candidate fact must be independently useful after the session is gone.\n\n## Quality gate\nEvery fact must pass at least one gate:\n1. GREPPABLE: names a file path, package, symbol, config key, CLI flag, issue/PR, service, or API a future agent can search for.\n2. RUNNABLE: contains a command, URL, setting, port, or procedure that can be executed or checked.\n3. NAVIGABLE: tells a future agent where to look and what that location means.\n4. DECISIVE: records a durable decision, rationale, constraint, convention, or preference.\n5. DIAGNOSTIC: captures a repeatable failure mode, root cause, or troubleshooting gotcha.\n\n## Ontology\n- domain is the activity profile. For coding-agent captures use \"software_engineering\".\n- category is subject matter: codebase | infrastructure | operations | decisions | product_domain | projects | people | preferences | observations.\n- kind is object shape. For this prompt, emit only kind=\"fact\".\n- memory_subtype must be one of:\n codebase_map | architecture_decision | infra_topology | access_pattern | operational_procedure | domain_rule | troubleshooting_gotcha | preference.\n\n## Examples\nGOOD:\n- \"The UI smoke tests live in packages/ui/tests/smoke.spec.ts and run through npm run test:e2e with mocked /api/memory/* responses.\"\n- \"Use workspace_id as the tenancy/access boundary; domain is reserved for activity profile such as software_engineering.\"\n- \"If Qdrant order_by fails with a missing index error, create a datetime payload index for the sorted field before retrying.\"\n- \"Prefer Node's built-in test runner for root tests; do not add Jest just for daemon unit tests.\"\n\nBAD:\n- \"The tests were fixed.\" (status only)\n- \"We reviewed the code.\" (session narration)\n- \"The deployment succeeded.\" (transient and not reusable)\n- \"The agent used npm.\" (tool narration)\n- \"There was an error.\" (no root cause or reusable detail)\n\n## Output format\nReturn strict JSON:\n{\"facts\":[\n {\n \"content\":\"One self-contained durable fact.\",\n \"category\":\"codebase\",\n \"memory_subtype\":\"codebase_map\",\n \"entities\":[\"repo-or-tool\",\"specific-module\"],\n \"confidence\":0.9,\n \"importance\":0.7,\n \"quality_score\":0.8,\n \"confidence_reason\":\"Explicitly stated in the transcript.\",\n \"repo\":\"optional/repo-or-package\",\n \"branch\":\"optional-branch\",\n \"task_key\":\"optional issue/PR/task key\",\n \"workstream_key\":\"optional stable workstream key\"\n }\n]}\n\nScoring:\n- confidence: 0.9 explicit, 0.7 strong inference, 0.55 weak but useful inference.\n- importance: 0.8+ for decisions, infra, procedures, access, recurring failures; 0.6+ for useful codebase maps/preferences.\n- quality_score: 0.8+ passes multiple gates, 0.6+ passes one strong gate, below 0.6 should usually be omitted.\n\nIf nothing passes the quality gate, return {\"facts\":[]}.";
|
|
12
|
+
export type Volatility = "stable" | "evolving" | "transient" | "ephemeral";
|
|
12
13
|
export interface ExtractedFact {
|
|
13
14
|
content: string;
|
|
14
15
|
category: string;
|
|
15
16
|
memory_subtype?: string | null;
|
|
17
|
+
subtype_reason?: string | null;
|
|
16
18
|
entities: string[];
|
|
17
19
|
confidence: number;
|
|
18
20
|
importance: number;
|
|
@@ -22,6 +24,12 @@ export interface ExtractedFact {
|
|
|
22
24
|
branch?: string | null;
|
|
23
25
|
task_key?: string | null;
|
|
24
26
|
workstream_key?: string | null;
|
|
27
|
+
subject?: string | null;
|
|
28
|
+
subject_specificity?: number | null;
|
|
29
|
+
volatility?: Volatility | null;
|
|
30
|
+
volatility_reason?: string | null;
|
|
31
|
+
self_contained?: boolean | null;
|
|
32
|
+
as_of?: string | null;
|
|
25
33
|
}
|
|
26
34
|
export interface FactQualitySignals {
|
|
27
35
|
wordCount: number;
|
|
@@ -39,4 +47,14 @@ export declare const isHighQualityExtractedFact: (fact: ExtractedFact) => boolea
|
|
|
39
47
|
* since the last high-water mark, extracts facts via LLM, and stores in Qdrant.
|
|
40
48
|
*/
|
|
41
49
|
export declare const tick: (config: BikkyConfig) => Promise<void>;
|
|
50
|
+
export interface ExtractionHealth {
|
|
51
|
+
/** ISO timestamp of the most recent extraction tick that ran. */
|
|
52
|
+
last_tick_at: string;
|
|
53
|
+
/** ISO timestamp of the most recent tick that saw ≥1 active session. Null if never. */
|
|
54
|
+
last_active_session_at: string | null;
|
|
55
|
+
/** Active session count from the most recent tick. */
|
|
56
|
+
active_session_count: number;
|
|
57
|
+
/** Configured copilot watcher path at the time of the tick. */
|
|
58
|
+
watcher_path: string;
|
|
59
|
+
}
|
|
42
60
|
//# sourceMappingURL=extraction.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extraction.d.ts","sourceRoot":"","sources":["../../src/daemon/extraction.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AASH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAIhD,OAAO,KAAK,EAAE,KAAK,EAAa,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"extraction.d.ts","sourceRoot":"","sources":["../../src/daemon/extraction.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AASH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAIhD,OAAO,KAAK,EAAE,KAAK,EAAa,MAAM,aAAa,CAAC;AA4BpD,eAAO,MAAM,SAAS,GAAI,IAAI,KAAK,KAAG,IAErC,CAAC;AAUF,eAAO,MAAM,yBAAyB,okGA0DmB,CAAC;AAgO1D,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,UAAU,GAAG,WAAW,GAAG,WAAW,CAAC;AAW3E,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAE/B,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,mBAAmB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,UAAU,CAAC,EAAE,UAAU,GAAG,IAAI,CAAC;IAC/B,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,cAAc,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IAChC,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB;AAED,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,YAAY,EAAE,OAAO,CAAC;IACtB,aAAa,EAAE,OAAO,CAAC;IACvB,oBAAoB,EAAE,MAAM,CAAC;CAC9B;AAqBD,eAAO,MAAM,kBAAkB,GAAI,MAAM,aAAa,KAAG,kBAoCxD,CAAC;AAEF,eAAO,MAAM,sBAAsB,GAAI,KAAK,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAG,aAAa,GAAG,IAgErF,CAAC;AAEF,eAAO,MAAM,0BAA0B,GAAI,MAAM,aAAa,KAAG,OAkBhE,CAAC;AA+QF;;;;GAIG;AACH,eAAO,MAAM,IAAI,GAAU,QAAQ,WAAW,KAAG,OAAO,CAAC,IAAI,CAgC5D,CAAC;AAIF,MAAM,WAAW,gBAAgB;IAC/B,iEAAiE;IACjE,YAAY,EAAE,MAAM,CAAC;IACrB,uFAAuF;IACvF,sBAAsB,EAAE,MAAM,GAAG,IAAI,CAAC;IACtC,sDAAsD;IACtD,oBAAoB,EAAE,MAAM,CAAC;IAC7B,+DAA+D;IAC/D,YAAY,EAAE,MAAM,CAAC;CACtB"}
|