claude-memory-layer 1.0.32 → 1.0.34
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/cli/index.js +1114 -74
- package/dist/cli/index.js.map +4 -4
- package/dist/core/index.js +414 -25
- package/dist/core/index.js.map +2 -2
- package/dist/hooks/post-tool-use.js +416 -27
- package/dist/hooks/post-tool-use.js.map +2 -2
- package/dist/hooks/semantic-daemon.js +416 -27
- package/dist/hooks/semantic-daemon.js.map +2 -2
- package/dist/hooks/session-end.js +416 -27
- package/dist/hooks/session-end.js.map +2 -2
- package/dist/hooks/session-start.js +416 -27
- package/dist/hooks/session-start.js.map +2 -2
- package/dist/hooks/stop.js +416 -27
- package/dist/hooks/stop.js.map +2 -2
- package/dist/hooks/user-prompt-submit.js +504 -34
- package/dist/hooks/user-prompt-submit.js.map +2 -2
- package/dist/index.js +416 -27
- package/dist/index.js.map +2 -2
- package/dist/mcp/index.js +567 -51
- package/dist/mcp/index.js.map +2 -2
- package/dist/server/api/index.js +850 -44
- package/dist/server/api/index.js.map +3 -3
- package/dist/server/index.js +1073 -64
- package/dist/server/index.js.map +3 -3
- package/dist/services/memory-service.js +416 -27
- package/dist/services/memory-service.js.map +2 -2
- package/dist/ui/assets/js/bootstrap.js +2 -0
- package/dist/ui/assets/js/overview.js +166 -3
- package/dist/ui/assets/js/state.js +3 -0
- package/dist/ui/index.html +20 -0
- package/dist/ui/style.css +193 -0
- package/package.json +5 -1
|
@@ -2360,10 +2360,15 @@ function sqliteClose(db) {
|
|
|
2360
2360
|
function toDateFromSQLite(value) {
|
|
2361
2361
|
if (value instanceof Date)
|
|
2362
2362
|
return value;
|
|
2363
|
-
if (typeof value === "string")
|
|
2364
|
-
return new Date(value);
|
|
2365
2363
|
if (typeof value === "number")
|
|
2366
2364
|
return new Date(value);
|
|
2365
|
+
if (typeof value === "string") {
|
|
2366
|
+
const trimmed = value.trim();
|
|
2367
|
+
if (/^\d{4}-\d{2}-\d{2}[ T]\d{2}:\d{2}:\d{2}(?:\.\d+)?$/.test(trimmed)) {
|
|
2368
|
+
return /* @__PURE__ */ new Date(trimmed.replace(" ", "T") + "Z");
|
|
2369
|
+
}
|
|
2370
|
+
return new Date(trimmed);
|
|
2371
|
+
}
|
|
2367
2372
|
return new Date(String(value));
|
|
2368
2373
|
}
|
|
2369
2374
|
function toSQLiteTimestamp(date) {
|
|
@@ -2429,6 +2434,13 @@ var MarkdownMirror2 = class {
|
|
|
2429
2434
|
};
|
|
2430
2435
|
|
|
2431
2436
|
// src/core/sqlite-event-store.ts
|
|
2437
|
+
function normalizeQueryRewriteKind(value) {
|
|
2438
|
+
const normalized = (value || "").trim().toLowerCase();
|
|
2439
|
+
if (normalized === "follow-up-context" || normalized === "intent-rewrite")
|
|
2440
|
+
return normalized;
|
|
2441
|
+
return "none";
|
|
2442
|
+
}
|
|
2443
|
+
var REWRITTEN_QUERY_REWRITE_KIND_SQL = `LOWER(TRIM(COALESCE(query_rewrite_kind, 'none'))) IN ('follow-up-context', 'intent-rewrite')`;
|
|
2432
2444
|
var SQLiteEventStore = class {
|
|
2433
2445
|
db;
|
|
2434
2446
|
initialized = false;
|
|
@@ -2689,6 +2701,8 @@ var SQLiteEventStore = class {
|
|
|
2689
2701
|
session_id TEXT,
|
|
2690
2702
|
project_hash TEXT,
|
|
2691
2703
|
query_text TEXT NOT NULL,
|
|
2704
|
+
raw_query_text TEXT,
|
|
2705
|
+
query_rewrite_kind TEXT,
|
|
2692
2706
|
strategy TEXT,
|
|
2693
2707
|
candidate_event_ids TEXT,
|
|
2694
2708
|
selected_event_ids TEXT,
|
|
@@ -2730,6 +2744,8 @@ var SQLiteEventStore = class {
|
|
|
2730
2744
|
CREATE INDEX IF NOT EXISTS idx_helpfulness_event ON memory_helpfulness(event_id);
|
|
2731
2745
|
CREATE INDEX IF NOT EXISTS idx_helpfulness_session ON memory_helpfulness(session_id);
|
|
2732
2746
|
CREATE INDEX IF NOT EXISTS idx_helpfulness_score ON memory_helpfulness(helpfulness_score DESC);
|
|
2747
|
+
CREATE INDEX IF NOT EXISTS idx_helpfulness_created_at ON memory_helpfulness(created_at);
|
|
2748
|
+
CREATE INDEX IF NOT EXISTS idx_helpfulness_measured_at ON memory_helpfulness(measured_at);
|
|
2733
2749
|
CREATE INDEX IF NOT EXISTS idx_retrieval_traces_created_at ON retrieval_traces(created_at DESC);
|
|
2734
2750
|
CREATE INDEX IF NOT EXISTS idx_retrieval_traces_project_hash ON retrieval_traces(project_hash);
|
|
2735
2751
|
CREATE INDEX IF NOT EXISTS idx_retrieval_traces_session_id ON retrieval_traces(session_id);
|
|
@@ -2763,6 +2779,18 @@ var SQLiteEventStore = class {
|
|
|
2763
2779
|
sqliteExec(this.db, `ALTER TABLE retrieval_traces ADD COLUMN candidate_details_json TEXT;`);
|
|
2764
2780
|
} catch {
|
|
2765
2781
|
}
|
|
2782
|
+
try {
|
|
2783
|
+
sqliteExec(this.db, `ALTER TABLE retrieval_traces ADD COLUMN raw_query_text TEXT;`);
|
|
2784
|
+
} catch {
|
|
2785
|
+
}
|
|
2786
|
+
try {
|
|
2787
|
+
sqliteExec(this.db, `ALTER TABLE retrieval_traces ADD COLUMN query_rewrite_kind TEXT;`);
|
|
2788
|
+
} catch {
|
|
2789
|
+
}
|
|
2790
|
+
try {
|
|
2791
|
+
sqliteExec(this.db, `CREATE INDEX IF NOT EXISTS idx_retrieval_traces_query_rewrite_kind ON retrieval_traces(query_rewrite_kind);`);
|
|
2792
|
+
} catch {
|
|
2793
|
+
}
|
|
2766
2794
|
const tableInfo = sqliteAll(this.db, "PRAGMA table_info(events)", []);
|
|
2767
2795
|
const columnNames = tableInfo.map((col) => col.name);
|
|
2768
2796
|
if (!columnNames.includes("access_count")) {
|
|
@@ -3548,8 +3576,11 @@ var SQLiteEventStore = class {
|
|
|
3548
3576
|
/**
|
|
3549
3577
|
* Get helpfulness statistics for dashboard
|
|
3550
3578
|
*/
|
|
3551
|
-
async getHelpfulnessStats() {
|
|
3579
|
+
async getHelpfulnessStats(since) {
|
|
3552
3580
|
await this.initialize();
|
|
3581
|
+
const sinceIso = since?.toISOString();
|
|
3582
|
+
const evaluatedWhere = sinceIso ? `WHERE measured_at IS NOT NULL AND datetime(created_at) >= datetime(?)` : `WHERE measured_at IS NOT NULL`;
|
|
3583
|
+
const totalWhere = sinceIso ? `WHERE datetime(created_at) >= datetime(?)` : ``;
|
|
3553
3584
|
const stats = sqliteGet(
|
|
3554
3585
|
this.db,
|
|
3555
3586
|
`SELECT
|
|
@@ -3559,11 +3590,13 @@ var SQLiteEventStore = class {
|
|
|
3559
3590
|
SUM(CASE WHEN helpfulness_score >= 0.4 AND helpfulness_score < 0.7 THEN 1 ELSE 0 END) as neutral,
|
|
3560
3591
|
SUM(CASE WHEN helpfulness_score < 0.4 THEN 1 ELSE 0 END) as unhelpful
|
|
3561
3592
|
FROM memory_helpfulness
|
|
3562
|
-
|
|
3593
|
+
${evaluatedWhere}`,
|
|
3594
|
+
sinceIso ? [sinceIso] : []
|
|
3563
3595
|
);
|
|
3564
3596
|
const totalRow = sqliteGet(
|
|
3565
3597
|
this.db,
|
|
3566
|
-
`SELECT COUNT(*) as total FROM memory_helpfulness
|
|
3598
|
+
`SELECT COUNT(*) as total FROM memory_helpfulness ${totalWhere}`,
|
|
3599
|
+
sinceIso ? [sinceIso] : []
|
|
3567
3600
|
);
|
|
3568
3601
|
return {
|
|
3569
3602
|
avgScore: Math.round((stats?.avg_score || 0) * 100) / 100,
|
|
@@ -3662,18 +3695,21 @@ var SQLiteEventStore = class {
|
|
|
3662
3695
|
async recordRetrievalTrace(input) {
|
|
3663
3696
|
await this.initialize();
|
|
3664
3697
|
const traceId = randomUUID5();
|
|
3698
|
+
const queryRewriteKind = normalizeQueryRewriteKind(input.queryRewriteKind);
|
|
3665
3699
|
sqliteRun(
|
|
3666
3700
|
this.db,
|
|
3667
3701
|
`INSERT INTO retrieval_traces (
|
|
3668
|
-
trace_id, session_id, project_hash, query_text, strategy,
|
|
3702
|
+
trace_id, session_id, project_hash, query_text, raw_query_text, query_rewrite_kind, strategy,
|
|
3669
3703
|
candidate_event_ids, selected_event_ids, candidate_details_json, selected_details_json,
|
|
3670
3704
|
candidate_count, selected_count, confidence, fallback_trace
|
|
3671
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
3705
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
3672
3706
|
[
|
|
3673
3707
|
traceId,
|
|
3674
3708
|
input.sessionId || null,
|
|
3675
3709
|
input.projectHash || null,
|
|
3676
3710
|
input.queryText,
|
|
3711
|
+
input.rawQueryText || null,
|
|
3712
|
+
queryRewriteKind,
|
|
3677
3713
|
input.strategy || null,
|
|
3678
3714
|
JSON.stringify(input.candidateEventIds || []),
|
|
3679
3715
|
JSON.stringify(input.selectedEventIds || []),
|
|
@@ -3699,6 +3735,8 @@ var SQLiteEventStore = class {
|
|
|
3699
3735
|
sessionId: row.session_id || void 0,
|
|
3700
3736
|
projectHash: row.project_hash || void 0,
|
|
3701
3737
|
queryText: row.query_text,
|
|
3738
|
+
rawQueryText: row.raw_query_text || void 0,
|
|
3739
|
+
queryRewriteKind: normalizeQueryRewriteKind(row.query_rewrite_kind),
|
|
3702
3740
|
strategy: row.strategy || void 0,
|
|
3703
3741
|
candidateEventIds: row.candidate_event_ids ? JSON.parse(row.candidate_event_ids) : [],
|
|
3704
3742
|
selectedEventIds: row.selected_event_ids ? JSON.parse(row.selected_event_ids) : [],
|
|
@@ -3725,6 +3763,11 @@ var SQLiteEventStore = class {
|
|
|
3725
3763
|
COUNT(*) as total_queries,
|
|
3726
3764
|
AVG(candidate_count) as avg_candidate_count,
|
|
3727
3765
|
AVG(selected_count) as avg_selected_count,
|
|
3766
|
+
SUM(CASE WHEN ${REWRITTEN_QUERY_REWRITE_KIND_SQL} THEN 1 ELSE 0 END) as rewritten_queries,
|
|
3767
|
+
SUM(CASE WHEN ${REWRITTEN_QUERY_REWRITE_KIND_SQL} AND selected_count > 0 THEN 1 ELSE 0 END) as rewritten_queries_with_selection,
|
|
3768
|
+
SUM(CASE WHEN NOT (${REWRITTEN_QUERY_REWRITE_KIND_SQL}) AND selected_count > 0 THEN 1 ELSE 0 END) as raw_queries_with_selection,
|
|
3769
|
+
AVG(CASE WHEN ${REWRITTEN_QUERY_REWRITE_KIND_SQL} THEN selected_count END) as avg_selected_count_for_rewritten_queries,
|
|
3770
|
+
AVG(CASE WHEN NOT (${REWRITTEN_QUERY_REWRITE_KIND_SQL}) THEN selected_count END) as avg_selected_count_for_raw_queries,
|
|
3728
3771
|
CASE
|
|
3729
3772
|
WHEN SUM(candidate_count) > 0 THEN (SUM(selected_count) * 1.0 / SUM(candidate_count))
|
|
3730
3773
|
ELSE 0
|
|
@@ -3732,15 +3775,41 @@ var SQLiteEventStore = class {
|
|
|
3732
3775
|
FROM retrieval_traces`,
|
|
3733
3776
|
[]
|
|
3734
3777
|
);
|
|
3778
|
+
const totalQueries = Number(row?.total_queries || 0);
|
|
3779
|
+
const rewrittenQueries = Number(row?.rewritten_queries || 0);
|
|
3780
|
+
const rawQueries = Math.max(0, totalQueries - rewrittenQueries);
|
|
3781
|
+
const rewrittenQueriesWithSelection = Number(row?.rewritten_queries_with_selection || 0);
|
|
3782
|
+
const rawQueriesWithSelection = Number(row?.raw_queries_with_selection || 0);
|
|
3735
3783
|
return {
|
|
3736
|
-
totalQueries
|
|
3784
|
+
totalQueries,
|
|
3737
3785
|
avgCandidateCount: Number(row?.avg_candidate_count || 0),
|
|
3738
3786
|
avgSelectedCount: Number(row?.avg_selected_count || 0),
|
|
3739
|
-
selectionRate: Number(row?.selection_rate || 0)
|
|
3787
|
+
selectionRate: Number(row?.selection_rate || 0),
|
|
3788
|
+
rewrittenQueries,
|
|
3789
|
+
rewriteRate: totalQueries > 0 ? rewrittenQueries / totalQueries : 0,
|
|
3790
|
+
rewrittenQueriesWithSelection,
|
|
3791
|
+
rawQueriesWithSelection,
|
|
3792
|
+
rewrittenSelectionRate: rewrittenQueries > 0 ? rewrittenQueriesWithSelection / rewrittenQueries : 0,
|
|
3793
|
+
rawSelectionRate: rawQueries > 0 ? rawQueriesWithSelection / rawQueries : 0,
|
|
3794
|
+
avgSelectedCountForRewrittenQueries: Number(row?.avg_selected_count_for_rewritten_queries || 0),
|
|
3795
|
+
avgSelectedCountForRawQueries: Number(row?.avg_selected_count_for_raw_queries || 0)
|
|
3740
3796
|
};
|
|
3741
3797
|
} catch (err) {
|
|
3742
3798
|
if (err?.message?.includes("no such table")) {
|
|
3743
|
-
return {
|
|
3799
|
+
return {
|
|
3800
|
+
totalQueries: 0,
|
|
3801
|
+
avgCandidateCount: 0,
|
|
3802
|
+
avgSelectedCount: 0,
|
|
3803
|
+
selectionRate: 0,
|
|
3804
|
+
rewrittenQueries: 0,
|
|
3805
|
+
rewriteRate: 0,
|
|
3806
|
+
rewrittenQueriesWithSelection: 0,
|
|
3807
|
+
rawQueriesWithSelection: 0,
|
|
3808
|
+
rewrittenSelectionRate: 0,
|
|
3809
|
+
rawSelectionRate: 0,
|
|
3810
|
+
avgSelectedCountForRewrittenQueries: 0,
|
|
3811
|
+
avgSelectedCountForRawQueries: 0
|
|
3812
|
+
};
|
|
3744
3813
|
}
|
|
3745
3814
|
throw err;
|
|
3746
3815
|
}
|
|
@@ -4552,6 +4621,57 @@ var COMMAND_ARTIFACT_PATTERNS = [
|
|
|
4552
4621
|
/<local-command-stdout>[\s\S]*?<\/local-command-stdout>/i,
|
|
4553
4622
|
/<local-command-stderr>[\s\S]*?<\/local-command-stderr>/i
|
|
4554
4623
|
];
|
|
4624
|
+
var CONTINUATION_QUERY_PATTERNS = [
|
|
4625
|
+
/^\s*(?:continue|resume|next|what(?:'s| is)? next|next\s+(?:step|task|action)|recommended\s+(?:next\s+)?(?:step|task|action)|what should (?:we|i) do next)\??\s*$/i,
|
|
4626
|
+
/^\s*(?:응\s*)?(?:이어서(?:\s*진행(?:해줘)?)?|계속(?:\s*해줘)?|다음\s*(?:단계|작업|추천\s*작업|추천|할\s*일)?(?:은|는)?(?:\s*(?:뭐야|진행(?:해줘)?))?\??|남은\s*(?:추가(?:로)?\s*)?(?:(?:할\s*만한\s*)?(?:작업|일)|할\s*일)?(?:은|는)?\s*(?:있어|있나|있나요|뭐야)\??|추천\s*작업(?:은|는)?(?:\s*뭐야)?\??|진행해줘)\s*$/i
|
|
4627
|
+
];
|
|
4628
|
+
var SHORT_REPAIR_FOLLOW_UP_PATTERNS = [
|
|
4629
|
+
/^\s*(?:fix\s+(?:it|that)|repair\s+(?:it|that)|resolve\s+(?:it|that)|that\s+bug|same\s+issue)\s*$/i,
|
|
4630
|
+
/^\s*(?:그거|그것|이거|이것)?\s*(?:고쳐줘|수정해줘|해결해줘|처리해줘)\s*$/i
|
|
4631
|
+
];
|
|
4632
|
+
var CURRENT_STATE_QUERY_PATTERNS = [
|
|
4633
|
+
/\bcurrent\b.*\b(?:state|status|deployment|blocker|pr|pull request)\b/i,
|
|
4634
|
+
/\b(?:still|as current|current)\b.*\b(?:unresolved|open|pending|not completed)\b/i,
|
|
4635
|
+
/\b(?:old|obsolete|stale|resolved|already resolved)\b.*\b(?:current|still|unresolved|open|state|status)\b/i,
|
|
4636
|
+
/(?:현재|아직|이전|오래된|해결된).*(?:상태|미해결|열린|블로커|PR|풀리퀘스트)/i
|
|
4637
|
+
];
|
|
4638
|
+
var STALE_CONTENT_PATTERNS = [
|
|
4639
|
+
/\b(?:obsolete|superseded|outdated)\b/i,
|
|
4640
|
+
/\bstale\s+(?:operational\s+)?state\b/i,
|
|
4641
|
+
/\bstale\s+after\b/i,
|
|
4642
|
+
/\bno\s+longer\s+(?:valid|current|applies?)\b/i,
|
|
4643
|
+
/\bearlier\s+(?:pull request|pr)\b[\s\S]{0,160}\b(?:open|not completed|had not completed)\b/i,
|
|
4644
|
+
/\bshould\s+not\s+be\s+injected\s+as\s+current\s+context\b/i,
|
|
4645
|
+
/(?:오래된|더 이상 유효하지|현재 상태가 아님)/i
|
|
4646
|
+
];
|
|
4647
|
+
var CONTINUATION_EXPANSION = "current next step plan roadmap status validation replay rerank memory usefulness continuation";
|
|
4648
|
+
var REPAIR_FOLLOW_UP_EXPANSION = "review blocker fix pattern dashboard error state metrics bucket validation sanitize rerun unresolved";
|
|
4649
|
+
var RETRIEVAL_PRIVACY_DECISION_EXPANSION = "retrieval telemetry privacy public api dashboard dashboards rawQueryText queryText raw query text expose safe trace metadata trace id reason strategy rewrite kind aggregate count counts candidate selected public panel";
|
|
4650
|
+
var DECISION_RECALL_TERMS = /* @__PURE__ */ new Set([
|
|
4651
|
+
"decide",
|
|
4652
|
+
"decided",
|
|
4653
|
+
"decision",
|
|
4654
|
+
"agreed",
|
|
4655
|
+
"policy",
|
|
4656
|
+
"constraint"
|
|
4657
|
+
]);
|
|
4658
|
+
var RETRIEVAL_PRIVACY_SURFACE_TERMS = /* @__PURE__ */ new Set([
|
|
4659
|
+
"retrieval",
|
|
4660
|
+
"dashboard",
|
|
4661
|
+
"telemetry",
|
|
4662
|
+
"trace"
|
|
4663
|
+
]);
|
|
4664
|
+
var DECISION_TOPIC_WEAK_TERMS = /* @__PURE__ */ new Set([
|
|
4665
|
+
"api",
|
|
4666
|
+
"dashboard",
|
|
4667
|
+
"retrieval",
|
|
4668
|
+
"trace",
|
|
4669
|
+
"telemetry",
|
|
4670
|
+
"query",
|
|
4671
|
+
"raw",
|
|
4672
|
+
"count",
|
|
4673
|
+
"counts"
|
|
4674
|
+
]);
|
|
4555
4675
|
var GENERIC_TECHNICAL_TERMS = /* @__PURE__ */ new Set([
|
|
4556
4676
|
"api",
|
|
4557
4677
|
"cli",
|
|
@@ -4569,6 +4689,87 @@ var GENERIC_TECHNICAL_TERMS = /* @__PURE__ */ new Set([
|
|
|
4569
4689
|
"db",
|
|
4570
4690
|
"sql"
|
|
4571
4691
|
]);
|
|
4692
|
+
var LOW_INFORMATION_QUERY_TERMS = /* @__PURE__ */ new Set([
|
|
4693
|
+
"the",
|
|
4694
|
+
"and",
|
|
4695
|
+
"or",
|
|
4696
|
+
"for",
|
|
4697
|
+
"from",
|
|
4698
|
+
"with",
|
|
4699
|
+
"without",
|
|
4700
|
+
"about",
|
|
4701
|
+
"what",
|
|
4702
|
+
"when",
|
|
4703
|
+
"where",
|
|
4704
|
+
"which",
|
|
4705
|
+
"who",
|
|
4706
|
+
"why",
|
|
4707
|
+
"how",
|
|
4708
|
+
"did",
|
|
4709
|
+
"does",
|
|
4710
|
+
"do",
|
|
4711
|
+
"we",
|
|
4712
|
+
"i",
|
|
4713
|
+
"in",
|
|
4714
|
+
"to",
|
|
4715
|
+
"of",
|
|
4716
|
+
"on",
|
|
4717
|
+
"as",
|
|
4718
|
+
"be",
|
|
4719
|
+
"was",
|
|
4720
|
+
"were",
|
|
4721
|
+
"decide",
|
|
4722
|
+
"decided",
|
|
4723
|
+
"decision",
|
|
4724
|
+
"agreed",
|
|
4725
|
+
"policy",
|
|
4726
|
+
"constraint",
|
|
4727
|
+
"showing",
|
|
4728
|
+
"can",
|
|
4729
|
+
"you",
|
|
4730
|
+
"me",
|
|
4731
|
+
"show",
|
|
4732
|
+
"tell",
|
|
4733
|
+
"please",
|
|
4734
|
+
"should",
|
|
4735
|
+
"would",
|
|
4736
|
+
"could",
|
|
4737
|
+
"this",
|
|
4738
|
+
"that",
|
|
4739
|
+
"these",
|
|
4740
|
+
"those",
|
|
4741
|
+
"use",
|
|
4742
|
+
"using",
|
|
4743
|
+
"treat",
|
|
4744
|
+
"continue",
|
|
4745
|
+
"resume",
|
|
4746
|
+
"next",
|
|
4747
|
+
"step",
|
|
4748
|
+
"task",
|
|
4749
|
+
"action",
|
|
4750
|
+
"current",
|
|
4751
|
+
"state",
|
|
4752
|
+
"status",
|
|
4753
|
+
"old",
|
|
4754
|
+
"already",
|
|
4755
|
+
"still",
|
|
4756
|
+
"near",
|
|
4757
|
+
"today",
|
|
4758
|
+
"\uC751",
|
|
4759
|
+
"\uADF8\uAC70",
|
|
4760
|
+
"\uADF8\uAC83",
|
|
4761
|
+
"\uC774\uAC70",
|
|
4762
|
+
"\uC774\uAC83",
|
|
4763
|
+
"\uB2E4\uC74C",
|
|
4764
|
+
"\uB2E8\uACC4",
|
|
4765
|
+
"\uC9C4\uD589",
|
|
4766
|
+
"\uC9C4\uD589\uD574\uC918",
|
|
4767
|
+
"\uACC4\uC18D",
|
|
4768
|
+
"\uC774\uC5B4\uC11C",
|
|
4769
|
+
"\uACE0\uCCD0\uC918",
|
|
4770
|
+
"\uC218\uC815\uD574\uC918",
|
|
4771
|
+
"\uD574\uACB0\uD574\uC918"
|
|
4772
|
+
]);
|
|
4572
4773
|
function isCommandArtifactQuery(query) {
|
|
4573
4774
|
const trimmed = query.trim();
|
|
4574
4775
|
if (!trimmed)
|
|
@@ -4580,6 +4781,73 @@ function isCommandArtifactQuery(query) {
|
|
|
4580
4781
|
return true;
|
|
4581
4782
|
return COMMAND_ARTIFACT_PATTERNS.some((pattern) => pattern.test(trimmed));
|
|
4582
4783
|
}
|
|
4784
|
+
function isGenericContinuationQuery(query) {
|
|
4785
|
+
const trimmed = query.trim();
|
|
4786
|
+
if (!trimmed)
|
|
4787
|
+
return false;
|
|
4788
|
+
if (!CONTINUATION_QUERY_PATTERNS.some((pattern) => pattern.test(trimmed)))
|
|
4789
|
+
return false;
|
|
4790
|
+
if (extractTechnicalQueryTerms(trimmed).length > 0)
|
|
4791
|
+
return false;
|
|
4792
|
+
const tokens = trimmed.match(/[A-Za-z0-9가-힣#._/-]+/g) ?? [];
|
|
4793
|
+
if (tokens.length > 10)
|
|
4794
|
+
return false;
|
|
4795
|
+
return !/[A-Za-z0-9_-]+\.[A-Za-z0-9]+/.test(trimmed) && !/(?:^|\s)(?:feat|fix|chore|refactor|docs)\/[A-Za-z0-9._-]+/.test(trimmed) && !/[A-Za-z]:?[\\/]|\/Users\/|\.\/|\.\.\//.test(trimmed);
|
|
4796
|
+
}
|
|
4797
|
+
function isShortRepairFollowUpQuery(query) {
|
|
4798
|
+
const trimmed = query.trim();
|
|
4799
|
+
if (!trimmed)
|
|
4800
|
+
return false;
|
|
4801
|
+
if (extractTechnicalQueryTerms(trimmed).length > 0)
|
|
4802
|
+
return false;
|
|
4803
|
+
const tokens = trimmed.match(/[A-Za-z0-9가-힣#._/-]+/g) ?? [];
|
|
4804
|
+
if (tokens.length > 8)
|
|
4805
|
+
return false;
|
|
4806
|
+
return SHORT_REPAIR_FOLLOW_UP_PATTERNS.some((pattern) => pattern.test(trimmed));
|
|
4807
|
+
}
|
|
4808
|
+
function isCurrentStateQuery(query) {
|
|
4809
|
+
const trimmed = query.trim();
|
|
4810
|
+
if (!trimmed)
|
|
4811
|
+
return false;
|
|
4812
|
+
return CURRENT_STATE_QUERY_PATTERNS.some((pattern) => pattern.test(trimmed));
|
|
4813
|
+
}
|
|
4814
|
+
function isStaleOrSupersededContent(content) {
|
|
4815
|
+
const trimmed = content.trim();
|
|
4816
|
+
if (!trimmed)
|
|
4817
|
+
return false;
|
|
4818
|
+
return STALE_CONTENT_PATTERNS.some((pattern) => pattern.test(trimmed));
|
|
4819
|
+
}
|
|
4820
|
+
function buildRetrievalQualityQuery(query) {
|
|
4821
|
+
const trimmed = query.trim();
|
|
4822
|
+
if (!trimmed)
|
|
4823
|
+
return query;
|
|
4824
|
+
if (isRetrievalPrivacyDecisionQuery(trimmed)) {
|
|
4825
|
+
return `${trimmed} ${RETRIEVAL_PRIVACY_DECISION_EXPANSION}`;
|
|
4826
|
+
}
|
|
4827
|
+
if (isGenericContinuationQuery(trimmed)) {
|
|
4828
|
+
return `${trimmed} ${CONTINUATION_EXPANSION}`;
|
|
4829
|
+
}
|
|
4830
|
+
if (isShortRepairFollowUpQuery(trimmed)) {
|
|
4831
|
+
return `${trimmed} ${REPAIR_FOLLOW_UP_EXPANSION}`;
|
|
4832
|
+
}
|
|
4833
|
+
return query;
|
|
4834
|
+
}
|
|
4835
|
+
function isRetrievalPrivacyDecisionQuery(query) {
|
|
4836
|
+
const trimmed = query.trim();
|
|
4837
|
+
if (!trimmed)
|
|
4838
|
+
return false;
|
|
4839
|
+
const terms = new Set(tokenizeQualityText(trimmed));
|
|
4840
|
+
const hasDecisionSignal = hasAnyTerm(terms, DECISION_RECALL_TERMS) || /(?:결정|정책|원칙)/i.test(trimmed);
|
|
4841
|
+
if (!hasDecisionSignal)
|
|
4842
|
+
return false;
|
|
4843
|
+
const hasRawQuerySignal = terms.has("raw") && terms.has("query");
|
|
4844
|
+
const hasPrivacySignal = terms.has("privacy") || terms.has("expose") || terms.has("redacted");
|
|
4845
|
+
const hasRetrievalSurface = hasAnyTerm(terms, RETRIEVAL_PRIVACY_SURFACE_TERMS) || terms.has("api") && terms.has("query");
|
|
4846
|
+
const hasQuerySurface = terms.has("query") && (terms.has("dashboard") || terms.has("trace") || terms.has("telemetry") || terms.has("api"));
|
|
4847
|
+
const hasKoreanRetrievalSurface = /(?:검색|리트리벌|retrieval|대시보드|트레이스|텔레메트리|telemetry)/i.test(trimmed);
|
|
4848
|
+
const hasKoreanPrivacySurface = /(?:원문|쿼리|프라이버시|개인정보|노출|트레이스|메타데이터)/i.test(trimmed);
|
|
4849
|
+
return (hasRetrievalSurface || hasKoreanRetrievalSurface && hasKoreanPrivacySurface) && (hasRawQuerySignal || hasPrivacySignal || hasQuerySurface || hasKoreanPrivacySurface);
|
|
4850
|
+
}
|
|
4583
4851
|
function extractTechnicalQueryTerms(query) {
|
|
4584
4852
|
const matches = query.match(/[A-Za-z][A-Za-z0-9_.:-]{2,}/g) ?? [];
|
|
4585
4853
|
const terms = matches.filter((term) => {
|
|
@@ -4597,9 +4865,87 @@ function hasTechnicalTermOverlap(query, content) {
|
|
|
4597
4865
|
const normalizedContent = content.toLowerCase();
|
|
4598
4866
|
return terms.some((term) => normalizedContent.includes(term));
|
|
4599
4867
|
}
|
|
4868
|
+
function hasDiscriminativeTermOverlap(query, content) {
|
|
4869
|
+
const queryTerms = extractDiscriminativeQueryTerms(query);
|
|
4870
|
+
const contentTerms = new Set(tokenizeQualityText(content));
|
|
4871
|
+
if (isRetrievalPrivacyDecisionQuery(query) && hasRetrievalPrivacyDecisionContent(contentTerms)) {
|
|
4872
|
+
return true;
|
|
4873
|
+
}
|
|
4874
|
+
if (shouldRequireDecisionTopicOverlap(query)) {
|
|
4875
|
+
const topicTerms = queryTerms.filter((term) => !DECISION_TOPIC_WEAK_TERMS.has(term));
|
|
4876
|
+
if (topicTerms.length > 0) {
|
|
4877
|
+
return topicTerms.some((term) => contentTerms.has(term));
|
|
4878
|
+
}
|
|
4879
|
+
}
|
|
4880
|
+
if (queryTerms.length < 3)
|
|
4881
|
+
return true;
|
|
4882
|
+
const requiredHits = queryTerms.length >= 3 ? 2 : 1;
|
|
4883
|
+
let hits = 0;
|
|
4884
|
+
for (const term of queryTerms) {
|
|
4885
|
+
if (contentTerms.has(term))
|
|
4886
|
+
hits += 1;
|
|
4887
|
+
if (hits >= requiredHits)
|
|
4888
|
+
return true;
|
|
4889
|
+
}
|
|
4890
|
+
return false;
|
|
4891
|
+
}
|
|
4600
4892
|
function shouldApplyTechnicalGuard(query) {
|
|
4601
4893
|
return extractTechnicalQueryTerms(query).length > 0;
|
|
4602
4894
|
}
|
|
4895
|
+
function hasAnyTerm(terms, expectedTerms) {
|
|
4896
|
+
let found = false;
|
|
4897
|
+
expectedTerms.forEach((term) => {
|
|
4898
|
+
if (terms.has(term))
|
|
4899
|
+
found = true;
|
|
4900
|
+
});
|
|
4901
|
+
return found;
|
|
4902
|
+
}
|
|
4903
|
+
function shouldRequireDecisionTopicOverlap(query) {
|
|
4904
|
+
if (isRetrievalPrivacyDecisionQuery(query))
|
|
4905
|
+
return false;
|
|
4906
|
+
const trimmed = query.trim();
|
|
4907
|
+
if (!trimmed)
|
|
4908
|
+
return false;
|
|
4909
|
+
const terms = new Set(tokenizeQualityText(trimmed));
|
|
4910
|
+
return hasAnyTerm(terms, DECISION_RECALL_TERMS) || /(?:결정|정책|원칙)/i.test(trimmed);
|
|
4911
|
+
}
|
|
4912
|
+
function extractDiscriminativeQueryTerms(query) {
|
|
4913
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4914
|
+
const terms = [];
|
|
4915
|
+
for (const token of tokenizeQualityText(query)) {
|
|
4916
|
+
if (LOW_INFORMATION_QUERY_TERMS.has(token))
|
|
4917
|
+
continue;
|
|
4918
|
+
if (GENERIC_TECHNICAL_TERMS.has(token))
|
|
4919
|
+
continue;
|
|
4920
|
+
if (seen.has(token))
|
|
4921
|
+
continue;
|
|
4922
|
+
seen.add(token);
|
|
4923
|
+
terms.push(token);
|
|
4924
|
+
}
|
|
4925
|
+
return terms;
|
|
4926
|
+
}
|
|
4927
|
+
function hasRetrievalPrivacyDecisionContent(contentTerms) {
|
|
4928
|
+
const hasDashboardTraceMetadata = contentTerms.has("dashboard") && (contentTerms.has("trace") || contentTerms.has("metadata")) && (contentTerms.has("safe") || contentTerms.has("strategy") || contentTerms.has("rewrite") || contentTerms.has("candidate") || contentTerms.has("selected") || contentTerms.has("count") || contentTerms.has("reason"));
|
|
4929
|
+
const hasRawQueryPrivacyPolicy = contentTerms.has("retrieval") && (contentTerms.has("privacy") || contentTerms.has("expose") || contentTerms.has("raw") && contentTerms.has("query") && (contentTerms.has("dashboard") || contentTerms.has("telemetry") || contentTerms.has("api") || contentTerms.has("public")));
|
|
4930
|
+
return hasDashboardTraceMetadata || hasRawQueryPrivacyPolicy;
|
|
4931
|
+
}
|
|
4932
|
+
function tokenizeQualityText(text) {
|
|
4933
|
+
return text.replace(/([a-z])([A-Z])/g, "$1 $2").toLowerCase().replace(/[^A-Za-z0-9가-힣\s_.:-]/g, " ").split(/\s+/).flatMap((token) => token.split(/(?=[._:-])|(?<=[._:-])/g)).map((token) => normalizeQualityToken(token.replace(/^[._:-]+|[._:-]+$/g, ""))).filter((token) => token.length >= 2);
|
|
4934
|
+
}
|
|
4935
|
+
function normalizeQualityToken(token) {
|
|
4936
|
+
if (token === "apis")
|
|
4937
|
+
return "api";
|
|
4938
|
+
if (token === "ids")
|
|
4939
|
+
return "id";
|
|
4940
|
+
if (LOW_INFORMATION_QUERY_TERMS.has(token) || GENERIC_TECHNICAL_TERMS.has(token))
|
|
4941
|
+
return token;
|
|
4942
|
+
if (token.length > 4 && token.endsWith("ies"))
|
|
4943
|
+
return `${token.slice(0, -3)}y`;
|
|
4944
|
+
if (token.length > 3 && token.endsWith("s") && !token.endsWith("ss") && !token.endsWith("us") && !token.endsWith("is")) {
|
|
4945
|
+
return token.slice(0, -1);
|
|
4946
|
+
}
|
|
4947
|
+
return token;
|
|
4948
|
+
}
|
|
4603
4949
|
|
|
4604
4950
|
// src/core/retriever.ts
|
|
4605
4951
|
var DEFAULT_OPTIONS = {
|
|
@@ -4652,6 +4998,7 @@ var Retriever = class {
|
|
|
4652
4998
|
const opts = { ...DEFAULT_OPTIONS, ...options };
|
|
4653
4999
|
const sessionFilter = opts.scope?.sessionId ?? opts.sessionId;
|
|
4654
5000
|
const fallbackTrace = [];
|
|
5001
|
+
const qualityQuery = buildRetrievalQualityQuery(query);
|
|
4655
5002
|
if (isCommandArtifactQuery(query)) {
|
|
4656
5003
|
fallbackTrace.push("guard:command-artifact-query");
|
|
4657
5004
|
const emptyMatch = this.matcher.matchSearchResults([], () => 0);
|
|
@@ -4668,6 +5015,7 @@ var Retriever = class {
|
|
|
4668
5015
|
const fallbackEnabled = (opts.strategy ?? "auto") === "auto";
|
|
4669
5016
|
const primaryStrategy = opts.strategy === "auto" ? "fast" : opts.strategy || "fast";
|
|
4670
5017
|
let current = await this.runStage(query, {
|
|
5018
|
+
qualityQuery,
|
|
4671
5019
|
strategy: primaryStrategy,
|
|
4672
5020
|
topK: opts.topK,
|
|
4673
5021
|
minScore: opts.minScore,
|
|
@@ -4685,6 +5033,7 @@ var Retriever = class {
|
|
|
4685
5033
|
fallbackTrace.push(`stage:primary:${primaryStrategy}`);
|
|
4686
5034
|
if (fallbackEnabled && this.shouldFallback(current.matchResult, current.results) && primaryStrategy !== "deep") {
|
|
4687
5035
|
current = await this.runStage(query, {
|
|
5036
|
+
qualityQuery,
|
|
4688
5037
|
strategy: "deep",
|
|
4689
5038
|
topK: opts.topK,
|
|
4690
5039
|
minScore: opts.minScore,
|
|
@@ -4702,6 +5051,7 @@ var Retriever = class {
|
|
|
4702
5051
|
}
|
|
4703
5052
|
if (fallbackEnabled && this.shouldFallback(current.matchResult, current.results)) {
|
|
4704
5053
|
current = await this.runStage(query, {
|
|
5054
|
+
qualityQuery,
|
|
4705
5055
|
strategy: "deep",
|
|
4706
5056
|
topK: opts.topK,
|
|
4707
5057
|
minScore: Math.max(0.5, opts.minScore - 0.15),
|
|
@@ -4718,11 +5068,21 @@ var Retriever = class {
|
|
|
4718
5068
|
fallbackTrace.push("fallback:scope-expanded");
|
|
4719
5069
|
}
|
|
4720
5070
|
if (fallbackEnabled && this.shouldFallback(current.matchResult, current.results)) {
|
|
4721
|
-
const summary = await this.buildSummaryFallback(
|
|
5071
|
+
const summary = await this.buildSummaryFallback(qualityQuery, opts.topK);
|
|
5072
|
+
const scopedSummary = await this.applyScopeFilters(summary, {
|
|
5073
|
+
scope: opts.scope,
|
|
5074
|
+
projectScopeMode: opts.projectScopeMode,
|
|
5075
|
+
projectHash: opts.projectHash,
|
|
5076
|
+
allowedProjectHashes: opts.allowedProjectHashes
|
|
5077
|
+
});
|
|
5078
|
+
const filteredSummary = this.applyQualityFilters(scopedSummary, {
|
|
5079
|
+
query,
|
|
5080
|
+
minScore: opts.minScore
|
|
5081
|
+
});
|
|
4722
5082
|
current = {
|
|
4723
|
-
results:
|
|
4724
|
-
candidateResults:
|
|
4725
|
-
matchResult: this.matcher.matchSearchResults(
|
|
5083
|
+
results: filteredSummary,
|
|
5084
|
+
candidateResults: filteredSummary,
|
|
5085
|
+
matchResult: this.matcher.matchSearchResults(filteredSummary, () => 0)
|
|
4726
5086
|
};
|
|
4727
5087
|
fallbackTrace.push("fallback:summary");
|
|
4728
5088
|
}
|
|
@@ -4747,7 +5107,10 @@ var Retriever = class {
|
|
|
4747
5107
|
semanticScore: r.semanticScore,
|
|
4748
5108
|
lexicalScore: r.lexicalScore,
|
|
4749
5109
|
recencyScore: r.recencyScore
|
|
4750
|
-
}))
|
|
5110
|
+
})),
|
|
5111
|
+
rawQueryText: current.queryRewriteKind ? query : void 0,
|
|
5112
|
+
effectiveQueryText: current.effectiveQueryText,
|
|
5113
|
+
queryRewriteKind: current.queryRewriteKind
|
|
4751
5114
|
};
|
|
4752
5115
|
}
|
|
4753
5116
|
async retrieveUnified(query, options = {}) {
|
|
@@ -4785,8 +5148,11 @@ var Retriever = class {
|
|
|
4785
5148
|
}
|
|
4786
5149
|
}
|
|
4787
5150
|
async runStage(query, input) {
|
|
4788
|
-
|
|
4789
|
-
let
|
|
5151
|
+
const searchQuery = input.qualityQuery ?? query;
|
|
5152
|
+
let rerankQuery = searchQuery;
|
|
5153
|
+
let effectiveQueryText;
|
|
5154
|
+
let queryRewriteKind;
|
|
5155
|
+
let initialResults = await this.searchByStrategy(searchQuery, {
|
|
4790
5156
|
strategy: input.strategy,
|
|
4791
5157
|
topK: input.topK,
|
|
4792
5158
|
minScore: input.minScore,
|
|
@@ -4794,9 +5160,12 @@ var Retriever = class {
|
|
|
4794
5160
|
});
|
|
4795
5161
|
if (input.intentRewrite && input.strategy === "deep" && this.queryRewriter) {
|
|
4796
5162
|
const rewritten = (await this.queryRewriter(query))?.trim();
|
|
4797
|
-
|
|
4798
|
-
|
|
4799
|
-
|
|
5163
|
+
const normalizedQuery = query.trim();
|
|
5164
|
+
if (rewritten && rewritten !== normalizedQuery) {
|
|
5165
|
+
effectiveQueryText = `${normalizedQuery} ${rewritten}`.trim();
|
|
5166
|
+
queryRewriteKind = "intent-rewrite";
|
|
5167
|
+
rerankQuery = buildRetrievalQualityQuery(effectiveQueryText);
|
|
5168
|
+
const rewrittenResults = await this.searchByStrategy(buildRetrievalQualityQuery(rewritten), {
|
|
4800
5169
|
strategy: "deep",
|
|
4801
5170
|
topK: input.topK,
|
|
4802
5171
|
minScore: Math.max(0.5, input.minScore - 0.1),
|
|
@@ -4823,10 +5192,14 @@ var Retriever = class {
|
|
|
4823
5192
|
});
|
|
4824
5193
|
const top = qualityFiltered.slice(0, input.topK);
|
|
4825
5194
|
const matchResult = this.matcher.matchSearchResults(top, () => 0);
|
|
4826
|
-
return { results: top, candidateResults: qualityFiltered, matchResult };
|
|
5195
|
+
return { results: top, candidateResults: qualityFiltered, matchResult, effectiveQueryText, queryRewriteKind };
|
|
4827
5196
|
}
|
|
4828
5197
|
applyQualityFilters(results, options) {
|
|
4829
5198
|
let filtered = [...results];
|
|
5199
|
+
if (isCurrentStateQuery(options.query)) {
|
|
5200
|
+
filtered = filtered.filter((result) => !isStaleOrSupersededContent(result.content));
|
|
5201
|
+
}
|
|
5202
|
+
filtered = filtered.filter((result) => hasDiscriminativeTermOverlap(options.query, result.content));
|
|
4830
5203
|
if (shouldApplyTechnicalGuard(options.query)) {
|
|
4831
5204
|
filtered = filtered.filter((result) => hasTechnicalTermOverlap(options.query, result.content));
|
|
4832
5205
|
}
|
|
@@ -5134,7 +5507,21 @@ _Context:_ ${sessionContext}`;
|
|
|
5134
5507
|
});
|
|
5135
5508
|
}
|
|
5136
5509
|
tokenize(text) {
|
|
5137
|
-
return text.toLowerCase().replace(/[^\p{L}\p{N}\s]/gu, " ").split(/\s+/).filter((t) => t.length >= 2).slice(0, 64);
|
|
5510
|
+
return text.replace(/([a-z])([A-Z])/g, "$1 $2").toLowerCase().replace(/[^\p{L}\p{N}\s]/gu, " ").split(/\s+/).map((token) => this.normalizeToken(token)).filter((t) => t.length >= 2).slice(0, 64);
|
|
5511
|
+
}
|
|
5512
|
+
normalizeToken(token) {
|
|
5513
|
+
if (token === "apis")
|
|
5514
|
+
return "api";
|
|
5515
|
+
if (token === "ids")
|
|
5516
|
+
return "id";
|
|
5517
|
+
if (token === "does")
|
|
5518
|
+
return token;
|
|
5519
|
+
if (token.length > 4 && token.endsWith("ies"))
|
|
5520
|
+
return `${token.slice(0, -3)}y`;
|
|
5521
|
+
if (token.length > 3 && token.endsWith("s") && !token.endsWith("ss") && !token.endsWith("us") && !token.endsWith("is") && !token.endsWith("ps")) {
|
|
5522
|
+
return token.slice(0, -1);
|
|
5523
|
+
}
|
|
5524
|
+
return token;
|
|
5138
5525
|
}
|
|
5139
5526
|
keywordOverlap(a, b) {
|
|
5140
5527
|
if (a.length === 0 || b.length === 0)
|
|
@@ -5197,9 +5584,9 @@ var RetrievalAnalyticsService = class {
|
|
|
5197
5584
|
await this.deps.initialize();
|
|
5198
5585
|
return this.deps.retrievalStore.getHelpfulMemories(limit);
|
|
5199
5586
|
}
|
|
5200
|
-
async getHelpfulnessStats() {
|
|
5587
|
+
async getHelpfulnessStats(since) {
|
|
5201
5588
|
await this.deps.initialize();
|
|
5202
|
-
return this.deps.retrievalStore.getHelpfulnessStats();
|
|
5589
|
+
return this.deps.retrievalStore.getHelpfulnessStats(since);
|
|
5203
5590
|
}
|
|
5204
5591
|
/**
|
|
5205
5592
|
* Extract topic keywords from event content (markdown headings and key terms).
|
|
@@ -5624,7 +6011,9 @@ var RetrievalOrchestrator = class {
|
|
|
5624
6011
|
await this.deps.traceStore.recordRetrievalTrace({
|
|
5625
6012
|
sessionId: options?.sessionId,
|
|
5626
6013
|
projectHash: projectHash || void 0,
|
|
5627
|
-
queryText: query,
|
|
6014
|
+
queryText: result.effectiveQueryText || query,
|
|
6015
|
+
rawQueryText: result.rawQueryText || (result.queryRewriteKind ? query : void 0),
|
|
6016
|
+
queryRewriteKind: result.queryRewriteKind || "none",
|
|
5628
6017
|
strategy: options?.strategy || "auto",
|
|
5629
6018
|
candidateEventIds,
|
|
5630
6019
|
selectedEventIds,
|
|
@@ -7614,8 +8003,8 @@ var MemoryService = class {
|
|
|
7614
8003
|
/**
|
|
7615
8004
|
* Get helpfulness statistics for dashboard
|
|
7616
8005
|
*/
|
|
7617
|
-
async getHelpfulnessStats() {
|
|
7618
|
-
return this.retrievalAnalyticsService.getHelpfulnessStats();
|
|
8006
|
+
async getHelpfulnessStats(since) {
|
|
8007
|
+
return this.retrievalAnalyticsService.getHelpfulnessStats(since);
|
|
7619
8008
|
}
|
|
7620
8009
|
/**
|
|
7621
8010
|
* Mark a consolidated memory as accessed
|