claude-memory-layer 1.0.39 → 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 +4304 -170
- 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 +1593 -43
- package/dist/server/api/index.js.map +4 -4
- package/dist/server/index.js +1593 -43
- 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
|
@@ -2766,6 +2766,127 @@ var SQLiteEventStore = class {
|
|
|
2766
2766
|
updated_at TEXT DEFAULT (datetime('now'))
|
|
2767
2767
|
);
|
|
2768
2768
|
|
|
2769
|
+
-- Memory Operations: facet assignments (derived, rebuildable projection)
|
|
2770
|
+
CREATE TABLE IF NOT EXISTS memory_facets (
|
|
2771
|
+
id TEXT PRIMARY KEY,
|
|
2772
|
+
target_type TEXT NOT NULL,
|
|
2773
|
+
target_id TEXT NOT NULL,
|
|
2774
|
+
dimension TEXT NOT NULL,
|
|
2775
|
+
value TEXT NOT NULL,
|
|
2776
|
+
confidence REAL NOT NULL DEFAULT 1.0,
|
|
2777
|
+
source TEXT NOT NULL DEFAULT 'manual',
|
|
2778
|
+
evidence_event_ids TEXT NOT NULL DEFAULT '[]',
|
|
2779
|
+
project_hash TEXT NOT NULL DEFAULT '',
|
|
2780
|
+
created_at TEXT NOT NULL,
|
|
2781
|
+
updated_at TEXT NOT NULL,
|
|
2782
|
+
UNIQUE(target_type, target_id, dimension, value, source, project_hash)
|
|
2783
|
+
);
|
|
2784
|
+
|
|
2785
|
+
-- Memory Operations: operational action projection
|
|
2786
|
+
CREATE TABLE IF NOT EXISTS memory_actions (
|
|
2787
|
+
action_id TEXT PRIMARY KEY,
|
|
2788
|
+
project_hash TEXT NOT NULL,
|
|
2789
|
+
title TEXT NOT NULL,
|
|
2790
|
+
status TEXT NOT NULL DEFAULT 'pending',
|
|
2791
|
+
priority INTEGER NOT NULL DEFAULT 0,
|
|
2792
|
+
source_event_ids TEXT NOT NULL DEFAULT '[]',
|
|
2793
|
+
related_entity_ids TEXT NOT NULL DEFAULT '[]',
|
|
2794
|
+
current_checkpoint_id TEXT,
|
|
2795
|
+
lease_id TEXT,
|
|
2796
|
+
created_at TEXT NOT NULL,
|
|
2797
|
+
updated_at TEXT NOT NULL
|
|
2798
|
+
);
|
|
2799
|
+
|
|
2800
|
+
-- Memory Operations: action dependency/reference edges
|
|
2801
|
+
CREATE TABLE IF NOT EXISTS memory_action_edges (
|
|
2802
|
+
edge_id TEXT PRIMARY KEY,
|
|
2803
|
+
src_action_id TEXT NOT NULL,
|
|
2804
|
+
rel_type TEXT NOT NULL,
|
|
2805
|
+
dst_type TEXT NOT NULL,
|
|
2806
|
+
dst_id TEXT NOT NULL,
|
|
2807
|
+
confidence REAL NOT NULL DEFAULT 1.0,
|
|
2808
|
+
source TEXT NOT NULL DEFAULT 'manual',
|
|
2809
|
+
created_at TEXT NOT NULL,
|
|
2810
|
+
UNIQUE(src_action_id, rel_type, dst_type, dst_id, source)
|
|
2811
|
+
);
|
|
2812
|
+
|
|
2813
|
+
-- Memory Operations: short-lived leases for operational work
|
|
2814
|
+
CREATE TABLE IF NOT EXISTS memory_leases (
|
|
2815
|
+
lease_id TEXT PRIMARY KEY,
|
|
2816
|
+
target_type TEXT NOT NULL,
|
|
2817
|
+
target_id TEXT NOT NULL,
|
|
2818
|
+
holder TEXT NOT NULL,
|
|
2819
|
+
expires_at TEXT NOT NULL,
|
|
2820
|
+
metadata_json TEXT,
|
|
2821
|
+
created_at TEXT NOT NULL,
|
|
2822
|
+
renewed_at TEXT,
|
|
2823
|
+
released_at TEXT
|
|
2824
|
+
);
|
|
2825
|
+
|
|
2826
|
+
-- Memory Operations: resumable checkpoints for delegated or long-running work
|
|
2827
|
+
CREATE TABLE IF NOT EXISTS memory_checkpoints (
|
|
2828
|
+
checkpoint_id TEXT PRIMARY KEY,
|
|
2829
|
+
project_hash TEXT NOT NULL,
|
|
2830
|
+
action_id TEXT,
|
|
2831
|
+
session_id TEXT,
|
|
2832
|
+
title TEXT NOT NULL,
|
|
2833
|
+
summary TEXT NOT NULL,
|
|
2834
|
+
state_json TEXT NOT NULL,
|
|
2835
|
+
source_event_ids TEXT NOT NULL DEFAULT '[]',
|
|
2836
|
+
created_at TEXT NOT NULL,
|
|
2837
|
+
expires_at TEXT
|
|
2838
|
+
);
|
|
2839
|
+
|
|
2840
|
+
-- Memory Operations: retention lifecycle score projection
|
|
2841
|
+
CREATE TABLE IF NOT EXISTS memory_retention_scores (
|
|
2842
|
+
score_id TEXT PRIMARY KEY,
|
|
2843
|
+
target_type TEXT NOT NULL,
|
|
2844
|
+
target_id TEXT NOT NULL,
|
|
2845
|
+
project_hash TEXT NOT NULL,
|
|
2846
|
+
policy_version TEXT NOT NULL,
|
|
2847
|
+
decision TEXT NOT NULL,
|
|
2848
|
+
lifecycle_score REAL NOT NULL,
|
|
2849
|
+
factors_json TEXT NOT NULL,
|
|
2850
|
+
reasons_json TEXT NOT NULL,
|
|
2851
|
+
dry_run_diff_json TEXT NOT NULL,
|
|
2852
|
+
source_event_ids TEXT NOT NULL DEFAULT '[]',
|
|
2853
|
+
evaluated_at TEXT NOT NULL,
|
|
2854
|
+
created_at TEXT NOT NULL,
|
|
2855
|
+
updated_at TEXT NOT NULL,
|
|
2856
|
+
UNIQUE(target_type, target_id, project_hash, policy_version)
|
|
2857
|
+
);
|
|
2858
|
+
|
|
2859
|
+
-- Memory Operations: procedural lessons derived from successful workflows
|
|
2860
|
+
CREATE TABLE IF NOT EXISTS memory_lessons (
|
|
2861
|
+
lesson_id TEXT PRIMARY KEY,
|
|
2862
|
+
project_hash TEXT NOT NULL DEFAULT '',
|
|
2863
|
+
name TEXT NOT NULL,
|
|
2864
|
+
trigger TEXT NOT NULL,
|
|
2865
|
+
steps_json TEXT NOT NULL,
|
|
2866
|
+
confidence REAL NOT NULL,
|
|
2867
|
+
source_session_ids TEXT NOT NULL DEFAULT '[]',
|
|
2868
|
+
source_event_ids TEXT NOT NULL DEFAULT '[]',
|
|
2869
|
+
failure_modes_json TEXT NOT NULL DEFAULT '[]',
|
|
2870
|
+
skill_candidate INTEGER NOT NULL DEFAULT 0,
|
|
2871
|
+
created_at TEXT NOT NULL,
|
|
2872
|
+
updated_at TEXT NOT NULL,
|
|
2873
|
+
UNIQUE(project_hash, name)
|
|
2874
|
+
);
|
|
2875
|
+
|
|
2876
|
+
-- Memory Operations: governance/audit trail for state-changing operations
|
|
2877
|
+
CREATE TABLE IF NOT EXISTS memory_governance_audit (
|
|
2878
|
+
audit_id TEXT PRIMARY KEY,
|
|
2879
|
+
operation TEXT NOT NULL,
|
|
2880
|
+
actor TEXT NOT NULL,
|
|
2881
|
+
project_hash TEXT,
|
|
2882
|
+
target_type TEXT NOT NULL,
|
|
2883
|
+
target_id TEXT NOT NULL,
|
|
2884
|
+
before_json TEXT,
|
|
2885
|
+
after_json TEXT,
|
|
2886
|
+
source_event_ids TEXT NOT NULL DEFAULT '[]',
|
|
2887
|
+
created_at TEXT NOT NULL
|
|
2888
|
+
);
|
|
2889
|
+
|
|
2769
2890
|
-- Create indexes
|
|
2770
2891
|
CREATE INDEX IF NOT EXISTS idx_events_session ON events(session_id);
|
|
2771
2892
|
CREATE INDEX IF NOT EXISTS idx_events_timestamp ON events(timestamp);
|
|
@@ -2792,6 +2913,23 @@ var SQLiteEventStore = class {
|
|
|
2792
2913
|
CREATE INDEX IF NOT EXISTS idx_retrieval_traces_created_at ON retrieval_traces(created_at DESC);
|
|
2793
2914
|
CREATE INDEX IF NOT EXISTS idx_retrieval_traces_project_hash ON retrieval_traces(project_hash);
|
|
2794
2915
|
CREATE INDEX IF NOT EXISTS idx_retrieval_traces_session_id ON retrieval_traces(session_id);
|
|
2916
|
+
CREATE INDEX IF NOT EXISTS idx_memory_facets_project_dimension_value ON memory_facets(project_hash, dimension, value);
|
|
2917
|
+
CREATE INDEX IF NOT EXISTS idx_memory_facets_target ON memory_facets(target_type, target_id);
|
|
2918
|
+
CREATE INDEX IF NOT EXISTS idx_memory_facets_dimension_value_confidence ON memory_facets(dimension, value, confidence DESC);
|
|
2919
|
+
CREATE INDEX IF NOT EXISTS idx_memory_actions_project_status_priority ON memory_actions(project_hash, status, priority DESC, updated_at DESC);
|
|
2920
|
+
CREATE INDEX IF NOT EXISTS idx_memory_action_edges_src ON memory_action_edges(src_action_id, rel_type);
|
|
2921
|
+
CREATE INDEX IF NOT EXISTS idx_memory_action_edges_dst ON memory_action_edges(dst_type, dst_id);
|
|
2922
|
+
CREATE INDEX IF NOT EXISTS idx_memory_leases_target_expires ON memory_leases(target_type, target_id, expires_at);
|
|
2923
|
+
CREATE INDEX IF NOT EXISTS idx_memory_checkpoints_project_action_created ON memory_checkpoints(project_hash, action_id, created_at DESC);
|
|
2924
|
+
CREATE INDEX IF NOT EXISTS idx_memory_checkpoints_project_session_created ON memory_checkpoints(project_hash, session_id, created_at DESC);
|
|
2925
|
+
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);
|
|
2926
|
+
CREATE INDEX IF NOT EXISTS idx_memory_retention_scores_target ON memory_retention_scores(target_type, target_id, project_hash);
|
|
2927
|
+
CREATE INDEX IF NOT EXISTS idx_memory_retention_scores_policy_evaluated ON memory_retention_scores(policy_version, evaluated_at DESC);
|
|
2928
|
+
CREATE INDEX IF NOT EXISTS idx_memory_lessons_project_confidence ON memory_lessons(project_hash, confidence DESC, updated_at DESC);
|
|
2929
|
+
CREATE INDEX IF NOT EXISTS idx_memory_lessons_skill_candidate ON memory_lessons(project_hash, skill_candidate, confidence DESC);
|
|
2930
|
+
CREATE INDEX IF NOT EXISTS idx_memory_lessons_updated ON memory_lessons(updated_at DESC);
|
|
2931
|
+
CREATE INDEX IF NOT EXISTS idx_memory_governance_audit_project_operation ON memory_governance_audit(project_hash, operation, created_at DESC);
|
|
2932
|
+
CREATE INDEX IF NOT EXISTS idx_memory_governance_audit_target ON memory_governance_audit(target_type, target_id, created_at DESC);
|
|
2795
2933
|
|
|
2796
2934
|
-- FTS5 Full-Text Search for fast keyword search
|
|
2797
2935
|
CREATE VIRTUAL TABLE IF NOT EXISTS events_fts USING fts5(
|
|
@@ -2814,6 +2952,48 @@ var SQLiteEventStore = class {
|
|
|
2814
2952
|
INSERT INTO events_fts(rowid, content, event_id) VALUES (NEW.rowid, NEW.content, NEW.id);
|
|
2815
2953
|
END;
|
|
2816
2954
|
`);
|
|
2955
|
+
try {
|
|
2956
|
+
sqliteExec(this.db, `ALTER TABLE memory_action_edges ADD COLUMN source TEXT NOT NULL DEFAULT 'manual';`);
|
|
2957
|
+
} catch {
|
|
2958
|
+
}
|
|
2959
|
+
try {
|
|
2960
|
+
const edgeIndexes = sqliteAll(this.db, `PRAGMA index_list(memory_action_edges)`, []);
|
|
2961
|
+
const hasSourceAwareUnique = edgeIndexes.some((index) => {
|
|
2962
|
+
if (Number(index.unique) !== 1)
|
|
2963
|
+
return false;
|
|
2964
|
+
if (!/^[A-Za-z0-9_]+$/.test(index.name))
|
|
2965
|
+
return false;
|
|
2966
|
+
const escapedName = index.name.replace(/"/g, '""');
|
|
2967
|
+
const columns = sqliteAll(this.db, 'PRAGMA index_info("' + escapedName + '")', []).map((column) => column.name);
|
|
2968
|
+
return columns.length === 5 && columns[0] === "src_action_id" && columns[1] === "rel_type" && columns[2] === "dst_type" && columns[3] === "dst_id" && columns[4] === "source";
|
|
2969
|
+
});
|
|
2970
|
+
if (!hasSourceAwareUnique) {
|
|
2971
|
+
sqliteExec(this.db, `
|
|
2972
|
+
DROP TABLE IF EXISTS memory_action_edges_v2;
|
|
2973
|
+
CREATE TABLE memory_action_edges_v2 (
|
|
2974
|
+
edge_id TEXT PRIMARY KEY,
|
|
2975
|
+
src_action_id TEXT NOT NULL,
|
|
2976
|
+
rel_type TEXT NOT NULL,
|
|
2977
|
+
dst_type TEXT NOT NULL,
|
|
2978
|
+
dst_id TEXT NOT NULL,
|
|
2979
|
+
confidence REAL NOT NULL DEFAULT 1.0,
|
|
2980
|
+
source TEXT NOT NULL DEFAULT 'manual',
|
|
2981
|
+
created_at TEXT NOT NULL,
|
|
2982
|
+
UNIQUE(src_action_id, rel_type, dst_type, dst_id, source)
|
|
2983
|
+
);
|
|
2984
|
+
INSERT OR IGNORE INTO memory_action_edges_v2 (
|
|
2985
|
+
edge_id, src_action_id, rel_type, dst_type, dst_id, confidence, source, created_at
|
|
2986
|
+
)
|
|
2987
|
+
SELECT edge_id, src_action_id, rel_type, dst_type, dst_id, confidence, source, created_at
|
|
2988
|
+
FROM memory_action_edges;
|
|
2989
|
+
DROP TABLE memory_action_edges;
|
|
2990
|
+
ALTER TABLE memory_action_edges_v2 RENAME TO memory_action_edges;
|
|
2991
|
+
CREATE INDEX IF NOT EXISTS idx_memory_action_edges_src ON memory_action_edges(src_action_id, rel_type);
|
|
2992
|
+
CREATE INDEX IF NOT EXISTS idx_memory_action_edges_dst ON memory_action_edges(dst_type, dst_id);
|
|
2993
|
+
`);
|
|
2994
|
+
}
|
|
2995
|
+
} catch {
|
|
2996
|
+
}
|
|
2817
2997
|
try {
|
|
2818
2998
|
sqliteExec(this.db, `ALTER TABLE retrieval_traces ADD COLUMN selected_details_json TEXT;`);
|
|
2819
2999
|
} catch {
|
|
@@ -4882,6 +5062,896 @@ var MemoryQueryService = class {
|
|
|
4882
5062
|
}
|
|
4883
5063
|
};
|
|
4884
5064
|
|
|
5065
|
+
// src/core/operations/facet-repository.ts
|
|
5066
|
+
import { randomUUID as randomUUID7 } from "crypto";
|
|
5067
|
+
|
|
5068
|
+
// src/core/operations/facets.ts
|
|
5069
|
+
import { z } from "zod";
|
|
5070
|
+
var FacetTargetTypeSchema = z.enum([
|
|
5071
|
+
"event",
|
|
5072
|
+
"entity",
|
|
5073
|
+
"edge",
|
|
5074
|
+
"consolidated_memory",
|
|
5075
|
+
"lesson",
|
|
5076
|
+
"action"
|
|
5077
|
+
]);
|
|
5078
|
+
var BUILT_IN_FACET_DIMENSIONS = [
|
|
5079
|
+
"kind",
|
|
5080
|
+
"workflow",
|
|
5081
|
+
"artifact",
|
|
5082
|
+
"source",
|
|
5083
|
+
"privacy",
|
|
5084
|
+
"quality",
|
|
5085
|
+
"retention",
|
|
5086
|
+
"project"
|
|
5087
|
+
];
|
|
5088
|
+
var customDimensionPattern = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/;
|
|
5089
|
+
var TrimmedStringSchema = z.preprocess(
|
|
5090
|
+
(value) => typeof value === "string" ? value.trim() : value,
|
|
5091
|
+
z.string().min(1)
|
|
5092
|
+
);
|
|
5093
|
+
var FacetDimensionSchema = z.preprocess(
|
|
5094
|
+
(value) => typeof value === "string" ? value.trim() : value,
|
|
5095
|
+
z.string().min(1).max(64).refine((value) => BUILT_IN_FACET_DIMENSIONS.indexOf(value) !== -1 || customDimensionPattern.test(value), {
|
|
5096
|
+
message: "Facet dimension must be built-in or lowercase kebab-case"
|
|
5097
|
+
})
|
|
5098
|
+
);
|
|
5099
|
+
var FacetSourceSchema = z.enum(["manual", "imported", "derived", "llm", "system"]).default("manual");
|
|
5100
|
+
var EvidenceEventIdsSchema = z.preprocess(
|
|
5101
|
+
(value) => Array.isArray(value) ? value.map((item) => typeof item === "string" ? item.trim() : item).filter(Boolean) : value,
|
|
5102
|
+
z.array(z.string().min(1)).default([])
|
|
5103
|
+
);
|
|
5104
|
+
var OptionalTrimmedStringSchema = z.preprocess(
|
|
5105
|
+
(value) => typeof value === "string" ? value.trim() : value,
|
|
5106
|
+
z.string().min(1).optional()
|
|
5107
|
+
);
|
|
5108
|
+
var MemoryFacetAssignmentInputSchema = z.object({
|
|
5109
|
+
targetType: FacetTargetTypeSchema,
|
|
5110
|
+
targetId: TrimmedStringSchema,
|
|
5111
|
+
dimension: FacetDimensionSchema,
|
|
5112
|
+
value: TrimmedStringSchema,
|
|
5113
|
+
confidence: z.number().min(0).max(1).default(1),
|
|
5114
|
+
source: FacetSourceSchema,
|
|
5115
|
+
evidenceEventIds: EvidenceEventIdsSchema,
|
|
5116
|
+
projectHash: OptionalTrimmedStringSchema,
|
|
5117
|
+
actor: OptionalTrimmedStringSchema
|
|
5118
|
+
});
|
|
5119
|
+
var MemoryFacetAssignmentSchema = MemoryFacetAssignmentInputSchema.extend({
|
|
5120
|
+
id: z.string().min(1),
|
|
5121
|
+
createdAt: z.date(),
|
|
5122
|
+
updatedAt: z.date()
|
|
5123
|
+
});
|
|
5124
|
+
var FacetRemoveInputSchema = z.object({
|
|
5125
|
+
targetType: FacetTargetTypeSchema,
|
|
5126
|
+
targetId: TrimmedStringSchema,
|
|
5127
|
+
dimension: FacetDimensionSchema,
|
|
5128
|
+
value: TrimmedStringSchema,
|
|
5129
|
+
source: FacetSourceSchema,
|
|
5130
|
+
projectHash: OptionalTrimmedStringSchema,
|
|
5131
|
+
actor: OptionalTrimmedStringSchema
|
|
5132
|
+
});
|
|
5133
|
+
var FacetQuerySchema = z.object({
|
|
5134
|
+
targetType: FacetTargetTypeSchema.optional(),
|
|
5135
|
+
targetId: OptionalTrimmedStringSchema,
|
|
5136
|
+
dimension: FacetDimensionSchema.optional(),
|
|
5137
|
+
value: OptionalTrimmedStringSchema,
|
|
5138
|
+
source: FacetSourceSchema.optional(),
|
|
5139
|
+
projectHash: OptionalTrimmedStringSchema,
|
|
5140
|
+
limit: z.number().int().positive().max(500).default(100)
|
|
5141
|
+
});
|
|
5142
|
+
function parseFacetAssignmentInput(input) {
|
|
5143
|
+
return MemoryFacetAssignmentInputSchema.parse(input);
|
|
5144
|
+
}
|
|
5145
|
+
function parseFacetRemoveInput(input) {
|
|
5146
|
+
return FacetRemoveInputSchema.parse(input);
|
|
5147
|
+
}
|
|
5148
|
+
function parseFacetQuery(input) {
|
|
5149
|
+
return FacetQuerySchema.parse(input ?? {});
|
|
5150
|
+
}
|
|
5151
|
+
|
|
5152
|
+
// src/core/operations/governance-audit.ts
|
|
5153
|
+
import { randomUUID as randomUUID6 } from "crypto";
|
|
5154
|
+
var MEMORY_GOVERNANCE_AUDIT_OPERATIONS = [
|
|
5155
|
+
"facet_tag",
|
|
5156
|
+
"action_update",
|
|
5157
|
+
"lease_acquire",
|
|
5158
|
+
"checkpoint_create",
|
|
5159
|
+
"retention_score",
|
|
5160
|
+
"quarantine",
|
|
5161
|
+
"verify",
|
|
5162
|
+
"lesson_promote"
|
|
5163
|
+
];
|
|
5164
|
+
function normalizeRequiredString(value, fieldName) {
|
|
5165
|
+
const normalized = value.trim();
|
|
5166
|
+
if (!normalized) {
|
|
5167
|
+
throw new Error(`${fieldName} is required`);
|
|
5168
|
+
}
|
|
5169
|
+
return normalized;
|
|
5170
|
+
}
|
|
5171
|
+
function normalizeOptionalString(value) {
|
|
5172
|
+
const normalized = value?.trim();
|
|
5173
|
+
return normalized ? normalized : void 0;
|
|
5174
|
+
}
|
|
5175
|
+
var REDACTED = "[REDACTED]";
|
|
5176
|
+
var sensitiveKeyPattern = /(?:api[_-]?key|secret|password|passwd|token|access[_-]?token|client[_-]?secret|crtfc[_-]?key|hashkey|serviceKey)/i;
|
|
5177
|
+
var POSIX_ABSOLUTE_PATH_PATTERN = /(^|[^A-Za-z0-9._\/\\-])\/(?!\/)[^\n\r"'<>|`]*/g;
|
|
5178
|
+
var WINDOWS_DRIVE_PATH_PATTERN = /(^|[^A-Za-z0-9._\/\\-])(?:[A-Za-z]:[\\/][^\n\r"'<>|`]*)/g;
|
|
5179
|
+
var WINDOWS_UNC_PATH_PATTERN = /(^|[^A-Za-z0-9._\/\\-])(?:\\\\[^\\\n\r"'<>|`]+\\[^\n\r"'<>|`]*)/g;
|
|
5180
|
+
var credentialQueryPattern = /\b((?:api[_-]?key|token|access[_-]?token|client[_-]?secret|crtfc[_-]?key|hashkey|serviceKey)=)[^&\s`"'<>]+/gi;
|
|
5181
|
+
var credentialAssignmentPattern = /\b((?:api[_-]?key|secret|password|passwd|token|access[_-]?token|client[_-]?secret|crtfc[_-]?key|hashkey|serviceKey)\s*[:=]\s*)[^\s`"'<>},]+/gi;
|
|
5182
|
+
function redactAbsolutePaths(value) {
|
|
5183
|
+
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}`);
|
|
5184
|
+
}
|
|
5185
|
+
function sanitizeAuditString(value) {
|
|
5186
|
+
return redactAbsolutePaths(value).replace(credentialQueryPattern, `$1${REDACTED}`).replace(credentialAssignmentPattern, `$1${REDACTED}`);
|
|
5187
|
+
}
|
|
5188
|
+
function sanitizeGovernanceAuditValue(value, key) {
|
|
5189
|
+
if (key && sensitiveKeyPattern.test(key)) {
|
|
5190
|
+
return REDACTED;
|
|
5191
|
+
}
|
|
5192
|
+
if (typeof value === "string") {
|
|
5193
|
+
return sanitizeAuditString(value);
|
|
5194
|
+
}
|
|
5195
|
+
if (value instanceof Date) {
|
|
5196
|
+
return sanitizeAuditString(value.toISOString());
|
|
5197
|
+
}
|
|
5198
|
+
if (Array.isArray(value)) {
|
|
5199
|
+
return value.map((item) => sanitizeGovernanceAuditValue(item));
|
|
5200
|
+
}
|
|
5201
|
+
if (value && typeof value === "object") {
|
|
5202
|
+
const sanitized = {};
|
|
5203
|
+
for (const [entryKey, entryValue] of Object.entries(value)) {
|
|
5204
|
+
sanitized[sanitizeAuditString(entryKey)] = sanitizeGovernanceAuditValue(entryValue, entryKey);
|
|
5205
|
+
}
|
|
5206
|
+
return sanitized;
|
|
5207
|
+
}
|
|
5208
|
+
return value;
|
|
5209
|
+
}
|
|
5210
|
+
function sanitizeAuditJson(value) {
|
|
5211
|
+
if (value === void 0)
|
|
5212
|
+
return void 0;
|
|
5213
|
+
return sanitizeGovernanceAuditValue(value);
|
|
5214
|
+
}
|
|
5215
|
+
function normalizeSourceEventIds(sourceEventIds) {
|
|
5216
|
+
return (sourceEventIds || []).map((sourceEventId) => sanitizeAuditString(sourceEventId.trim())).filter((sourceEventId) => sourceEventId.length > 0);
|
|
5217
|
+
}
|
|
5218
|
+
function normalizeOperation(operation) {
|
|
5219
|
+
if (MEMORY_GOVERNANCE_AUDIT_OPERATIONS.indexOf(operation) === -1) {
|
|
5220
|
+
throw new Error(`Unsupported governance audit operation: ${operation}`);
|
|
5221
|
+
}
|
|
5222
|
+
return operation;
|
|
5223
|
+
}
|
|
5224
|
+
async function writeGovernanceAuditEntry(db, input) {
|
|
5225
|
+
const entry = {
|
|
5226
|
+
auditId: randomUUID6(),
|
|
5227
|
+
operation: normalizeOperation(input.operation),
|
|
5228
|
+
actor: sanitizeAuditString(normalizeRequiredString(input.actor, "actor")),
|
|
5229
|
+
projectHash: normalizeOptionalString(input.projectHash),
|
|
5230
|
+
targetType: normalizeRequiredString(input.targetType, "targetType"),
|
|
5231
|
+
targetId: sanitizeAuditString(normalizeRequiredString(input.targetId, "targetId")),
|
|
5232
|
+
beforeJson: sanitizeAuditJson(input.beforeJson),
|
|
5233
|
+
afterJson: sanitizeAuditJson(input.afterJson),
|
|
5234
|
+
sourceEventIds: normalizeSourceEventIds(input.sourceEventIds),
|
|
5235
|
+
createdAt: /* @__PURE__ */ new Date()
|
|
5236
|
+
};
|
|
5237
|
+
sqliteRun(
|
|
5238
|
+
db,
|
|
5239
|
+
`INSERT INTO memory_governance_audit (
|
|
5240
|
+
audit_id, operation, actor, project_hash, target_type, target_id,
|
|
5241
|
+
before_json, after_json, source_event_ids, created_at
|
|
5242
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
5243
|
+
[
|
|
5244
|
+
entry.auditId,
|
|
5245
|
+
entry.operation,
|
|
5246
|
+
entry.actor,
|
|
5247
|
+
entry.projectHash ?? null,
|
|
5248
|
+
entry.targetType,
|
|
5249
|
+
entry.targetId,
|
|
5250
|
+
entry.beforeJson === void 0 ? null : JSON.stringify(entry.beforeJson),
|
|
5251
|
+
entry.afterJson === void 0 ? null : JSON.stringify(entry.afterJson),
|
|
5252
|
+
JSON.stringify(entry.sourceEventIds),
|
|
5253
|
+
entry.createdAt.toISOString()
|
|
5254
|
+
]
|
|
5255
|
+
);
|
|
5256
|
+
return entry;
|
|
5257
|
+
}
|
|
5258
|
+
|
|
5259
|
+
// src/core/operations/facet-repository.ts
|
|
5260
|
+
function parseStringArray(value) {
|
|
5261
|
+
if (typeof value !== "string")
|
|
5262
|
+
return [];
|
|
5263
|
+
try {
|
|
5264
|
+
const parsed = JSON.parse(value);
|
|
5265
|
+
if (!Array.isArray(parsed))
|
|
5266
|
+
return [];
|
|
5267
|
+
return parsed.filter((item) => typeof item === "string" && item.length > 0);
|
|
5268
|
+
} catch {
|
|
5269
|
+
return [];
|
|
5270
|
+
}
|
|
5271
|
+
}
|
|
5272
|
+
function projectHashToStorage(projectHash) {
|
|
5273
|
+
return projectHash ?? "";
|
|
5274
|
+
}
|
|
5275
|
+
function rowToFacet(row) {
|
|
5276
|
+
const projectHash = typeof row.project_hash === "string" && row.project_hash.length > 0 ? row.project_hash : void 0;
|
|
5277
|
+
return {
|
|
5278
|
+
id: row.id,
|
|
5279
|
+
targetType: FacetTargetTypeSchema.parse(row.target_type),
|
|
5280
|
+
targetId: row.target_id,
|
|
5281
|
+
dimension: row.dimension,
|
|
5282
|
+
value: row.value,
|
|
5283
|
+
confidence: Number(row.confidence),
|
|
5284
|
+
source: FacetSourceSchema.parse(row.source),
|
|
5285
|
+
evidenceEventIds: parseStringArray(row.evidence_event_ids),
|
|
5286
|
+
projectHash,
|
|
5287
|
+
createdAt: toDateFromSQLite(row.created_at),
|
|
5288
|
+
updatedAt: toDateFromSQLite(row.updated_at)
|
|
5289
|
+
};
|
|
5290
|
+
}
|
|
5291
|
+
function facetToAuditJson(facet) {
|
|
5292
|
+
return {
|
|
5293
|
+
id: facet.id,
|
|
5294
|
+
targetType: facet.targetType,
|
|
5295
|
+
targetId: facet.targetId,
|
|
5296
|
+
dimension: facet.dimension,
|
|
5297
|
+
value: facet.value,
|
|
5298
|
+
confidence: facet.confidence,
|
|
5299
|
+
source: facet.source,
|
|
5300
|
+
evidenceEventIds: facet.evidenceEventIds,
|
|
5301
|
+
projectHash: facet.projectHash,
|
|
5302
|
+
createdAt: facet.createdAt.toISOString(),
|
|
5303
|
+
updatedAt: facet.updatedAt.toISOString()
|
|
5304
|
+
};
|
|
5305
|
+
}
|
|
5306
|
+
var FacetRepository = class {
|
|
5307
|
+
constructor(db) {
|
|
5308
|
+
this.db = db;
|
|
5309
|
+
}
|
|
5310
|
+
async assign(input) {
|
|
5311
|
+
const assignment = parseFacetAssignmentInput(input);
|
|
5312
|
+
const existing = this.findByUniqueKey(assignment);
|
|
5313
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
5314
|
+
if (existing) {
|
|
5315
|
+
sqliteRun(
|
|
5316
|
+
this.db,
|
|
5317
|
+
`UPDATE memory_facets
|
|
5318
|
+
SET confidence = ?, evidence_event_ids = ?, project_hash = ?, updated_at = ?
|
|
5319
|
+
WHERE id = ?`,
|
|
5320
|
+
[
|
|
5321
|
+
assignment.confidence,
|
|
5322
|
+
JSON.stringify(assignment.evidenceEventIds),
|
|
5323
|
+
projectHashToStorage(assignment.projectHash),
|
|
5324
|
+
now,
|
|
5325
|
+
existing.id
|
|
5326
|
+
]
|
|
5327
|
+
);
|
|
5328
|
+
const saved2 = this.getById(existing.id);
|
|
5329
|
+
await this.auditAssignment(assignment, existing, saved2);
|
|
5330
|
+
return saved2;
|
|
5331
|
+
}
|
|
5332
|
+
const id = randomUUID7();
|
|
5333
|
+
sqliteRun(
|
|
5334
|
+
this.db,
|
|
5335
|
+
`INSERT INTO memory_facets (
|
|
5336
|
+
id, target_type, target_id, dimension, value, confidence, source,
|
|
5337
|
+
evidence_event_ids, project_hash, created_at, updated_at
|
|
5338
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
5339
|
+
[
|
|
5340
|
+
id,
|
|
5341
|
+
assignment.targetType,
|
|
5342
|
+
assignment.targetId,
|
|
5343
|
+
assignment.dimension,
|
|
5344
|
+
assignment.value,
|
|
5345
|
+
assignment.confidence,
|
|
5346
|
+
assignment.source,
|
|
5347
|
+
JSON.stringify(assignment.evidenceEventIds),
|
|
5348
|
+
projectHashToStorage(assignment.projectHash),
|
|
5349
|
+
now,
|
|
5350
|
+
now
|
|
5351
|
+
]
|
|
5352
|
+
);
|
|
5353
|
+
const saved = this.getById(id);
|
|
5354
|
+
await this.auditAssignment(assignment, null, saved);
|
|
5355
|
+
return saved;
|
|
5356
|
+
}
|
|
5357
|
+
async remove(input) {
|
|
5358
|
+
const removeInput = parseFacetRemoveInput(input);
|
|
5359
|
+
const { sql, params } = this.removeSql(removeInput);
|
|
5360
|
+
const result = sqliteRun(this.db, sql, params);
|
|
5361
|
+
return result.changes > 0;
|
|
5362
|
+
}
|
|
5363
|
+
async query(input) {
|
|
5364
|
+
const query = parseFacetQuery(input);
|
|
5365
|
+
const { sql, params } = this.querySql(query);
|
|
5366
|
+
const rows = sqliteAll(this.db, sql, params);
|
|
5367
|
+
return rows.map(rowToFacet);
|
|
5368
|
+
}
|
|
5369
|
+
async listForTarget(targetType, targetId) {
|
|
5370
|
+
const parsedTargetType = FacetTargetTypeSchema.parse(targetType);
|
|
5371
|
+
const trimmedTargetId = targetId.trim();
|
|
5372
|
+
if (!trimmedTargetId) {
|
|
5373
|
+
throw new Error("targetId is required");
|
|
5374
|
+
}
|
|
5375
|
+
return this.query({ targetType: parsedTargetType, targetId: trimmedTargetId, limit: 500 });
|
|
5376
|
+
}
|
|
5377
|
+
getById(id) {
|
|
5378
|
+
const row = sqliteGet(this.db, `SELECT * FROM memory_facets WHERE id = ?`, [id]);
|
|
5379
|
+
if (!row) {
|
|
5380
|
+
throw new Error(`Memory facet not found after write: ${id}`);
|
|
5381
|
+
}
|
|
5382
|
+
return rowToFacet(row);
|
|
5383
|
+
}
|
|
5384
|
+
findByUniqueKey(input) {
|
|
5385
|
+
const row = sqliteGet(
|
|
5386
|
+
this.db,
|
|
5387
|
+
`SELECT * FROM memory_facets
|
|
5388
|
+
WHERE target_type = ? AND target_id = ? AND dimension = ? AND value = ? AND source = ? AND project_hash = ?`,
|
|
5389
|
+
[
|
|
5390
|
+
input.targetType,
|
|
5391
|
+
input.targetId,
|
|
5392
|
+
input.dimension,
|
|
5393
|
+
input.value,
|
|
5394
|
+
input.source,
|
|
5395
|
+
projectHashToStorage(input.projectHash)
|
|
5396
|
+
]
|
|
5397
|
+
);
|
|
5398
|
+
return row ? rowToFacet(row) : null;
|
|
5399
|
+
}
|
|
5400
|
+
async auditAssignment(input, before, after) {
|
|
5401
|
+
await writeGovernanceAuditEntry(this.db, {
|
|
5402
|
+
operation: "facet_tag",
|
|
5403
|
+
actor: input.actor ?? "cml-core",
|
|
5404
|
+
projectHash: input.projectHash,
|
|
5405
|
+
targetType: input.targetType,
|
|
5406
|
+
targetId: input.targetId,
|
|
5407
|
+
beforeJson: before ? facetToAuditJson(before) : void 0,
|
|
5408
|
+
afterJson: facetToAuditJson(after),
|
|
5409
|
+
sourceEventIds: input.evidenceEventIds
|
|
5410
|
+
});
|
|
5411
|
+
}
|
|
5412
|
+
querySql(query) {
|
|
5413
|
+
const clauses = [];
|
|
5414
|
+
const params = [];
|
|
5415
|
+
if (query.targetType) {
|
|
5416
|
+
clauses.push("target_type = ?");
|
|
5417
|
+
params.push(query.targetType);
|
|
5418
|
+
}
|
|
5419
|
+
if (query.targetId) {
|
|
5420
|
+
clauses.push("target_id = ?");
|
|
5421
|
+
params.push(query.targetId);
|
|
5422
|
+
}
|
|
5423
|
+
if (query.dimension) {
|
|
5424
|
+
clauses.push("dimension = ?");
|
|
5425
|
+
params.push(query.dimension);
|
|
5426
|
+
}
|
|
5427
|
+
if (query.value) {
|
|
5428
|
+
clauses.push("value = ?");
|
|
5429
|
+
params.push(query.value);
|
|
5430
|
+
}
|
|
5431
|
+
if (query.source) {
|
|
5432
|
+
clauses.push("source = ?");
|
|
5433
|
+
params.push(query.source);
|
|
5434
|
+
}
|
|
5435
|
+
if (query.projectHash) {
|
|
5436
|
+
clauses.push("project_hash = ?");
|
|
5437
|
+
params.push(query.projectHash);
|
|
5438
|
+
}
|
|
5439
|
+
const where = clauses.length > 0 ? `WHERE ${clauses.join(" AND ")}` : "";
|
|
5440
|
+
params.push(query.limit);
|
|
5441
|
+
return {
|
|
5442
|
+
sql: `SELECT * FROM memory_facets ${where} ORDER BY confidence DESC, updated_at DESC LIMIT ?`,
|
|
5443
|
+
params
|
|
5444
|
+
};
|
|
5445
|
+
}
|
|
5446
|
+
removeSql(input) {
|
|
5447
|
+
const clauses = [
|
|
5448
|
+
"target_type = ?",
|
|
5449
|
+
"target_id = ?",
|
|
5450
|
+
"dimension = ?",
|
|
5451
|
+
"value = ?",
|
|
5452
|
+
"source = ?",
|
|
5453
|
+
"project_hash = ?"
|
|
5454
|
+
];
|
|
5455
|
+
const params = [
|
|
5456
|
+
input.targetType,
|
|
5457
|
+
input.targetId,
|
|
5458
|
+
input.dimension,
|
|
5459
|
+
input.value,
|
|
5460
|
+
input.source,
|
|
5461
|
+
projectHashToStorage(input.projectHash)
|
|
5462
|
+
];
|
|
5463
|
+
return {
|
|
5464
|
+
sql: `DELETE FROM memory_facets WHERE ${clauses.join(" AND ")}`,
|
|
5465
|
+
params
|
|
5466
|
+
};
|
|
5467
|
+
}
|
|
5468
|
+
};
|
|
5469
|
+
|
|
5470
|
+
// src/core/operations/graph-path-service.ts
|
|
5471
|
+
var DEFAULT_WEIGHT = 0.5;
|
|
5472
|
+
var MAX_HOPS = 2;
|
|
5473
|
+
var DEFAULT_MAX_RESULTS = 20;
|
|
5474
|
+
var MAX_RESULTS = 100;
|
|
5475
|
+
var GraphPathService = class {
|
|
5476
|
+
constructor(db) {
|
|
5477
|
+
this.db = db;
|
|
5478
|
+
}
|
|
5479
|
+
expand(input) {
|
|
5480
|
+
const graph = this.loadGraph(input.direction ?? "both");
|
|
5481
|
+
const effectiveMaxHops = normalizeMaxHops(input.maxHops);
|
|
5482
|
+
const maxResults = normalizeMaxResults(input.maxResults);
|
|
5483
|
+
const startNodes = input.startNodes.map((node) => graph.node(node));
|
|
5484
|
+
const startKeys = new Set(input.startNodes.map(nodeKey));
|
|
5485
|
+
const bestByTarget = /* @__PURE__ */ new Map();
|
|
5486
|
+
const queue = startNodes.map((node) => ({
|
|
5487
|
+
key: nodeKey(node),
|
|
5488
|
+
hops: 0,
|
|
5489
|
+
totalCost: 0,
|
|
5490
|
+
steps: [],
|
|
5491
|
+
visited: /* @__PURE__ */ new Set([nodeKey(node)])
|
|
5492
|
+
}));
|
|
5493
|
+
while (queue.length > 0) {
|
|
5494
|
+
queue.sort((a, b) => a.totalCost - b.totalCost || a.hops - b.hops || a.key.localeCompare(b.key));
|
|
5495
|
+
const current = queue.shift();
|
|
5496
|
+
if (current.hops >= effectiveMaxHops)
|
|
5497
|
+
continue;
|
|
5498
|
+
for (const edge of graph.adjacency.get(current.key) ?? []) {
|
|
5499
|
+
if (current.visited.has(edge.toKey))
|
|
5500
|
+
continue;
|
|
5501
|
+
const nextHops = current.hops + 1;
|
|
5502
|
+
const nextTotalCost = current.totalCost + edge.step.cost;
|
|
5503
|
+
const nextSteps = [...current.steps, edge.step];
|
|
5504
|
+
const nextSignature = pathSignature(nextSteps);
|
|
5505
|
+
const existing = bestByTarget.get(edge.toKey);
|
|
5506
|
+
if (!existing || isBetterPath(nextTotalCost, nextHops, nextSignature, existing)) {
|
|
5507
|
+
if (!startKeys.has(edge.toKey)) {
|
|
5508
|
+
bestByTarget.set(edge.toKey, { hops: nextHops, totalCost: nextTotalCost, signature: nextSignature, steps: nextSteps });
|
|
5509
|
+
}
|
|
5510
|
+
const nextVisited = new Set(current.visited);
|
|
5511
|
+
nextVisited.add(edge.toKey);
|
|
5512
|
+
queue.push({
|
|
5513
|
+
key: edge.toKey,
|
|
5514
|
+
hops: nextHops,
|
|
5515
|
+
totalCost: nextTotalCost,
|
|
5516
|
+
steps: nextSteps,
|
|
5517
|
+
visited: nextVisited
|
|
5518
|
+
});
|
|
5519
|
+
}
|
|
5520
|
+
}
|
|
5521
|
+
}
|
|
5522
|
+
const paths = Array.from(bestByTarget.entries()).map(([key, path12]) => ({
|
|
5523
|
+
target: graph.node(nodeFromKey(key)),
|
|
5524
|
+
hops: path12.hops,
|
|
5525
|
+
totalCost: path12.totalCost,
|
|
5526
|
+
scoreContribution: path12.totalCost > 0 ? 1 / path12.totalCost : 0,
|
|
5527
|
+
steps: path12.steps
|
|
5528
|
+
})).sort((a, b) => b.scoreContribution - a.scoreContribution || a.hops - b.hops || a.target.name.localeCompare(b.target.name)).slice(0, maxResults);
|
|
5529
|
+
return { startNodes, effectiveMaxHops, paths };
|
|
5530
|
+
}
|
|
5531
|
+
loadGraph(direction) {
|
|
5532
|
+
const entityLabels = new Map(
|
|
5533
|
+
sqliteAll(this.db, `SELECT entity_id, title FROM entities WHERE status = 'active'`).map((row) => [row.entity_id, row.title])
|
|
5534
|
+
);
|
|
5535
|
+
const labelNode = (node) => ({
|
|
5536
|
+
...node,
|
|
5537
|
+
name: node.type === "entity" ? entityLabels.get(node.id) ?? node.id : node.id
|
|
5538
|
+
});
|
|
5539
|
+
const adjacency = /* @__PURE__ */ new Map();
|
|
5540
|
+
const edges = sqliteAll(
|
|
5541
|
+
this.db,
|
|
5542
|
+
`SELECT edge_id, src_type, src_id, rel_type, dst_type, dst_id, meta_json FROM edges`
|
|
5543
|
+
);
|
|
5544
|
+
for (const edge of edges) {
|
|
5545
|
+
const src = labelNode({ type: edge.src_type, id: edge.src_id });
|
|
5546
|
+
const dst = labelNode({ type: edge.dst_type, id: edge.dst_id });
|
|
5547
|
+
const weight = edgeWeight(edge.meta_json);
|
|
5548
|
+
const cost = 1 / weight;
|
|
5549
|
+
const baseStep = {
|
|
5550
|
+
edgeId: edge.edge_id,
|
|
5551
|
+
relationType: edge.rel_type,
|
|
5552
|
+
from: src,
|
|
5553
|
+
to: dst,
|
|
5554
|
+
weight,
|
|
5555
|
+
cost,
|
|
5556
|
+
scoreContribution: weight
|
|
5557
|
+
};
|
|
5558
|
+
if (direction === "outgoing" || direction === "both") {
|
|
5559
|
+
addTraversal(adjacency, nodeKey(src), {
|
|
5560
|
+
toKey: nodeKey(dst),
|
|
5561
|
+
step: { ...baseStep, direction: "outgoing" }
|
|
5562
|
+
});
|
|
5563
|
+
}
|
|
5564
|
+
if (direction === "incoming" || direction === "both") {
|
|
5565
|
+
addTraversal(adjacency, nodeKey(dst), {
|
|
5566
|
+
toKey: nodeKey(src),
|
|
5567
|
+
step: { ...baseStep, direction: "incoming" }
|
|
5568
|
+
});
|
|
5569
|
+
}
|
|
5570
|
+
}
|
|
5571
|
+
return { adjacency, node: labelNode };
|
|
5572
|
+
}
|
|
5573
|
+
};
|
|
5574
|
+
function addTraversal(adjacency, fromKey, edge) {
|
|
5575
|
+
const edges = adjacency.get(fromKey) ?? [];
|
|
5576
|
+
edges.push(edge);
|
|
5577
|
+
adjacency.set(fromKey, edges);
|
|
5578
|
+
}
|
|
5579
|
+
function normalizeMaxHops(maxHops) {
|
|
5580
|
+
if (maxHops === void 0)
|
|
5581
|
+
return 1;
|
|
5582
|
+
if (!Number.isFinite(maxHops))
|
|
5583
|
+
return MAX_HOPS;
|
|
5584
|
+
return Math.min(Math.max(0, Math.trunc(maxHops)), MAX_HOPS);
|
|
5585
|
+
}
|
|
5586
|
+
function normalizeMaxResults(maxResults) {
|
|
5587
|
+
if (maxResults === void 0)
|
|
5588
|
+
return DEFAULT_MAX_RESULTS;
|
|
5589
|
+
if (!Number.isFinite(maxResults))
|
|
5590
|
+
return DEFAULT_MAX_RESULTS;
|
|
5591
|
+
return Math.min(Math.max(0, Math.trunc(maxResults)), MAX_RESULTS);
|
|
5592
|
+
}
|
|
5593
|
+
function isBetterPath(totalCost, hops, signature, existing) {
|
|
5594
|
+
return totalCost < existing.totalCost || totalCost === existing.totalCost && hops < existing.hops || totalCost === existing.totalCost && hops === existing.hops && signature < existing.signature;
|
|
5595
|
+
}
|
|
5596
|
+
function pathSignature(steps) {
|
|
5597
|
+
return steps.map((step) => `${step.edgeId}:${step.direction}:${nodeKey(step.from)}>${nodeKey(step.to)}`).join("|");
|
|
5598
|
+
}
|
|
5599
|
+
function edgeWeight(metaJson) {
|
|
5600
|
+
const meta = parseMeta(metaJson);
|
|
5601
|
+
const raw = meta.weight;
|
|
5602
|
+
if (typeof raw === "number" && Number.isFinite(raw) && raw > 0)
|
|
5603
|
+
return raw;
|
|
5604
|
+
if (typeof raw === "string") {
|
|
5605
|
+
const parsed = Number(raw);
|
|
5606
|
+
if (Number.isFinite(parsed) && parsed > 0)
|
|
5607
|
+
return parsed;
|
|
5608
|
+
}
|
|
5609
|
+
return DEFAULT_WEIGHT;
|
|
5610
|
+
}
|
|
5611
|
+
function parseMeta(metaJson) {
|
|
5612
|
+
if (!metaJson)
|
|
5613
|
+
return {};
|
|
5614
|
+
try {
|
|
5615
|
+
const parsed = JSON.parse(metaJson);
|
|
5616
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
|
|
5617
|
+
} catch {
|
|
5618
|
+
return {};
|
|
5619
|
+
}
|
|
5620
|
+
}
|
|
5621
|
+
function nodeKey(node) {
|
|
5622
|
+
return `${node.type}:${node.id}`;
|
|
5623
|
+
}
|
|
5624
|
+
function nodeFromKey(key) {
|
|
5625
|
+
const index = key.indexOf(":");
|
|
5626
|
+
if (index === -1)
|
|
5627
|
+
return { type: "entity", id: key };
|
|
5628
|
+
return { type: key.slice(0, index), id: key.slice(index + 1) };
|
|
5629
|
+
}
|
|
5630
|
+
|
|
5631
|
+
// src/core/operations/query-entity-extractor.ts
|
|
5632
|
+
var DEFAULT_MAX_CANDIDATES = 20;
|
|
5633
|
+
var MAX_CANDIDATES = 100;
|
|
5634
|
+
var MAX_CANDIDATE_TEXT_LENGTH = 200;
|
|
5635
|
+
var SOURCE_PRIORITY = {
|
|
5636
|
+
entity_alias: 0,
|
|
5637
|
+
quoted: 1,
|
|
5638
|
+
file_path: 2,
|
|
5639
|
+
package_identifier: 3,
|
|
5640
|
+
capitalized_term: 4
|
|
5641
|
+
};
|
|
5642
|
+
var SOURCE_CONFIDENCE = {
|
|
5643
|
+
entity_alias: 0.95,
|
|
5644
|
+
quoted: 0.85,
|
|
5645
|
+
file_path: 0.8,
|
|
5646
|
+
package_identifier: 0.75,
|
|
5647
|
+
capitalized_term: 0.6
|
|
5648
|
+
};
|
|
5649
|
+
var SENTENCE_START_STOPWORDS = /* @__PURE__ */ new Set([
|
|
5650
|
+
"A",
|
|
5651
|
+
"An",
|
|
5652
|
+
"And",
|
|
5653
|
+
"Are",
|
|
5654
|
+
"Can",
|
|
5655
|
+
"Compare",
|
|
5656
|
+
"Does",
|
|
5657
|
+
"Explain",
|
|
5658
|
+
"Find",
|
|
5659
|
+
"How",
|
|
5660
|
+
"I",
|
|
5661
|
+
"If",
|
|
5662
|
+
"In",
|
|
5663
|
+
"Is",
|
|
5664
|
+
"List",
|
|
5665
|
+
"Please",
|
|
5666
|
+
"Should",
|
|
5667
|
+
"Show",
|
|
5668
|
+
"Tell",
|
|
5669
|
+
"The",
|
|
5670
|
+
"This",
|
|
5671
|
+
"Use",
|
|
5672
|
+
"What",
|
|
5673
|
+
"When",
|
|
5674
|
+
"Where",
|
|
5675
|
+
"Which",
|
|
5676
|
+
"Why",
|
|
5677
|
+
"With"
|
|
5678
|
+
]);
|
|
5679
|
+
var QueryEntityExtractor = class {
|
|
5680
|
+
constructor(db) {
|
|
5681
|
+
this.db = db;
|
|
5682
|
+
}
|
|
5683
|
+
extract(query, options = {}) {
|
|
5684
|
+
const maxCandidates = normalizeMaxCandidates(options.maxCandidates);
|
|
5685
|
+
const candidates = [];
|
|
5686
|
+
const quotedRanges = this.extractQuoted(query, candidates);
|
|
5687
|
+
if (options.includeAliases !== false) {
|
|
5688
|
+
this.extractKnownAliases(query, candidates);
|
|
5689
|
+
}
|
|
5690
|
+
this.extractFilePaths(query, candidates);
|
|
5691
|
+
this.extractPackageIdentifiers(query, candidates);
|
|
5692
|
+
this.extractCapitalizedTerms(query, candidates, quotedRanges);
|
|
5693
|
+
return {
|
|
5694
|
+
query,
|
|
5695
|
+
candidates: dedupeAndSort(candidates).slice(0, maxCandidates).map(stripPriority)
|
|
5696
|
+
};
|
|
5697
|
+
}
|
|
5698
|
+
extractQuoted(query, candidates) {
|
|
5699
|
+
const ranges = [];
|
|
5700
|
+
const regex = /(["'`])((?:(?!\1)[^\n]){2,200})\1/g;
|
|
5701
|
+
let match;
|
|
5702
|
+
while ((match = regex.exec(query)) !== null) {
|
|
5703
|
+
const text = cleanCandidateText(match[2] ?? "");
|
|
5704
|
+
if (!isUsefulCandidate(text))
|
|
5705
|
+
continue;
|
|
5706
|
+
const start = match.index + 1;
|
|
5707
|
+
const end = start + text.length;
|
|
5708
|
+
ranges.push([match.index, match.index + match[0].length]);
|
|
5709
|
+
pushCandidate(candidates, {
|
|
5710
|
+
text,
|
|
5711
|
+
source: "quoted",
|
|
5712
|
+
start,
|
|
5713
|
+
end
|
|
5714
|
+
});
|
|
5715
|
+
}
|
|
5716
|
+
return ranges;
|
|
5717
|
+
}
|
|
5718
|
+
extractKnownAliases(query, candidates) {
|
|
5719
|
+
if (!this.db)
|
|
5720
|
+
return;
|
|
5721
|
+
const rows = sqliteAll(
|
|
5722
|
+
this.db,
|
|
5723
|
+
`SELECT
|
|
5724
|
+
a.entity_id,
|
|
5725
|
+
a.entity_type,
|
|
5726
|
+
a.canonical_key AS alias_key,
|
|
5727
|
+
e.canonical_key AS entity_canonical_key,
|
|
5728
|
+
e.title
|
|
5729
|
+
FROM entity_aliases a
|
|
5730
|
+
JOIN entities e ON e.entity_id = a.entity_id
|
|
5731
|
+
WHERE e.status = 'active'
|
|
5732
|
+
ORDER BY e.title COLLATE NOCASE, a.canonical_key COLLATE NOCASE`
|
|
5733
|
+
);
|
|
5734
|
+
const normalizedQuery = normalizeForContainment(query);
|
|
5735
|
+
const seenAliases = /* @__PURE__ */ new Set();
|
|
5736
|
+
for (const row of rows) {
|
|
5737
|
+
const aliasLabels = uniqueStrings([
|
|
5738
|
+
row.title,
|
|
5739
|
+
aliasLabelFromCanonicalKey(row.alias_key),
|
|
5740
|
+
aliasLabelFromCanonicalKey(row.entity_canonical_key)
|
|
5741
|
+
]).filter(isUsefulCandidate);
|
|
5742
|
+
for (const alias of aliasLabels) {
|
|
5743
|
+
const normalizedAlias = normalizeForContainment(alias);
|
|
5744
|
+
if (!normalizedAlias || !containsPhrase(normalizedQuery, normalizedAlias))
|
|
5745
|
+
continue;
|
|
5746
|
+
const aliasKey = `${row.entity_id}:${normalizedAlias}`;
|
|
5747
|
+
if (seenAliases.has(aliasKey))
|
|
5748
|
+
continue;
|
|
5749
|
+
seenAliases.add(aliasKey);
|
|
5750
|
+
const range = findRange(query, alias);
|
|
5751
|
+
pushCandidate(candidates, {
|
|
5752
|
+
text: row.title,
|
|
5753
|
+
source: "entity_alias",
|
|
5754
|
+
start: range.start,
|
|
5755
|
+
end: range.end,
|
|
5756
|
+
entityId: row.entity_id,
|
|
5757
|
+
entityType: row.entity_type,
|
|
5758
|
+
canonicalKey: row.entity_canonical_key,
|
|
5759
|
+
matchedAlias: normalizedAlias
|
|
5760
|
+
});
|
|
5761
|
+
}
|
|
5762
|
+
}
|
|
5763
|
+
}
|
|
5764
|
+
extractFilePaths(query, candidates) {
|
|
5765
|
+
const regex = /(^|[\s([{<])((?:\.{1,2}\/|~\/|\/)?(?:[A-Za-z0-9_.-]+\/)+[A-Za-z0-9_.-]+\.[A-Za-z0-9][A-Za-z0-9._-]*)(?=$|[\s)\]},>`.,;:!?])/g;
|
|
5766
|
+
let match;
|
|
5767
|
+
while ((match = regex.exec(query)) !== null) {
|
|
5768
|
+
const text = cleanCandidateText(match[2] ?? "");
|
|
5769
|
+
if (!isUsefulCandidate(text))
|
|
5770
|
+
continue;
|
|
5771
|
+
const start = match.index + (match[1]?.length ?? 0);
|
|
5772
|
+
pushCandidate(candidates, {
|
|
5773
|
+
text,
|
|
5774
|
+
source: "file_path",
|
|
5775
|
+
start,
|
|
5776
|
+
end: start + text.length
|
|
5777
|
+
});
|
|
5778
|
+
}
|
|
5779
|
+
}
|
|
5780
|
+
extractPackageIdentifiers(query, candidates) {
|
|
5781
|
+
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;
|
|
5782
|
+
let match;
|
|
5783
|
+
while ((match = regex.exec(query)) !== null) {
|
|
5784
|
+
const text = cleanCandidateText(match[2] ?? "");
|
|
5785
|
+
if (!isUsefulCandidate(text) || text.includes("/.") || text.includes("./"))
|
|
5786
|
+
continue;
|
|
5787
|
+
const start = match.index + (match[1]?.length ?? 0);
|
|
5788
|
+
pushCandidate(candidates, {
|
|
5789
|
+
text,
|
|
5790
|
+
source: "package_identifier",
|
|
5791
|
+
start,
|
|
5792
|
+
end: start + text.length
|
|
5793
|
+
});
|
|
5794
|
+
}
|
|
5795
|
+
}
|
|
5796
|
+
extractCapitalizedTerms(query, candidates, ignoredRanges) {
|
|
5797
|
+
const tokens = collectCapitalizedTokens(query).filter((token) => !isInsideAnyRange(token.start, ignoredRanges)).filter((token) => !SENTENCE_START_STOPWORDS.has(token.text));
|
|
5798
|
+
const groups = [];
|
|
5799
|
+
let current = [];
|
|
5800
|
+
for (const token of tokens) {
|
|
5801
|
+
const previous = current[current.length - 1];
|
|
5802
|
+
if (previous && query.slice(previous.end, token.start).match(/^\s+$/)) {
|
|
5803
|
+
current.push(token);
|
|
5804
|
+
} else {
|
|
5805
|
+
if (current.length > 0)
|
|
5806
|
+
groups.push(current);
|
|
5807
|
+
current = [token];
|
|
5808
|
+
}
|
|
5809
|
+
}
|
|
5810
|
+
if (current.length > 0)
|
|
5811
|
+
groups.push(current);
|
|
5812
|
+
for (const group of groups) {
|
|
5813
|
+
if (group.length === 1 && !isStrongSingleCapitalized(group[0].text))
|
|
5814
|
+
continue;
|
|
5815
|
+
const start = group[0].start;
|
|
5816
|
+
const end = group[group.length - 1].end;
|
|
5817
|
+
const text = query.slice(start, end);
|
|
5818
|
+
if (!isUsefulCandidate(text))
|
|
5819
|
+
continue;
|
|
5820
|
+
pushCandidate(candidates, {
|
|
5821
|
+
text,
|
|
5822
|
+
source: "capitalized_term",
|
|
5823
|
+
start,
|
|
5824
|
+
end
|
|
5825
|
+
});
|
|
5826
|
+
}
|
|
5827
|
+
}
|
|
5828
|
+
};
|
|
5829
|
+
function collectCapitalizedTokens(query) {
|
|
5830
|
+
const regex = /\b(?:[A-Z]{2,}[A-Z0-9]*|[A-Z][A-Za-z0-9]*(?:[._-][A-Za-z0-9]+)*)\b/g;
|
|
5831
|
+
const tokens = [];
|
|
5832
|
+
let match;
|
|
5833
|
+
while ((match = regex.exec(query)) !== null) {
|
|
5834
|
+
tokens.push({ text: match[0], start: match.index, end: match.index + match[0].length });
|
|
5835
|
+
}
|
|
5836
|
+
return tokens;
|
|
5837
|
+
}
|
|
5838
|
+
function pushCandidate(candidates, input) {
|
|
5839
|
+
const text = cleanCandidateText(input.text);
|
|
5840
|
+
if (!isUsefulCandidate(text))
|
|
5841
|
+
return;
|
|
5842
|
+
const source = input.source;
|
|
5843
|
+
candidates.push({
|
|
5844
|
+
...input,
|
|
5845
|
+
text,
|
|
5846
|
+
normalized: normalizeCandidate(text),
|
|
5847
|
+
confidence: input.confidence ?? SOURCE_CONFIDENCE[source],
|
|
5848
|
+
priority: SOURCE_PRIORITY[source]
|
|
5849
|
+
});
|
|
5850
|
+
}
|
|
5851
|
+
function dedupeAndSort(candidates) {
|
|
5852
|
+
const sorted = [...candidates].sort(compareCandidates);
|
|
5853
|
+
const seenAliasKeys = /* @__PURE__ */ new Set();
|
|
5854
|
+
const seenNormalized = /* @__PURE__ */ new Set();
|
|
5855
|
+
const result = [];
|
|
5856
|
+
for (const candidate of sorted) {
|
|
5857
|
+
if (candidate.source === "entity_alias") {
|
|
5858
|
+
const aliasKey = `alias:${candidate.entityId ?? ""}:${normalizeCandidate(candidate.matchedAlias ?? candidate.text)}`;
|
|
5859
|
+
if (seenAliasKeys.has(aliasKey))
|
|
5860
|
+
continue;
|
|
5861
|
+
seenAliasKeys.add(aliasKey);
|
|
5862
|
+
seenNormalized.add(candidate.normalized);
|
|
5863
|
+
result.push(candidate);
|
|
5864
|
+
continue;
|
|
5865
|
+
}
|
|
5866
|
+
if (seenNormalized.has(candidate.normalized))
|
|
5867
|
+
continue;
|
|
5868
|
+
seenNormalized.add(candidate.normalized);
|
|
5869
|
+
result.push(candidate);
|
|
5870
|
+
}
|
|
5871
|
+
return result.sort(compareCandidates);
|
|
5872
|
+
}
|
|
5873
|
+
function compareCandidates(a, b) {
|
|
5874
|
+
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 ?? "");
|
|
5875
|
+
}
|
|
5876
|
+
function compareStrings(a, b) {
|
|
5877
|
+
if (a === b)
|
|
5878
|
+
return 0;
|
|
5879
|
+
return a < b ? -1 : 1;
|
|
5880
|
+
}
|
|
5881
|
+
function stripPriority(candidate) {
|
|
5882
|
+
const { priority: _priority, ...publicCandidate } = candidate;
|
|
5883
|
+
return publicCandidate;
|
|
5884
|
+
}
|
|
5885
|
+
function normalizeMaxCandidates(maxCandidates) {
|
|
5886
|
+
if (maxCandidates === void 0)
|
|
5887
|
+
return DEFAULT_MAX_CANDIDATES;
|
|
5888
|
+
if (!Number.isFinite(maxCandidates))
|
|
5889
|
+
return DEFAULT_MAX_CANDIDATES;
|
|
5890
|
+
return Math.min(Math.max(0, Math.trunc(maxCandidates)), MAX_CANDIDATES);
|
|
5891
|
+
}
|
|
5892
|
+
function cleanCandidateText(text) {
|
|
5893
|
+
return text.normalize("NFKC").replace(/\s+/g, " ").trim().replace(/[.,;:!?]+$/g, "");
|
|
5894
|
+
}
|
|
5895
|
+
function normalizeCandidate(text) {
|
|
5896
|
+
return cleanCandidateText(text).toLowerCase();
|
|
5897
|
+
}
|
|
5898
|
+
function normalizeForContainment(text) {
|
|
5899
|
+
return text.normalize("NFKC").toLowerCase().replace(/[^\p{L}\p{N}@/._-]+/gu, " ").replace(/[._-]+/g, " ").replace(/\s+/g, " ").trim();
|
|
5900
|
+
}
|
|
5901
|
+
function containsPhrase(normalizedHaystack, normalizedNeedle) {
|
|
5902
|
+
return ` ${normalizedHaystack} `.includes(` ${normalizedNeedle} `);
|
|
5903
|
+
}
|
|
5904
|
+
function aliasLabelFromCanonicalKey(canonicalKey) {
|
|
5905
|
+
const raw = canonicalKey.includes(":") ? canonicalKey.slice(canonicalKey.lastIndexOf(":") + 1) : canonicalKey;
|
|
5906
|
+
return raw.replace(/[._-]+/g, " ").replace(/\s+/g, " ").trim();
|
|
5907
|
+
}
|
|
5908
|
+
function findRange(query, alias) {
|
|
5909
|
+
const normalizedAlias = normalizeForContainment(alias);
|
|
5910
|
+
const directIndex = query.toLowerCase().indexOf(alias.toLowerCase());
|
|
5911
|
+
if (directIndex >= 0)
|
|
5912
|
+
return { start: directIndex, end: directIndex + alias.length };
|
|
5913
|
+
const normalizedQuery = normalizeForContainment(query);
|
|
5914
|
+
const normalizedIndex = normalizedQuery.indexOf(normalizedAlias);
|
|
5915
|
+
if (normalizedIndex < 0)
|
|
5916
|
+
return { start: 0, end: 0 };
|
|
5917
|
+
const queryLower = query.toLowerCase();
|
|
5918
|
+
const words = normalizedAlias.split(" ").filter(Boolean);
|
|
5919
|
+
if (words.length === 0)
|
|
5920
|
+
return { start: 0, end: 0 };
|
|
5921
|
+
const first = queryLower.indexOf(words[0]);
|
|
5922
|
+
const lastWord = words[words.length - 1];
|
|
5923
|
+
const last = queryLower.indexOf(lastWord, first >= 0 ? first : 0);
|
|
5924
|
+
if (first >= 0 && last >= 0)
|
|
5925
|
+
return { start: first, end: last + lastWord.length };
|
|
5926
|
+
return { start: normalizedIndex, end: normalizedIndex + normalizedAlias.length };
|
|
5927
|
+
}
|
|
5928
|
+
function isUsefulCandidate(text) {
|
|
5929
|
+
const cleaned = cleanCandidateText(text);
|
|
5930
|
+
return cleaned.length >= 2 && cleaned.length <= MAX_CANDIDATE_TEXT_LENGTH && /[\p{L}\p{N}]/u.test(cleaned);
|
|
5931
|
+
}
|
|
5932
|
+
function isInsideAnyRange(index, ranges) {
|
|
5933
|
+
return ranges.some(([start, end]) => index >= start && index < end);
|
|
5934
|
+
}
|
|
5935
|
+
function isStrongSingleCapitalized(text) {
|
|
5936
|
+
if (/^[A-Z]{2,}[A-Z0-9]*$/.test(text))
|
|
5937
|
+
return true;
|
|
5938
|
+
if (/^[A-Z][a-z]+[A-Z][A-Za-z0-9]*$/.test(text))
|
|
5939
|
+
return true;
|
|
5940
|
+
return text.length >= 4 && !SENTENCE_START_STOPWORDS.has(text);
|
|
5941
|
+
}
|
|
5942
|
+
function uniqueStrings(values) {
|
|
5943
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5944
|
+
const result = [];
|
|
5945
|
+
for (const value of values) {
|
|
5946
|
+
const key = normalizeForContainment(value);
|
|
5947
|
+
if (!key || seen.has(key))
|
|
5948
|
+
continue;
|
|
5949
|
+
seen.add(key);
|
|
5950
|
+
result.push(value);
|
|
5951
|
+
}
|
|
5952
|
+
return result;
|
|
5953
|
+
}
|
|
5954
|
+
|
|
4885
5955
|
// src/core/retrieval-quality.ts
|
|
4886
5956
|
var COMMAND_ARTIFACT_PATTERNS = [
|
|
4887
5957
|
/<\/?(?:local-command-(?:stdout|stderr)|command-(?:name|message))\b/i,
|
|
@@ -5244,6 +6314,7 @@ var Retriever = class {
|
|
|
5244
6314
|
sharedVectorStore;
|
|
5245
6315
|
graduation;
|
|
5246
6316
|
queryRewriter;
|
|
6317
|
+
queryGraphExpansionEnabled;
|
|
5247
6318
|
constructor(eventStore, vectorStore, embedder, matcher, sharedOptions) {
|
|
5248
6319
|
this.eventStore = eventStore;
|
|
5249
6320
|
this.vectorStore = vectorStore;
|
|
@@ -5251,6 +6322,7 @@ var Retriever = class {
|
|
|
5251
6322
|
this.matcher = matcher;
|
|
5252
6323
|
this.sharedStore = sharedOptions?.sharedStore;
|
|
5253
6324
|
this.sharedVectorStore = sharedOptions?.sharedVectorStore;
|
|
6325
|
+
this.queryGraphExpansionEnabled = sharedOptions?.queryGraphExpansionEnabled === true;
|
|
5254
6326
|
}
|
|
5255
6327
|
setGraduationPipeline(graduation) {
|
|
5256
6328
|
this.graduation = graduation;
|
|
@@ -5296,7 +6368,8 @@ var Retriever = class {
|
|
|
5296
6368
|
graphHop: opts.graphHop,
|
|
5297
6369
|
projectScopeMode: opts.projectScopeMode,
|
|
5298
6370
|
projectHash: opts.projectHash,
|
|
5299
|
-
allowedProjectHashes: opts.allowedProjectHashes
|
|
6371
|
+
allowedProjectHashes: opts.allowedProjectHashes,
|
|
6372
|
+
facets: opts.facets
|
|
5300
6373
|
});
|
|
5301
6374
|
fallbackTrace.push(`stage:primary:${primaryStrategy}`);
|
|
5302
6375
|
if (fallbackEnabled && this.shouldFallback(current.matchResult, current.results) && primaryStrategy !== "deep") {
|
|
@@ -5313,7 +6386,8 @@ var Retriever = class {
|
|
|
5313
6386
|
graphHop: opts.graphHop,
|
|
5314
6387
|
projectScopeMode: opts.projectScopeMode,
|
|
5315
6388
|
projectHash: opts.projectHash,
|
|
5316
|
-
allowedProjectHashes: opts.allowedProjectHashes
|
|
6389
|
+
allowedProjectHashes: opts.allowedProjectHashes,
|
|
6390
|
+
facets: opts.facets
|
|
5317
6391
|
});
|
|
5318
6392
|
fallbackTrace.push("fallback:deep");
|
|
5319
6393
|
}
|
|
@@ -5331,7 +6405,8 @@ var Retriever = class {
|
|
|
5331
6405
|
graphHop: opts.graphHop,
|
|
5332
6406
|
projectScopeMode: opts.projectScopeMode,
|
|
5333
6407
|
projectHash: opts.projectHash,
|
|
5334
|
-
allowedProjectHashes: opts.allowedProjectHashes
|
|
6408
|
+
allowedProjectHashes: opts.allowedProjectHashes,
|
|
6409
|
+
facets: opts.facets
|
|
5335
6410
|
});
|
|
5336
6411
|
fallbackTrace.push("fallback:scope-expanded");
|
|
5337
6412
|
}
|
|
@@ -5341,7 +6416,8 @@ var Retriever = class {
|
|
|
5341
6416
|
scope: opts.scope,
|
|
5342
6417
|
projectScopeMode: opts.projectScopeMode,
|
|
5343
6418
|
projectHash: opts.projectHash,
|
|
5344
|
-
allowedProjectHashes: opts.allowedProjectHashes
|
|
6419
|
+
allowedProjectHashes: opts.allowedProjectHashes,
|
|
6420
|
+
facets: opts.facets
|
|
5345
6421
|
});
|
|
5346
6422
|
const filteredSummary = this.applyQualityFilters(scopedSummary, {
|
|
5347
6423
|
query,
|
|
@@ -5362,20 +6438,8 @@ var Retriever = class {
|
|
|
5362
6438
|
totalTokens: this.estimateTokens(context),
|
|
5363
6439
|
context,
|
|
5364
6440
|
fallbackTrace,
|
|
5365
|
-
selectedDebug: current.results.slice(0, opts.topK).map((r) => (
|
|
5366
|
-
|
|
5367
|
-
score: r.score,
|
|
5368
|
-
semanticScore: r.semanticScore,
|
|
5369
|
-
lexicalScore: r.lexicalScore,
|
|
5370
|
-
recencyScore: r.recencyScore
|
|
5371
|
-
})),
|
|
5372
|
-
candidateDebug: (current.candidateResults || []).slice(0, Math.max(opts.topK * 3, 20)).map((r) => ({
|
|
5373
|
-
eventId: r.eventId,
|
|
5374
|
-
score: r.score,
|
|
5375
|
-
semanticScore: r.semanticScore,
|
|
5376
|
-
lexicalScore: r.lexicalScore,
|
|
5377
|
-
recencyScore: r.recencyScore
|
|
5378
|
-
})),
|
|
6441
|
+
selectedDebug: current.results.slice(0, opts.topK).map((r) => this.debugDetailForResult(r)),
|
|
6442
|
+
candidateDebug: (current.candidateResults || []).slice(0, Math.max(opts.topK * 3, 20)).map((r) => this.debugDetailForResult(r)),
|
|
5379
6443
|
rawQueryText: current.queryRewriteKind ? query : void 0,
|
|
5380
6444
|
effectiveQueryText: current.effectiveQueryText,
|
|
5381
6445
|
queryRewriteKind: current.queryRewriteKind
|
|
@@ -5443,7 +6507,9 @@ var Retriever = class {
|
|
|
5443
6507
|
}
|
|
5444
6508
|
}
|
|
5445
6509
|
const expandedResults = input.graphHop?.enabled === false ? initialResults : await this.expandGraphHops(initialResults, {
|
|
5446
|
-
|
|
6510
|
+
query,
|
|
6511
|
+
queryGraphEnabled: this.queryGraphExpansionEnabled,
|
|
6512
|
+
maxHops: clampGraphHops(input.graphHop?.maxHops ?? 1),
|
|
5447
6513
|
hopPenalty: Math.max(0, input.graphHop?.hopPenalty ?? 0.08),
|
|
5448
6514
|
limit: input.topK * 4
|
|
5449
6515
|
});
|
|
@@ -5452,7 +6518,8 @@ var Retriever = class {
|
|
|
5452
6518
|
scope: input.scope,
|
|
5453
6519
|
projectScopeMode: input.projectScopeMode,
|
|
5454
6520
|
projectHash: input.projectHash,
|
|
5455
|
-
allowedProjectHashes: input.allowedProjectHashes
|
|
6521
|
+
allowedProjectHashes: input.allowedProjectHashes,
|
|
6522
|
+
facets: input.facets
|
|
5456
6523
|
});
|
|
5457
6524
|
const qualityFiltered = this.applyQualityFilters(filtered, {
|
|
5458
6525
|
query,
|
|
@@ -5467,9 +6534,13 @@ var Retriever = class {
|
|
|
5467
6534
|
if (isCurrentStateQuery(options.query)) {
|
|
5468
6535
|
filtered = filtered.filter((result) => !isStaleOrSupersededContent(result.content));
|
|
5469
6536
|
}
|
|
5470
|
-
filtered = filtered.filter(
|
|
6537
|
+
filtered = filtered.filter(
|
|
6538
|
+
(result) => this.isGraphPathResult(result) || hasDiscriminativeTermOverlap(options.query, result.content)
|
|
6539
|
+
);
|
|
5471
6540
|
if (shouldApplyTechnicalGuard(options.query)) {
|
|
5472
|
-
filtered = filtered.filter(
|
|
6541
|
+
filtered = filtered.filter(
|
|
6542
|
+
(result) => this.isGraphPathResult(result) || hasTechnicalTermOverlap(options.query, result.content)
|
|
6543
|
+
);
|
|
5473
6544
|
}
|
|
5474
6545
|
if (filtered.length <= 2)
|
|
5475
6546
|
return filtered;
|
|
@@ -5532,7 +6603,61 @@ var Retriever = class {
|
|
|
5532
6603
|
if (frontier.length === 0 || byId.size >= opts.limit)
|
|
5533
6604
|
break;
|
|
5534
6605
|
}
|
|
5535
|
-
|
|
6606
|
+
if (opts.queryGraphEnabled) {
|
|
6607
|
+
await this.expandQueryGraphPaths(opts.query, byId, opts);
|
|
6608
|
+
}
|
|
6609
|
+
return [...byId.values()].sort((a, b) => b.score - a.score || compareStable(a.eventId, b.eventId)).slice(0, opts.limit);
|
|
6610
|
+
}
|
|
6611
|
+
async expandQueryGraphPaths(query, byId, opts) {
|
|
6612
|
+
if (!query.trim() || !this.eventStore.getDatabase)
|
|
6613
|
+
return;
|
|
6614
|
+
try {
|
|
6615
|
+
const db = this.eventStore.getDatabase();
|
|
6616
|
+
const extraction = new QueryEntityExtractor(db).extract(query, {
|
|
6617
|
+
maxCandidates: Math.min(8, opts.limit),
|
|
6618
|
+
includeAliases: true
|
|
6619
|
+
});
|
|
6620
|
+
const startCandidates = extraction.candidates.filter((candidate) => candidate.entityId).slice(0, 8);
|
|
6621
|
+
const startNodes = uniqueEntityStartNodes(startCandidates);
|
|
6622
|
+
if (startNodes.length === 0)
|
|
6623
|
+
return;
|
|
6624
|
+
const expansion = new GraphPathService(db).expand({
|
|
6625
|
+
startNodes: startNodes.map((node) => ({ type: "entity", id: node.entityId })),
|
|
6626
|
+
maxHops: opts.maxHops,
|
|
6627
|
+
maxResults: opts.limit,
|
|
6628
|
+
direction: "both"
|
|
6629
|
+
});
|
|
6630
|
+
const titleByEntityId = new Map(startNodes.map((node) => [node.entityId, node.title]));
|
|
6631
|
+
for (const path12 of expansion.paths) {
|
|
6632
|
+
if (path12.target.type !== "event")
|
|
6633
|
+
continue;
|
|
6634
|
+
const target = await this.eventStore.getEvent(path12.target.id);
|
|
6635
|
+
if (!target)
|
|
6636
|
+
continue;
|
|
6637
|
+
const graphPath = toRetrievalGraphPathDebug(path12, titleByEntityId);
|
|
6638
|
+
const score = graphPathScore(path12, opts.hopPenalty);
|
|
6639
|
+
const existing = byId.get(target.id);
|
|
6640
|
+
const graphPaths = mergeGraphPaths(existing?.graphPaths ?? [], [graphPath]);
|
|
6641
|
+
const row = {
|
|
6642
|
+
id: existing?.id ?? `graph-path-${path12.hops}-${target.id}`,
|
|
6643
|
+
eventId: target.id,
|
|
6644
|
+
content: target.content,
|
|
6645
|
+
score: Math.max(existing?.score ?? 0, score),
|
|
6646
|
+
sessionId: target.sessionId,
|
|
6647
|
+
eventType: target.eventType,
|
|
6648
|
+
timestamp: target.timestamp.toISOString(),
|
|
6649
|
+
semanticScore: existing?.semanticScore,
|
|
6650
|
+
lexicalScore: existing?.lexicalScore,
|
|
6651
|
+
recencyScore: existing?.recencyScore,
|
|
6652
|
+
facetMatches: existing?.facetMatches,
|
|
6653
|
+
graphPaths
|
|
6654
|
+
};
|
|
6655
|
+
byId.set(row.eventId, row);
|
|
6656
|
+
if (byId.size >= opts.limit)
|
|
6657
|
+
break;
|
|
6658
|
+
}
|
|
6659
|
+
} catch {
|
|
6660
|
+
}
|
|
5536
6661
|
}
|
|
5537
6662
|
shouldFallback(matchResult, results) {
|
|
5538
6663
|
if (results.length === 0)
|
|
@@ -5623,12 +6748,13 @@ var Retriever = class {
|
|
|
5623
6748
|
async applyScopeFilters(results, options) {
|
|
5624
6749
|
const scope = options?.scope;
|
|
5625
6750
|
const projectScopeMode = options?.projectScopeMode ?? "global";
|
|
6751
|
+
const facetFilters = this.normalizeFacetFilters(options?.facets);
|
|
5626
6752
|
const allowedProjectHashes = new Set(
|
|
5627
6753
|
[options?.projectHash, ...options?.allowedProjectHashes || []].filter(
|
|
5628
6754
|
(value) => typeof value === "string" && value.length > 0
|
|
5629
6755
|
)
|
|
5630
6756
|
);
|
|
5631
|
-
if (!scope && projectScopeMode === "global")
|
|
6757
|
+
if (!scope && projectScopeMode === "global" && facetFilters === null)
|
|
5632
6758
|
return results;
|
|
5633
6759
|
const normalizedIncludes = (scope?.contentIncludes || []).map((s) => s.toLowerCase());
|
|
5634
6760
|
const filtered = [];
|
|
@@ -5654,14 +6780,83 @@ var Retriever = class {
|
|
|
5654
6780
|
const projectHash = this.extractProjectHash(event.metadata);
|
|
5655
6781
|
filtered.push({ result, projectHash });
|
|
5656
6782
|
}
|
|
6783
|
+
let scopedResults;
|
|
5657
6784
|
if (projectScopeMode === "global" || allowedProjectHashes.size === 0) {
|
|
5658
|
-
|
|
6785
|
+
scopedResults = filtered.map((x) => x.result);
|
|
6786
|
+
} else {
|
|
6787
|
+
const projectMatched = filtered.filter((x) => x.projectHash && allowedProjectHashes.has(x.projectHash));
|
|
6788
|
+
scopedResults = projectScopeMode === "strict" ? projectMatched.map((x) => x.result) : (projectMatched.length > 0 ? projectMatched : filtered).map((x) => x.result);
|
|
6789
|
+
}
|
|
6790
|
+
return this.applyFacetFilters(scopedResults, {
|
|
6791
|
+
facets: facetFilters,
|
|
6792
|
+
projectHash: options?.projectHash
|
|
6793
|
+
});
|
|
6794
|
+
}
|
|
6795
|
+
normalizeFacetFilters(facets) {
|
|
6796
|
+
if (!facets || facets.length === 0)
|
|
6797
|
+
return null;
|
|
6798
|
+
const normalized = [];
|
|
6799
|
+
for (const facet of facets) {
|
|
6800
|
+
const parsedDimension = FacetDimensionSchema.safeParse(facet.dimension);
|
|
6801
|
+
const value = typeof facet.value === "string" ? facet.value.trim() : "";
|
|
6802
|
+
if (!parsedDimension.success || !value)
|
|
6803
|
+
return [];
|
|
6804
|
+
normalized.push({ dimension: parsedDimension.data, value });
|
|
6805
|
+
}
|
|
6806
|
+
return normalized;
|
|
6807
|
+
}
|
|
6808
|
+
async applyFacetFilters(results, options) {
|
|
6809
|
+
if (options.facets === null)
|
|
6810
|
+
return results;
|
|
6811
|
+
if (options.facets.length === 0)
|
|
6812
|
+
return [];
|
|
6813
|
+
if (!options.projectHash)
|
|
6814
|
+
return [];
|
|
6815
|
+
if (!this.eventStore.getDatabase)
|
|
6816
|
+
return [];
|
|
6817
|
+
const repo = new FacetRepository(this.eventStore.getDatabase());
|
|
6818
|
+
const filtered = [];
|
|
6819
|
+
for (const result of results) {
|
|
6820
|
+
const matches = [];
|
|
6821
|
+
let matchedAll = true;
|
|
6822
|
+
for (const facet of options.facets) {
|
|
6823
|
+
const rows = await repo.query({
|
|
6824
|
+
targetType: "event",
|
|
6825
|
+
targetId: result.eventId,
|
|
6826
|
+
dimension: facet.dimension,
|
|
6827
|
+
value: facet.value,
|
|
6828
|
+
projectHash: options.projectHash
|
|
6829
|
+
});
|
|
6830
|
+
if (rows.length === 0) {
|
|
6831
|
+
matchedAll = false;
|
|
6832
|
+
break;
|
|
6833
|
+
}
|
|
6834
|
+
matches.push(facet);
|
|
6835
|
+
}
|
|
6836
|
+
if (matchedAll) {
|
|
6837
|
+
filtered.push({ ...result, facetMatches: matches });
|
|
6838
|
+
}
|
|
5659
6839
|
}
|
|
5660
|
-
|
|
5661
|
-
|
|
5662
|
-
|
|
6840
|
+
return filtered;
|
|
6841
|
+
}
|
|
6842
|
+
debugDetailForResult(result) {
|
|
6843
|
+
const detail = {
|
|
6844
|
+
eventId: result.eventId,
|
|
6845
|
+
score: result.score,
|
|
6846
|
+
semanticScore: result.semanticScore,
|
|
6847
|
+
lexicalScore: result.lexicalScore,
|
|
6848
|
+
recencyScore: result.recencyScore
|
|
6849
|
+
};
|
|
6850
|
+
if (result.facetMatches && result.facetMatches.length > 0) {
|
|
6851
|
+
detail.facetMatches = result.facetMatches;
|
|
6852
|
+
}
|
|
6853
|
+
if (result.graphPaths && result.graphPaths.length > 0) {
|
|
6854
|
+
detail.graphPaths = result.graphPaths;
|
|
5663
6855
|
}
|
|
5664
|
-
return
|
|
6856
|
+
return detail;
|
|
6857
|
+
}
|
|
6858
|
+
isGraphPathResult(result) {
|
|
6859
|
+
return (result.graphPaths || []).length > 0;
|
|
5665
6860
|
}
|
|
5666
6861
|
extractProjectHash(metadata) {
|
|
5667
6862
|
if (!metadata || typeof metadata !== "object")
|
|
@@ -5805,8 +7000,60 @@ _Context:_ ${sessionContext}`;
|
|
|
5805
7000
|
return Math.ceil(text.length / 4);
|
|
5806
7001
|
}
|
|
5807
7002
|
};
|
|
5808
|
-
function
|
|
5809
|
-
|
|
7003
|
+
function uniqueEntityStartNodes(candidates) {
|
|
7004
|
+
const seen = /* @__PURE__ */ new Set();
|
|
7005
|
+
const nodes = [];
|
|
7006
|
+
for (const candidate of candidates) {
|
|
7007
|
+
if (!candidate.entityId || seen.has(candidate.entityId))
|
|
7008
|
+
continue;
|
|
7009
|
+
seen.add(candidate.entityId);
|
|
7010
|
+
nodes.push({ entityId: candidate.entityId, title: candidate.text });
|
|
7011
|
+
}
|
|
7012
|
+
return nodes;
|
|
7013
|
+
}
|
|
7014
|
+
function toRetrievalGraphPathDebug(path12, titleByEntityId) {
|
|
7015
|
+
const firstStep = path12.steps[0];
|
|
7016
|
+
const startNode = firstStep?.direction === "incoming" ? firstStep.to : firstStep?.from;
|
|
7017
|
+
const startEntityId = startNode?.type === "entity" ? startNode.id : "";
|
|
7018
|
+
return {
|
|
7019
|
+
startEntityId,
|
|
7020
|
+
startEntityTitle: titleByEntityId.get(startEntityId) ?? startNode?.name,
|
|
7021
|
+
targetId: path12.target.id,
|
|
7022
|
+
targetType: path12.target.type,
|
|
7023
|
+
hops: path12.hops,
|
|
7024
|
+
relationPath: path12.steps.map((step) => step.relationType)
|
|
7025
|
+
};
|
|
7026
|
+
}
|
|
7027
|
+
function graphPathScore(path12, hopPenalty) {
|
|
7028
|
+
const base = Math.min(0.95, Math.max(0, path12.scoreContribution));
|
|
7029
|
+
return Math.max(0.05, base - hopPenalty * Math.max(0, path12.hops - 1));
|
|
7030
|
+
}
|
|
7031
|
+
function clampGraphHops(maxHops) {
|
|
7032
|
+
if (!Number.isFinite(maxHops))
|
|
7033
|
+
return 2;
|
|
7034
|
+
return Math.min(Math.max(0, Math.trunc(maxHops)), 2);
|
|
7035
|
+
}
|
|
7036
|
+
function mergeGraphPaths(existing, incoming) {
|
|
7037
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
7038
|
+
for (const path12 of [...existing, ...incoming]) {
|
|
7039
|
+
const key = [path12.startEntityId, path12.targetType, path12.targetId, path12.hops, ...path12.relationPath].join("\0");
|
|
7040
|
+
if (!byKey.has(key))
|
|
7041
|
+
byKey.set(key, path12);
|
|
7042
|
+
}
|
|
7043
|
+
return [...byKey.values()].sort((a, b) => a.hops - b.hops || compareStable(graphPathSignature(a), graphPathSignature(b))).slice(0, 3);
|
|
7044
|
+
}
|
|
7045
|
+
function graphPathSignature(path12) {
|
|
7046
|
+
return [path12.startEntityId, path12.targetType, path12.targetId, path12.hops, ...path12.relationPath].join("|");
|
|
7047
|
+
}
|
|
7048
|
+
function compareStable(a, b) {
|
|
7049
|
+
if (a < b)
|
|
7050
|
+
return -1;
|
|
7051
|
+
if (a > b)
|
|
7052
|
+
return 1;
|
|
7053
|
+
return 0;
|
|
7054
|
+
}
|
|
7055
|
+
function createRetriever(eventStore, vectorStore, embedder, matcher, sharedOptions) {
|
|
7056
|
+
return new Retriever(eventStore, vectorStore, embedder, matcher, sharedOptions);
|
|
5810
7057
|
}
|
|
5811
7058
|
|
|
5812
7059
|
// src/core/engine/retrieval-analytics-service.ts
|
|
@@ -5992,10 +7239,15 @@ var RetrievalDisclosureService = class {
|
|
|
5992
7239
|
{
|
|
5993
7240
|
semanticScore: debug?.semanticScore,
|
|
5994
7241
|
lexicalScore: debug?.lexicalScore,
|
|
5995
|
-
recencyScore: debug?.recencyScore
|
|
7242
|
+
recencyScore: debug?.recencyScore,
|
|
7243
|
+
...debug?.facetMatches && debug.facetMatches.length > 0 ? { facetMatches: debug.facetMatches } : {},
|
|
7244
|
+
...debug?.graphPaths && debug.graphPaths.length > 0 ? { graphPaths: this.sanitizeGraphPaths(debug.graphPaths) } : {}
|
|
5996
7245
|
}
|
|
5997
7246
|
);
|
|
5998
7247
|
}
|
|
7248
|
+
sanitizeGraphPaths(graphPaths) {
|
|
7249
|
+
return sanitizeGovernanceAuditValue(graphPaths ?? []);
|
|
7250
|
+
}
|
|
5999
7251
|
eventToEnvelope(event, score, reasons, extraMetadata) {
|
|
6000
7252
|
return {
|
|
6001
7253
|
id: toDisclosureResultId(event.id),
|
|
@@ -6055,6 +7307,10 @@ var RetrievalDisclosureService = class {
|
|
|
6055
7307
|
reasons.add("keyword_match");
|
|
6056
7308
|
if ((debug?.recencyScore ?? 0) > 0)
|
|
6057
7309
|
reasons.add("recent_relevance");
|
|
7310
|
+
if ((debug?.facetMatches || []).length > 0)
|
|
7311
|
+
reasons.add("facet_match");
|
|
7312
|
+
if ((debug?.graphPaths || []).length > 0)
|
|
7313
|
+
reasons.add("entity_overlap");
|
|
6058
7314
|
if ((result.fallbackTrace || []).some((step) => step === "fallback:summary"))
|
|
6059
7315
|
reasons.add("summary_fallback");
|
|
6060
7316
|
if (memory.sessionContext)
|
|
@@ -6182,6 +7438,7 @@ var RetrievalOrchestrator = class {
|
|
|
6182
7438
|
const rerankWeights = lightweightFastRead ? void 0 : await this.getRerankWeights(options?.adaptiveRerank === true);
|
|
6183
7439
|
const projectHash = this.deps.getProjectHash();
|
|
6184
7440
|
const projectScopeMode = retrieverOptions.projectScopeMode ?? (projectHash ? "strict" : "global");
|
|
7441
|
+
const graphHop = this.resolveGraphHopOptions(retrieverOptions.graphHop);
|
|
6185
7442
|
let result;
|
|
6186
7443
|
if (retrieverOptions.includeShared && this.deps.hasSharedStore()) {
|
|
6187
7444
|
result = await this.deps.retriever.retrieveUnified(query, {
|
|
@@ -6189,6 +7446,7 @@ var RetrievalOrchestrator = class {
|
|
|
6189
7446
|
intentRewrite: retrieverOptions.intentRewrite === true,
|
|
6190
7447
|
rerankWeights,
|
|
6191
7448
|
includeShared: true,
|
|
7449
|
+
graphHop,
|
|
6192
7450
|
projectHash: projectHash || void 0,
|
|
6193
7451
|
projectScopeMode,
|
|
6194
7452
|
allowedProjectHashes: retrieverOptions.allowedProjectHashes
|
|
@@ -6198,6 +7456,7 @@ var RetrievalOrchestrator = class {
|
|
|
6198
7456
|
...retrieverOptions,
|
|
6199
7457
|
intentRewrite: retrieverOptions.intentRewrite === true,
|
|
6200
7458
|
rerankWeights,
|
|
7459
|
+
graphHop,
|
|
6201
7460
|
projectHash: projectHash || void 0,
|
|
6202
7461
|
projectScopeMode,
|
|
6203
7462
|
allowedProjectHashes: retrieverOptions.allowedProjectHashes
|
|
@@ -6259,6 +7518,29 @@ var RetrievalOrchestrator = class {
|
|
|
6259
7518
|
await this.deps.initialize();
|
|
6260
7519
|
await this.deps.accessStore.recordRetrieval(eventId, sessionId, score, query);
|
|
6261
7520
|
}
|
|
7521
|
+
resolveGraphHopOptions(callerOptions) {
|
|
7522
|
+
const graphExpansion = this.deps.memoryOperationsConfig?.graphExpansion;
|
|
7523
|
+
const durableOptions = graphExpansion?.enabled === true ? { enabled: true, maxHops: graphExpansion.maxHops } : void 0;
|
|
7524
|
+
if (!callerOptions)
|
|
7525
|
+
return durableOptions;
|
|
7526
|
+
if (!graphExpansion)
|
|
7527
|
+
return callerOptions;
|
|
7528
|
+
if (graphExpansion.enabled !== true) {
|
|
7529
|
+
return {
|
|
7530
|
+
...callerOptions,
|
|
7531
|
+
enabled: false,
|
|
7532
|
+
maxHops: graphExpansion.maxHops ?? callerOptions.maxHops
|
|
7533
|
+
};
|
|
7534
|
+
}
|
|
7535
|
+
return {
|
|
7536
|
+
enabled: callerOptions.enabled === false ? false : true,
|
|
7537
|
+
maxHops: Math.min(
|
|
7538
|
+
graphExpansion.maxHops ?? Number.POSITIVE_INFINITY,
|
|
7539
|
+
callerOptions.maxHops ?? graphExpansion.maxHops ?? 1
|
|
7540
|
+
),
|
|
7541
|
+
hopPenalty: callerOptions.hopPenalty
|
|
7542
|
+
};
|
|
7543
|
+
}
|
|
6262
7544
|
async recordAutomaticTrace(query, result, options, projectHash) {
|
|
6263
7545
|
const selectedEventIds = result.memories.map((memory) => memory.event.id);
|
|
6264
7546
|
const selectedDetails = (result.selectedDebug || []).map((detail) => ({
|
|
@@ -6400,7 +7682,8 @@ function createRetrievalServices(deps) {
|
|
|
6400
7682
|
deps.eventStore,
|
|
6401
7683
|
deps.vectorStore,
|
|
6402
7684
|
deps.embedder,
|
|
6403
|
-
deps.matcher
|
|
7685
|
+
deps.matcher,
|
|
7686
|
+
{ queryGraphExpansionEnabled: deps.memoryOperationsConfig?.graphExpansion?.enabled === true }
|
|
6404
7687
|
);
|
|
6405
7688
|
const retrievalOrchestrator = createRetrievalOrchestrator({
|
|
6406
7689
|
initialize: deps.initialize,
|
|
@@ -6408,7 +7691,8 @@ function createRetrievalServices(deps) {
|
|
|
6408
7691
|
traceStore: deps.eventStore,
|
|
6409
7692
|
accessStore: deps.eventStore,
|
|
6410
7693
|
getProjectHash: deps.getProjectHash,
|
|
6411
|
-
hasSharedStore: deps.hasSharedStore
|
|
7694
|
+
hasSharedStore: deps.hasSharedStore,
|
|
7695
|
+
memoryOperationsConfig: deps.memoryOperationsConfig
|
|
6412
7696
|
});
|
|
6413
7697
|
const retrievalDisclosureService = createRetrievalDisclosureService({
|
|
6414
7698
|
initialize: deps.initialize,
|
|
@@ -6427,13 +7711,14 @@ function createRetrievalServices(deps) {
|
|
|
6427
7711
|
retrievalAnalyticsService
|
|
6428
7712
|
};
|
|
6429
7713
|
}
|
|
6430
|
-
function defaultCreateRetriever(eventStore, vectorStore, embedder, matcher) {
|
|
7714
|
+
function defaultCreateRetriever(eventStore, vectorStore, embedder, matcher, options) {
|
|
6431
7715
|
assertDefaultRetrieverStore(eventStore);
|
|
6432
7716
|
return createRetriever(
|
|
6433
7717
|
eventStore,
|
|
6434
7718
|
vectorStore,
|
|
6435
7719
|
embedder,
|
|
6436
|
-
matcher
|
|
7720
|
+
matcher,
|
|
7721
|
+
options
|
|
6437
7722
|
);
|
|
6438
7723
|
}
|
|
6439
7724
|
function assertDefaultRetrieverStore(eventStore) {
|
|
@@ -6479,6 +7764,7 @@ function createMemoryEngineServices(options) {
|
|
|
6479
7764
|
matcher,
|
|
6480
7765
|
getProjectHash: options.getProjectHash,
|
|
6481
7766
|
hasSharedStore: options.hasSharedStore,
|
|
7767
|
+
memoryOperationsConfig: options.memoryOperationsConfig,
|
|
6482
7768
|
sharedStore: options.sharedStore
|
|
6483
7769
|
});
|
|
6484
7770
|
const ingestService = new MemoryIngestService({
|
|
@@ -6965,7 +8251,7 @@ function createSharedEventStore(dbPath) {
|
|
|
6965
8251
|
}
|
|
6966
8252
|
|
|
6967
8253
|
// src/core/shared-promoter.ts
|
|
6968
|
-
import { randomUUID as
|
|
8254
|
+
import { randomUUID as randomUUID8 } from "crypto";
|
|
6969
8255
|
var SharedPromoter = class {
|
|
6970
8256
|
constructor(sharedStore, sharedVectorStore, embedder, config) {
|
|
6971
8257
|
this.sharedStore = sharedStore;
|
|
@@ -7033,7 +8319,7 @@ var SharedPromoter = class {
|
|
|
7033
8319
|
const embeddingContent = this.createEmbeddingContent(input);
|
|
7034
8320
|
const embedding = await this.embedder.embed(embeddingContent);
|
|
7035
8321
|
await this.sharedVectorStore.upsert({
|
|
7036
|
-
id:
|
|
8322
|
+
id: randomUUID8(),
|
|
7037
8323
|
entryId,
|
|
7038
8324
|
entryType: "troubleshooting",
|
|
7039
8325
|
content: embeddingContent,
|
|
@@ -7149,7 +8435,7 @@ function createSharedPromoter(sharedStore, sharedVectorStore, embedder, config)
|
|
|
7149
8435
|
}
|
|
7150
8436
|
|
|
7151
8437
|
// src/core/shared-store.ts
|
|
7152
|
-
import { randomUUID as
|
|
8438
|
+
import { randomUUID as randomUUID9 } from "crypto";
|
|
7153
8439
|
var SharedStore = class {
|
|
7154
8440
|
constructor(sharedEventStore) {
|
|
7155
8441
|
this.sharedEventStore = sharedEventStore;
|
|
@@ -7161,7 +8447,7 @@ var SharedStore = class {
|
|
|
7161
8447
|
* Promote a verified troubleshooting entry to shared storage
|
|
7162
8448
|
*/
|
|
7163
8449
|
async promoteEntry(input) {
|
|
7164
|
-
const entryId =
|
|
8450
|
+
const entryId = randomUUID9();
|
|
7165
8451
|
await dbRun(
|
|
7166
8452
|
this.db,
|
|
7167
8453
|
`INSERT INTO shared_troubleshooting (
|
|
@@ -7695,6 +8981,7 @@ function createMemoryServiceComposition(options) {
|
|
|
7695
8981
|
getProjectHash: options.getProjectHash,
|
|
7696
8982
|
getProjectPath: options.getProjectPath,
|
|
7697
8983
|
hasSharedStore: () => sharedMemoryServices?.isEnabled() ?? false,
|
|
8984
|
+
memoryOperationsConfig: options.config.operations,
|
|
7698
8985
|
sharedStore: {
|
|
7699
8986
|
get: (entryId) => sharedMemoryServices?.getEntryForDisclosure(entryId) ?? Promise.resolve(null)
|
|
7700
8987
|
},
|
|
@@ -7840,6 +9127,18 @@ var DEFAULT_ENABLED_SHARED_STORE_CONFIG = {
|
|
|
7840
9127
|
sharedStoragePath: SHARED_STORAGE_PATH
|
|
7841
9128
|
};
|
|
7842
9129
|
var DEFAULT_SHARED_STORAGE_PATH = SHARED_STORAGE_PATH;
|
|
9130
|
+
var DISABLED_MEMORY_OPERATIONS_CONFIG = {
|
|
9131
|
+
enabled: false,
|
|
9132
|
+
facets: { enabled: true },
|
|
9133
|
+
actions: { enabled: true },
|
|
9134
|
+
retention: { enabled: false, policyVersion: "v1" },
|
|
9135
|
+
graphExpansion: { enabled: false, maxHops: 1 },
|
|
9136
|
+
lessons: { enabled: false }
|
|
9137
|
+
};
|
|
9138
|
+
var DEFAULT_ENABLED_MEMORY_OPERATIONS_CONFIG = {
|
|
9139
|
+
...DISABLED_MEMORY_OPERATIONS_CONFIG,
|
|
9140
|
+
enabled: true
|
|
9141
|
+
};
|
|
7843
9142
|
|
|
7844
9143
|
// src/services/memory-service-registry.ts
|
|
7845
9144
|
import * as path10 from "path";
|