claude-memory-layer 1.0.40 → 1.0.41
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 +4305 -171
- package/dist/cli/index.js.map +4 -4
- package/dist/core/index.js +8753 -4293
- package/dist/core/index.js.map +4 -4
- package/dist/hooks/post-tool-use.js +1339 -40
- package/dist/hooks/post-tool-use.js.map +4 -4
- package/dist/hooks/semantic-daemon.js +1339 -40
- package/dist/hooks/semantic-daemon.js.map +4 -4
- package/dist/hooks/session-end.js +1339 -40
- package/dist/hooks/session-end.js.map +4 -4
- package/dist/hooks/session-start.js +1339 -40
- package/dist/hooks/session-start.js.map +4 -4
- package/dist/hooks/stop.js +1339 -40
- package/dist/hooks/stop.js.map +4 -4
- package/dist/hooks/user-prompt-submit.js +1341 -42
- package/dist/hooks/user-prompt-submit.js.map +4 -4
- package/dist/index.js +10080 -5605
- package/dist/index.js.map +4 -4
- package/dist/mcp/index.js +4389 -159
- package/dist/mcp/index.js.map +4 -4
- package/dist/server/api/index.js +1592 -42
- package/dist/server/api/index.js.map +4 -4
- package/dist/server/index.js +1592 -42
- package/dist/server/index.js.map +4 -4
- package/dist/services/memory-service.js +1339 -40
- package/dist/services/memory-service.js.map +4 -4
- package/dist/ui/assets/js/bootstrap.js +2 -0
- package/dist/ui/assets/js/overview.js +137 -1
- package/dist/ui/assets/js/state.js +1 -0
- package/dist/ui/index.html +53 -0
- package/package.json +2 -2
|
@@ -2885,6 +2885,127 @@ var SQLiteEventStore = class {
|
|
|
2885
2885
|
updated_at TEXT DEFAULT (datetime('now'))
|
|
2886
2886
|
);
|
|
2887
2887
|
|
|
2888
|
+
-- Memory Operations: facet assignments (derived, rebuildable projection)
|
|
2889
|
+
CREATE TABLE IF NOT EXISTS memory_facets (
|
|
2890
|
+
id TEXT PRIMARY KEY,
|
|
2891
|
+
target_type TEXT NOT NULL,
|
|
2892
|
+
target_id TEXT NOT NULL,
|
|
2893
|
+
dimension TEXT NOT NULL,
|
|
2894
|
+
value TEXT NOT NULL,
|
|
2895
|
+
confidence REAL NOT NULL DEFAULT 1.0,
|
|
2896
|
+
source TEXT NOT NULL DEFAULT 'manual',
|
|
2897
|
+
evidence_event_ids TEXT NOT NULL DEFAULT '[]',
|
|
2898
|
+
project_hash TEXT NOT NULL DEFAULT '',
|
|
2899
|
+
created_at TEXT NOT NULL,
|
|
2900
|
+
updated_at TEXT NOT NULL,
|
|
2901
|
+
UNIQUE(target_type, target_id, dimension, value, source, project_hash)
|
|
2902
|
+
);
|
|
2903
|
+
|
|
2904
|
+
-- Memory Operations: operational action projection
|
|
2905
|
+
CREATE TABLE IF NOT EXISTS memory_actions (
|
|
2906
|
+
action_id TEXT PRIMARY KEY,
|
|
2907
|
+
project_hash TEXT NOT NULL,
|
|
2908
|
+
title TEXT NOT NULL,
|
|
2909
|
+
status TEXT NOT NULL DEFAULT 'pending',
|
|
2910
|
+
priority INTEGER NOT NULL DEFAULT 0,
|
|
2911
|
+
source_event_ids TEXT NOT NULL DEFAULT '[]',
|
|
2912
|
+
related_entity_ids TEXT NOT NULL DEFAULT '[]',
|
|
2913
|
+
current_checkpoint_id TEXT,
|
|
2914
|
+
lease_id TEXT,
|
|
2915
|
+
created_at TEXT NOT NULL,
|
|
2916
|
+
updated_at TEXT NOT NULL
|
|
2917
|
+
);
|
|
2918
|
+
|
|
2919
|
+
-- Memory Operations: action dependency/reference edges
|
|
2920
|
+
CREATE TABLE IF NOT EXISTS memory_action_edges (
|
|
2921
|
+
edge_id TEXT PRIMARY KEY,
|
|
2922
|
+
src_action_id TEXT NOT NULL,
|
|
2923
|
+
rel_type TEXT NOT NULL,
|
|
2924
|
+
dst_type TEXT NOT NULL,
|
|
2925
|
+
dst_id TEXT NOT NULL,
|
|
2926
|
+
confidence REAL NOT NULL DEFAULT 1.0,
|
|
2927
|
+
source TEXT NOT NULL DEFAULT 'manual',
|
|
2928
|
+
created_at TEXT NOT NULL,
|
|
2929
|
+
UNIQUE(src_action_id, rel_type, dst_type, dst_id, source)
|
|
2930
|
+
);
|
|
2931
|
+
|
|
2932
|
+
-- Memory Operations: short-lived leases for operational work
|
|
2933
|
+
CREATE TABLE IF NOT EXISTS memory_leases (
|
|
2934
|
+
lease_id TEXT PRIMARY KEY,
|
|
2935
|
+
target_type TEXT NOT NULL,
|
|
2936
|
+
target_id TEXT NOT NULL,
|
|
2937
|
+
holder TEXT NOT NULL,
|
|
2938
|
+
expires_at TEXT NOT NULL,
|
|
2939
|
+
metadata_json TEXT,
|
|
2940
|
+
created_at TEXT NOT NULL,
|
|
2941
|
+
renewed_at TEXT,
|
|
2942
|
+
released_at TEXT
|
|
2943
|
+
);
|
|
2944
|
+
|
|
2945
|
+
-- Memory Operations: resumable checkpoints for delegated or long-running work
|
|
2946
|
+
CREATE TABLE IF NOT EXISTS memory_checkpoints (
|
|
2947
|
+
checkpoint_id TEXT PRIMARY KEY,
|
|
2948
|
+
project_hash TEXT NOT NULL,
|
|
2949
|
+
action_id TEXT,
|
|
2950
|
+
session_id TEXT,
|
|
2951
|
+
title TEXT NOT NULL,
|
|
2952
|
+
summary TEXT NOT NULL,
|
|
2953
|
+
state_json TEXT NOT NULL,
|
|
2954
|
+
source_event_ids TEXT NOT NULL DEFAULT '[]',
|
|
2955
|
+
created_at TEXT NOT NULL,
|
|
2956
|
+
expires_at TEXT
|
|
2957
|
+
);
|
|
2958
|
+
|
|
2959
|
+
-- Memory Operations: retention lifecycle score projection
|
|
2960
|
+
CREATE TABLE IF NOT EXISTS memory_retention_scores (
|
|
2961
|
+
score_id TEXT PRIMARY KEY,
|
|
2962
|
+
target_type TEXT NOT NULL,
|
|
2963
|
+
target_id TEXT NOT NULL,
|
|
2964
|
+
project_hash TEXT NOT NULL,
|
|
2965
|
+
policy_version TEXT NOT NULL,
|
|
2966
|
+
decision TEXT NOT NULL,
|
|
2967
|
+
lifecycle_score REAL NOT NULL,
|
|
2968
|
+
factors_json TEXT NOT NULL,
|
|
2969
|
+
reasons_json TEXT NOT NULL,
|
|
2970
|
+
dry_run_diff_json TEXT NOT NULL,
|
|
2971
|
+
source_event_ids TEXT NOT NULL DEFAULT '[]',
|
|
2972
|
+
evaluated_at TEXT NOT NULL,
|
|
2973
|
+
created_at TEXT NOT NULL,
|
|
2974
|
+
updated_at TEXT NOT NULL,
|
|
2975
|
+
UNIQUE(target_type, target_id, project_hash, policy_version)
|
|
2976
|
+
);
|
|
2977
|
+
|
|
2978
|
+
-- Memory Operations: procedural lessons derived from successful workflows
|
|
2979
|
+
CREATE TABLE IF NOT EXISTS memory_lessons (
|
|
2980
|
+
lesson_id TEXT PRIMARY KEY,
|
|
2981
|
+
project_hash TEXT NOT NULL DEFAULT '',
|
|
2982
|
+
name TEXT NOT NULL,
|
|
2983
|
+
trigger TEXT NOT NULL,
|
|
2984
|
+
steps_json TEXT NOT NULL,
|
|
2985
|
+
confidence REAL NOT NULL,
|
|
2986
|
+
source_session_ids TEXT NOT NULL DEFAULT '[]',
|
|
2987
|
+
source_event_ids TEXT NOT NULL DEFAULT '[]',
|
|
2988
|
+
failure_modes_json TEXT NOT NULL DEFAULT '[]',
|
|
2989
|
+
skill_candidate INTEGER NOT NULL DEFAULT 0,
|
|
2990
|
+
created_at TEXT NOT NULL,
|
|
2991
|
+
updated_at TEXT NOT NULL,
|
|
2992
|
+
UNIQUE(project_hash, name)
|
|
2993
|
+
);
|
|
2994
|
+
|
|
2995
|
+
-- Memory Operations: governance/audit trail for state-changing operations
|
|
2996
|
+
CREATE TABLE IF NOT EXISTS memory_governance_audit (
|
|
2997
|
+
audit_id TEXT PRIMARY KEY,
|
|
2998
|
+
operation TEXT NOT NULL,
|
|
2999
|
+
actor TEXT NOT NULL,
|
|
3000
|
+
project_hash TEXT,
|
|
3001
|
+
target_type TEXT NOT NULL,
|
|
3002
|
+
target_id TEXT NOT NULL,
|
|
3003
|
+
before_json TEXT,
|
|
3004
|
+
after_json TEXT,
|
|
3005
|
+
source_event_ids TEXT NOT NULL DEFAULT '[]',
|
|
3006
|
+
created_at TEXT NOT NULL
|
|
3007
|
+
);
|
|
3008
|
+
|
|
2888
3009
|
-- Create indexes
|
|
2889
3010
|
CREATE INDEX IF NOT EXISTS idx_events_session ON events(session_id);
|
|
2890
3011
|
CREATE INDEX IF NOT EXISTS idx_events_timestamp ON events(timestamp);
|
|
@@ -2911,6 +3032,23 @@ var SQLiteEventStore = class {
|
|
|
2911
3032
|
CREATE INDEX IF NOT EXISTS idx_retrieval_traces_created_at ON retrieval_traces(created_at DESC);
|
|
2912
3033
|
CREATE INDEX IF NOT EXISTS idx_retrieval_traces_project_hash ON retrieval_traces(project_hash);
|
|
2913
3034
|
CREATE INDEX IF NOT EXISTS idx_retrieval_traces_session_id ON retrieval_traces(session_id);
|
|
3035
|
+
CREATE INDEX IF NOT EXISTS idx_memory_facets_project_dimension_value ON memory_facets(project_hash, dimension, value);
|
|
3036
|
+
CREATE INDEX IF NOT EXISTS idx_memory_facets_target ON memory_facets(target_type, target_id);
|
|
3037
|
+
CREATE INDEX IF NOT EXISTS idx_memory_facets_dimension_value_confidence ON memory_facets(dimension, value, confidence DESC);
|
|
3038
|
+
CREATE INDEX IF NOT EXISTS idx_memory_actions_project_status_priority ON memory_actions(project_hash, status, priority DESC, updated_at DESC);
|
|
3039
|
+
CREATE INDEX IF NOT EXISTS idx_memory_action_edges_src ON memory_action_edges(src_action_id, rel_type);
|
|
3040
|
+
CREATE INDEX IF NOT EXISTS idx_memory_action_edges_dst ON memory_action_edges(dst_type, dst_id);
|
|
3041
|
+
CREATE INDEX IF NOT EXISTS idx_memory_leases_target_expires ON memory_leases(target_type, target_id, expires_at);
|
|
3042
|
+
CREATE INDEX IF NOT EXISTS idx_memory_checkpoints_project_action_created ON memory_checkpoints(project_hash, action_id, created_at DESC);
|
|
3043
|
+
CREATE INDEX IF NOT EXISTS idx_memory_checkpoints_project_session_created ON memory_checkpoints(project_hash, session_id, created_at DESC);
|
|
3044
|
+
CREATE INDEX IF NOT EXISTS idx_memory_retention_scores_project_decision_score ON memory_retention_scores(project_hash, decision, lifecycle_score ASC, evaluated_at DESC);
|
|
3045
|
+
CREATE INDEX IF NOT EXISTS idx_memory_retention_scores_target ON memory_retention_scores(target_type, target_id, project_hash);
|
|
3046
|
+
CREATE INDEX IF NOT EXISTS idx_memory_retention_scores_policy_evaluated ON memory_retention_scores(policy_version, evaluated_at DESC);
|
|
3047
|
+
CREATE INDEX IF NOT EXISTS idx_memory_lessons_project_confidence ON memory_lessons(project_hash, confidence DESC, updated_at DESC);
|
|
3048
|
+
CREATE INDEX IF NOT EXISTS idx_memory_lessons_skill_candidate ON memory_lessons(project_hash, skill_candidate, confidence DESC);
|
|
3049
|
+
CREATE INDEX IF NOT EXISTS idx_memory_lessons_updated ON memory_lessons(updated_at DESC);
|
|
3050
|
+
CREATE INDEX IF NOT EXISTS idx_memory_governance_audit_project_operation ON memory_governance_audit(project_hash, operation, created_at DESC);
|
|
3051
|
+
CREATE INDEX IF NOT EXISTS idx_memory_governance_audit_target ON memory_governance_audit(target_type, target_id, created_at DESC);
|
|
2914
3052
|
|
|
2915
3053
|
-- FTS5 Full-Text Search for fast keyword search
|
|
2916
3054
|
CREATE VIRTUAL TABLE IF NOT EXISTS events_fts USING fts5(
|
|
@@ -2933,6 +3071,48 @@ var SQLiteEventStore = class {
|
|
|
2933
3071
|
INSERT INTO events_fts(rowid, content, event_id) VALUES (NEW.rowid, NEW.content, NEW.id);
|
|
2934
3072
|
END;
|
|
2935
3073
|
`);
|
|
3074
|
+
try {
|
|
3075
|
+
sqliteExec(this.db, `ALTER TABLE memory_action_edges ADD COLUMN source TEXT NOT NULL DEFAULT 'manual';`);
|
|
3076
|
+
} catch {
|
|
3077
|
+
}
|
|
3078
|
+
try {
|
|
3079
|
+
const edgeIndexes = sqliteAll(this.db, `PRAGMA index_list(memory_action_edges)`, []);
|
|
3080
|
+
const hasSourceAwareUnique = edgeIndexes.some((index) => {
|
|
3081
|
+
if (Number(index.unique) !== 1)
|
|
3082
|
+
return false;
|
|
3083
|
+
if (!/^[A-Za-z0-9_]+$/.test(index.name))
|
|
3084
|
+
return false;
|
|
3085
|
+
const escapedName = index.name.replace(/"/g, '""');
|
|
3086
|
+
const columns = sqliteAll(this.db, 'PRAGMA index_info("' + escapedName + '")', []).map((column) => column.name);
|
|
3087
|
+
return columns.length === 5 && columns[0] === "src_action_id" && columns[1] === "rel_type" && columns[2] === "dst_type" && columns[3] === "dst_id" && columns[4] === "source";
|
|
3088
|
+
});
|
|
3089
|
+
if (!hasSourceAwareUnique) {
|
|
3090
|
+
sqliteExec(this.db, `
|
|
3091
|
+
DROP TABLE IF EXISTS memory_action_edges_v2;
|
|
3092
|
+
CREATE TABLE memory_action_edges_v2 (
|
|
3093
|
+
edge_id TEXT PRIMARY KEY,
|
|
3094
|
+
src_action_id TEXT NOT NULL,
|
|
3095
|
+
rel_type TEXT NOT NULL,
|
|
3096
|
+
dst_type TEXT NOT NULL,
|
|
3097
|
+
dst_id TEXT NOT NULL,
|
|
3098
|
+
confidence REAL NOT NULL DEFAULT 1.0,
|
|
3099
|
+
source TEXT NOT NULL DEFAULT 'manual',
|
|
3100
|
+
created_at TEXT NOT NULL,
|
|
3101
|
+
UNIQUE(src_action_id, rel_type, dst_type, dst_id, source)
|
|
3102
|
+
);
|
|
3103
|
+
INSERT OR IGNORE INTO memory_action_edges_v2 (
|
|
3104
|
+
edge_id, src_action_id, rel_type, dst_type, dst_id, confidence, source, created_at
|
|
3105
|
+
)
|
|
3106
|
+
SELECT edge_id, src_action_id, rel_type, dst_type, dst_id, confidence, source, created_at
|
|
3107
|
+
FROM memory_action_edges;
|
|
3108
|
+
DROP TABLE memory_action_edges;
|
|
3109
|
+
ALTER TABLE memory_action_edges_v2 RENAME TO memory_action_edges;
|
|
3110
|
+
CREATE INDEX IF NOT EXISTS idx_memory_action_edges_src ON memory_action_edges(src_action_id, rel_type);
|
|
3111
|
+
CREATE INDEX IF NOT EXISTS idx_memory_action_edges_dst ON memory_action_edges(dst_type, dst_id);
|
|
3112
|
+
`);
|
|
3113
|
+
}
|
|
3114
|
+
} catch {
|
|
3115
|
+
}
|
|
2936
3116
|
try {
|
|
2937
3117
|
sqliteExec(this.db, `ALTER TABLE retrieval_traces ADD COLUMN selected_details_json TEXT;`);
|
|
2938
3118
|
} catch {
|
|
@@ -5001,6 +5181,896 @@ var MemoryQueryService = class {
|
|
|
5001
5181
|
}
|
|
5002
5182
|
};
|
|
5003
5183
|
|
|
5184
|
+
// src/core/operations/facet-repository.ts
|
|
5185
|
+
import { randomUUID as randomUUID7 } from "crypto";
|
|
5186
|
+
|
|
5187
|
+
// src/core/operations/facets.ts
|
|
5188
|
+
import { z } from "zod";
|
|
5189
|
+
var FacetTargetTypeSchema = z.enum([
|
|
5190
|
+
"event",
|
|
5191
|
+
"entity",
|
|
5192
|
+
"edge",
|
|
5193
|
+
"consolidated_memory",
|
|
5194
|
+
"lesson",
|
|
5195
|
+
"action"
|
|
5196
|
+
]);
|
|
5197
|
+
var BUILT_IN_FACET_DIMENSIONS = [
|
|
5198
|
+
"kind",
|
|
5199
|
+
"workflow",
|
|
5200
|
+
"artifact",
|
|
5201
|
+
"source",
|
|
5202
|
+
"privacy",
|
|
5203
|
+
"quality",
|
|
5204
|
+
"retention",
|
|
5205
|
+
"project"
|
|
5206
|
+
];
|
|
5207
|
+
var customDimensionPattern = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/;
|
|
5208
|
+
var TrimmedStringSchema = z.preprocess(
|
|
5209
|
+
(value) => typeof value === "string" ? value.trim() : value,
|
|
5210
|
+
z.string().min(1)
|
|
5211
|
+
);
|
|
5212
|
+
var FacetDimensionSchema = z.preprocess(
|
|
5213
|
+
(value) => typeof value === "string" ? value.trim() : value,
|
|
5214
|
+
z.string().min(1).max(64).refine((value) => BUILT_IN_FACET_DIMENSIONS.indexOf(value) !== -1 || customDimensionPattern.test(value), {
|
|
5215
|
+
message: "Facet dimension must be built-in or lowercase kebab-case"
|
|
5216
|
+
})
|
|
5217
|
+
);
|
|
5218
|
+
var FacetSourceSchema = z.enum(["manual", "imported", "derived", "llm", "system"]).default("manual");
|
|
5219
|
+
var EvidenceEventIdsSchema = z.preprocess(
|
|
5220
|
+
(value) => Array.isArray(value) ? value.map((item) => typeof item === "string" ? item.trim() : item).filter(Boolean) : value,
|
|
5221
|
+
z.array(z.string().min(1)).default([])
|
|
5222
|
+
);
|
|
5223
|
+
var OptionalTrimmedStringSchema = z.preprocess(
|
|
5224
|
+
(value) => typeof value === "string" ? value.trim() : value,
|
|
5225
|
+
z.string().min(1).optional()
|
|
5226
|
+
);
|
|
5227
|
+
var MemoryFacetAssignmentInputSchema = z.object({
|
|
5228
|
+
targetType: FacetTargetTypeSchema,
|
|
5229
|
+
targetId: TrimmedStringSchema,
|
|
5230
|
+
dimension: FacetDimensionSchema,
|
|
5231
|
+
value: TrimmedStringSchema,
|
|
5232
|
+
confidence: z.number().min(0).max(1).default(1),
|
|
5233
|
+
source: FacetSourceSchema,
|
|
5234
|
+
evidenceEventIds: EvidenceEventIdsSchema,
|
|
5235
|
+
projectHash: OptionalTrimmedStringSchema,
|
|
5236
|
+
actor: OptionalTrimmedStringSchema
|
|
5237
|
+
});
|
|
5238
|
+
var MemoryFacetAssignmentSchema = MemoryFacetAssignmentInputSchema.extend({
|
|
5239
|
+
id: z.string().min(1),
|
|
5240
|
+
createdAt: z.date(),
|
|
5241
|
+
updatedAt: z.date()
|
|
5242
|
+
});
|
|
5243
|
+
var FacetRemoveInputSchema = z.object({
|
|
5244
|
+
targetType: FacetTargetTypeSchema,
|
|
5245
|
+
targetId: TrimmedStringSchema,
|
|
5246
|
+
dimension: FacetDimensionSchema,
|
|
5247
|
+
value: TrimmedStringSchema,
|
|
5248
|
+
source: FacetSourceSchema,
|
|
5249
|
+
projectHash: OptionalTrimmedStringSchema,
|
|
5250
|
+
actor: OptionalTrimmedStringSchema
|
|
5251
|
+
});
|
|
5252
|
+
var FacetQuerySchema = z.object({
|
|
5253
|
+
targetType: FacetTargetTypeSchema.optional(),
|
|
5254
|
+
targetId: OptionalTrimmedStringSchema,
|
|
5255
|
+
dimension: FacetDimensionSchema.optional(),
|
|
5256
|
+
value: OptionalTrimmedStringSchema,
|
|
5257
|
+
source: FacetSourceSchema.optional(),
|
|
5258
|
+
projectHash: OptionalTrimmedStringSchema,
|
|
5259
|
+
limit: z.number().int().positive().max(500).default(100)
|
|
5260
|
+
});
|
|
5261
|
+
function parseFacetAssignmentInput(input) {
|
|
5262
|
+
return MemoryFacetAssignmentInputSchema.parse(input);
|
|
5263
|
+
}
|
|
5264
|
+
function parseFacetRemoveInput(input) {
|
|
5265
|
+
return FacetRemoveInputSchema.parse(input);
|
|
5266
|
+
}
|
|
5267
|
+
function parseFacetQuery(input) {
|
|
5268
|
+
return FacetQuerySchema.parse(input ?? {});
|
|
5269
|
+
}
|
|
5270
|
+
|
|
5271
|
+
// src/core/operations/governance-audit.ts
|
|
5272
|
+
import { randomUUID as randomUUID6 } from "crypto";
|
|
5273
|
+
var MEMORY_GOVERNANCE_AUDIT_OPERATIONS = [
|
|
5274
|
+
"facet_tag",
|
|
5275
|
+
"action_update",
|
|
5276
|
+
"lease_acquire",
|
|
5277
|
+
"checkpoint_create",
|
|
5278
|
+
"retention_score",
|
|
5279
|
+
"quarantine",
|
|
5280
|
+
"verify",
|
|
5281
|
+
"lesson_promote"
|
|
5282
|
+
];
|
|
5283
|
+
function normalizeRequiredString(value, fieldName) {
|
|
5284
|
+
const normalized = value.trim();
|
|
5285
|
+
if (!normalized) {
|
|
5286
|
+
throw new Error(`${fieldName} is required`);
|
|
5287
|
+
}
|
|
5288
|
+
return normalized;
|
|
5289
|
+
}
|
|
5290
|
+
function normalizeOptionalString(value) {
|
|
5291
|
+
const normalized = value?.trim();
|
|
5292
|
+
return normalized ? normalized : void 0;
|
|
5293
|
+
}
|
|
5294
|
+
var REDACTED = "[REDACTED]";
|
|
5295
|
+
var sensitiveKeyPattern = /(?:api[_-]?key|secret|password|passwd|token|access[_-]?token|client[_-]?secret|crtfc[_-]?key|hashkey|serviceKey)/i;
|
|
5296
|
+
var POSIX_ABSOLUTE_PATH_PATTERN = /(^|[^A-Za-z0-9._\/\\-])\/(?!\/)[^\n\r"'<>|`]*/g;
|
|
5297
|
+
var WINDOWS_DRIVE_PATH_PATTERN = /(^|[^A-Za-z0-9._\/\\-])(?:[A-Za-z]:[\\/][^\n\r"'<>|`]*)/g;
|
|
5298
|
+
var WINDOWS_UNC_PATH_PATTERN = /(^|[^A-Za-z0-9._\/\\-])(?:\\\\[^\\\n\r"'<>|`]+\\[^\n\r"'<>|`]*)/g;
|
|
5299
|
+
var credentialQueryPattern = /\b((?:api[_-]?key|token|access[_-]?token|client[_-]?secret|crtfc[_-]?key|hashkey|serviceKey)=)[^&\s`"'<>]+/gi;
|
|
5300
|
+
var credentialAssignmentPattern = /\b((?:api[_-]?key|secret|password|passwd|token|access[_-]?token|client[_-]?secret|crtfc[_-]?key|hashkey|serviceKey)\s*[:=]\s*)[^\s`"'<>},]+/gi;
|
|
5301
|
+
function redactAbsolutePaths(value) {
|
|
5302
|
+
return value.replace(WINDOWS_UNC_PATH_PATTERN, (_match, prefix) => `${prefix}${REDACTED}`).replace(WINDOWS_DRIVE_PATH_PATTERN, (_match, prefix) => `${prefix}${REDACTED}`).replace(POSIX_ABSOLUTE_PATH_PATTERN, (_match, prefix) => `${prefix}${REDACTED}`);
|
|
5303
|
+
}
|
|
5304
|
+
function sanitizeAuditString(value) {
|
|
5305
|
+
return redactAbsolutePaths(value).replace(credentialQueryPattern, `$1${REDACTED}`).replace(credentialAssignmentPattern, `$1${REDACTED}`);
|
|
5306
|
+
}
|
|
5307
|
+
function sanitizeGovernanceAuditValue(value, key) {
|
|
5308
|
+
if (key && sensitiveKeyPattern.test(key)) {
|
|
5309
|
+
return REDACTED;
|
|
5310
|
+
}
|
|
5311
|
+
if (typeof value === "string") {
|
|
5312
|
+
return sanitizeAuditString(value);
|
|
5313
|
+
}
|
|
5314
|
+
if (value instanceof Date) {
|
|
5315
|
+
return sanitizeAuditString(value.toISOString());
|
|
5316
|
+
}
|
|
5317
|
+
if (Array.isArray(value)) {
|
|
5318
|
+
return value.map((item) => sanitizeGovernanceAuditValue(item));
|
|
5319
|
+
}
|
|
5320
|
+
if (value && typeof value === "object") {
|
|
5321
|
+
const sanitized = {};
|
|
5322
|
+
for (const [entryKey, entryValue] of Object.entries(value)) {
|
|
5323
|
+
sanitized[sanitizeAuditString(entryKey)] = sanitizeGovernanceAuditValue(entryValue, entryKey);
|
|
5324
|
+
}
|
|
5325
|
+
return sanitized;
|
|
5326
|
+
}
|
|
5327
|
+
return value;
|
|
5328
|
+
}
|
|
5329
|
+
function sanitizeAuditJson(value) {
|
|
5330
|
+
if (value === void 0)
|
|
5331
|
+
return void 0;
|
|
5332
|
+
return sanitizeGovernanceAuditValue(value);
|
|
5333
|
+
}
|
|
5334
|
+
function normalizeSourceEventIds(sourceEventIds) {
|
|
5335
|
+
return (sourceEventIds || []).map((sourceEventId) => sanitizeAuditString(sourceEventId.trim())).filter((sourceEventId) => sourceEventId.length > 0);
|
|
5336
|
+
}
|
|
5337
|
+
function normalizeOperation(operation) {
|
|
5338
|
+
if (MEMORY_GOVERNANCE_AUDIT_OPERATIONS.indexOf(operation) === -1) {
|
|
5339
|
+
throw new Error(`Unsupported governance audit operation: ${operation}`);
|
|
5340
|
+
}
|
|
5341
|
+
return operation;
|
|
5342
|
+
}
|
|
5343
|
+
async function writeGovernanceAuditEntry(db, input) {
|
|
5344
|
+
const entry = {
|
|
5345
|
+
auditId: randomUUID6(),
|
|
5346
|
+
operation: normalizeOperation(input.operation),
|
|
5347
|
+
actor: sanitizeAuditString(normalizeRequiredString(input.actor, "actor")),
|
|
5348
|
+
projectHash: normalizeOptionalString(input.projectHash),
|
|
5349
|
+
targetType: normalizeRequiredString(input.targetType, "targetType"),
|
|
5350
|
+
targetId: sanitizeAuditString(normalizeRequiredString(input.targetId, "targetId")),
|
|
5351
|
+
beforeJson: sanitizeAuditJson(input.beforeJson),
|
|
5352
|
+
afterJson: sanitizeAuditJson(input.afterJson),
|
|
5353
|
+
sourceEventIds: normalizeSourceEventIds(input.sourceEventIds),
|
|
5354
|
+
createdAt: /* @__PURE__ */ new Date()
|
|
5355
|
+
};
|
|
5356
|
+
sqliteRun(
|
|
5357
|
+
db,
|
|
5358
|
+
`INSERT INTO memory_governance_audit (
|
|
5359
|
+
audit_id, operation, actor, project_hash, target_type, target_id,
|
|
5360
|
+
before_json, after_json, source_event_ids, created_at
|
|
5361
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
5362
|
+
[
|
|
5363
|
+
entry.auditId,
|
|
5364
|
+
entry.operation,
|
|
5365
|
+
entry.actor,
|
|
5366
|
+
entry.projectHash ?? null,
|
|
5367
|
+
entry.targetType,
|
|
5368
|
+
entry.targetId,
|
|
5369
|
+
entry.beforeJson === void 0 ? null : JSON.stringify(entry.beforeJson),
|
|
5370
|
+
entry.afterJson === void 0 ? null : JSON.stringify(entry.afterJson),
|
|
5371
|
+
JSON.stringify(entry.sourceEventIds),
|
|
5372
|
+
entry.createdAt.toISOString()
|
|
5373
|
+
]
|
|
5374
|
+
);
|
|
5375
|
+
return entry;
|
|
5376
|
+
}
|
|
5377
|
+
|
|
5378
|
+
// src/core/operations/facet-repository.ts
|
|
5379
|
+
function parseStringArray(value) {
|
|
5380
|
+
if (typeof value !== "string")
|
|
5381
|
+
return [];
|
|
5382
|
+
try {
|
|
5383
|
+
const parsed = JSON.parse(value);
|
|
5384
|
+
if (!Array.isArray(parsed))
|
|
5385
|
+
return [];
|
|
5386
|
+
return parsed.filter((item) => typeof item === "string" && item.length > 0);
|
|
5387
|
+
} catch {
|
|
5388
|
+
return [];
|
|
5389
|
+
}
|
|
5390
|
+
}
|
|
5391
|
+
function projectHashToStorage(projectHash) {
|
|
5392
|
+
return projectHash ?? "";
|
|
5393
|
+
}
|
|
5394
|
+
function rowToFacet(row) {
|
|
5395
|
+
const projectHash = typeof row.project_hash === "string" && row.project_hash.length > 0 ? row.project_hash : void 0;
|
|
5396
|
+
return {
|
|
5397
|
+
id: row.id,
|
|
5398
|
+
targetType: FacetTargetTypeSchema.parse(row.target_type),
|
|
5399
|
+
targetId: row.target_id,
|
|
5400
|
+
dimension: row.dimension,
|
|
5401
|
+
value: row.value,
|
|
5402
|
+
confidence: Number(row.confidence),
|
|
5403
|
+
source: FacetSourceSchema.parse(row.source),
|
|
5404
|
+
evidenceEventIds: parseStringArray(row.evidence_event_ids),
|
|
5405
|
+
projectHash,
|
|
5406
|
+
createdAt: toDateFromSQLite(row.created_at),
|
|
5407
|
+
updatedAt: toDateFromSQLite(row.updated_at)
|
|
5408
|
+
};
|
|
5409
|
+
}
|
|
5410
|
+
function facetToAuditJson(facet) {
|
|
5411
|
+
return {
|
|
5412
|
+
id: facet.id,
|
|
5413
|
+
targetType: facet.targetType,
|
|
5414
|
+
targetId: facet.targetId,
|
|
5415
|
+
dimension: facet.dimension,
|
|
5416
|
+
value: facet.value,
|
|
5417
|
+
confidence: facet.confidence,
|
|
5418
|
+
source: facet.source,
|
|
5419
|
+
evidenceEventIds: facet.evidenceEventIds,
|
|
5420
|
+
projectHash: facet.projectHash,
|
|
5421
|
+
createdAt: facet.createdAt.toISOString(),
|
|
5422
|
+
updatedAt: facet.updatedAt.toISOString()
|
|
5423
|
+
};
|
|
5424
|
+
}
|
|
5425
|
+
var FacetRepository = class {
|
|
5426
|
+
constructor(db) {
|
|
5427
|
+
this.db = db;
|
|
5428
|
+
}
|
|
5429
|
+
async assign(input) {
|
|
5430
|
+
const assignment = parseFacetAssignmentInput(input);
|
|
5431
|
+
const existing = this.findByUniqueKey(assignment);
|
|
5432
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
5433
|
+
if (existing) {
|
|
5434
|
+
sqliteRun(
|
|
5435
|
+
this.db,
|
|
5436
|
+
`UPDATE memory_facets
|
|
5437
|
+
SET confidence = ?, evidence_event_ids = ?, project_hash = ?, updated_at = ?
|
|
5438
|
+
WHERE id = ?`,
|
|
5439
|
+
[
|
|
5440
|
+
assignment.confidence,
|
|
5441
|
+
JSON.stringify(assignment.evidenceEventIds),
|
|
5442
|
+
projectHashToStorage(assignment.projectHash),
|
|
5443
|
+
now,
|
|
5444
|
+
existing.id
|
|
5445
|
+
]
|
|
5446
|
+
);
|
|
5447
|
+
const saved2 = this.getById(existing.id);
|
|
5448
|
+
await this.auditAssignment(assignment, existing, saved2);
|
|
5449
|
+
return saved2;
|
|
5450
|
+
}
|
|
5451
|
+
const id = randomUUID7();
|
|
5452
|
+
sqliteRun(
|
|
5453
|
+
this.db,
|
|
5454
|
+
`INSERT INTO memory_facets (
|
|
5455
|
+
id, target_type, target_id, dimension, value, confidence, source,
|
|
5456
|
+
evidence_event_ids, project_hash, created_at, updated_at
|
|
5457
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
5458
|
+
[
|
|
5459
|
+
id,
|
|
5460
|
+
assignment.targetType,
|
|
5461
|
+
assignment.targetId,
|
|
5462
|
+
assignment.dimension,
|
|
5463
|
+
assignment.value,
|
|
5464
|
+
assignment.confidence,
|
|
5465
|
+
assignment.source,
|
|
5466
|
+
JSON.stringify(assignment.evidenceEventIds),
|
|
5467
|
+
projectHashToStorage(assignment.projectHash),
|
|
5468
|
+
now,
|
|
5469
|
+
now
|
|
5470
|
+
]
|
|
5471
|
+
);
|
|
5472
|
+
const saved = this.getById(id);
|
|
5473
|
+
await this.auditAssignment(assignment, null, saved);
|
|
5474
|
+
return saved;
|
|
5475
|
+
}
|
|
5476
|
+
async remove(input) {
|
|
5477
|
+
const removeInput = parseFacetRemoveInput(input);
|
|
5478
|
+
const { sql, params } = this.removeSql(removeInput);
|
|
5479
|
+
const result = sqliteRun(this.db, sql, params);
|
|
5480
|
+
return result.changes > 0;
|
|
5481
|
+
}
|
|
5482
|
+
async query(input) {
|
|
5483
|
+
const query = parseFacetQuery(input);
|
|
5484
|
+
const { sql, params } = this.querySql(query);
|
|
5485
|
+
const rows = sqliteAll(this.db, sql, params);
|
|
5486
|
+
return rows.map(rowToFacet);
|
|
5487
|
+
}
|
|
5488
|
+
async listForTarget(targetType, targetId) {
|
|
5489
|
+
const parsedTargetType = FacetTargetTypeSchema.parse(targetType);
|
|
5490
|
+
const trimmedTargetId = targetId.trim();
|
|
5491
|
+
if (!trimmedTargetId) {
|
|
5492
|
+
throw new Error("targetId is required");
|
|
5493
|
+
}
|
|
5494
|
+
return this.query({ targetType: parsedTargetType, targetId: trimmedTargetId, limit: 500 });
|
|
5495
|
+
}
|
|
5496
|
+
getById(id) {
|
|
5497
|
+
const row = sqliteGet(this.db, `SELECT * FROM memory_facets WHERE id = ?`, [id]);
|
|
5498
|
+
if (!row) {
|
|
5499
|
+
throw new Error(`Memory facet not found after write: ${id}`);
|
|
5500
|
+
}
|
|
5501
|
+
return rowToFacet(row);
|
|
5502
|
+
}
|
|
5503
|
+
findByUniqueKey(input) {
|
|
5504
|
+
const row = sqliteGet(
|
|
5505
|
+
this.db,
|
|
5506
|
+
`SELECT * FROM memory_facets
|
|
5507
|
+
WHERE target_type = ? AND target_id = ? AND dimension = ? AND value = ? AND source = ? AND project_hash = ?`,
|
|
5508
|
+
[
|
|
5509
|
+
input.targetType,
|
|
5510
|
+
input.targetId,
|
|
5511
|
+
input.dimension,
|
|
5512
|
+
input.value,
|
|
5513
|
+
input.source,
|
|
5514
|
+
projectHashToStorage(input.projectHash)
|
|
5515
|
+
]
|
|
5516
|
+
);
|
|
5517
|
+
return row ? rowToFacet(row) : null;
|
|
5518
|
+
}
|
|
5519
|
+
async auditAssignment(input, before, after) {
|
|
5520
|
+
await writeGovernanceAuditEntry(this.db, {
|
|
5521
|
+
operation: "facet_tag",
|
|
5522
|
+
actor: input.actor ?? "cml-core",
|
|
5523
|
+
projectHash: input.projectHash,
|
|
5524
|
+
targetType: input.targetType,
|
|
5525
|
+
targetId: input.targetId,
|
|
5526
|
+
beforeJson: before ? facetToAuditJson(before) : void 0,
|
|
5527
|
+
afterJson: facetToAuditJson(after),
|
|
5528
|
+
sourceEventIds: input.evidenceEventIds
|
|
5529
|
+
});
|
|
5530
|
+
}
|
|
5531
|
+
querySql(query) {
|
|
5532
|
+
const clauses = [];
|
|
5533
|
+
const params = [];
|
|
5534
|
+
if (query.targetType) {
|
|
5535
|
+
clauses.push("target_type = ?");
|
|
5536
|
+
params.push(query.targetType);
|
|
5537
|
+
}
|
|
5538
|
+
if (query.targetId) {
|
|
5539
|
+
clauses.push("target_id = ?");
|
|
5540
|
+
params.push(query.targetId);
|
|
5541
|
+
}
|
|
5542
|
+
if (query.dimension) {
|
|
5543
|
+
clauses.push("dimension = ?");
|
|
5544
|
+
params.push(query.dimension);
|
|
5545
|
+
}
|
|
5546
|
+
if (query.value) {
|
|
5547
|
+
clauses.push("value = ?");
|
|
5548
|
+
params.push(query.value);
|
|
5549
|
+
}
|
|
5550
|
+
if (query.source) {
|
|
5551
|
+
clauses.push("source = ?");
|
|
5552
|
+
params.push(query.source);
|
|
5553
|
+
}
|
|
5554
|
+
if (query.projectHash) {
|
|
5555
|
+
clauses.push("project_hash = ?");
|
|
5556
|
+
params.push(query.projectHash);
|
|
5557
|
+
}
|
|
5558
|
+
const where = clauses.length > 0 ? `WHERE ${clauses.join(" AND ")}` : "";
|
|
5559
|
+
params.push(query.limit);
|
|
5560
|
+
return {
|
|
5561
|
+
sql: `SELECT * FROM memory_facets ${where} ORDER BY confidence DESC, updated_at DESC LIMIT ?`,
|
|
5562
|
+
params
|
|
5563
|
+
};
|
|
5564
|
+
}
|
|
5565
|
+
removeSql(input) {
|
|
5566
|
+
const clauses = [
|
|
5567
|
+
"target_type = ?",
|
|
5568
|
+
"target_id = ?",
|
|
5569
|
+
"dimension = ?",
|
|
5570
|
+
"value = ?",
|
|
5571
|
+
"source = ?",
|
|
5572
|
+
"project_hash = ?"
|
|
5573
|
+
];
|
|
5574
|
+
const params = [
|
|
5575
|
+
input.targetType,
|
|
5576
|
+
input.targetId,
|
|
5577
|
+
input.dimension,
|
|
5578
|
+
input.value,
|
|
5579
|
+
input.source,
|
|
5580
|
+
projectHashToStorage(input.projectHash)
|
|
5581
|
+
];
|
|
5582
|
+
return {
|
|
5583
|
+
sql: `DELETE FROM memory_facets WHERE ${clauses.join(" AND ")}`,
|
|
5584
|
+
params
|
|
5585
|
+
};
|
|
5586
|
+
}
|
|
5587
|
+
};
|
|
5588
|
+
|
|
5589
|
+
// src/core/operations/graph-path-service.ts
|
|
5590
|
+
var DEFAULT_WEIGHT = 0.5;
|
|
5591
|
+
var MAX_HOPS = 2;
|
|
5592
|
+
var DEFAULT_MAX_RESULTS = 20;
|
|
5593
|
+
var MAX_RESULTS = 100;
|
|
5594
|
+
var GraphPathService = class {
|
|
5595
|
+
constructor(db) {
|
|
5596
|
+
this.db = db;
|
|
5597
|
+
}
|
|
5598
|
+
expand(input) {
|
|
5599
|
+
const graph = this.loadGraph(input.direction ?? "both");
|
|
5600
|
+
const effectiveMaxHops = normalizeMaxHops(input.maxHops);
|
|
5601
|
+
const maxResults = normalizeMaxResults(input.maxResults);
|
|
5602
|
+
const startNodes = input.startNodes.map((node) => graph.node(node));
|
|
5603
|
+
const startKeys = new Set(input.startNodes.map(nodeKey));
|
|
5604
|
+
const bestByTarget = /* @__PURE__ */ new Map();
|
|
5605
|
+
const queue = startNodes.map((node) => ({
|
|
5606
|
+
key: nodeKey(node),
|
|
5607
|
+
hops: 0,
|
|
5608
|
+
totalCost: 0,
|
|
5609
|
+
steps: [],
|
|
5610
|
+
visited: /* @__PURE__ */ new Set([nodeKey(node)])
|
|
5611
|
+
}));
|
|
5612
|
+
while (queue.length > 0) {
|
|
5613
|
+
queue.sort((a, b) => a.totalCost - b.totalCost || a.hops - b.hops || a.key.localeCompare(b.key));
|
|
5614
|
+
const current = queue.shift();
|
|
5615
|
+
if (current.hops >= effectiveMaxHops)
|
|
5616
|
+
continue;
|
|
5617
|
+
for (const edge of graph.adjacency.get(current.key) ?? []) {
|
|
5618
|
+
if (current.visited.has(edge.toKey))
|
|
5619
|
+
continue;
|
|
5620
|
+
const nextHops = current.hops + 1;
|
|
5621
|
+
const nextTotalCost = current.totalCost + edge.step.cost;
|
|
5622
|
+
const nextSteps = [...current.steps, edge.step];
|
|
5623
|
+
const nextSignature = pathSignature(nextSteps);
|
|
5624
|
+
const existing = bestByTarget.get(edge.toKey);
|
|
5625
|
+
if (!existing || isBetterPath(nextTotalCost, nextHops, nextSignature, existing)) {
|
|
5626
|
+
if (!startKeys.has(edge.toKey)) {
|
|
5627
|
+
bestByTarget.set(edge.toKey, { hops: nextHops, totalCost: nextTotalCost, signature: nextSignature, steps: nextSteps });
|
|
5628
|
+
}
|
|
5629
|
+
const nextVisited = new Set(current.visited);
|
|
5630
|
+
nextVisited.add(edge.toKey);
|
|
5631
|
+
queue.push({
|
|
5632
|
+
key: edge.toKey,
|
|
5633
|
+
hops: nextHops,
|
|
5634
|
+
totalCost: nextTotalCost,
|
|
5635
|
+
steps: nextSteps,
|
|
5636
|
+
visited: nextVisited
|
|
5637
|
+
});
|
|
5638
|
+
}
|
|
5639
|
+
}
|
|
5640
|
+
}
|
|
5641
|
+
const paths = Array.from(bestByTarget.entries()).map(([key, path12]) => ({
|
|
5642
|
+
target: graph.node(nodeFromKey(key)),
|
|
5643
|
+
hops: path12.hops,
|
|
5644
|
+
totalCost: path12.totalCost,
|
|
5645
|
+
scoreContribution: path12.totalCost > 0 ? 1 / path12.totalCost : 0,
|
|
5646
|
+
steps: path12.steps
|
|
5647
|
+
})).sort((a, b) => b.scoreContribution - a.scoreContribution || a.hops - b.hops || a.target.name.localeCompare(b.target.name)).slice(0, maxResults);
|
|
5648
|
+
return { startNodes, effectiveMaxHops, paths };
|
|
5649
|
+
}
|
|
5650
|
+
loadGraph(direction) {
|
|
5651
|
+
const entityLabels = new Map(
|
|
5652
|
+
sqliteAll(this.db, `SELECT entity_id, title FROM entities WHERE status = 'active'`).map((row) => [row.entity_id, row.title])
|
|
5653
|
+
);
|
|
5654
|
+
const labelNode = (node) => ({
|
|
5655
|
+
...node,
|
|
5656
|
+
name: node.type === "entity" ? entityLabels.get(node.id) ?? node.id : node.id
|
|
5657
|
+
});
|
|
5658
|
+
const adjacency = /* @__PURE__ */ new Map();
|
|
5659
|
+
const edges = sqliteAll(
|
|
5660
|
+
this.db,
|
|
5661
|
+
`SELECT edge_id, src_type, src_id, rel_type, dst_type, dst_id, meta_json FROM edges`
|
|
5662
|
+
);
|
|
5663
|
+
for (const edge of edges) {
|
|
5664
|
+
const src = labelNode({ type: edge.src_type, id: edge.src_id });
|
|
5665
|
+
const dst = labelNode({ type: edge.dst_type, id: edge.dst_id });
|
|
5666
|
+
const weight = edgeWeight(edge.meta_json);
|
|
5667
|
+
const cost = 1 / weight;
|
|
5668
|
+
const baseStep = {
|
|
5669
|
+
edgeId: edge.edge_id,
|
|
5670
|
+
relationType: edge.rel_type,
|
|
5671
|
+
from: src,
|
|
5672
|
+
to: dst,
|
|
5673
|
+
weight,
|
|
5674
|
+
cost,
|
|
5675
|
+
scoreContribution: weight
|
|
5676
|
+
};
|
|
5677
|
+
if (direction === "outgoing" || direction === "both") {
|
|
5678
|
+
addTraversal(adjacency, nodeKey(src), {
|
|
5679
|
+
toKey: nodeKey(dst),
|
|
5680
|
+
step: { ...baseStep, direction: "outgoing" }
|
|
5681
|
+
});
|
|
5682
|
+
}
|
|
5683
|
+
if (direction === "incoming" || direction === "both") {
|
|
5684
|
+
addTraversal(adjacency, nodeKey(dst), {
|
|
5685
|
+
toKey: nodeKey(src),
|
|
5686
|
+
step: { ...baseStep, direction: "incoming" }
|
|
5687
|
+
});
|
|
5688
|
+
}
|
|
5689
|
+
}
|
|
5690
|
+
return { adjacency, node: labelNode };
|
|
5691
|
+
}
|
|
5692
|
+
};
|
|
5693
|
+
function addTraversal(adjacency, fromKey, edge) {
|
|
5694
|
+
const edges = adjacency.get(fromKey) ?? [];
|
|
5695
|
+
edges.push(edge);
|
|
5696
|
+
adjacency.set(fromKey, edges);
|
|
5697
|
+
}
|
|
5698
|
+
function normalizeMaxHops(maxHops) {
|
|
5699
|
+
if (maxHops === void 0)
|
|
5700
|
+
return 1;
|
|
5701
|
+
if (!Number.isFinite(maxHops))
|
|
5702
|
+
return MAX_HOPS;
|
|
5703
|
+
return Math.min(Math.max(0, Math.trunc(maxHops)), MAX_HOPS);
|
|
5704
|
+
}
|
|
5705
|
+
function normalizeMaxResults(maxResults) {
|
|
5706
|
+
if (maxResults === void 0)
|
|
5707
|
+
return DEFAULT_MAX_RESULTS;
|
|
5708
|
+
if (!Number.isFinite(maxResults))
|
|
5709
|
+
return DEFAULT_MAX_RESULTS;
|
|
5710
|
+
return Math.min(Math.max(0, Math.trunc(maxResults)), MAX_RESULTS);
|
|
5711
|
+
}
|
|
5712
|
+
function isBetterPath(totalCost, hops, signature, existing) {
|
|
5713
|
+
return totalCost < existing.totalCost || totalCost === existing.totalCost && hops < existing.hops || totalCost === existing.totalCost && hops === existing.hops && signature < existing.signature;
|
|
5714
|
+
}
|
|
5715
|
+
function pathSignature(steps) {
|
|
5716
|
+
return steps.map((step) => `${step.edgeId}:${step.direction}:${nodeKey(step.from)}>${nodeKey(step.to)}`).join("|");
|
|
5717
|
+
}
|
|
5718
|
+
function edgeWeight(metaJson) {
|
|
5719
|
+
const meta = parseMeta(metaJson);
|
|
5720
|
+
const raw = meta.weight;
|
|
5721
|
+
if (typeof raw === "number" && Number.isFinite(raw) && raw > 0)
|
|
5722
|
+
return raw;
|
|
5723
|
+
if (typeof raw === "string") {
|
|
5724
|
+
const parsed = Number(raw);
|
|
5725
|
+
if (Number.isFinite(parsed) && parsed > 0)
|
|
5726
|
+
return parsed;
|
|
5727
|
+
}
|
|
5728
|
+
return DEFAULT_WEIGHT;
|
|
5729
|
+
}
|
|
5730
|
+
function parseMeta(metaJson) {
|
|
5731
|
+
if (!metaJson)
|
|
5732
|
+
return {};
|
|
5733
|
+
try {
|
|
5734
|
+
const parsed = JSON.parse(metaJson);
|
|
5735
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
|
|
5736
|
+
} catch {
|
|
5737
|
+
return {};
|
|
5738
|
+
}
|
|
5739
|
+
}
|
|
5740
|
+
function nodeKey(node) {
|
|
5741
|
+
return `${node.type}:${node.id}`;
|
|
5742
|
+
}
|
|
5743
|
+
function nodeFromKey(key) {
|
|
5744
|
+
const index = key.indexOf(":");
|
|
5745
|
+
if (index === -1)
|
|
5746
|
+
return { type: "entity", id: key };
|
|
5747
|
+
return { type: key.slice(0, index), id: key.slice(index + 1) };
|
|
5748
|
+
}
|
|
5749
|
+
|
|
5750
|
+
// src/core/operations/query-entity-extractor.ts
|
|
5751
|
+
var DEFAULT_MAX_CANDIDATES = 20;
|
|
5752
|
+
var MAX_CANDIDATES = 100;
|
|
5753
|
+
var MAX_CANDIDATE_TEXT_LENGTH = 200;
|
|
5754
|
+
var SOURCE_PRIORITY = {
|
|
5755
|
+
entity_alias: 0,
|
|
5756
|
+
quoted: 1,
|
|
5757
|
+
file_path: 2,
|
|
5758
|
+
package_identifier: 3,
|
|
5759
|
+
capitalized_term: 4
|
|
5760
|
+
};
|
|
5761
|
+
var SOURCE_CONFIDENCE = {
|
|
5762
|
+
entity_alias: 0.95,
|
|
5763
|
+
quoted: 0.85,
|
|
5764
|
+
file_path: 0.8,
|
|
5765
|
+
package_identifier: 0.75,
|
|
5766
|
+
capitalized_term: 0.6
|
|
5767
|
+
};
|
|
5768
|
+
var SENTENCE_START_STOPWORDS = /* @__PURE__ */ new Set([
|
|
5769
|
+
"A",
|
|
5770
|
+
"An",
|
|
5771
|
+
"And",
|
|
5772
|
+
"Are",
|
|
5773
|
+
"Can",
|
|
5774
|
+
"Compare",
|
|
5775
|
+
"Does",
|
|
5776
|
+
"Explain",
|
|
5777
|
+
"Find",
|
|
5778
|
+
"How",
|
|
5779
|
+
"I",
|
|
5780
|
+
"If",
|
|
5781
|
+
"In",
|
|
5782
|
+
"Is",
|
|
5783
|
+
"List",
|
|
5784
|
+
"Please",
|
|
5785
|
+
"Should",
|
|
5786
|
+
"Show",
|
|
5787
|
+
"Tell",
|
|
5788
|
+
"The",
|
|
5789
|
+
"This",
|
|
5790
|
+
"Use",
|
|
5791
|
+
"What",
|
|
5792
|
+
"When",
|
|
5793
|
+
"Where",
|
|
5794
|
+
"Which",
|
|
5795
|
+
"Why",
|
|
5796
|
+
"With"
|
|
5797
|
+
]);
|
|
5798
|
+
var QueryEntityExtractor = class {
|
|
5799
|
+
constructor(db) {
|
|
5800
|
+
this.db = db;
|
|
5801
|
+
}
|
|
5802
|
+
extract(query, options = {}) {
|
|
5803
|
+
const maxCandidates = normalizeMaxCandidates(options.maxCandidates);
|
|
5804
|
+
const candidates = [];
|
|
5805
|
+
const quotedRanges = this.extractQuoted(query, candidates);
|
|
5806
|
+
if (options.includeAliases !== false) {
|
|
5807
|
+
this.extractKnownAliases(query, candidates);
|
|
5808
|
+
}
|
|
5809
|
+
this.extractFilePaths(query, candidates);
|
|
5810
|
+
this.extractPackageIdentifiers(query, candidates);
|
|
5811
|
+
this.extractCapitalizedTerms(query, candidates, quotedRanges);
|
|
5812
|
+
return {
|
|
5813
|
+
query,
|
|
5814
|
+
candidates: dedupeAndSort(candidates).slice(0, maxCandidates).map(stripPriority)
|
|
5815
|
+
};
|
|
5816
|
+
}
|
|
5817
|
+
extractQuoted(query, candidates) {
|
|
5818
|
+
const ranges = [];
|
|
5819
|
+
const regex = /(["'`])((?:(?!\1)[^\n]){2,200})\1/g;
|
|
5820
|
+
let match;
|
|
5821
|
+
while ((match = regex.exec(query)) !== null) {
|
|
5822
|
+
const text = cleanCandidateText(match[2] ?? "");
|
|
5823
|
+
if (!isUsefulCandidate(text))
|
|
5824
|
+
continue;
|
|
5825
|
+
const start = match.index + 1;
|
|
5826
|
+
const end = start + text.length;
|
|
5827
|
+
ranges.push([match.index, match.index + match[0].length]);
|
|
5828
|
+
pushCandidate(candidates, {
|
|
5829
|
+
text,
|
|
5830
|
+
source: "quoted",
|
|
5831
|
+
start,
|
|
5832
|
+
end
|
|
5833
|
+
});
|
|
5834
|
+
}
|
|
5835
|
+
return ranges;
|
|
5836
|
+
}
|
|
5837
|
+
extractKnownAliases(query, candidates) {
|
|
5838
|
+
if (!this.db)
|
|
5839
|
+
return;
|
|
5840
|
+
const rows = sqliteAll(
|
|
5841
|
+
this.db,
|
|
5842
|
+
`SELECT
|
|
5843
|
+
a.entity_id,
|
|
5844
|
+
a.entity_type,
|
|
5845
|
+
a.canonical_key AS alias_key,
|
|
5846
|
+
e.canonical_key AS entity_canonical_key,
|
|
5847
|
+
e.title
|
|
5848
|
+
FROM entity_aliases a
|
|
5849
|
+
JOIN entities e ON e.entity_id = a.entity_id
|
|
5850
|
+
WHERE e.status = 'active'
|
|
5851
|
+
ORDER BY e.title COLLATE NOCASE, a.canonical_key COLLATE NOCASE`
|
|
5852
|
+
);
|
|
5853
|
+
const normalizedQuery = normalizeForContainment(query);
|
|
5854
|
+
const seenAliases = /* @__PURE__ */ new Set();
|
|
5855
|
+
for (const row of rows) {
|
|
5856
|
+
const aliasLabels = uniqueStrings([
|
|
5857
|
+
row.title,
|
|
5858
|
+
aliasLabelFromCanonicalKey(row.alias_key),
|
|
5859
|
+
aliasLabelFromCanonicalKey(row.entity_canonical_key)
|
|
5860
|
+
]).filter(isUsefulCandidate);
|
|
5861
|
+
for (const alias of aliasLabels) {
|
|
5862
|
+
const normalizedAlias = normalizeForContainment(alias);
|
|
5863
|
+
if (!normalizedAlias || !containsPhrase(normalizedQuery, normalizedAlias))
|
|
5864
|
+
continue;
|
|
5865
|
+
const aliasKey = `${row.entity_id}:${normalizedAlias}`;
|
|
5866
|
+
if (seenAliases.has(aliasKey))
|
|
5867
|
+
continue;
|
|
5868
|
+
seenAliases.add(aliasKey);
|
|
5869
|
+
const range = findRange(query, alias);
|
|
5870
|
+
pushCandidate(candidates, {
|
|
5871
|
+
text: row.title,
|
|
5872
|
+
source: "entity_alias",
|
|
5873
|
+
start: range.start,
|
|
5874
|
+
end: range.end,
|
|
5875
|
+
entityId: row.entity_id,
|
|
5876
|
+
entityType: row.entity_type,
|
|
5877
|
+
canonicalKey: row.entity_canonical_key,
|
|
5878
|
+
matchedAlias: normalizedAlias
|
|
5879
|
+
});
|
|
5880
|
+
}
|
|
5881
|
+
}
|
|
5882
|
+
}
|
|
5883
|
+
extractFilePaths(query, candidates) {
|
|
5884
|
+
const regex = /(^|[\s([{<])((?:\.{1,2}\/|~\/|\/)?(?:[A-Za-z0-9_.-]+\/)+[A-Za-z0-9_.-]+\.[A-Za-z0-9][A-Za-z0-9._-]*)(?=$|[\s)\]},>`.,;:!?])/g;
|
|
5885
|
+
let match;
|
|
5886
|
+
while ((match = regex.exec(query)) !== null) {
|
|
5887
|
+
const text = cleanCandidateText(match[2] ?? "");
|
|
5888
|
+
if (!isUsefulCandidate(text))
|
|
5889
|
+
continue;
|
|
5890
|
+
const start = match.index + (match[1]?.length ?? 0);
|
|
5891
|
+
pushCandidate(candidates, {
|
|
5892
|
+
text,
|
|
5893
|
+
source: "file_path",
|
|
5894
|
+
start,
|
|
5895
|
+
end: start + text.length
|
|
5896
|
+
});
|
|
5897
|
+
}
|
|
5898
|
+
}
|
|
5899
|
+
extractPackageIdentifiers(query, candidates) {
|
|
5900
|
+
const regex = /(^|[\s([{<`])(@[a-z0-9][a-z0-9._-]*\/[a-z0-9][a-z0-9._-]*|[a-z0-9][a-z0-9._]*[-_][a-z0-9._-]*)(?=$|[\s)\]},>`.,;:!?])/gi;
|
|
5901
|
+
let match;
|
|
5902
|
+
while ((match = regex.exec(query)) !== null) {
|
|
5903
|
+
const text = cleanCandidateText(match[2] ?? "");
|
|
5904
|
+
if (!isUsefulCandidate(text) || text.includes("/.") || text.includes("./"))
|
|
5905
|
+
continue;
|
|
5906
|
+
const start = match.index + (match[1]?.length ?? 0);
|
|
5907
|
+
pushCandidate(candidates, {
|
|
5908
|
+
text,
|
|
5909
|
+
source: "package_identifier",
|
|
5910
|
+
start,
|
|
5911
|
+
end: start + text.length
|
|
5912
|
+
});
|
|
5913
|
+
}
|
|
5914
|
+
}
|
|
5915
|
+
extractCapitalizedTerms(query, candidates, ignoredRanges) {
|
|
5916
|
+
const tokens = collectCapitalizedTokens(query).filter((token) => !isInsideAnyRange(token.start, ignoredRanges)).filter((token) => !SENTENCE_START_STOPWORDS.has(token.text));
|
|
5917
|
+
const groups = [];
|
|
5918
|
+
let current = [];
|
|
5919
|
+
for (const token of tokens) {
|
|
5920
|
+
const previous = current[current.length - 1];
|
|
5921
|
+
if (previous && query.slice(previous.end, token.start).match(/^\s+$/)) {
|
|
5922
|
+
current.push(token);
|
|
5923
|
+
} else {
|
|
5924
|
+
if (current.length > 0)
|
|
5925
|
+
groups.push(current);
|
|
5926
|
+
current = [token];
|
|
5927
|
+
}
|
|
5928
|
+
}
|
|
5929
|
+
if (current.length > 0)
|
|
5930
|
+
groups.push(current);
|
|
5931
|
+
for (const group of groups) {
|
|
5932
|
+
if (group.length === 1 && !isStrongSingleCapitalized(group[0].text))
|
|
5933
|
+
continue;
|
|
5934
|
+
const start = group[0].start;
|
|
5935
|
+
const end = group[group.length - 1].end;
|
|
5936
|
+
const text = query.slice(start, end);
|
|
5937
|
+
if (!isUsefulCandidate(text))
|
|
5938
|
+
continue;
|
|
5939
|
+
pushCandidate(candidates, {
|
|
5940
|
+
text,
|
|
5941
|
+
source: "capitalized_term",
|
|
5942
|
+
start,
|
|
5943
|
+
end
|
|
5944
|
+
});
|
|
5945
|
+
}
|
|
5946
|
+
}
|
|
5947
|
+
};
|
|
5948
|
+
function collectCapitalizedTokens(query) {
|
|
5949
|
+
const regex = /\b(?:[A-Z]{2,}[A-Z0-9]*|[A-Z][A-Za-z0-9]*(?:[._-][A-Za-z0-9]+)*)\b/g;
|
|
5950
|
+
const tokens = [];
|
|
5951
|
+
let match;
|
|
5952
|
+
while ((match = regex.exec(query)) !== null) {
|
|
5953
|
+
tokens.push({ text: match[0], start: match.index, end: match.index + match[0].length });
|
|
5954
|
+
}
|
|
5955
|
+
return tokens;
|
|
5956
|
+
}
|
|
5957
|
+
function pushCandidate(candidates, input) {
|
|
5958
|
+
const text = cleanCandidateText(input.text);
|
|
5959
|
+
if (!isUsefulCandidate(text))
|
|
5960
|
+
return;
|
|
5961
|
+
const source = input.source;
|
|
5962
|
+
candidates.push({
|
|
5963
|
+
...input,
|
|
5964
|
+
text,
|
|
5965
|
+
normalized: normalizeCandidate(text),
|
|
5966
|
+
confidence: input.confidence ?? SOURCE_CONFIDENCE[source],
|
|
5967
|
+
priority: SOURCE_PRIORITY[source]
|
|
5968
|
+
});
|
|
5969
|
+
}
|
|
5970
|
+
function dedupeAndSort(candidates) {
|
|
5971
|
+
const sorted = [...candidates].sort(compareCandidates);
|
|
5972
|
+
const seenAliasKeys = /* @__PURE__ */ new Set();
|
|
5973
|
+
const seenNormalized = /* @__PURE__ */ new Set();
|
|
5974
|
+
const result = [];
|
|
5975
|
+
for (const candidate of sorted) {
|
|
5976
|
+
if (candidate.source === "entity_alias") {
|
|
5977
|
+
const aliasKey = `alias:${candidate.entityId ?? ""}:${normalizeCandidate(candidate.matchedAlias ?? candidate.text)}`;
|
|
5978
|
+
if (seenAliasKeys.has(aliasKey))
|
|
5979
|
+
continue;
|
|
5980
|
+
seenAliasKeys.add(aliasKey);
|
|
5981
|
+
seenNormalized.add(candidate.normalized);
|
|
5982
|
+
result.push(candidate);
|
|
5983
|
+
continue;
|
|
5984
|
+
}
|
|
5985
|
+
if (seenNormalized.has(candidate.normalized))
|
|
5986
|
+
continue;
|
|
5987
|
+
seenNormalized.add(candidate.normalized);
|
|
5988
|
+
result.push(candidate);
|
|
5989
|
+
}
|
|
5990
|
+
return result.sort(compareCandidates);
|
|
5991
|
+
}
|
|
5992
|
+
function compareCandidates(a, b) {
|
|
5993
|
+
return a.priority - b.priority || a.start - b.start || a.end - b.end || compareStrings(a.text, b.text) || compareStrings(a.entityId ?? "", b.entityId ?? "") || compareStrings(a.matchedAlias ?? "", b.matchedAlias ?? "");
|
|
5994
|
+
}
|
|
5995
|
+
function compareStrings(a, b) {
|
|
5996
|
+
if (a === b)
|
|
5997
|
+
return 0;
|
|
5998
|
+
return a < b ? -1 : 1;
|
|
5999
|
+
}
|
|
6000
|
+
function stripPriority(candidate) {
|
|
6001
|
+
const { priority: _priority, ...publicCandidate } = candidate;
|
|
6002
|
+
return publicCandidate;
|
|
6003
|
+
}
|
|
6004
|
+
function normalizeMaxCandidates(maxCandidates) {
|
|
6005
|
+
if (maxCandidates === void 0)
|
|
6006
|
+
return DEFAULT_MAX_CANDIDATES;
|
|
6007
|
+
if (!Number.isFinite(maxCandidates))
|
|
6008
|
+
return DEFAULT_MAX_CANDIDATES;
|
|
6009
|
+
return Math.min(Math.max(0, Math.trunc(maxCandidates)), MAX_CANDIDATES);
|
|
6010
|
+
}
|
|
6011
|
+
function cleanCandidateText(text) {
|
|
6012
|
+
return text.normalize("NFKC").replace(/\s+/g, " ").trim().replace(/[.,;:!?]+$/g, "");
|
|
6013
|
+
}
|
|
6014
|
+
function normalizeCandidate(text) {
|
|
6015
|
+
return cleanCandidateText(text).toLowerCase();
|
|
6016
|
+
}
|
|
6017
|
+
function normalizeForContainment(text) {
|
|
6018
|
+
return text.normalize("NFKC").toLowerCase().replace(/[^\p{L}\p{N}@/._-]+/gu, " ").replace(/[._-]+/g, " ").replace(/\s+/g, " ").trim();
|
|
6019
|
+
}
|
|
6020
|
+
function containsPhrase(normalizedHaystack, normalizedNeedle) {
|
|
6021
|
+
return ` ${normalizedHaystack} `.includes(` ${normalizedNeedle} `);
|
|
6022
|
+
}
|
|
6023
|
+
function aliasLabelFromCanonicalKey(canonicalKey) {
|
|
6024
|
+
const raw = canonicalKey.includes(":") ? canonicalKey.slice(canonicalKey.lastIndexOf(":") + 1) : canonicalKey;
|
|
6025
|
+
return raw.replace(/[._-]+/g, " ").replace(/\s+/g, " ").trim();
|
|
6026
|
+
}
|
|
6027
|
+
function findRange(query, alias) {
|
|
6028
|
+
const normalizedAlias = normalizeForContainment(alias);
|
|
6029
|
+
const directIndex = query.toLowerCase().indexOf(alias.toLowerCase());
|
|
6030
|
+
if (directIndex >= 0)
|
|
6031
|
+
return { start: directIndex, end: directIndex + alias.length };
|
|
6032
|
+
const normalizedQuery = normalizeForContainment(query);
|
|
6033
|
+
const normalizedIndex = normalizedQuery.indexOf(normalizedAlias);
|
|
6034
|
+
if (normalizedIndex < 0)
|
|
6035
|
+
return { start: 0, end: 0 };
|
|
6036
|
+
const queryLower = query.toLowerCase();
|
|
6037
|
+
const words = normalizedAlias.split(" ").filter(Boolean);
|
|
6038
|
+
if (words.length === 0)
|
|
6039
|
+
return { start: 0, end: 0 };
|
|
6040
|
+
const first = queryLower.indexOf(words[0]);
|
|
6041
|
+
const lastWord = words[words.length - 1];
|
|
6042
|
+
const last = queryLower.indexOf(lastWord, first >= 0 ? first : 0);
|
|
6043
|
+
if (first >= 0 && last >= 0)
|
|
6044
|
+
return { start: first, end: last + lastWord.length };
|
|
6045
|
+
return { start: normalizedIndex, end: normalizedIndex + normalizedAlias.length };
|
|
6046
|
+
}
|
|
6047
|
+
function isUsefulCandidate(text) {
|
|
6048
|
+
const cleaned = cleanCandidateText(text);
|
|
6049
|
+
return cleaned.length >= 2 && cleaned.length <= MAX_CANDIDATE_TEXT_LENGTH && /[\p{L}\p{N}]/u.test(cleaned);
|
|
6050
|
+
}
|
|
6051
|
+
function isInsideAnyRange(index, ranges) {
|
|
6052
|
+
return ranges.some(([start, end]) => index >= start && index < end);
|
|
6053
|
+
}
|
|
6054
|
+
function isStrongSingleCapitalized(text) {
|
|
6055
|
+
if (/^[A-Z]{2,}[A-Z0-9]*$/.test(text))
|
|
6056
|
+
return true;
|
|
6057
|
+
if (/^[A-Z][a-z]+[A-Z][A-Za-z0-9]*$/.test(text))
|
|
6058
|
+
return true;
|
|
6059
|
+
return text.length >= 4 && !SENTENCE_START_STOPWORDS.has(text);
|
|
6060
|
+
}
|
|
6061
|
+
function uniqueStrings(values) {
|
|
6062
|
+
const seen = /* @__PURE__ */ new Set();
|
|
6063
|
+
const result = [];
|
|
6064
|
+
for (const value of values) {
|
|
6065
|
+
const key = normalizeForContainment(value);
|
|
6066
|
+
if (!key || seen.has(key))
|
|
6067
|
+
continue;
|
|
6068
|
+
seen.add(key);
|
|
6069
|
+
result.push(value);
|
|
6070
|
+
}
|
|
6071
|
+
return result;
|
|
6072
|
+
}
|
|
6073
|
+
|
|
5004
6074
|
// src/core/retrieval-quality.ts
|
|
5005
6075
|
var COMMAND_ARTIFACT_PATTERNS = [
|
|
5006
6076
|
/<\/?(?:local-command-(?:stdout|stderr)|command-(?:name|message))\b/i,
|
|
@@ -5363,6 +6433,7 @@ var Retriever = class {
|
|
|
5363
6433
|
sharedVectorStore;
|
|
5364
6434
|
graduation;
|
|
5365
6435
|
queryRewriter;
|
|
6436
|
+
queryGraphExpansionEnabled;
|
|
5366
6437
|
constructor(eventStore, vectorStore, embedder, matcher, sharedOptions) {
|
|
5367
6438
|
this.eventStore = eventStore;
|
|
5368
6439
|
this.vectorStore = vectorStore;
|
|
@@ -5370,6 +6441,7 @@ var Retriever = class {
|
|
|
5370
6441
|
this.matcher = matcher;
|
|
5371
6442
|
this.sharedStore = sharedOptions?.sharedStore;
|
|
5372
6443
|
this.sharedVectorStore = sharedOptions?.sharedVectorStore;
|
|
6444
|
+
this.queryGraphExpansionEnabled = sharedOptions?.queryGraphExpansionEnabled === true;
|
|
5373
6445
|
}
|
|
5374
6446
|
setGraduationPipeline(graduation) {
|
|
5375
6447
|
this.graduation = graduation;
|
|
@@ -5415,7 +6487,8 @@ var Retriever = class {
|
|
|
5415
6487
|
graphHop: opts.graphHop,
|
|
5416
6488
|
projectScopeMode: opts.projectScopeMode,
|
|
5417
6489
|
projectHash: opts.projectHash,
|
|
5418
|
-
allowedProjectHashes: opts.allowedProjectHashes
|
|
6490
|
+
allowedProjectHashes: opts.allowedProjectHashes,
|
|
6491
|
+
facets: opts.facets
|
|
5419
6492
|
});
|
|
5420
6493
|
fallbackTrace.push(`stage:primary:${primaryStrategy}`);
|
|
5421
6494
|
if (fallbackEnabled && this.shouldFallback(current.matchResult, current.results) && primaryStrategy !== "deep") {
|
|
@@ -5432,7 +6505,8 @@ var Retriever = class {
|
|
|
5432
6505
|
graphHop: opts.graphHop,
|
|
5433
6506
|
projectScopeMode: opts.projectScopeMode,
|
|
5434
6507
|
projectHash: opts.projectHash,
|
|
5435
|
-
allowedProjectHashes: opts.allowedProjectHashes
|
|
6508
|
+
allowedProjectHashes: opts.allowedProjectHashes,
|
|
6509
|
+
facets: opts.facets
|
|
5436
6510
|
});
|
|
5437
6511
|
fallbackTrace.push("fallback:deep");
|
|
5438
6512
|
}
|
|
@@ -5450,7 +6524,8 @@ var Retriever = class {
|
|
|
5450
6524
|
graphHop: opts.graphHop,
|
|
5451
6525
|
projectScopeMode: opts.projectScopeMode,
|
|
5452
6526
|
projectHash: opts.projectHash,
|
|
5453
|
-
allowedProjectHashes: opts.allowedProjectHashes
|
|
6527
|
+
allowedProjectHashes: opts.allowedProjectHashes,
|
|
6528
|
+
facets: opts.facets
|
|
5454
6529
|
});
|
|
5455
6530
|
fallbackTrace.push("fallback:scope-expanded");
|
|
5456
6531
|
}
|
|
@@ -5460,7 +6535,8 @@ var Retriever = class {
|
|
|
5460
6535
|
scope: opts.scope,
|
|
5461
6536
|
projectScopeMode: opts.projectScopeMode,
|
|
5462
6537
|
projectHash: opts.projectHash,
|
|
5463
|
-
allowedProjectHashes: opts.allowedProjectHashes
|
|
6538
|
+
allowedProjectHashes: opts.allowedProjectHashes,
|
|
6539
|
+
facets: opts.facets
|
|
5464
6540
|
});
|
|
5465
6541
|
const filteredSummary = this.applyQualityFilters(scopedSummary, {
|
|
5466
6542
|
query,
|
|
@@ -5481,20 +6557,8 @@ var Retriever = class {
|
|
|
5481
6557
|
totalTokens: this.estimateTokens(context),
|
|
5482
6558
|
context,
|
|
5483
6559
|
fallbackTrace,
|
|
5484
|
-
selectedDebug: current.results.slice(0, opts.topK).map((r) => (
|
|
5485
|
-
|
|
5486
|
-
score: r.score,
|
|
5487
|
-
semanticScore: r.semanticScore,
|
|
5488
|
-
lexicalScore: r.lexicalScore,
|
|
5489
|
-
recencyScore: r.recencyScore
|
|
5490
|
-
})),
|
|
5491
|
-
candidateDebug: (current.candidateResults || []).slice(0, Math.max(opts.topK * 3, 20)).map((r) => ({
|
|
5492
|
-
eventId: r.eventId,
|
|
5493
|
-
score: r.score,
|
|
5494
|
-
semanticScore: r.semanticScore,
|
|
5495
|
-
lexicalScore: r.lexicalScore,
|
|
5496
|
-
recencyScore: r.recencyScore
|
|
5497
|
-
})),
|
|
6560
|
+
selectedDebug: current.results.slice(0, opts.topK).map((r) => this.debugDetailForResult(r)),
|
|
6561
|
+
candidateDebug: (current.candidateResults || []).slice(0, Math.max(opts.topK * 3, 20)).map((r) => this.debugDetailForResult(r)),
|
|
5498
6562
|
rawQueryText: current.queryRewriteKind ? query : void 0,
|
|
5499
6563
|
effectiveQueryText: current.effectiveQueryText,
|
|
5500
6564
|
queryRewriteKind: current.queryRewriteKind
|
|
@@ -5562,7 +6626,9 @@ var Retriever = class {
|
|
|
5562
6626
|
}
|
|
5563
6627
|
}
|
|
5564
6628
|
const expandedResults = input.graphHop?.enabled === false ? initialResults : await this.expandGraphHops(initialResults, {
|
|
5565
|
-
|
|
6629
|
+
query,
|
|
6630
|
+
queryGraphEnabled: this.queryGraphExpansionEnabled,
|
|
6631
|
+
maxHops: clampGraphHops(input.graphHop?.maxHops ?? 1),
|
|
5566
6632
|
hopPenalty: Math.max(0, input.graphHop?.hopPenalty ?? 0.08),
|
|
5567
6633
|
limit: input.topK * 4
|
|
5568
6634
|
});
|
|
@@ -5571,7 +6637,8 @@ var Retriever = class {
|
|
|
5571
6637
|
scope: input.scope,
|
|
5572
6638
|
projectScopeMode: input.projectScopeMode,
|
|
5573
6639
|
projectHash: input.projectHash,
|
|
5574
|
-
allowedProjectHashes: input.allowedProjectHashes
|
|
6640
|
+
allowedProjectHashes: input.allowedProjectHashes,
|
|
6641
|
+
facets: input.facets
|
|
5575
6642
|
});
|
|
5576
6643
|
const qualityFiltered = this.applyQualityFilters(filtered, {
|
|
5577
6644
|
query,
|
|
@@ -5586,9 +6653,13 @@ var Retriever = class {
|
|
|
5586
6653
|
if (isCurrentStateQuery(options.query)) {
|
|
5587
6654
|
filtered = filtered.filter((result) => !isStaleOrSupersededContent(result.content));
|
|
5588
6655
|
}
|
|
5589
|
-
filtered = filtered.filter(
|
|
6656
|
+
filtered = filtered.filter(
|
|
6657
|
+
(result) => this.isGraphPathResult(result) || hasDiscriminativeTermOverlap(options.query, result.content)
|
|
6658
|
+
);
|
|
5590
6659
|
if (shouldApplyTechnicalGuard(options.query)) {
|
|
5591
|
-
filtered = filtered.filter(
|
|
6660
|
+
filtered = filtered.filter(
|
|
6661
|
+
(result) => this.isGraphPathResult(result) || hasTechnicalTermOverlap(options.query, result.content)
|
|
6662
|
+
);
|
|
5592
6663
|
}
|
|
5593
6664
|
if (filtered.length <= 2)
|
|
5594
6665
|
return filtered;
|
|
@@ -5651,7 +6722,61 @@ var Retriever = class {
|
|
|
5651
6722
|
if (frontier.length === 0 || byId.size >= opts.limit)
|
|
5652
6723
|
break;
|
|
5653
6724
|
}
|
|
5654
|
-
|
|
6725
|
+
if (opts.queryGraphEnabled) {
|
|
6726
|
+
await this.expandQueryGraphPaths(opts.query, byId, opts);
|
|
6727
|
+
}
|
|
6728
|
+
return [...byId.values()].sort((a, b) => b.score - a.score || compareStable(a.eventId, b.eventId)).slice(0, opts.limit);
|
|
6729
|
+
}
|
|
6730
|
+
async expandQueryGraphPaths(query, byId, opts) {
|
|
6731
|
+
if (!query.trim() || !this.eventStore.getDatabase)
|
|
6732
|
+
return;
|
|
6733
|
+
try {
|
|
6734
|
+
const db = this.eventStore.getDatabase();
|
|
6735
|
+
const extraction = new QueryEntityExtractor(db).extract(query, {
|
|
6736
|
+
maxCandidates: Math.min(8, opts.limit),
|
|
6737
|
+
includeAliases: true
|
|
6738
|
+
});
|
|
6739
|
+
const startCandidates = extraction.candidates.filter((candidate) => candidate.entityId).slice(0, 8);
|
|
6740
|
+
const startNodes = uniqueEntityStartNodes(startCandidates);
|
|
6741
|
+
if (startNodes.length === 0)
|
|
6742
|
+
return;
|
|
6743
|
+
const expansion = new GraphPathService(db).expand({
|
|
6744
|
+
startNodes: startNodes.map((node) => ({ type: "entity", id: node.entityId })),
|
|
6745
|
+
maxHops: opts.maxHops,
|
|
6746
|
+
maxResults: opts.limit,
|
|
6747
|
+
direction: "both"
|
|
6748
|
+
});
|
|
6749
|
+
const titleByEntityId = new Map(startNodes.map((node) => [node.entityId, node.title]));
|
|
6750
|
+
for (const path12 of expansion.paths) {
|
|
6751
|
+
if (path12.target.type !== "event")
|
|
6752
|
+
continue;
|
|
6753
|
+
const target = await this.eventStore.getEvent(path12.target.id);
|
|
6754
|
+
if (!target)
|
|
6755
|
+
continue;
|
|
6756
|
+
const graphPath = toRetrievalGraphPathDebug(path12, titleByEntityId);
|
|
6757
|
+
const score = graphPathScore(path12, opts.hopPenalty);
|
|
6758
|
+
const existing = byId.get(target.id);
|
|
6759
|
+
const graphPaths = mergeGraphPaths(existing?.graphPaths ?? [], [graphPath]);
|
|
6760
|
+
const row = {
|
|
6761
|
+
id: existing?.id ?? `graph-path-${path12.hops}-${target.id}`,
|
|
6762
|
+
eventId: target.id,
|
|
6763
|
+
content: target.content,
|
|
6764
|
+
score: Math.max(existing?.score ?? 0, score),
|
|
6765
|
+
sessionId: target.sessionId,
|
|
6766
|
+
eventType: target.eventType,
|
|
6767
|
+
timestamp: target.timestamp.toISOString(),
|
|
6768
|
+
semanticScore: existing?.semanticScore,
|
|
6769
|
+
lexicalScore: existing?.lexicalScore,
|
|
6770
|
+
recencyScore: existing?.recencyScore,
|
|
6771
|
+
facetMatches: existing?.facetMatches,
|
|
6772
|
+
graphPaths
|
|
6773
|
+
};
|
|
6774
|
+
byId.set(row.eventId, row);
|
|
6775
|
+
if (byId.size >= opts.limit)
|
|
6776
|
+
break;
|
|
6777
|
+
}
|
|
6778
|
+
} catch {
|
|
6779
|
+
}
|
|
5655
6780
|
}
|
|
5656
6781
|
shouldFallback(matchResult, results) {
|
|
5657
6782
|
if (results.length === 0)
|
|
@@ -5742,12 +6867,13 @@ var Retriever = class {
|
|
|
5742
6867
|
async applyScopeFilters(results, options) {
|
|
5743
6868
|
const scope = options?.scope;
|
|
5744
6869
|
const projectScopeMode = options?.projectScopeMode ?? "global";
|
|
6870
|
+
const facetFilters = this.normalizeFacetFilters(options?.facets);
|
|
5745
6871
|
const allowedProjectHashes = new Set(
|
|
5746
6872
|
[options?.projectHash, ...options?.allowedProjectHashes || []].filter(
|
|
5747
6873
|
(value) => typeof value === "string" && value.length > 0
|
|
5748
6874
|
)
|
|
5749
6875
|
);
|
|
5750
|
-
if (!scope && projectScopeMode === "global")
|
|
6876
|
+
if (!scope && projectScopeMode === "global" && facetFilters === null)
|
|
5751
6877
|
return results;
|
|
5752
6878
|
const normalizedIncludes = (scope?.contentIncludes || []).map((s) => s.toLowerCase());
|
|
5753
6879
|
const filtered = [];
|
|
@@ -5773,14 +6899,83 @@ var Retriever = class {
|
|
|
5773
6899
|
const projectHash = this.extractProjectHash(event.metadata);
|
|
5774
6900
|
filtered.push({ result, projectHash });
|
|
5775
6901
|
}
|
|
6902
|
+
let scopedResults;
|
|
5776
6903
|
if (projectScopeMode === "global" || allowedProjectHashes.size === 0) {
|
|
5777
|
-
|
|
6904
|
+
scopedResults = filtered.map((x) => x.result);
|
|
6905
|
+
} else {
|
|
6906
|
+
const projectMatched = filtered.filter((x) => x.projectHash && allowedProjectHashes.has(x.projectHash));
|
|
6907
|
+
scopedResults = projectScopeMode === "strict" ? projectMatched.map((x) => x.result) : (projectMatched.length > 0 ? projectMatched : filtered).map((x) => x.result);
|
|
5778
6908
|
}
|
|
5779
|
-
|
|
5780
|
-
|
|
5781
|
-
|
|
6909
|
+
return this.applyFacetFilters(scopedResults, {
|
|
6910
|
+
facets: facetFilters,
|
|
6911
|
+
projectHash: options?.projectHash
|
|
6912
|
+
});
|
|
6913
|
+
}
|
|
6914
|
+
normalizeFacetFilters(facets) {
|
|
6915
|
+
if (!facets || facets.length === 0)
|
|
6916
|
+
return null;
|
|
6917
|
+
const normalized = [];
|
|
6918
|
+
for (const facet of facets) {
|
|
6919
|
+
const parsedDimension = FacetDimensionSchema.safeParse(facet.dimension);
|
|
6920
|
+
const value = typeof facet.value === "string" ? facet.value.trim() : "";
|
|
6921
|
+
if (!parsedDimension.success || !value)
|
|
6922
|
+
return [];
|
|
6923
|
+
normalized.push({ dimension: parsedDimension.data, value });
|
|
5782
6924
|
}
|
|
5783
|
-
return
|
|
6925
|
+
return normalized;
|
|
6926
|
+
}
|
|
6927
|
+
async applyFacetFilters(results, options) {
|
|
6928
|
+
if (options.facets === null)
|
|
6929
|
+
return results;
|
|
6930
|
+
if (options.facets.length === 0)
|
|
6931
|
+
return [];
|
|
6932
|
+
if (!options.projectHash)
|
|
6933
|
+
return [];
|
|
6934
|
+
if (!this.eventStore.getDatabase)
|
|
6935
|
+
return [];
|
|
6936
|
+
const repo = new FacetRepository(this.eventStore.getDatabase());
|
|
6937
|
+
const filtered = [];
|
|
6938
|
+
for (const result of results) {
|
|
6939
|
+
const matches = [];
|
|
6940
|
+
let matchedAll = true;
|
|
6941
|
+
for (const facet of options.facets) {
|
|
6942
|
+
const rows = await repo.query({
|
|
6943
|
+
targetType: "event",
|
|
6944
|
+
targetId: result.eventId,
|
|
6945
|
+
dimension: facet.dimension,
|
|
6946
|
+
value: facet.value,
|
|
6947
|
+
projectHash: options.projectHash
|
|
6948
|
+
});
|
|
6949
|
+
if (rows.length === 0) {
|
|
6950
|
+
matchedAll = false;
|
|
6951
|
+
break;
|
|
6952
|
+
}
|
|
6953
|
+
matches.push(facet);
|
|
6954
|
+
}
|
|
6955
|
+
if (matchedAll) {
|
|
6956
|
+
filtered.push({ ...result, facetMatches: matches });
|
|
6957
|
+
}
|
|
6958
|
+
}
|
|
6959
|
+
return filtered;
|
|
6960
|
+
}
|
|
6961
|
+
debugDetailForResult(result) {
|
|
6962
|
+
const detail = {
|
|
6963
|
+
eventId: result.eventId,
|
|
6964
|
+
score: result.score,
|
|
6965
|
+
semanticScore: result.semanticScore,
|
|
6966
|
+
lexicalScore: result.lexicalScore,
|
|
6967
|
+
recencyScore: result.recencyScore
|
|
6968
|
+
};
|
|
6969
|
+
if (result.facetMatches && result.facetMatches.length > 0) {
|
|
6970
|
+
detail.facetMatches = result.facetMatches;
|
|
6971
|
+
}
|
|
6972
|
+
if (result.graphPaths && result.graphPaths.length > 0) {
|
|
6973
|
+
detail.graphPaths = result.graphPaths;
|
|
6974
|
+
}
|
|
6975
|
+
return detail;
|
|
6976
|
+
}
|
|
6977
|
+
isGraphPathResult(result) {
|
|
6978
|
+
return (result.graphPaths || []).length > 0;
|
|
5784
6979
|
}
|
|
5785
6980
|
extractProjectHash(metadata) {
|
|
5786
6981
|
if (!metadata || typeof metadata !== "object")
|
|
@@ -5924,8 +7119,60 @@ _Context:_ ${sessionContext}`;
|
|
|
5924
7119
|
return Math.ceil(text.length / 4);
|
|
5925
7120
|
}
|
|
5926
7121
|
};
|
|
5927
|
-
function
|
|
5928
|
-
|
|
7122
|
+
function uniqueEntityStartNodes(candidates) {
|
|
7123
|
+
const seen = /* @__PURE__ */ new Set();
|
|
7124
|
+
const nodes = [];
|
|
7125
|
+
for (const candidate of candidates) {
|
|
7126
|
+
if (!candidate.entityId || seen.has(candidate.entityId))
|
|
7127
|
+
continue;
|
|
7128
|
+
seen.add(candidate.entityId);
|
|
7129
|
+
nodes.push({ entityId: candidate.entityId, title: candidate.text });
|
|
7130
|
+
}
|
|
7131
|
+
return nodes;
|
|
7132
|
+
}
|
|
7133
|
+
function toRetrievalGraphPathDebug(path12, titleByEntityId) {
|
|
7134
|
+
const firstStep = path12.steps[0];
|
|
7135
|
+
const startNode = firstStep?.direction === "incoming" ? firstStep.to : firstStep?.from;
|
|
7136
|
+
const startEntityId = startNode?.type === "entity" ? startNode.id : "";
|
|
7137
|
+
return {
|
|
7138
|
+
startEntityId,
|
|
7139
|
+
startEntityTitle: titleByEntityId.get(startEntityId) ?? startNode?.name,
|
|
7140
|
+
targetId: path12.target.id,
|
|
7141
|
+
targetType: path12.target.type,
|
|
7142
|
+
hops: path12.hops,
|
|
7143
|
+
relationPath: path12.steps.map((step) => step.relationType)
|
|
7144
|
+
};
|
|
7145
|
+
}
|
|
7146
|
+
function graphPathScore(path12, hopPenalty) {
|
|
7147
|
+
const base = Math.min(0.95, Math.max(0, path12.scoreContribution));
|
|
7148
|
+
return Math.max(0.05, base - hopPenalty * Math.max(0, path12.hops - 1));
|
|
7149
|
+
}
|
|
7150
|
+
function clampGraphHops(maxHops) {
|
|
7151
|
+
if (!Number.isFinite(maxHops))
|
|
7152
|
+
return 2;
|
|
7153
|
+
return Math.min(Math.max(0, Math.trunc(maxHops)), 2);
|
|
7154
|
+
}
|
|
7155
|
+
function mergeGraphPaths(existing, incoming) {
|
|
7156
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
7157
|
+
for (const path12 of [...existing, ...incoming]) {
|
|
7158
|
+
const key = [path12.startEntityId, path12.targetType, path12.targetId, path12.hops, ...path12.relationPath].join("\0");
|
|
7159
|
+
if (!byKey.has(key))
|
|
7160
|
+
byKey.set(key, path12);
|
|
7161
|
+
}
|
|
7162
|
+
return [...byKey.values()].sort((a, b) => a.hops - b.hops || compareStable(graphPathSignature(a), graphPathSignature(b))).slice(0, 3);
|
|
7163
|
+
}
|
|
7164
|
+
function graphPathSignature(path12) {
|
|
7165
|
+
return [path12.startEntityId, path12.targetType, path12.targetId, path12.hops, ...path12.relationPath].join("|");
|
|
7166
|
+
}
|
|
7167
|
+
function compareStable(a, b) {
|
|
7168
|
+
if (a < b)
|
|
7169
|
+
return -1;
|
|
7170
|
+
if (a > b)
|
|
7171
|
+
return 1;
|
|
7172
|
+
return 0;
|
|
7173
|
+
}
|
|
7174
|
+
function createRetriever(eventStore, vectorStore, embedder, matcher, sharedOptions) {
|
|
7175
|
+
return new Retriever(eventStore, vectorStore, embedder, matcher, sharedOptions);
|
|
5929
7176
|
}
|
|
5930
7177
|
|
|
5931
7178
|
// src/core/engine/retrieval-analytics-service.ts
|
|
@@ -6111,10 +7358,15 @@ var RetrievalDisclosureService = class {
|
|
|
6111
7358
|
{
|
|
6112
7359
|
semanticScore: debug?.semanticScore,
|
|
6113
7360
|
lexicalScore: debug?.lexicalScore,
|
|
6114
|
-
recencyScore: debug?.recencyScore
|
|
7361
|
+
recencyScore: debug?.recencyScore,
|
|
7362
|
+
...debug?.facetMatches && debug.facetMatches.length > 0 ? { facetMatches: debug.facetMatches } : {},
|
|
7363
|
+
...debug?.graphPaths && debug.graphPaths.length > 0 ? { graphPaths: this.sanitizeGraphPaths(debug.graphPaths) } : {}
|
|
6115
7364
|
}
|
|
6116
7365
|
);
|
|
6117
7366
|
}
|
|
7367
|
+
sanitizeGraphPaths(graphPaths) {
|
|
7368
|
+
return sanitizeGovernanceAuditValue(graphPaths ?? []);
|
|
7369
|
+
}
|
|
6118
7370
|
eventToEnvelope(event, score, reasons, extraMetadata) {
|
|
6119
7371
|
return {
|
|
6120
7372
|
id: toDisclosureResultId(event.id),
|
|
@@ -6174,6 +7426,10 @@ var RetrievalDisclosureService = class {
|
|
|
6174
7426
|
reasons.add("keyword_match");
|
|
6175
7427
|
if ((debug?.recencyScore ?? 0) > 0)
|
|
6176
7428
|
reasons.add("recent_relevance");
|
|
7429
|
+
if ((debug?.facetMatches || []).length > 0)
|
|
7430
|
+
reasons.add("facet_match");
|
|
7431
|
+
if ((debug?.graphPaths || []).length > 0)
|
|
7432
|
+
reasons.add("entity_overlap");
|
|
6177
7433
|
if ((result.fallbackTrace || []).some((step) => step === "fallback:summary"))
|
|
6178
7434
|
reasons.add("summary_fallback");
|
|
6179
7435
|
if (memory.sessionContext)
|
|
@@ -6301,6 +7557,7 @@ var RetrievalOrchestrator = class {
|
|
|
6301
7557
|
const rerankWeights = lightweightFastRead ? void 0 : await this.getRerankWeights(options?.adaptiveRerank === true);
|
|
6302
7558
|
const projectHash = this.deps.getProjectHash();
|
|
6303
7559
|
const projectScopeMode = retrieverOptions.projectScopeMode ?? (projectHash ? "strict" : "global");
|
|
7560
|
+
const graphHop = this.resolveGraphHopOptions(retrieverOptions.graphHop);
|
|
6304
7561
|
let result;
|
|
6305
7562
|
if (retrieverOptions.includeShared && this.deps.hasSharedStore()) {
|
|
6306
7563
|
result = await this.deps.retriever.retrieveUnified(query, {
|
|
@@ -6308,6 +7565,7 @@ var RetrievalOrchestrator = class {
|
|
|
6308
7565
|
intentRewrite: retrieverOptions.intentRewrite === true,
|
|
6309
7566
|
rerankWeights,
|
|
6310
7567
|
includeShared: true,
|
|
7568
|
+
graphHop,
|
|
6311
7569
|
projectHash: projectHash || void 0,
|
|
6312
7570
|
projectScopeMode,
|
|
6313
7571
|
allowedProjectHashes: retrieverOptions.allowedProjectHashes
|
|
@@ -6317,6 +7575,7 @@ var RetrievalOrchestrator = class {
|
|
|
6317
7575
|
...retrieverOptions,
|
|
6318
7576
|
intentRewrite: retrieverOptions.intentRewrite === true,
|
|
6319
7577
|
rerankWeights,
|
|
7578
|
+
graphHop,
|
|
6320
7579
|
projectHash: projectHash || void 0,
|
|
6321
7580
|
projectScopeMode,
|
|
6322
7581
|
allowedProjectHashes: retrieverOptions.allowedProjectHashes
|
|
@@ -6378,6 +7637,29 @@ var RetrievalOrchestrator = class {
|
|
|
6378
7637
|
await this.deps.initialize();
|
|
6379
7638
|
await this.deps.accessStore.recordRetrieval(eventId, sessionId, score, query);
|
|
6380
7639
|
}
|
|
7640
|
+
resolveGraphHopOptions(callerOptions) {
|
|
7641
|
+
const graphExpansion = this.deps.memoryOperationsConfig?.graphExpansion;
|
|
7642
|
+
const durableOptions = graphExpansion?.enabled === true ? { enabled: true, maxHops: graphExpansion.maxHops } : void 0;
|
|
7643
|
+
if (!callerOptions)
|
|
7644
|
+
return durableOptions;
|
|
7645
|
+
if (!graphExpansion)
|
|
7646
|
+
return callerOptions;
|
|
7647
|
+
if (graphExpansion.enabled !== true) {
|
|
7648
|
+
return {
|
|
7649
|
+
...callerOptions,
|
|
7650
|
+
enabled: false,
|
|
7651
|
+
maxHops: graphExpansion.maxHops ?? callerOptions.maxHops
|
|
7652
|
+
};
|
|
7653
|
+
}
|
|
7654
|
+
return {
|
|
7655
|
+
enabled: callerOptions.enabled === false ? false : true,
|
|
7656
|
+
maxHops: Math.min(
|
|
7657
|
+
graphExpansion.maxHops ?? Number.POSITIVE_INFINITY,
|
|
7658
|
+
callerOptions.maxHops ?? graphExpansion.maxHops ?? 1
|
|
7659
|
+
),
|
|
7660
|
+
hopPenalty: callerOptions.hopPenalty
|
|
7661
|
+
};
|
|
7662
|
+
}
|
|
6381
7663
|
async recordAutomaticTrace(query, result, options, projectHash) {
|
|
6382
7664
|
const selectedEventIds = result.memories.map((memory) => memory.event.id);
|
|
6383
7665
|
const selectedDetails = (result.selectedDebug || []).map((detail) => ({
|
|
@@ -6519,7 +7801,8 @@ function createRetrievalServices(deps) {
|
|
|
6519
7801
|
deps.eventStore,
|
|
6520
7802
|
deps.vectorStore,
|
|
6521
7803
|
deps.embedder,
|
|
6522
|
-
deps.matcher
|
|
7804
|
+
deps.matcher,
|
|
7805
|
+
{ queryGraphExpansionEnabled: deps.memoryOperationsConfig?.graphExpansion?.enabled === true }
|
|
6523
7806
|
);
|
|
6524
7807
|
const retrievalOrchestrator = createRetrievalOrchestrator({
|
|
6525
7808
|
initialize: deps.initialize,
|
|
@@ -6527,7 +7810,8 @@ function createRetrievalServices(deps) {
|
|
|
6527
7810
|
traceStore: deps.eventStore,
|
|
6528
7811
|
accessStore: deps.eventStore,
|
|
6529
7812
|
getProjectHash: deps.getProjectHash,
|
|
6530
|
-
hasSharedStore: deps.hasSharedStore
|
|
7813
|
+
hasSharedStore: deps.hasSharedStore,
|
|
7814
|
+
memoryOperationsConfig: deps.memoryOperationsConfig
|
|
6531
7815
|
});
|
|
6532
7816
|
const retrievalDisclosureService = createRetrievalDisclosureService({
|
|
6533
7817
|
initialize: deps.initialize,
|
|
@@ -6546,13 +7830,14 @@ function createRetrievalServices(deps) {
|
|
|
6546
7830
|
retrievalAnalyticsService
|
|
6547
7831
|
};
|
|
6548
7832
|
}
|
|
6549
|
-
function defaultCreateRetriever(eventStore, vectorStore, embedder, matcher) {
|
|
7833
|
+
function defaultCreateRetriever(eventStore, vectorStore, embedder, matcher, options) {
|
|
6550
7834
|
assertDefaultRetrieverStore(eventStore);
|
|
6551
7835
|
return createRetriever(
|
|
6552
7836
|
eventStore,
|
|
6553
7837
|
vectorStore,
|
|
6554
7838
|
embedder,
|
|
6555
|
-
matcher
|
|
7839
|
+
matcher,
|
|
7840
|
+
options
|
|
6556
7841
|
);
|
|
6557
7842
|
}
|
|
6558
7843
|
function assertDefaultRetrieverStore(eventStore) {
|
|
@@ -6598,6 +7883,7 @@ function createMemoryEngineServices(options) {
|
|
|
6598
7883
|
matcher,
|
|
6599
7884
|
getProjectHash: options.getProjectHash,
|
|
6600
7885
|
hasSharedStore: options.hasSharedStore,
|
|
7886
|
+
memoryOperationsConfig: options.memoryOperationsConfig,
|
|
6601
7887
|
sharedStore: options.sharedStore
|
|
6602
7888
|
});
|
|
6603
7889
|
const ingestService = new MemoryIngestService({
|
|
@@ -7084,7 +8370,7 @@ function createSharedEventStore(dbPath) {
|
|
|
7084
8370
|
}
|
|
7085
8371
|
|
|
7086
8372
|
// src/core/shared-promoter.ts
|
|
7087
|
-
import { randomUUID as
|
|
8373
|
+
import { randomUUID as randomUUID8 } from "crypto";
|
|
7088
8374
|
var SharedPromoter = class {
|
|
7089
8375
|
constructor(sharedStore, sharedVectorStore, embedder, config) {
|
|
7090
8376
|
this.sharedStore = sharedStore;
|
|
@@ -7152,7 +8438,7 @@ var SharedPromoter = class {
|
|
|
7152
8438
|
const embeddingContent = this.createEmbeddingContent(input);
|
|
7153
8439
|
const embedding = await this.embedder.embed(embeddingContent);
|
|
7154
8440
|
await this.sharedVectorStore.upsert({
|
|
7155
|
-
id:
|
|
8441
|
+
id: randomUUID8(),
|
|
7156
8442
|
entryId,
|
|
7157
8443
|
entryType: "troubleshooting",
|
|
7158
8444
|
content: embeddingContent,
|
|
@@ -7268,7 +8554,7 @@ function createSharedPromoter(sharedStore, sharedVectorStore, embedder, config)
|
|
|
7268
8554
|
}
|
|
7269
8555
|
|
|
7270
8556
|
// src/core/shared-store.ts
|
|
7271
|
-
import { randomUUID as
|
|
8557
|
+
import { randomUUID as randomUUID9 } from "crypto";
|
|
7272
8558
|
var SharedStore = class {
|
|
7273
8559
|
constructor(sharedEventStore) {
|
|
7274
8560
|
this.sharedEventStore = sharedEventStore;
|
|
@@ -7280,7 +8566,7 @@ var SharedStore = class {
|
|
|
7280
8566
|
* Promote a verified troubleshooting entry to shared storage
|
|
7281
8567
|
*/
|
|
7282
8568
|
async promoteEntry(input) {
|
|
7283
|
-
const entryId =
|
|
8569
|
+
const entryId = randomUUID9();
|
|
7284
8570
|
await dbRun(
|
|
7285
8571
|
this.db,
|
|
7286
8572
|
`INSERT INTO shared_troubleshooting (
|
|
@@ -7814,6 +9100,7 @@ function createMemoryServiceComposition(options) {
|
|
|
7814
9100
|
getProjectHash: options.getProjectHash,
|
|
7815
9101
|
getProjectPath: options.getProjectPath,
|
|
7816
9102
|
hasSharedStore: () => sharedMemoryServices?.isEnabled() ?? false,
|
|
9103
|
+
memoryOperationsConfig: options.config.operations,
|
|
7817
9104
|
sharedStore: {
|
|
7818
9105
|
get: (entryId) => sharedMemoryServices?.getEntryForDisclosure(entryId) ?? Promise.resolve(null)
|
|
7819
9106
|
},
|
|
@@ -7934,6 +9221,18 @@ var DEFAULT_ENABLED_SHARED_STORE_CONFIG = {
|
|
|
7934
9221
|
sharedStoragePath: SHARED_STORAGE_PATH
|
|
7935
9222
|
};
|
|
7936
9223
|
var DEFAULT_SHARED_STORAGE_PATH = SHARED_STORAGE_PATH;
|
|
9224
|
+
var DISABLED_MEMORY_OPERATIONS_CONFIG = {
|
|
9225
|
+
enabled: false,
|
|
9226
|
+
facets: { enabled: true },
|
|
9227
|
+
actions: { enabled: true },
|
|
9228
|
+
retention: { enabled: false, policyVersion: "v1" },
|
|
9229
|
+
graphExpansion: { enabled: false, maxHops: 1 },
|
|
9230
|
+
lessons: { enabled: false }
|
|
9231
|
+
};
|
|
9232
|
+
var DEFAULT_ENABLED_MEMORY_OPERATIONS_CONFIG = {
|
|
9233
|
+
...DISABLED_MEMORY_OPERATIONS_CONFIG,
|
|
9234
|
+
enabled: true
|
|
9235
|
+
};
|
|
7937
9236
|
|
|
7938
9237
|
// src/services/memory-service-registry.ts
|
|
7939
9238
|
import * as path10 from "path";
|