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
package/dist/core/index.js
CHANGED
|
@@ -1965,10 +1965,15 @@ function sqliteTransaction(db, fn) {
|
|
|
1965
1965
|
function toDateFromSQLite(value) {
|
|
1966
1966
|
if (value instanceof Date)
|
|
1967
1967
|
return value;
|
|
1968
|
-
if (typeof value === "string")
|
|
1969
|
-
return new Date(value);
|
|
1970
1968
|
if (typeof value === "number")
|
|
1971
1969
|
return new Date(value);
|
|
1970
|
+
if (typeof value === "string") {
|
|
1971
|
+
const trimmed = value.trim();
|
|
1972
|
+
if (/^\d{4}-\d{2}-\d{2}[ T]\d{2}:\d{2}:\d{2}(?:\.\d+)?$/.test(trimmed)) {
|
|
1973
|
+
return /* @__PURE__ */ new Date(trimmed.replace(" ", "T") + "Z");
|
|
1974
|
+
}
|
|
1975
|
+
return new Date(trimmed);
|
|
1976
|
+
}
|
|
1972
1977
|
return new Date(String(value));
|
|
1973
1978
|
}
|
|
1974
1979
|
function toSQLiteTimestamp(date) {
|
|
@@ -2034,6 +2039,13 @@ var MarkdownMirror2 = class {
|
|
|
2034
2039
|
};
|
|
2035
2040
|
|
|
2036
2041
|
// src/core/sqlite-event-store.ts
|
|
2042
|
+
function normalizeQueryRewriteKind(value) {
|
|
2043
|
+
const normalized = (value || "").trim().toLowerCase();
|
|
2044
|
+
if (normalized === "follow-up-context" || normalized === "intent-rewrite")
|
|
2045
|
+
return normalized;
|
|
2046
|
+
return "none";
|
|
2047
|
+
}
|
|
2048
|
+
var REWRITTEN_QUERY_REWRITE_KIND_SQL = `LOWER(TRIM(COALESCE(query_rewrite_kind, 'none'))) IN ('follow-up-context', 'intent-rewrite')`;
|
|
2037
2049
|
var SQLiteEventStore = class {
|
|
2038
2050
|
db;
|
|
2039
2051
|
initialized = false;
|
|
@@ -2294,6 +2306,8 @@ var SQLiteEventStore = class {
|
|
|
2294
2306
|
session_id TEXT,
|
|
2295
2307
|
project_hash TEXT,
|
|
2296
2308
|
query_text TEXT NOT NULL,
|
|
2309
|
+
raw_query_text TEXT,
|
|
2310
|
+
query_rewrite_kind TEXT,
|
|
2297
2311
|
strategy TEXT,
|
|
2298
2312
|
candidate_event_ids TEXT,
|
|
2299
2313
|
selected_event_ids TEXT,
|
|
@@ -2335,6 +2349,8 @@ var SQLiteEventStore = class {
|
|
|
2335
2349
|
CREATE INDEX IF NOT EXISTS idx_helpfulness_event ON memory_helpfulness(event_id);
|
|
2336
2350
|
CREATE INDEX IF NOT EXISTS idx_helpfulness_session ON memory_helpfulness(session_id);
|
|
2337
2351
|
CREATE INDEX IF NOT EXISTS idx_helpfulness_score ON memory_helpfulness(helpfulness_score DESC);
|
|
2352
|
+
CREATE INDEX IF NOT EXISTS idx_helpfulness_created_at ON memory_helpfulness(created_at);
|
|
2353
|
+
CREATE INDEX IF NOT EXISTS idx_helpfulness_measured_at ON memory_helpfulness(measured_at);
|
|
2338
2354
|
CREATE INDEX IF NOT EXISTS idx_retrieval_traces_created_at ON retrieval_traces(created_at DESC);
|
|
2339
2355
|
CREATE INDEX IF NOT EXISTS idx_retrieval_traces_project_hash ON retrieval_traces(project_hash);
|
|
2340
2356
|
CREATE INDEX IF NOT EXISTS idx_retrieval_traces_session_id ON retrieval_traces(session_id);
|
|
@@ -2368,6 +2384,18 @@ var SQLiteEventStore = class {
|
|
|
2368
2384
|
sqliteExec(this.db, `ALTER TABLE retrieval_traces ADD COLUMN candidate_details_json TEXT;`);
|
|
2369
2385
|
} catch {
|
|
2370
2386
|
}
|
|
2387
|
+
try {
|
|
2388
|
+
sqliteExec(this.db, `ALTER TABLE retrieval_traces ADD COLUMN raw_query_text TEXT;`);
|
|
2389
|
+
} catch {
|
|
2390
|
+
}
|
|
2391
|
+
try {
|
|
2392
|
+
sqliteExec(this.db, `ALTER TABLE retrieval_traces ADD COLUMN query_rewrite_kind TEXT;`);
|
|
2393
|
+
} catch {
|
|
2394
|
+
}
|
|
2395
|
+
try {
|
|
2396
|
+
sqliteExec(this.db, `CREATE INDEX IF NOT EXISTS idx_retrieval_traces_query_rewrite_kind ON retrieval_traces(query_rewrite_kind);`);
|
|
2397
|
+
} catch {
|
|
2398
|
+
}
|
|
2371
2399
|
const tableInfo = sqliteAll(this.db, "PRAGMA table_info(events)", []);
|
|
2372
2400
|
const columnNames = tableInfo.map((col) => col.name);
|
|
2373
2401
|
if (!columnNames.includes("access_count")) {
|
|
@@ -3153,8 +3181,11 @@ var SQLiteEventStore = class {
|
|
|
3153
3181
|
/**
|
|
3154
3182
|
* Get helpfulness statistics for dashboard
|
|
3155
3183
|
*/
|
|
3156
|
-
async getHelpfulnessStats() {
|
|
3184
|
+
async getHelpfulnessStats(since) {
|
|
3157
3185
|
await this.initialize();
|
|
3186
|
+
const sinceIso = since?.toISOString();
|
|
3187
|
+
const evaluatedWhere = sinceIso ? `WHERE measured_at IS NOT NULL AND datetime(created_at) >= datetime(?)` : `WHERE measured_at IS NOT NULL`;
|
|
3188
|
+
const totalWhere = sinceIso ? `WHERE datetime(created_at) >= datetime(?)` : ``;
|
|
3158
3189
|
const stats = sqliteGet(
|
|
3159
3190
|
this.db,
|
|
3160
3191
|
`SELECT
|
|
@@ -3164,11 +3195,13 @@ var SQLiteEventStore = class {
|
|
|
3164
3195
|
SUM(CASE WHEN helpfulness_score >= 0.4 AND helpfulness_score < 0.7 THEN 1 ELSE 0 END) as neutral,
|
|
3165
3196
|
SUM(CASE WHEN helpfulness_score < 0.4 THEN 1 ELSE 0 END) as unhelpful
|
|
3166
3197
|
FROM memory_helpfulness
|
|
3167
|
-
|
|
3198
|
+
${evaluatedWhere}`,
|
|
3199
|
+
sinceIso ? [sinceIso] : []
|
|
3168
3200
|
);
|
|
3169
3201
|
const totalRow = sqliteGet(
|
|
3170
3202
|
this.db,
|
|
3171
|
-
`SELECT COUNT(*) as total FROM memory_helpfulness
|
|
3203
|
+
`SELECT COUNT(*) as total FROM memory_helpfulness ${totalWhere}`,
|
|
3204
|
+
sinceIso ? [sinceIso] : []
|
|
3172
3205
|
);
|
|
3173
3206
|
return {
|
|
3174
3207
|
avgScore: Math.round((stats?.avg_score || 0) * 100) / 100,
|
|
@@ -3267,18 +3300,21 @@ var SQLiteEventStore = class {
|
|
|
3267
3300
|
async recordRetrievalTrace(input) {
|
|
3268
3301
|
await this.initialize();
|
|
3269
3302
|
const traceId = randomUUID();
|
|
3303
|
+
const queryRewriteKind = normalizeQueryRewriteKind(input.queryRewriteKind);
|
|
3270
3304
|
sqliteRun(
|
|
3271
3305
|
this.db,
|
|
3272
3306
|
`INSERT INTO retrieval_traces (
|
|
3273
|
-
trace_id, session_id, project_hash, query_text, strategy,
|
|
3307
|
+
trace_id, session_id, project_hash, query_text, raw_query_text, query_rewrite_kind, strategy,
|
|
3274
3308
|
candidate_event_ids, selected_event_ids, candidate_details_json, selected_details_json,
|
|
3275
3309
|
candidate_count, selected_count, confidence, fallback_trace
|
|
3276
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
3310
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
3277
3311
|
[
|
|
3278
3312
|
traceId,
|
|
3279
3313
|
input.sessionId || null,
|
|
3280
3314
|
input.projectHash || null,
|
|
3281
3315
|
input.queryText,
|
|
3316
|
+
input.rawQueryText || null,
|
|
3317
|
+
queryRewriteKind,
|
|
3282
3318
|
input.strategy || null,
|
|
3283
3319
|
JSON.stringify(input.candidateEventIds || []),
|
|
3284
3320
|
JSON.stringify(input.selectedEventIds || []),
|
|
@@ -3304,6 +3340,8 @@ var SQLiteEventStore = class {
|
|
|
3304
3340
|
sessionId: row.session_id || void 0,
|
|
3305
3341
|
projectHash: row.project_hash || void 0,
|
|
3306
3342
|
queryText: row.query_text,
|
|
3343
|
+
rawQueryText: row.raw_query_text || void 0,
|
|
3344
|
+
queryRewriteKind: normalizeQueryRewriteKind(row.query_rewrite_kind),
|
|
3307
3345
|
strategy: row.strategy || void 0,
|
|
3308
3346
|
candidateEventIds: row.candidate_event_ids ? JSON.parse(row.candidate_event_ids) : [],
|
|
3309
3347
|
selectedEventIds: row.selected_event_ids ? JSON.parse(row.selected_event_ids) : [],
|
|
@@ -3330,6 +3368,11 @@ var SQLiteEventStore = class {
|
|
|
3330
3368
|
COUNT(*) as total_queries,
|
|
3331
3369
|
AVG(candidate_count) as avg_candidate_count,
|
|
3332
3370
|
AVG(selected_count) as avg_selected_count,
|
|
3371
|
+
SUM(CASE WHEN ${REWRITTEN_QUERY_REWRITE_KIND_SQL} THEN 1 ELSE 0 END) as rewritten_queries,
|
|
3372
|
+
SUM(CASE WHEN ${REWRITTEN_QUERY_REWRITE_KIND_SQL} AND selected_count > 0 THEN 1 ELSE 0 END) as rewritten_queries_with_selection,
|
|
3373
|
+
SUM(CASE WHEN NOT (${REWRITTEN_QUERY_REWRITE_KIND_SQL}) AND selected_count > 0 THEN 1 ELSE 0 END) as raw_queries_with_selection,
|
|
3374
|
+
AVG(CASE WHEN ${REWRITTEN_QUERY_REWRITE_KIND_SQL} THEN selected_count END) as avg_selected_count_for_rewritten_queries,
|
|
3375
|
+
AVG(CASE WHEN NOT (${REWRITTEN_QUERY_REWRITE_KIND_SQL}) THEN selected_count END) as avg_selected_count_for_raw_queries,
|
|
3333
3376
|
CASE
|
|
3334
3377
|
WHEN SUM(candidate_count) > 0 THEN (SUM(selected_count) * 1.0 / SUM(candidate_count))
|
|
3335
3378
|
ELSE 0
|
|
@@ -3337,15 +3380,41 @@ var SQLiteEventStore = class {
|
|
|
3337
3380
|
FROM retrieval_traces`,
|
|
3338
3381
|
[]
|
|
3339
3382
|
);
|
|
3383
|
+
const totalQueries = Number(row?.total_queries || 0);
|
|
3384
|
+
const rewrittenQueries = Number(row?.rewritten_queries || 0);
|
|
3385
|
+
const rawQueries = Math.max(0, totalQueries - rewrittenQueries);
|
|
3386
|
+
const rewrittenQueriesWithSelection = Number(row?.rewritten_queries_with_selection || 0);
|
|
3387
|
+
const rawQueriesWithSelection = Number(row?.raw_queries_with_selection || 0);
|
|
3340
3388
|
return {
|
|
3341
|
-
totalQueries
|
|
3389
|
+
totalQueries,
|
|
3342
3390
|
avgCandidateCount: Number(row?.avg_candidate_count || 0),
|
|
3343
3391
|
avgSelectedCount: Number(row?.avg_selected_count || 0),
|
|
3344
|
-
selectionRate: Number(row?.selection_rate || 0)
|
|
3392
|
+
selectionRate: Number(row?.selection_rate || 0),
|
|
3393
|
+
rewrittenQueries,
|
|
3394
|
+
rewriteRate: totalQueries > 0 ? rewrittenQueries / totalQueries : 0,
|
|
3395
|
+
rewrittenQueriesWithSelection,
|
|
3396
|
+
rawQueriesWithSelection,
|
|
3397
|
+
rewrittenSelectionRate: rewrittenQueries > 0 ? rewrittenQueriesWithSelection / rewrittenQueries : 0,
|
|
3398
|
+
rawSelectionRate: rawQueries > 0 ? rawQueriesWithSelection / rawQueries : 0,
|
|
3399
|
+
avgSelectedCountForRewrittenQueries: Number(row?.avg_selected_count_for_rewritten_queries || 0),
|
|
3400
|
+
avgSelectedCountForRawQueries: Number(row?.avg_selected_count_for_raw_queries || 0)
|
|
3345
3401
|
};
|
|
3346
3402
|
} catch (err) {
|
|
3347
3403
|
if (err?.message?.includes("no such table")) {
|
|
3348
|
-
return {
|
|
3404
|
+
return {
|
|
3405
|
+
totalQueries: 0,
|
|
3406
|
+
avgCandidateCount: 0,
|
|
3407
|
+
avgSelectedCount: 0,
|
|
3408
|
+
selectionRate: 0,
|
|
3409
|
+
rewrittenQueries: 0,
|
|
3410
|
+
rewriteRate: 0,
|
|
3411
|
+
rewrittenQueriesWithSelection: 0,
|
|
3412
|
+
rawQueriesWithSelection: 0,
|
|
3413
|
+
rewrittenSelectionRate: 0,
|
|
3414
|
+
rawSelectionRate: 0,
|
|
3415
|
+
avgSelectedCountForRewrittenQueries: 0,
|
|
3416
|
+
avgSelectedCountForRawQueries: 0
|
|
3417
|
+
};
|
|
3349
3418
|
}
|
|
3350
3419
|
throw err;
|
|
3351
3420
|
}
|
|
@@ -3697,6 +3766,57 @@ var COMMAND_ARTIFACT_PATTERNS = [
|
|
|
3697
3766
|
/<local-command-stdout>[\s\S]*?<\/local-command-stdout>/i,
|
|
3698
3767
|
/<local-command-stderr>[\s\S]*?<\/local-command-stderr>/i
|
|
3699
3768
|
];
|
|
3769
|
+
var CONTINUATION_QUERY_PATTERNS = [
|
|
3770
|
+
/^\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,
|
|
3771
|
+
/^\s*(?:응\s*)?(?:이어서(?:\s*진행(?:해줘)?)?|계속(?:\s*해줘)?|다음\s*(?:단계|작업|추천\s*작업|추천|할\s*일)?(?:은|는)?(?:\s*(?:뭐야|진행(?:해줘)?))?\??|남은\s*(?:추가(?:로)?\s*)?(?:(?:할\s*만한\s*)?(?:작업|일)|할\s*일)?(?:은|는)?\s*(?:있어|있나|있나요|뭐야)\??|추천\s*작업(?:은|는)?(?:\s*뭐야)?\??|진행해줘)\s*$/i
|
|
3772
|
+
];
|
|
3773
|
+
var SHORT_REPAIR_FOLLOW_UP_PATTERNS = [
|
|
3774
|
+
/^\s*(?:fix\s+(?:it|that)|repair\s+(?:it|that)|resolve\s+(?:it|that)|that\s+bug|same\s+issue)\s*$/i,
|
|
3775
|
+
/^\s*(?:그거|그것|이거|이것)?\s*(?:고쳐줘|수정해줘|해결해줘|처리해줘)\s*$/i
|
|
3776
|
+
];
|
|
3777
|
+
var CURRENT_STATE_QUERY_PATTERNS = [
|
|
3778
|
+
/\bcurrent\b.*\b(?:state|status|deployment|blocker|pr|pull request)\b/i,
|
|
3779
|
+
/\b(?:still|as current|current)\b.*\b(?:unresolved|open|pending|not completed)\b/i,
|
|
3780
|
+
/\b(?:old|obsolete|stale|resolved|already resolved)\b.*\b(?:current|still|unresolved|open|state|status)\b/i,
|
|
3781
|
+
/(?:현재|아직|이전|오래된|해결된).*(?:상태|미해결|열린|블로커|PR|풀리퀘스트)/i
|
|
3782
|
+
];
|
|
3783
|
+
var STALE_CONTENT_PATTERNS = [
|
|
3784
|
+
/\b(?:obsolete|superseded|outdated)\b/i,
|
|
3785
|
+
/\bstale\s+(?:operational\s+)?state\b/i,
|
|
3786
|
+
/\bstale\s+after\b/i,
|
|
3787
|
+
/\bno\s+longer\s+(?:valid|current|applies?)\b/i,
|
|
3788
|
+
/\bearlier\s+(?:pull request|pr)\b[\s\S]{0,160}\b(?:open|not completed|had not completed)\b/i,
|
|
3789
|
+
/\bshould\s+not\s+be\s+injected\s+as\s+current\s+context\b/i,
|
|
3790
|
+
/(?:오래된|더 이상 유효하지|현재 상태가 아님)/i
|
|
3791
|
+
];
|
|
3792
|
+
var CONTINUATION_EXPANSION = "current next step plan roadmap status validation replay rerank memory usefulness continuation";
|
|
3793
|
+
var REPAIR_FOLLOW_UP_EXPANSION = "review blocker fix pattern dashboard error state metrics bucket validation sanitize rerun unresolved";
|
|
3794
|
+
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";
|
|
3795
|
+
var DECISION_RECALL_TERMS = /* @__PURE__ */ new Set([
|
|
3796
|
+
"decide",
|
|
3797
|
+
"decided",
|
|
3798
|
+
"decision",
|
|
3799
|
+
"agreed",
|
|
3800
|
+
"policy",
|
|
3801
|
+
"constraint"
|
|
3802
|
+
]);
|
|
3803
|
+
var RETRIEVAL_PRIVACY_SURFACE_TERMS = /* @__PURE__ */ new Set([
|
|
3804
|
+
"retrieval",
|
|
3805
|
+
"dashboard",
|
|
3806
|
+
"telemetry",
|
|
3807
|
+
"trace"
|
|
3808
|
+
]);
|
|
3809
|
+
var DECISION_TOPIC_WEAK_TERMS = /* @__PURE__ */ new Set([
|
|
3810
|
+
"api",
|
|
3811
|
+
"dashboard",
|
|
3812
|
+
"retrieval",
|
|
3813
|
+
"trace",
|
|
3814
|
+
"telemetry",
|
|
3815
|
+
"query",
|
|
3816
|
+
"raw",
|
|
3817
|
+
"count",
|
|
3818
|
+
"counts"
|
|
3819
|
+
]);
|
|
3700
3820
|
var GENERIC_TECHNICAL_TERMS = /* @__PURE__ */ new Set([
|
|
3701
3821
|
"api",
|
|
3702
3822
|
"cli",
|
|
@@ -3714,6 +3834,87 @@ var GENERIC_TECHNICAL_TERMS = /* @__PURE__ */ new Set([
|
|
|
3714
3834
|
"db",
|
|
3715
3835
|
"sql"
|
|
3716
3836
|
]);
|
|
3837
|
+
var LOW_INFORMATION_QUERY_TERMS = /* @__PURE__ */ new Set([
|
|
3838
|
+
"the",
|
|
3839
|
+
"and",
|
|
3840
|
+
"or",
|
|
3841
|
+
"for",
|
|
3842
|
+
"from",
|
|
3843
|
+
"with",
|
|
3844
|
+
"without",
|
|
3845
|
+
"about",
|
|
3846
|
+
"what",
|
|
3847
|
+
"when",
|
|
3848
|
+
"where",
|
|
3849
|
+
"which",
|
|
3850
|
+
"who",
|
|
3851
|
+
"why",
|
|
3852
|
+
"how",
|
|
3853
|
+
"did",
|
|
3854
|
+
"does",
|
|
3855
|
+
"do",
|
|
3856
|
+
"we",
|
|
3857
|
+
"i",
|
|
3858
|
+
"in",
|
|
3859
|
+
"to",
|
|
3860
|
+
"of",
|
|
3861
|
+
"on",
|
|
3862
|
+
"as",
|
|
3863
|
+
"be",
|
|
3864
|
+
"was",
|
|
3865
|
+
"were",
|
|
3866
|
+
"decide",
|
|
3867
|
+
"decided",
|
|
3868
|
+
"decision",
|
|
3869
|
+
"agreed",
|
|
3870
|
+
"policy",
|
|
3871
|
+
"constraint",
|
|
3872
|
+
"showing",
|
|
3873
|
+
"can",
|
|
3874
|
+
"you",
|
|
3875
|
+
"me",
|
|
3876
|
+
"show",
|
|
3877
|
+
"tell",
|
|
3878
|
+
"please",
|
|
3879
|
+
"should",
|
|
3880
|
+
"would",
|
|
3881
|
+
"could",
|
|
3882
|
+
"this",
|
|
3883
|
+
"that",
|
|
3884
|
+
"these",
|
|
3885
|
+
"those",
|
|
3886
|
+
"use",
|
|
3887
|
+
"using",
|
|
3888
|
+
"treat",
|
|
3889
|
+
"continue",
|
|
3890
|
+
"resume",
|
|
3891
|
+
"next",
|
|
3892
|
+
"step",
|
|
3893
|
+
"task",
|
|
3894
|
+
"action",
|
|
3895
|
+
"current",
|
|
3896
|
+
"state",
|
|
3897
|
+
"status",
|
|
3898
|
+
"old",
|
|
3899
|
+
"already",
|
|
3900
|
+
"still",
|
|
3901
|
+
"near",
|
|
3902
|
+
"today",
|
|
3903
|
+
"\uC751",
|
|
3904
|
+
"\uADF8\uAC70",
|
|
3905
|
+
"\uADF8\uAC83",
|
|
3906
|
+
"\uC774\uAC70",
|
|
3907
|
+
"\uC774\uAC83",
|
|
3908
|
+
"\uB2E4\uC74C",
|
|
3909
|
+
"\uB2E8\uACC4",
|
|
3910
|
+
"\uC9C4\uD589",
|
|
3911
|
+
"\uC9C4\uD589\uD574\uC918",
|
|
3912
|
+
"\uACC4\uC18D",
|
|
3913
|
+
"\uC774\uC5B4\uC11C",
|
|
3914
|
+
"\uACE0\uCCD0\uC918",
|
|
3915
|
+
"\uC218\uC815\uD574\uC918",
|
|
3916
|
+
"\uD574\uACB0\uD574\uC918"
|
|
3917
|
+
]);
|
|
3717
3918
|
function isCommandArtifactQuery(query) {
|
|
3718
3919
|
const trimmed = query.trim();
|
|
3719
3920
|
if (!trimmed)
|
|
@@ -3725,6 +3926,73 @@ function isCommandArtifactQuery(query) {
|
|
|
3725
3926
|
return true;
|
|
3726
3927
|
return COMMAND_ARTIFACT_PATTERNS.some((pattern) => pattern.test(trimmed));
|
|
3727
3928
|
}
|
|
3929
|
+
function isGenericContinuationQuery(query) {
|
|
3930
|
+
const trimmed = query.trim();
|
|
3931
|
+
if (!trimmed)
|
|
3932
|
+
return false;
|
|
3933
|
+
if (!CONTINUATION_QUERY_PATTERNS.some((pattern) => pattern.test(trimmed)))
|
|
3934
|
+
return false;
|
|
3935
|
+
if (extractTechnicalQueryTerms(trimmed).length > 0)
|
|
3936
|
+
return false;
|
|
3937
|
+
const tokens = trimmed.match(/[A-Za-z0-9가-힣#._/-]+/g) ?? [];
|
|
3938
|
+
if (tokens.length > 10)
|
|
3939
|
+
return false;
|
|
3940
|
+
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);
|
|
3941
|
+
}
|
|
3942
|
+
function isShortRepairFollowUpQuery(query) {
|
|
3943
|
+
const trimmed = query.trim();
|
|
3944
|
+
if (!trimmed)
|
|
3945
|
+
return false;
|
|
3946
|
+
if (extractTechnicalQueryTerms(trimmed).length > 0)
|
|
3947
|
+
return false;
|
|
3948
|
+
const tokens = trimmed.match(/[A-Za-z0-9가-힣#._/-]+/g) ?? [];
|
|
3949
|
+
if (tokens.length > 8)
|
|
3950
|
+
return false;
|
|
3951
|
+
return SHORT_REPAIR_FOLLOW_UP_PATTERNS.some((pattern) => pattern.test(trimmed));
|
|
3952
|
+
}
|
|
3953
|
+
function isCurrentStateQuery(query) {
|
|
3954
|
+
const trimmed = query.trim();
|
|
3955
|
+
if (!trimmed)
|
|
3956
|
+
return false;
|
|
3957
|
+
return CURRENT_STATE_QUERY_PATTERNS.some((pattern) => pattern.test(trimmed));
|
|
3958
|
+
}
|
|
3959
|
+
function isStaleOrSupersededContent(content) {
|
|
3960
|
+
const trimmed = content.trim();
|
|
3961
|
+
if (!trimmed)
|
|
3962
|
+
return false;
|
|
3963
|
+
return STALE_CONTENT_PATTERNS.some((pattern) => pattern.test(trimmed));
|
|
3964
|
+
}
|
|
3965
|
+
function buildRetrievalQualityQuery(query) {
|
|
3966
|
+
const trimmed = query.trim();
|
|
3967
|
+
if (!trimmed)
|
|
3968
|
+
return query;
|
|
3969
|
+
if (isRetrievalPrivacyDecisionQuery(trimmed)) {
|
|
3970
|
+
return `${trimmed} ${RETRIEVAL_PRIVACY_DECISION_EXPANSION}`;
|
|
3971
|
+
}
|
|
3972
|
+
if (isGenericContinuationQuery(trimmed)) {
|
|
3973
|
+
return `${trimmed} ${CONTINUATION_EXPANSION}`;
|
|
3974
|
+
}
|
|
3975
|
+
if (isShortRepairFollowUpQuery(trimmed)) {
|
|
3976
|
+
return `${trimmed} ${REPAIR_FOLLOW_UP_EXPANSION}`;
|
|
3977
|
+
}
|
|
3978
|
+
return query;
|
|
3979
|
+
}
|
|
3980
|
+
function isRetrievalPrivacyDecisionQuery(query) {
|
|
3981
|
+
const trimmed = query.trim();
|
|
3982
|
+
if (!trimmed)
|
|
3983
|
+
return false;
|
|
3984
|
+
const terms = new Set(tokenizeQualityText(trimmed));
|
|
3985
|
+
const hasDecisionSignal = hasAnyTerm(terms, DECISION_RECALL_TERMS) || /(?:결정|정책|원칙)/i.test(trimmed);
|
|
3986
|
+
if (!hasDecisionSignal)
|
|
3987
|
+
return false;
|
|
3988
|
+
const hasRawQuerySignal = terms.has("raw") && terms.has("query");
|
|
3989
|
+
const hasPrivacySignal = terms.has("privacy") || terms.has("expose") || terms.has("redacted");
|
|
3990
|
+
const hasRetrievalSurface = hasAnyTerm(terms, RETRIEVAL_PRIVACY_SURFACE_TERMS) || terms.has("api") && terms.has("query");
|
|
3991
|
+
const hasQuerySurface = terms.has("query") && (terms.has("dashboard") || terms.has("trace") || terms.has("telemetry") || terms.has("api"));
|
|
3992
|
+
const hasKoreanRetrievalSurface = /(?:검색|리트리벌|retrieval|대시보드|트레이스|텔레메트리|telemetry)/i.test(trimmed);
|
|
3993
|
+
const hasKoreanPrivacySurface = /(?:원문|쿼리|프라이버시|개인정보|노출|트레이스|메타데이터)/i.test(trimmed);
|
|
3994
|
+
return (hasRetrievalSurface || hasKoreanRetrievalSurface && hasKoreanPrivacySurface) && (hasRawQuerySignal || hasPrivacySignal || hasQuerySurface || hasKoreanPrivacySurface);
|
|
3995
|
+
}
|
|
3728
3996
|
function extractTechnicalQueryTerms(query) {
|
|
3729
3997
|
const matches = query.match(/[A-Za-z][A-Za-z0-9_.:-]{2,}/g) ?? [];
|
|
3730
3998
|
const terms = matches.filter((term) => {
|
|
@@ -3742,9 +4010,87 @@ function hasTechnicalTermOverlap(query, content) {
|
|
|
3742
4010
|
const normalizedContent = content.toLowerCase();
|
|
3743
4011
|
return terms.some((term) => normalizedContent.includes(term));
|
|
3744
4012
|
}
|
|
4013
|
+
function hasDiscriminativeTermOverlap(query, content) {
|
|
4014
|
+
const queryTerms = extractDiscriminativeQueryTerms(query);
|
|
4015
|
+
const contentTerms = new Set(tokenizeQualityText(content));
|
|
4016
|
+
if (isRetrievalPrivacyDecisionQuery(query) && hasRetrievalPrivacyDecisionContent(contentTerms)) {
|
|
4017
|
+
return true;
|
|
4018
|
+
}
|
|
4019
|
+
if (shouldRequireDecisionTopicOverlap(query)) {
|
|
4020
|
+
const topicTerms = queryTerms.filter((term) => !DECISION_TOPIC_WEAK_TERMS.has(term));
|
|
4021
|
+
if (topicTerms.length > 0) {
|
|
4022
|
+
return topicTerms.some((term) => contentTerms.has(term));
|
|
4023
|
+
}
|
|
4024
|
+
}
|
|
4025
|
+
if (queryTerms.length < 3)
|
|
4026
|
+
return true;
|
|
4027
|
+
const requiredHits = queryTerms.length >= 3 ? 2 : 1;
|
|
4028
|
+
let hits = 0;
|
|
4029
|
+
for (const term of queryTerms) {
|
|
4030
|
+
if (contentTerms.has(term))
|
|
4031
|
+
hits += 1;
|
|
4032
|
+
if (hits >= requiredHits)
|
|
4033
|
+
return true;
|
|
4034
|
+
}
|
|
4035
|
+
return false;
|
|
4036
|
+
}
|
|
3745
4037
|
function shouldApplyTechnicalGuard(query) {
|
|
3746
4038
|
return extractTechnicalQueryTerms(query).length > 0;
|
|
3747
4039
|
}
|
|
4040
|
+
function hasAnyTerm(terms, expectedTerms) {
|
|
4041
|
+
let found = false;
|
|
4042
|
+
expectedTerms.forEach((term) => {
|
|
4043
|
+
if (terms.has(term))
|
|
4044
|
+
found = true;
|
|
4045
|
+
});
|
|
4046
|
+
return found;
|
|
4047
|
+
}
|
|
4048
|
+
function shouldRequireDecisionTopicOverlap(query) {
|
|
4049
|
+
if (isRetrievalPrivacyDecisionQuery(query))
|
|
4050
|
+
return false;
|
|
4051
|
+
const trimmed = query.trim();
|
|
4052
|
+
if (!trimmed)
|
|
4053
|
+
return false;
|
|
4054
|
+
const terms = new Set(tokenizeQualityText(trimmed));
|
|
4055
|
+
return hasAnyTerm(terms, DECISION_RECALL_TERMS) || /(?:결정|정책|원칙)/i.test(trimmed);
|
|
4056
|
+
}
|
|
4057
|
+
function extractDiscriminativeQueryTerms(query) {
|
|
4058
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4059
|
+
const terms = [];
|
|
4060
|
+
for (const token of tokenizeQualityText(query)) {
|
|
4061
|
+
if (LOW_INFORMATION_QUERY_TERMS.has(token))
|
|
4062
|
+
continue;
|
|
4063
|
+
if (GENERIC_TECHNICAL_TERMS.has(token))
|
|
4064
|
+
continue;
|
|
4065
|
+
if (seen.has(token))
|
|
4066
|
+
continue;
|
|
4067
|
+
seen.add(token);
|
|
4068
|
+
terms.push(token);
|
|
4069
|
+
}
|
|
4070
|
+
return terms;
|
|
4071
|
+
}
|
|
4072
|
+
function hasRetrievalPrivacyDecisionContent(contentTerms) {
|
|
4073
|
+
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"));
|
|
4074
|
+
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")));
|
|
4075
|
+
return hasDashboardTraceMetadata || hasRawQueryPrivacyPolicy;
|
|
4076
|
+
}
|
|
4077
|
+
function tokenizeQualityText(text) {
|
|
4078
|
+
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);
|
|
4079
|
+
}
|
|
4080
|
+
function normalizeQualityToken(token) {
|
|
4081
|
+
if (token === "apis")
|
|
4082
|
+
return "api";
|
|
4083
|
+
if (token === "ids")
|
|
4084
|
+
return "id";
|
|
4085
|
+
if (LOW_INFORMATION_QUERY_TERMS.has(token) || GENERIC_TECHNICAL_TERMS.has(token))
|
|
4086
|
+
return token;
|
|
4087
|
+
if (token.length > 4 && token.endsWith("ies"))
|
|
4088
|
+
return `${token.slice(0, -3)}y`;
|
|
4089
|
+
if (token.length > 3 && token.endsWith("s") && !token.endsWith("ss") && !token.endsWith("us") && !token.endsWith("is")) {
|
|
4090
|
+
return token.slice(0, -1);
|
|
4091
|
+
}
|
|
4092
|
+
return token;
|
|
4093
|
+
}
|
|
3748
4094
|
|
|
3749
4095
|
// src/core/retriever.ts
|
|
3750
4096
|
var DEFAULT_OPTIONS = {
|
|
@@ -3797,6 +4143,7 @@ var Retriever = class {
|
|
|
3797
4143
|
const opts = { ...DEFAULT_OPTIONS, ...options };
|
|
3798
4144
|
const sessionFilter = opts.scope?.sessionId ?? opts.sessionId;
|
|
3799
4145
|
const fallbackTrace = [];
|
|
4146
|
+
const qualityQuery = buildRetrievalQualityQuery(query);
|
|
3800
4147
|
if (isCommandArtifactQuery(query)) {
|
|
3801
4148
|
fallbackTrace.push("guard:command-artifact-query");
|
|
3802
4149
|
const emptyMatch = this.matcher.matchSearchResults([], () => 0);
|
|
@@ -3813,6 +4160,7 @@ var Retriever = class {
|
|
|
3813
4160
|
const fallbackEnabled = (opts.strategy ?? "auto") === "auto";
|
|
3814
4161
|
const primaryStrategy = opts.strategy === "auto" ? "fast" : opts.strategy || "fast";
|
|
3815
4162
|
let current = await this.runStage(query, {
|
|
4163
|
+
qualityQuery,
|
|
3816
4164
|
strategy: primaryStrategy,
|
|
3817
4165
|
topK: opts.topK,
|
|
3818
4166
|
minScore: opts.minScore,
|
|
@@ -3830,6 +4178,7 @@ var Retriever = class {
|
|
|
3830
4178
|
fallbackTrace.push(`stage:primary:${primaryStrategy}`);
|
|
3831
4179
|
if (fallbackEnabled && this.shouldFallback(current.matchResult, current.results) && primaryStrategy !== "deep") {
|
|
3832
4180
|
current = await this.runStage(query, {
|
|
4181
|
+
qualityQuery,
|
|
3833
4182
|
strategy: "deep",
|
|
3834
4183
|
topK: opts.topK,
|
|
3835
4184
|
minScore: opts.minScore,
|
|
@@ -3847,6 +4196,7 @@ var Retriever = class {
|
|
|
3847
4196
|
}
|
|
3848
4197
|
if (fallbackEnabled && this.shouldFallback(current.matchResult, current.results)) {
|
|
3849
4198
|
current = await this.runStage(query, {
|
|
4199
|
+
qualityQuery,
|
|
3850
4200
|
strategy: "deep",
|
|
3851
4201
|
topK: opts.topK,
|
|
3852
4202
|
minScore: Math.max(0.5, opts.minScore - 0.15),
|
|
@@ -3863,11 +4213,21 @@ var Retriever = class {
|
|
|
3863
4213
|
fallbackTrace.push("fallback:scope-expanded");
|
|
3864
4214
|
}
|
|
3865
4215
|
if (fallbackEnabled && this.shouldFallback(current.matchResult, current.results)) {
|
|
3866
|
-
const summary = await this.buildSummaryFallback(
|
|
4216
|
+
const summary = await this.buildSummaryFallback(qualityQuery, opts.topK);
|
|
4217
|
+
const scopedSummary = await this.applyScopeFilters(summary, {
|
|
4218
|
+
scope: opts.scope,
|
|
4219
|
+
projectScopeMode: opts.projectScopeMode,
|
|
4220
|
+
projectHash: opts.projectHash,
|
|
4221
|
+
allowedProjectHashes: opts.allowedProjectHashes
|
|
4222
|
+
});
|
|
4223
|
+
const filteredSummary = this.applyQualityFilters(scopedSummary, {
|
|
4224
|
+
query,
|
|
4225
|
+
minScore: opts.minScore
|
|
4226
|
+
});
|
|
3867
4227
|
current = {
|
|
3868
|
-
results:
|
|
3869
|
-
candidateResults:
|
|
3870
|
-
matchResult: this.matcher.matchSearchResults(
|
|
4228
|
+
results: filteredSummary,
|
|
4229
|
+
candidateResults: filteredSummary,
|
|
4230
|
+
matchResult: this.matcher.matchSearchResults(filteredSummary, () => 0)
|
|
3871
4231
|
};
|
|
3872
4232
|
fallbackTrace.push("fallback:summary");
|
|
3873
4233
|
}
|
|
@@ -3892,7 +4252,10 @@ var Retriever = class {
|
|
|
3892
4252
|
semanticScore: r.semanticScore,
|
|
3893
4253
|
lexicalScore: r.lexicalScore,
|
|
3894
4254
|
recencyScore: r.recencyScore
|
|
3895
|
-
}))
|
|
4255
|
+
})),
|
|
4256
|
+
rawQueryText: current.queryRewriteKind ? query : void 0,
|
|
4257
|
+
effectiveQueryText: current.effectiveQueryText,
|
|
4258
|
+
queryRewriteKind: current.queryRewriteKind
|
|
3896
4259
|
};
|
|
3897
4260
|
}
|
|
3898
4261
|
async retrieveUnified(query, options = {}) {
|
|
@@ -3930,8 +4293,11 @@ var Retriever = class {
|
|
|
3930
4293
|
}
|
|
3931
4294
|
}
|
|
3932
4295
|
async runStage(query, input) {
|
|
3933
|
-
|
|
3934
|
-
let
|
|
4296
|
+
const searchQuery = input.qualityQuery ?? query;
|
|
4297
|
+
let rerankQuery = searchQuery;
|
|
4298
|
+
let effectiveQueryText;
|
|
4299
|
+
let queryRewriteKind;
|
|
4300
|
+
let initialResults = await this.searchByStrategy(searchQuery, {
|
|
3935
4301
|
strategy: input.strategy,
|
|
3936
4302
|
topK: input.topK,
|
|
3937
4303
|
minScore: input.minScore,
|
|
@@ -3939,9 +4305,12 @@ var Retriever = class {
|
|
|
3939
4305
|
});
|
|
3940
4306
|
if (input.intentRewrite && input.strategy === "deep" && this.queryRewriter) {
|
|
3941
4307
|
const rewritten = (await this.queryRewriter(query))?.trim();
|
|
3942
|
-
|
|
3943
|
-
|
|
3944
|
-
|
|
4308
|
+
const normalizedQuery = query.trim();
|
|
4309
|
+
if (rewritten && rewritten !== normalizedQuery) {
|
|
4310
|
+
effectiveQueryText = `${normalizedQuery} ${rewritten}`.trim();
|
|
4311
|
+
queryRewriteKind = "intent-rewrite";
|
|
4312
|
+
rerankQuery = buildRetrievalQualityQuery(effectiveQueryText);
|
|
4313
|
+
const rewrittenResults = await this.searchByStrategy(buildRetrievalQualityQuery(rewritten), {
|
|
3945
4314
|
strategy: "deep",
|
|
3946
4315
|
topK: input.topK,
|
|
3947
4316
|
minScore: Math.max(0.5, input.minScore - 0.1),
|
|
@@ -3968,10 +4337,14 @@ var Retriever = class {
|
|
|
3968
4337
|
});
|
|
3969
4338
|
const top = qualityFiltered.slice(0, input.topK);
|
|
3970
4339
|
const matchResult = this.matcher.matchSearchResults(top, () => 0);
|
|
3971
|
-
return { results: top, candidateResults: qualityFiltered, matchResult };
|
|
4340
|
+
return { results: top, candidateResults: qualityFiltered, matchResult, effectiveQueryText, queryRewriteKind };
|
|
3972
4341
|
}
|
|
3973
4342
|
applyQualityFilters(results, options) {
|
|
3974
4343
|
let filtered = [...results];
|
|
4344
|
+
if (isCurrentStateQuery(options.query)) {
|
|
4345
|
+
filtered = filtered.filter((result) => !isStaleOrSupersededContent(result.content));
|
|
4346
|
+
}
|
|
4347
|
+
filtered = filtered.filter((result) => hasDiscriminativeTermOverlap(options.query, result.content));
|
|
3975
4348
|
if (shouldApplyTechnicalGuard(options.query)) {
|
|
3976
4349
|
filtered = filtered.filter((result) => hasTechnicalTermOverlap(options.query, result.content));
|
|
3977
4350
|
}
|
|
@@ -4279,7 +4652,21 @@ _Context:_ ${sessionContext}`;
|
|
|
4279
4652
|
});
|
|
4280
4653
|
}
|
|
4281
4654
|
tokenize(text) {
|
|
4282
|
-
return text.toLowerCase().replace(/[^\p{L}\p{N}\s]/gu, " ").split(/\s+/).filter((t) => t.length >= 2).slice(0, 64);
|
|
4655
|
+
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);
|
|
4656
|
+
}
|
|
4657
|
+
normalizeToken(token) {
|
|
4658
|
+
if (token === "apis")
|
|
4659
|
+
return "api";
|
|
4660
|
+
if (token === "ids")
|
|
4661
|
+
return "id";
|
|
4662
|
+
if (token === "does")
|
|
4663
|
+
return token;
|
|
4664
|
+
if (token.length > 4 && token.endsWith("ies"))
|
|
4665
|
+
return `${token.slice(0, -3)}y`;
|
|
4666
|
+
if (token.length > 3 && token.endsWith("s") && !token.endsWith("ss") && !token.endsWith("us") && !token.endsWith("is") && !token.endsWith("ps")) {
|
|
4667
|
+
return token.slice(0, -1);
|
|
4668
|
+
}
|
|
4669
|
+
return token;
|
|
4283
4670
|
}
|
|
4284
4671
|
keywordOverlap(a, b) {
|
|
4285
4672
|
if (a.length === 0 || b.length === 0)
|
|
@@ -4342,9 +4729,9 @@ var RetrievalAnalyticsService = class {
|
|
|
4342
4729
|
await this.deps.initialize();
|
|
4343
4730
|
return this.deps.retrievalStore.getHelpfulMemories(limit);
|
|
4344
4731
|
}
|
|
4345
|
-
async getHelpfulnessStats() {
|
|
4732
|
+
async getHelpfulnessStats(since) {
|
|
4346
4733
|
await this.deps.initialize();
|
|
4347
|
-
return this.deps.retrievalStore.getHelpfulnessStats();
|
|
4734
|
+
return this.deps.retrievalStore.getHelpfulnessStats(since);
|
|
4348
4735
|
}
|
|
4349
4736
|
/**
|
|
4350
4737
|
* Extract topic keywords from event content (markdown headings and key terms).
|
|
@@ -4769,7 +5156,9 @@ var RetrievalOrchestrator = class {
|
|
|
4769
5156
|
await this.deps.traceStore.recordRetrievalTrace({
|
|
4770
5157
|
sessionId: options?.sessionId,
|
|
4771
5158
|
projectHash: projectHash || void 0,
|
|
4772
|
-
queryText: query,
|
|
5159
|
+
queryText: result.effectiveQueryText || query,
|
|
5160
|
+
rawQueryText: result.rawQueryText || (result.queryRewriteKind ? query : void 0),
|
|
5161
|
+
queryRewriteKind: result.queryRewriteKind || "none",
|
|
4773
5162
|
strategy: options?.strategy || "auto",
|
|
4774
5163
|
candidateEventIds,
|
|
4775
5164
|
selectedEventIds,
|