@orquentra/codebuddy 2.6.0 → 2.7.0
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/CLAUDE_CODEX.md +6 -4
- package/WORKFLOW.md +5 -5
- package/dist/{chunk-U4HAJLIB.js → chunk-3QGW53GD.js} +16 -3
- package/dist/chunk-3QGW53GD.js.map +1 -0
- package/dist/{chunk-L5AYZC3F.js → chunk-5EDGAZQN.js} +438 -68
- package/dist/chunk-5EDGAZQN.js.map +1 -0
- package/dist/chunk-5UH3BEQR.js +32 -0
- package/dist/chunk-5UH3BEQR.js.map +1 -0
- package/dist/{chunk-JFDRBPYF.js → chunk-6JCKMIQY.js} +2 -2
- package/dist/{chunk-UN57FKQX.js → chunk-AK2UAE32.js} +152 -862
- package/dist/chunk-AK2UAE32.js.map +1 -0
- package/dist/{chunk-GDJH7XJC.js → chunk-AL6VBTNA.js} +77 -3
- package/dist/chunk-AL6VBTNA.js.map +1 -0
- package/dist/{chunk-WHZSVY2L.js → chunk-AQHPEJR6.js} +94 -23
- package/dist/chunk-AQHPEJR6.js.map +1 -0
- package/dist/{chunk-PCE4STAY.js → chunk-BQM7FJ7D.js} +6 -6
- package/dist/chunk-BQM7FJ7D.js.map +1 -0
- package/dist/{chunk-XBJQKCLU.js → chunk-CAEPHLHC.js} +13 -9
- package/dist/chunk-CAEPHLHC.js.map +1 -0
- package/dist/chunk-CG5B3LYG.js +66 -0
- package/dist/chunk-CG5B3LYG.js.map +1 -0
- package/dist/chunk-CSRD5OQA.js +79 -0
- package/dist/chunk-CSRD5OQA.js.map +1 -0
- package/dist/{chunk-IHQM2ZDV.js → chunk-DFV2SK6C.js} +1 -1
- package/dist/{chunk-IHQM2ZDV.js.map → chunk-DFV2SK6C.js.map} +1 -1
- package/dist/chunk-EEUFU7KV.js +1634 -0
- package/dist/chunk-EEUFU7KV.js.map +1 -0
- package/dist/{chunk-UFZD3JAA.js → chunk-ESZXCZIN.js} +3 -3
- package/dist/{chunk-PYLH4MH5.js → chunk-FJOTP632.js} +1 -1
- package/dist/{chunk-SYWJG5NA.js → chunk-GKNYMNFM.js} +17 -7
- package/dist/{chunk-SYWJG5NA.js.map → chunk-GKNYMNFM.js.map} +1 -1
- package/dist/{chunk-JPHJBCZB.js → chunk-HPOR6YXA.js} +3 -2
- package/dist/chunk-HPOR6YXA.js.map +1 -0
- package/dist/chunk-JUAFCLRB.js +1014 -0
- package/dist/chunk-JUAFCLRB.js.map +1 -0
- package/dist/{chunk-776EQXDV.js → chunk-KNR2P2UY.js} +110 -83
- package/dist/chunk-KNR2P2UY.js.map +1 -0
- package/dist/{chunk-N34XHWBA.js → chunk-LCSBL5TT.js} +24 -11
- package/dist/chunk-LCSBL5TT.js.map +1 -0
- package/dist/{chunk-QDBDYCM2.js → chunk-MSUAVR3R.js} +76 -11
- package/dist/chunk-MSUAVR3R.js.map +1 -0
- package/dist/{chunk-6MKEIDX5.js → chunk-SFMIMLXR.js} +2 -2
- package/dist/chunk-TGFTKWOR.js +16 -0
- package/dist/chunk-TGFTKWOR.js.map +1 -0
- package/dist/chunk-TUR6YKEE.js +55 -0
- package/dist/chunk-TUR6YKEE.js.map +1 -0
- package/dist/chunk-Y7D7PUBE.js +321 -0
- package/dist/chunk-Y7D7PUBE.js.map +1 -0
- package/dist/{chunk-VAKBJV26.js → chunk-YJIA2MFL.js} +918 -2238
- package/dist/chunk-YJIA2MFL.js.map +1 -0
- package/dist/cli/index.js +14 -3
- package/dist/cli/index.js.map +1 -1
- package/dist/{cloud-7WM3YLI4.js → cloud-LXZUR3U2.js} +3 -3
- package/dist/{codebuddy-DM-i0Fu5.d.ts → codebuddy-DO7ZhoWn.d.ts} +37 -3
- package/dist/config-GEZFLXK6.js +12 -0
- package/dist/docker-autostart-KDME7TVC.js +12 -0
- package/dist/index.d.ts +48 -3
- package/dist/index.js +32 -25
- package/dist/langgraph/index.d.ts +1 -1
- package/dist/lean-command-CWJAVQ5G.js +18 -0
- package/dist/local-ST4WMRCG.js +17 -0
- package/dist/{main-HFHLGID4.js → main-AHNGHC4L.js} +327 -329
- package/dist/main-AHNGHC4L.js.map +1 -0
- package/dist/{memory-4RGVLPKG.js → memory-TGHVTBME.js} +9 -8
- package/dist/memory-TGHVTBME.js.map +1 -0
- package/dist/operations-7BTAAUHF.js +26 -0
- package/dist/postgres-docker-QQZQ2ABK.js +16 -0
- package/dist/pretool-command-3SXQSMAO.js +13 -0
- package/dist/{rerank-3P3C2UHN.js → rerank-IQAEWLQR.js} +6 -5
- package/dist/rerank-IQAEWLQR.js.map +1 -0
- package/dist/runtime-E7JQZ2BZ.js +46 -0
- package/dist/runtime-E7JQZ2BZ.js.map +1 -0
- package/dist/stdio-HOV4P3DQ.js +12 -0
- package/dist/stdio-HOV4P3DQ.js.map +1 -0
- package/dist/summaries-XV3JGGCC.js +20 -0
- package/dist/summaries-XV3JGGCC.js.map +1 -0
- package/dist/{zvec-MAPSJQSR.js → zvec-KP4KNB7Y.js} +10 -3
- package/dist/zvec-KP4KNB7Y.js.map +1 -0
- package/package.json +7 -10
- package/src/db/migrations/0006_fact_idempotency_key.sql +2 -0
- package/src/db/migrations/meta/0006_snapshot.json +1258 -0
- package/src/db/migrations/meta/_journal.json +7 -0
- package/dist/chunk-776EQXDV.js.map +0 -1
- package/dist/chunk-GDJH7XJC.js.map +0 -1
- package/dist/chunk-JPHJBCZB.js.map +0 -1
- package/dist/chunk-L5AYZC3F.js.map +0 -1
- package/dist/chunk-N34XHWBA.js.map +0 -1
- package/dist/chunk-PCE4STAY.js.map +0 -1
- package/dist/chunk-PQK3GKZH.js +0 -98
- package/dist/chunk-PQK3GKZH.js.map +0 -1
- package/dist/chunk-QDBDYCM2.js.map +0 -1
- package/dist/chunk-U4HAJLIB.js.map +0 -1
- package/dist/chunk-UN57FKQX.js.map +0 -1
- package/dist/chunk-VAKBJV26.js.map +0 -1
- package/dist/chunk-WHZSVY2L.js.map +0 -1
- package/dist/chunk-XBJQKCLU.js.map +0 -1
- package/dist/config-KFEHLV6W.js +0 -11
- package/dist/lean-command-C5YPFKHP.js +0 -15
- package/dist/local-OTOY56B4.js +0 -13
- package/dist/main-HFHLGID4.js.map +0 -1
- package/dist/memory-4RGVLPKG.js.map +0 -1
- package/dist/operations-LOBAQRLO.js +0 -24
- package/dist/pretool-command-MQQ2UNQM.js +0 -10
- package/dist/rerank-3P3C2UHN.js.map +0 -1
- package/dist/summaries-ZBV5IN7Y.js +0 -18
- /package/dist/{chunk-JFDRBPYF.js.map → chunk-6JCKMIQY.js.map} +0 -0
- /package/dist/{chunk-UFZD3JAA.js.map → chunk-ESZXCZIN.js.map} +0 -0
- /package/dist/{chunk-PYLH4MH5.js.map → chunk-FJOTP632.js.map} +0 -0
- /package/dist/{chunk-6MKEIDX5.js.map → chunk-SFMIMLXR.js.map} +0 -0
- /package/dist/{cloud-7WM3YLI4.js.map → cloud-LXZUR3U2.js.map} +0 -0
- /package/dist/{config-KFEHLV6W.js.map → config-GEZFLXK6.js.map} +0 -0
- /package/dist/{lean-command-C5YPFKHP.js.map → docker-autostart-KDME7TVC.js.map} +0 -0
- /package/dist/{local-OTOY56B4.js.map → lean-command-CWJAVQ5G.js.map} +0 -0
- /package/dist/{operations-LOBAQRLO.js.map → local-ST4WMRCG.js.map} +0 -0
- /package/dist/{pretool-command-MQQ2UNQM.js.map → operations-7BTAAUHF.js.map} +0 -0
- /package/dist/{summaries-ZBV5IN7Y.js.map → postgres-docker-QQZQ2ABK.js.map} +0 -0
- /package/dist/{zvec-MAPSJQSR.js.map → pretool-command-3SXQSMAO.js.map} +0 -0
|
@@ -1,7 +1,19 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
MemoryFileStore,
|
|
3
|
+
auditLog,
|
|
4
|
+
candidateLimit,
|
|
5
|
+
embeddings,
|
|
6
|
+
facts,
|
|
7
|
+
interactions,
|
|
8
|
+
isEligibleMemory,
|
|
9
|
+
lexicalTerms,
|
|
10
|
+
modelCalls,
|
|
11
|
+
namespaces,
|
|
12
|
+
schema_exports,
|
|
13
|
+
sessionSummaries,
|
|
14
|
+
shares,
|
|
15
|
+
validSearchVector
|
|
16
|
+
} from "./chunk-JUAFCLRB.js";
|
|
5
17
|
import {
|
|
6
18
|
clampBudgetForCallerModel,
|
|
7
19
|
computeContentHash,
|
|
@@ -9,13 +21,17 @@ import {
|
|
|
9
21
|
countTokens,
|
|
10
22
|
generateEntityId,
|
|
11
23
|
generateSessionId
|
|
12
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-CAEPHLHC.js";
|
|
13
25
|
import {
|
|
14
26
|
OpenRouterEmbeddingProvider
|
|
15
|
-
} from "./chunk-
|
|
27
|
+
} from "./chunk-6JCKMIQY.js";
|
|
16
28
|
import {
|
|
17
29
|
LocalModelProvider
|
|
18
|
-
} from "./chunk-
|
|
30
|
+
} from "./chunk-3QGW53GD.js";
|
|
31
|
+
import {
|
|
32
|
+
REDACTED,
|
|
33
|
+
redactPersisted
|
|
34
|
+
} from "./chunk-AL6VBTNA.js";
|
|
19
35
|
import {
|
|
20
36
|
DEFAULT_CLOUD_EMBEDDING_MODELS,
|
|
21
37
|
DEFAULT_EMBEDDING_MODELS,
|
|
@@ -23,328 +39,24 @@ import {
|
|
|
23
39
|
EMBEDDING_DIMENSIONS,
|
|
24
40
|
assertCompatibleEmbeddingModels,
|
|
25
41
|
getModelDefinition
|
|
26
|
-
} from "./chunk-
|
|
42
|
+
} from "./chunk-HPOR6YXA.js";
|
|
27
43
|
import {
|
|
28
44
|
MISSING_DATABASE_URL,
|
|
29
|
-
assertNoSymlinkBetween,
|
|
30
45
|
checkConfigPermissions,
|
|
31
46
|
checkProjectScaffold,
|
|
32
|
-
composeMarkdown,
|
|
33
47
|
defaultNamespaceForCwd,
|
|
34
48
|
loadRuntimeConfig,
|
|
35
|
-
|
|
49
|
+
migrateLegacyNamespace,
|
|
36
50
|
readConfigFile,
|
|
37
|
-
redactSecrets
|
|
38
|
-
|
|
39
|
-
} from "./chunk-776EQXDV.js";
|
|
40
|
-
import {
|
|
41
|
-
__export
|
|
42
|
-
} from "./chunk-PZ5AY32C.js";
|
|
43
|
-
|
|
44
|
-
// src/db/schema.ts
|
|
45
|
-
var schema_exports = {};
|
|
46
|
-
__export(schema_exports, {
|
|
47
|
-
HNSW_EF_CONSTRUCTION: () => HNSW_EF_CONSTRUCTION,
|
|
48
|
-
HNSW_M: () => HNSW_M,
|
|
49
|
-
VECTOR_DIMENSIONS: () => VECTOR_DIMENSIONS,
|
|
50
|
-
auditActionEnum: () => auditActionEnum,
|
|
51
|
-
auditLog: () => auditLog,
|
|
52
|
-
checkpoints: () => checkpoints,
|
|
53
|
-
embeddingOwnerTypeEnum: () => embeddingOwnerTypeEnum,
|
|
54
|
-
embeddingStatusEnum: () => embeddingStatusEnum,
|
|
55
|
-
embeddings: () => embeddings,
|
|
56
|
-
facts: () => facts,
|
|
57
|
-
interactionRoleEnum: () => interactionRoleEnum,
|
|
58
|
-
interactions: () => interactions,
|
|
59
|
-
memoryTypeEnum: () => memoryTypeEnum,
|
|
60
|
-
modelCallStatusEnum: () => modelCallStatusEnum,
|
|
61
|
-
modelCallTypeEnum: () => modelCallTypeEnum,
|
|
62
|
-
modelCalls: () => modelCalls,
|
|
63
|
-
namespaces: () => namespaces,
|
|
64
|
-
sessionSummaries: () => sessionSummaries,
|
|
65
|
-
shareModeEnum: () => shareModeEnum,
|
|
66
|
-
shares: () => shares
|
|
67
|
-
});
|
|
68
|
-
import { sql } from "drizzle-orm";
|
|
69
|
-
import {
|
|
70
|
-
boolean,
|
|
71
|
-
customType,
|
|
72
|
-
index,
|
|
73
|
-
integer,
|
|
74
|
-
jsonb,
|
|
75
|
-
numeric,
|
|
76
|
-
pgEnum,
|
|
77
|
-
pgTable,
|
|
78
|
-
text,
|
|
79
|
-
timestamp,
|
|
80
|
-
uniqueIndex
|
|
81
|
-
} from "drizzle-orm/pg-core";
|
|
82
|
-
var VECTOR_DIMENSIONS = 384;
|
|
83
|
-
var HNSW_M = 16;
|
|
84
|
-
var HNSW_EF_CONSTRUCTION = 64;
|
|
85
|
-
var vector = customType({
|
|
86
|
-
dataType() {
|
|
87
|
-
return `vector(${VECTOR_DIMENSIONS})`;
|
|
88
|
-
}
|
|
89
|
-
});
|
|
90
|
-
var memoryTypeEnum = pgEnum("memory_type", ["interaction", "fact", "summary"]);
|
|
91
|
-
var interactionRoleEnum = pgEnum("interaction_role", ["user", "assistant", "system"]);
|
|
92
|
-
var embeddingOwnerTypeEnum = pgEnum("embedding_owner_type", [
|
|
93
|
-
"interaction",
|
|
94
|
-
"fact",
|
|
95
|
-
"summary"
|
|
96
|
-
]);
|
|
97
|
-
var embeddingStatusEnum = pgEnum("embedding_status", [
|
|
98
|
-
"pending",
|
|
99
|
-
"processing",
|
|
100
|
-
"ready",
|
|
101
|
-
"failed"
|
|
102
|
-
]);
|
|
103
|
-
var shareModeEnum = pgEnum("share_mode", ["reference", "snapshot"]);
|
|
104
|
-
var modelCallTypeEnum = pgEnum("model_call_type", ["embedding", "llm"]);
|
|
105
|
-
var modelCallStatusEnum = pgEnum("model_call_status", [
|
|
106
|
-
"success",
|
|
107
|
-
"failed",
|
|
108
|
-
"timeout",
|
|
109
|
-
"rate_limited",
|
|
110
|
-
"gated",
|
|
111
|
-
"fallback"
|
|
112
|
-
]);
|
|
113
|
-
var auditActionEnum = pgEnum("audit_action", [
|
|
114
|
-
"remember",
|
|
115
|
-
"remember_batch",
|
|
116
|
-
"share",
|
|
117
|
-
"forget",
|
|
118
|
-
"config"
|
|
119
|
-
]);
|
|
120
|
-
var createdAt = timestamp("created_at", { withTimezone: true }).notNull().defaultNow();
|
|
121
|
-
var updatedAt = timestamp("updated_at", { withTimezone: true }).notNull().defaultNow();
|
|
122
|
-
var namespaces = pgTable(
|
|
123
|
-
"namespaces",
|
|
124
|
-
{
|
|
125
|
-
id: text("id").primaryKey(),
|
|
126
|
-
name: text("name").notNull(),
|
|
127
|
-
createdAt,
|
|
128
|
-
updatedAt
|
|
129
|
-
},
|
|
130
|
-
(table) => ({
|
|
131
|
-
nameUnique: uniqueIndex("namespaces_name_unique").on(table.name)
|
|
132
|
-
})
|
|
133
|
-
);
|
|
134
|
-
var interactions = pgTable(
|
|
135
|
-
"interactions",
|
|
136
|
-
{
|
|
137
|
-
id: text("id").primaryKey(),
|
|
138
|
-
namespaceId: text("namespace_id").notNull().references(() => namespaces.id, { onDelete: "cascade" }),
|
|
139
|
-
sessionId: text("session_id").notNull(),
|
|
140
|
-
role: interactionRoleEnum("role").notNull().default("user"),
|
|
141
|
-
content: text("content").notNull(),
|
|
142
|
-
contentHash: text("content_hash").notNull(),
|
|
143
|
-
tokenCount: integer("token_count").notNull().default(0),
|
|
144
|
-
createdByAgent: text("created_by_agent"),
|
|
145
|
-
createdAt
|
|
146
|
-
},
|
|
147
|
-
(table) => ({
|
|
148
|
-
namespaceContentHashUnique: uniqueIndex("interactions_namespace_content_hash_unique").on(
|
|
149
|
-
table.namespaceId,
|
|
150
|
-
table.contentHash
|
|
151
|
-
),
|
|
152
|
-
namespaceSessionCreatedIdx: index("interactions_namespace_session_created_idx").on(
|
|
153
|
-
table.namespaceId,
|
|
154
|
-
table.sessionId,
|
|
155
|
-
table.createdAt
|
|
156
|
-
)
|
|
157
|
-
})
|
|
158
|
-
);
|
|
159
|
-
var sessionSummaries = pgTable(
|
|
160
|
-
"session_summaries",
|
|
161
|
-
{
|
|
162
|
-
id: text("id").primaryKey(),
|
|
163
|
-
namespaceId: text("namespace_id").notNull().references(() => namespaces.id, { onDelete: "cascade" }),
|
|
164
|
-
sessionId: text("session_id").notNull(),
|
|
165
|
-
content: text("content").notNull(),
|
|
166
|
-
version: integer("version").notNull().default(1),
|
|
167
|
-
tokenCount: integer("token_count").notNull().default(0),
|
|
168
|
-
createdByAgent: text("created_by_agent"),
|
|
169
|
-
/** See facts.fileBacked. */
|
|
170
|
-
fileBacked: boolean("file_backed").notNull().default(false),
|
|
171
|
-
createdAt
|
|
172
|
-
},
|
|
173
|
-
(table) => ({
|
|
174
|
-
namespaceSessionVersionIdx: index("session_summaries_namespace_session_version_idx").on(
|
|
175
|
-
table.namespaceId,
|
|
176
|
-
table.sessionId,
|
|
177
|
-
table.version
|
|
178
|
-
)
|
|
179
|
-
})
|
|
180
|
-
);
|
|
181
|
-
var facts = pgTable(
|
|
182
|
-
"facts",
|
|
183
|
-
{
|
|
184
|
-
id: text("id").primaryKey(),
|
|
185
|
-
namespaceId: text("namespace_id").notNull().references(() => namespaces.id, { onDelete: "cascade" }),
|
|
186
|
-
subject: text("subject").notNull(),
|
|
187
|
-
predicate: text("predicate").notNull(),
|
|
188
|
-
object: text("object").notNull(),
|
|
189
|
-
propositionKey: text("proposition_key"),
|
|
190
|
-
content: text("content").notNull(),
|
|
191
|
-
contentHash: text("content_hash").notNull(),
|
|
192
|
-
confidence: numeric("confidence", { precision: 5, scale: 4 }).notNull().default("1"),
|
|
193
|
-
/**
|
|
194
|
-
* Historical provenance only — deliberately not a foreign key. Interactions
|
|
195
|
-
* are ephemeral DB rows; Markdown facts must re-import after a rebuild that
|
|
196
|
-
* lost them, and the ID must survive so `sourceDeleted` can be tracked.
|
|
197
|
-
*/
|
|
198
|
-
sourceInteractionId: text("source_interaction_id"),
|
|
199
|
-
sourceDeleted: boolean("source_deleted").notNull().default(false),
|
|
200
|
-
supersededBy: text("superseded_by"),
|
|
201
|
-
/**
|
|
202
|
-
* True once a reindex imported this row from its Markdown file. Only such
|
|
203
|
-
* rows are deleted when the file disappears; DB-only legacy rows are kept.
|
|
204
|
-
*/
|
|
205
|
-
fileBacked: boolean("file_backed").notNull().default(false),
|
|
206
|
-
embeddingModel: text("embedding_model"),
|
|
207
|
-
createdByAgent: text("created_by_agent"),
|
|
208
|
-
createdAt,
|
|
209
|
-
updatedAt
|
|
210
|
-
},
|
|
211
|
-
(table) => ({
|
|
212
|
-
namespaceContentHashUnique: uniqueIndex("facts_namespace_content_hash_unique").on(
|
|
213
|
-
table.namespaceId,
|
|
214
|
-
table.contentHash
|
|
215
|
-
),
|
|
216
|
-
namespaceSubjectIdx: index("facts_namespace_subject_idx").on(table.namespaceId, table.subject),
|
|
217
|
-
sourceInteractionIdx: index("facts_source_interaction_idx").on(table.sourceInteractionId)
|
|
218
|
-
})
|
|
219
|
-
);
|
|
220
|
-
var embeddings = pgTable(
|
|
221
|
-
"embeddings",
|
|
222
|
-
{
|
|
223
|
-
id: text("id").primaryKey(),
|
|
224
|
-
namespaceId: text("namespace_id").notNull().references(() => namespaces.id, { onDelete: "cascade" }),
|
|
225
|
-
ownerType: embeddingOwnerTypeEnum("owner_type").notNull(),
|
|
226
|
-
ownerId: text("owner_id").notNull(),
|
|
227
|
-
vector: vector("vector").notNull(),
|
|
228
|
-
embeddingModel: text("embedding_model").notNull(),
|
|
229
|
-
embeddingSpace: text("embedding_space"),
|
|
230
|
-
status: embeddingStatusEnum("status").notNull().default("pending"),
|
|
231
|
-
attempts: integer("attempts").notNull().default(0),
|
|
232
|
-
lastError: text("last_error"),
|
|
233
|
-
/** Lease deadline while status='processing'; null otherwise. */
|
|
234
|
-
leaseUntil: timestamp("lease_until", { withTimezone: true }),
|
|
235
|
-
leaseToken: text("lease_token"),
|
|
236
|
-
createdAt,
|
|
237
|
-
updatedAt
|
|
238
|
-
},
|
|
239
|
-
(table) => ({
|
|
240
|
-
ownerUnique: uniqueIndex("embeddings_owner_unique").on(table.ownerType, table.ownerId),
|
|
241
|
-
namespaceModelStatusIdx: index("embeddings_namespace_model_status_idx").on(
|
|
242
|
-
table.namespaceId,
|
|
243
|
-
table.embeddingModel,
|
|
244
|
-
table.status
|
|
245
|
-
),
|
|
246
|
-
namespaceSpaceStatusIdx: index("embeddings_namespace_space_status_idx").on(
|
|
247
|
-
table.namespaceId,
|
|
248
|
-
table.embeddingSpace,
|
|
249
|
-
table.status
|
|
250
|
-
),
|
|
251
|
-
vectorHnswIdx: index("embeddings_vector_hnsw_idx").using(
|
|
252
|
-
"hnsw",
|
|
253
|
-
sql`${table.vector} vector_cosine_ops`
|
|
254
|
-
)
|
|
255
|
-
})
|
|
256
|
-
);
|
|
257
|
-
var shares = pgTable(
|
|
258
|
-
"shares",
|
|
259
|
-
{
|
|
260
|
-
id: text("id").primaryKey(),
|
|
261
|
-
fromNamespaceId: text("from_namespace_id").notNull().references(() => namespaces.id, { onDelete: "cascade" }),
|
|
262
|
-
toNamespaceId: text("to_namespace_id").notNull().references(() => namespaces.id, { onDelete: "cascade" }),
|
|
263
|
-
sourceFactId: text("source_fact_id").notNull().references(() => facts.id, { onDelete: "cascade" }),
|
|
264
|
-
snapshotFactId: text("snapshot_fact_id").references(() => facts.id, { onDelete: "set null" }),
|
|
265
|
-
mode: shareModeEnum("mode").notNull().default("reference"),
|
|
266
|
-
tombstoned: boolean("tombstoned").notNull().default(false),
|
|
267
|
-
createdByAgent: text("created_by_agent"),
|
|
268
|
-
createdAt
|
|
269
|
-
},
|
|
270
|
-
(table) => ({
|
|
271
|
-
shareUnique: uniqueIndex("shares_target_source_unique").on(
|
|
272
|
-
table.toNamespaceId,
|
|
273
|
-
table.sourceFactId,
|
|
274
|
-
table.mode
|
|
275
|
-
),
|
|
276
|
-
fromNamespaceIdx: index("shares_from_namespace_idx").on(table.fromNamespaceId)
|
|
277
|
-
})
|
|
278
|
-
);
|
|
279
|
-
var checkpoints = pgTable(
|
|
280
|
-
"checkpoints",
|
|
281
|
-
{
|
|
282
|
-
id: text("id").primaryKey(),
|
|
283
|
-
namespaceId: text("namespace_id").notNull().references(() => namespaces.id, { onDelete: "cascade" }),
|
|
284
|
-
threadId: text("thread_id").notNull(),
|
|
285
|
-
checkpointId: text("checkpoint_id").notNull(),
|
|
286
|
-
parentId: text("parent_id"),
|
|
287
|
-
channelValues: jsonb("channel_values").notNull().default(sql`'{}'::jsonb`),
|
|
288
|
-
metadata: jsonb("metadata").notNull().default(sql`'{}'::jsonb`),
|
|
289
|
-
createdAt
|
|
290
|
-
},
|
|
291
|
-
(table) => ({
|
|
292
|
-
checkpointUnique: uniqueIndex("checkpoints_thread_checkpoint_unique").on(
|
|
293
|
-
table.namespaceId,
|
|
294
|
-
table.threadId,
|
|
295
|
-
table.checkpointId
|
|
296
|
-
)
|
|
297
|
-
})
|
|
298
|
-
);
|
|
299
|
-
var modelCalls = pgTable(
|
|
300
|
-
"model_calls",
|
|
301
|
-
{
|
|
302
|
-
id: text("id").primaryKey(),
|
|
303
|
-
namespaceId: text("namespace_id").references(() => namespaces.id, { onDelete: "set null" }),
|
|
304
|
-
model: text("model").notNull(),
|
|
305
|
-
type: modelCallTypeEnum("type").notNull(),
|
|
306
|
-
latencyMs: integer("latency_ms"),
|
|
307
|
-
tokens: integer("tokens"),
|
|
308
|
-
status: modelCallStatusEnum("status").notNull(),
|
|
309
|
-
error: text("error"),
|
|
310
|
-
metadata: jsonb("metadata").notNull().default(sql`'{}'::jsonb`),
|
|
311
|
-
createdAt
|
|
312
|
-
},
|
|
313
|
-
(table) => ({
|
|
314
|
-
modelCreatedIdx: index("model_calls_model_created_idx").on(table.model, table.createdAt),
|
|
315
|
-
namespaceCreatedIdx: index("model_calls_namespace_created_idx").on(
|
|
316
|
-
table.namespaceId,
|
|
317
|
-
table.createdAt
|
|
318
|
-
)
|
|
319
|
-
})
|
|
320
|
-
);
|
|
321
|
-
var auditLog = pgTable(
|
|
322
|
-
"audit_log",
|
|
323
|
-
{
|
|
324
|
-
id: text("id").primaryKey(),
|
|
325
|
-
namespaceId: text("namespace_id").references(() => namespaces.id, { onDelete: "set null" }),
|
|
326
|
-
action: auditActionEnum("action").notNull(),
|
|
327
|
-
entityType: text("entity_type").notNull(),
|
|
328
|
-
entityId: text("entity_id"),
|
|
329
|
-
createdByAgent: text("created_by_agent"),
|
|
330
|
-
metadata: jsonb("metadata").notNull().default(sql`'{}'::jsonb`),
|
|
331
|
-
createdAt
|
|
332
|
-
},
|
|
333
|
-
(table) => ({
|
|
334
|
-
namespaceActionCreatedIdx: index("audit_log_namespace_action_created_idx").on(
|
|
335
|
-
table.namespaceId,
|
|
336
|
-
table.action,
|
|
337
|
-
table.createdAt
|
|
338
|
-
)
|
|
339
|
-
})
|
|
340
|
-
);
|
|
51
|
+
redactSecrets
|
|
52
|
+
} from "./chunk-KNR2P2UY.js";
|
|
341
53
|
|
|
342
54
|
// src/db/bootstrap.ts
|
|
343
|
-
async function enablePgvector(
|
|
344
|
-
await
|
|
55
|
+
async function enablePgvector(sql2) {
|
|
56
|
+
await sql2`create extension if not exists vector`;
|
|
345
57
|
}
|
|
346
|
-
async function checkPgvector(
|
|
347
|
-
const result = await
|
|
58
|
+
async function checkPgvector(sql2) {
|
|
59
|
+
const result = await sql2`
|
|
348
60
|
select extversion from pg_extension where extname = 'vector'
|
|
349
61
|
`;
|
|
350
62
|
return {
|
|
@@ -352,16 +64,30 @@ async function checkPgvector(sql3) {
|
|
|
352
64
|
installedVersion: result[0]?.extversion ?? null
|
|
353
65
|
};
|
|
354
66
|
}
|
|
355
|
-
async function bootstrapDatabase(
|
|
356
|
-
await enablePgvector(
|
|
357
|
-
return checkPgvector(
|
|
67
|
+
async function bootstrapDatabase(sql2) {
|
|
68
|
+
await withSchemaLock(sql2, () => enablePgvector(sql2));
|
|
69
|
+
return checkPgvector(sql2);
|
|
70
|
+
}
|
|
71
|
+
var SCHEMA_LOCK = 7310250421;
|
|
72
|
+
async function withSchemaLock(sql2, work) {
|
|
73
|
+
const reserved = await sql2.reserve();
|
|
74
|
+
try {
|
|
75
|
+
await reserved`select pg_advisory_lock(${SCHEMA_LOCK}::bigint)`;
|
|
76
|
+
try {
|
|
77
|
+
return await work();
|
|
78
|
+
} finally {
|
|
79
|
+
await reserved`select pg_advisory_unlock(${SCHEMA_LOCK}::bigint)`;
|
|
80
|
+
}
|
|
81
|
+
} finally {
|
|
82
|
+
reserved.release();
|
|
83
|
+
}
|
|
358
84
|
}
|
|
359
85
|
|
|
360
86
|
// src/db/client.ts
|
|
361
87
|
import { drizzle } from "drizzle-orm/postgres-js";
|
|
362
88
|
import postgres from "postgres";
|
|
363
89
|
function createDatabaseClient(config) {
|
|
364
|
-
const
|
|
90
|
+
const sql2 = postgres(config.postgresUrl, {
|
|
365
91
|
max: config.maxConnections ?? 10,
|
|
366
92
|
...config.connectTimeoutSeconds !== void 0 ? { connect_timeout: config.connectTimeoutSeconds } : {},
|
|
367
93
|
// postgres.js logs NOTICE messages (e.g. "extension already exists,
|
|
@@ -375,10 +101,10 @@ function createDatabaseClient(config) {
|
|
|
375
101
|
}
|
|
376
102
|
});
|
|
377
103
|
return {
|
|
378
|
-
sql:
|
|
379
|
-
db: drizzle(
|
|
104
|
+
sql: sql2,
|
|
105
|
+
db: drizzle(sql2, { schema: schema_exports }),
|
|
380
106
|
async close() {
|
|
381
|
-
await
|
|
107
|
+
await sql2.end({ timeout: 2 });
|
|
382
108
|
}
|
|
383
109
|
};
|
|
384
110
|
}
|
|
@@ -416,165 +142,13 @@ If this is an npm-installed package, make sure src/db/migrations was included in
|
|
|
416
142
|
}
|
|
417
143
|
async function runMigrations(client) {
|
|
418
144
|
const migrationsFolder = resolveMigrationsFolder();
|
|
419
|
-
await migrate(client.db, { migrationsFolder });
|
|
145
|
+
await withSchemaLock(client.sql, () => migrate(client.db, { migrationsFolder }));
|
|
420
146
|
}
|
|
421
147
|
|
|
422
148
|
// src/db/repository.ts
|
|
423
149
|
import { AsyncLocalStorage } from "async_hooks";
|
|
424
150
|
import { createHash } from "crypto";
|
|
425
|
-
import { and, desc, eq, inArray, isNull, lt, ne, or, sql
|
|
426
|
-
|
|
427
|
-
// src/core/memory-search.ts
|
|
428
|
-
function candidateLimit(limit) {
|
|
429
|
-
if (!Number.isFinite(limit)) return 256;
|
|
430
|
-
return Math.max(1, Math.min(1024, Math.trunc(limit)));
|
|
431
|
-
}
|
|
432
|
-
function validSearchVector(vector2) {
|
|
433
|
-
return vector2.length === VECTOR_DIMENSIONS && vector2.every((value) => Number.isFinite(Math.fround(value))) && vector2.some((value) => value !== 0);
|
|
434
|
-
}
|
|
435
|
-
var QUERY_STOPWORDS = /* @__PURE__ */ new Set([
|
|
436
|
-
"a",
|
|
437
|
-
"about",
|
|
438
|
-
"after",
|
|
439
|
-
"again",
|
|
440
|
-
"all",
|
|
441
|
-
"also",
|
|
442
|
-
"always",
|
|
443
|
-
"an",
|
|
444
|
-
"and",
|
|
445
|
-
"any",
|
|
446
|
-
"are",
|
|
447
|
-
"as",
|
|
448
|
-
"at",
|
|
449
|
-
"back",
|
|
450
|
-
"be",
|
|
451
|
-
"been",
|
|
452
|
-
"before",
|
|
453
|
-
"being",
|
|
454
|
-
"but",
|
|
455
|
-
"by",
|
|
456
|
-
"can",
|
|
457
|
-
"could",
|
|
458
|
-
"did",
|
|
459
|
-
"do",
|
|
460
|
-
"does",
|
|
461
|
-
"done",
|
|
462
|
-
"each",
|
|
463
|
-
"even",
|
|
464
|
-
"ever",
|
|
465
|
-
"for",
|
|
466
|
-
"from",
|
|
467
|
-
"get",
|
|
468
|
-
"gets",
|
|
469
|
-
"got",
|
|
470
|
-
"had",
|
|
471
|
-
"has",
|
|
472
|
-
"have",
|
|
473
|
-
"here",
|
|
474
|
-
"how",
|
|
475
|
-
"i",
|
|
476
|
-
"if",
|
|
477
|
-
"in",
|
|
478
|
-
"into",
|
|
479
|
-
"is",
|
|
480
|
-
"it",
|
|
481
|
-
"its",
|
|
482
|
-
"just",
|
|
483
|
-
"like",
|
|
484
|
-
"make",
|
|
485
|
-
"me",
|
|
486
|
-
"more",
|
|
487
|
-
"most",
|
|
488
|
-
"my",
|
|
489
|
-
"never",
|
|
490
|
-
"no",
|
|
491
|
-
"not",
|
|
492
|
-
"now",
|
|
493
|
-
"of",
|
|
494
|
-
"on",
|
|
495
|
-
"only",
|
|
496
|
-
"or",
|
|
497
|
-
"other",
|
|
498
|
-
"our",
|
|
499
|
-
"out",
|
|
500
|
-
"over",
|
|
501
|
-
"please",
|
|
502
|
-
"really",
|
|
503
|
-
"same",
|
|
504
|
-
"should",
|
|
505
|
-
"so",
|
|
506
|
-
"some",
|
|
507
|
-
"sometimes",
|
|
508
|
-
"still",
|
|
509
|
-
"such",
|
|
510
|
-
"than",
|
|
511
|
-
"that",
|
|
512
|
-
"the",
|
|
513
|
-
"their",
|
|
514
|
-
"them",
|
|
515
|
-
"then",
|
|
516
|
-
"there",
|
|
517
|
-
"these",
|
|
518
|
-
"they",
|
|
519
|
-
"this",
|
|
520
|
-
"those",
|
|
521
|
-
"through",
|
|
522
|
-
"to",
|
|
523
|
-
"too",
|
|
524
|
-
"under",
|
|
525
|
-
"up",
|
|
526
|
-
"us",
|
|
527
|
-
"very",
|
|
528
|
-
"was",
|
|
529
|
-
"we",
|
|
530
|
-
"were",
|
|
531
|
-
"what",
|
|
532
|
-
"when",
|
|
533
|
-
"where",
|
|
534
|
-
"which",
|
|
535
|
-
"while",
|
|
536
|
-
"who",
|
|
537
|
-
"why",
|
|
538
|
-
"will",
|
|
539
|
-
"with",
|
|
540
|
-
"would",
|
|
541
|
-
"yet",
|
|
542
|
-
"you",
|
|
543
|
-
"your"
|
|
544
|
-
]);
|
|
545
|
-
function lexicalTerms(query) {
|
|
546
|
-
return [
|
|
547
|
-
...new Set(
|
|
548
|
-
query.slice(0, 4096).toLowerCase().match(/[\p{L}\p{N}_]+/gu) ?? []
|
|
549
|
-
)
|
|
550
|
-
].filter((term) => !QUERY_STOPWORDS.has(term)).slice(0, 32).map((term) => term.slice(0, 64));
|
|
551
|
-
}
|
|
552
|
-
var CLAIM_BOILERPLATE = /* @__PURE__ */ new Set([
|
|
553
|
-
"decided",
|
|
554
|
-
"decide",
|
|
555
|
-
"decision",
|
|
556
|
-
"made",
|
|
557
|
-
"use",
|
|
558
|
-
"uses",
|
|
559
|
-
"used",
|
|
560
|
-
"using",
|
|
561
|
-
"now",
|
|
562
|
-
"going",
|
|
563
|
-
"will"
|
|
564
|
-
]);
|
|
565
|
-
function termOverlap(a, b) {
|
|
566
|
-
const terms = (text2) => new Set(
|
|
567
|
-
(text2.toLowerCase().match(/[\p{L}\p{N}_]+/gu) ?? []).filter(
|
|
568
|
-
(term) => !QUERY_STOPWORDS.has(term) && !CLAIM_BOILERPLATE.has(term)
|
|
569
|
-
)
|
|
570
|
-
);
|
|
571
|
-
const left = terms(a);
|
|
572
|
-
const right = terms(b);
|
|
573
|
-
if (left.size === 0 || right.size === 0) return 0;
|
|
574
|
-
let shared = 0;
|
|
575
|
-
for (const term of left) if (right.has(term)) shared += 1;
|
|
576
|
-
return shared / (left.size + right.size - shared);
|
|
577
|
-
}
|
|
151
|
+
import { and, desc, eq, inArray, isNull, lt, ne, or, sql } from "drizzle-orm";
|
|
578
152
|
|
|
579
153
|
// src/providers/embedding-space.ts
|
|
580
154
|
function embeddingSpace(response) {
|
|
@@ -592,12 +166,12 @@ function embeddingSpace(response) {
|
|
|
592
166
|
function embeddingSpaceKey(space) {
|
|
593
167
|
return JSON.stringify([space.model, space.revision, space.schema, space.dimensions]);
|
|
594
168
|
}
|
|
595
|
-
function validEmbeddingVector(
|
|
596
|
-
return dimensions > 0 &&
|
|
169
|
+
function validEmbeddingVector(vector, dimensions) {
|
|
170
|
+
return dimensions > 0 && vector.length === dimensions && vector.every(Number.isFinite);
|
|
597
171
|
}
|
|
598
172
|
function validateEmbeddingResponse(response, count) {
|
|
599
173
|
embeddingSpace(response);
|
|
600
|
-
if (response.vectors.length !== count || response.vectors.some((
|
|
174
|
+
if (response.vectors.length !== count || response.vectors.some((vector) => !validEmbeddingVector(vector, response.dimensions))) {
|
|
601
175
|
throw new Error("Provider returned invalid embedding dimensions, values, or batch size.");
|
|
602
176
|
}
|
|
603
177
|
}
|
|
@@ -628,7 +202,7 @@ var PostgresMemoryRepository = class {
|
|
|
628
202
|
async withSessionLock(namespaceId, sessionId, fn) {
|
|
629
203
|
const [k1, k2] = advisoryLockKeys(namespaceId, sessionId);
|
|
630
204
|
return this.client.db.transaction(async (tx) => {
|
|
631
|
-
await tx.execute(
|
|
205
|
+
await tx.execute(sql`select pg_advisory_xact_lock(${k1}, ${k2})`);
|
|
632
206
|
return this.sessionTransaction.run(tx, fn);
|
|
633
207
|
});
|
|
634
208
|
}
|
|
@@ -678,12 +252,20 @@ var PostgresMemoryRepository = class {
|
|
|
678
252
|
}
|
|
679
253
|
async writeFact(input) {
|
|
680
254
|
return this.db.transaction(async (tx) => {
|
|
255
|
+
const key = input.fact.idempotencyKey;
|
|
681
256
|
const existing = await tx.select({ id: facts.id }).from(facts).where(
|
|
682
257
|
and(
|
|
683
258
|
eq(facts.namespaceId, input.fact.namespaceId),
|
|
684
|
-
|
|
259
|
+
key ? (
|
|
260
|
+
// Rows written before 0006 stored the key as their content hash.
|
|
261
|
+
or(
|
|
262
|
+
eq(facts.idempotencyKey, key),
|
|
263
|
+
eq(facts.contentHash, key),
|
|
264
|
+
eq(facts.contentHash, input.fact.contentHash)
|
|
265
|
+
)
|
|
266
|
+
) : eq(facts.contentHash, input.fact.contentHash)
|
|
685
267
|
)
|
|
686
|
-
).limit(1);
|
|
268
|
+
).orderBy(key ? sql`(${facts.idempotencyKey} = ${key}) desc nulls last` : facts.id).limit(1);
|
|
687
269
|
const existingRow = existing[0];
|
|
688
270
|
if (existingRow) {
|
|
689
271
|
return {
|
|
@@ -706,6 +288,7 @@ var PostgresMemoryRepository = class {
|
|
|
706
288
|
content: input.fact.content,
|
|
707
289
|
contentHash: input.fact.contentHash,
|
|
708
290
|
propositionKey: input.fact.propositionKey ?? null,
|
|
291
|
+
idempotencyKey: input.fact.idempotencyKey ?? null,
|
|
709
292
|
confidence: String(input.fact.confidence ?? 1),
|
|
710
293
|
sourceDeleted: input.fact.sourceDeleted ?? false,
|
|
711
294
|
supersededBy: input.fact.supersededBy ?? null,
|
|
@@ -796,6 +379,7 @@ var PostgresMemoryRepository = class {
|
|
|
796
379
|
sourceDeleted: fact.sourceDeleted,
|
|
797
380
|
supersededBy: fact.supersededBy,
|
|
798
381
|
propositionKey: fact.propositionKey ?? null,
|
|
382
|
+
idempotencyKey: fact.idempotencyKey ?? null,
|
|
799
383
|
createdByAgent: fact.createdByAgent ?? null,
|
|
800
384
|
createdAt: fact.createdAt,
|
|
801
385
|
fileBacked: true
|
|
@@ -807,9 +391,9 @@ var PostgresMemoryRepository = class {
|
|
|
807
391
|
return { status: "inserted" };
|
|
808
392
|
}
|
|
809
393
|
const contentChanged = existing.content !== fact.content;
|
|
810
|
-
const metadataChanged = existing.subject !== values.subject || existing.predicate !== values.predicate || existing.object !== values.object || Number(existing.confidence) !== Number(fact.confidence.toFixed(4)) || existing.sourceInteractionId !== values.sourceInteractionId || existing.sourceDeleted !== values.sourceDeleted || existing.supersededBy !== values.supersededBy || existing.propositionKey !== values.propositionKey || existing.createdByAgent !== values.createdByAgent || existing.createdAt.getTime() !== values.createdAt.getTime();
|
|
394
|
+
const metadataChanged = existing.subject !== values.subject || existing.predicate !== values.predicate || existing.object !== values.object || Number(existing.confidence) !== Number(fact.confidence.toFixed(4)) || existing.sourceInteractionId !== values.sourceInteractionId || existing.sourceDeleted !== values.sourceDeleted || existing.supersededBy !== values.supersededBy || existing.propositionKey !== values.propositionKey || existing.idempotencyKey !== values.idempotencyKey || existing.createdByAgent !== values.createdByAgent || existing.createdAt.getTime() !== values.createdAt.getTime();
|
|
811
395
|
if (contentChanged || metadataChanged || !existing.fileBacked) {
|
|
812
|
-
await tx.update(facts).set({ ...values, updatedAt:
|
|
396
|
+
await tx.update(facts).set({ ...values, updatedAt: sql`now()` }).where(and(eq(facts.namespaceId, fact.namespaceId), eq(facts.id, fact.id)));
|
|
813
397
|
}
|
|
814
398
|
await upsertPendingEmbedding(tx, input.embedding, contentChanged);
|
|
815
399
|
if (!contentChanged && !metadataChanged) return { status: "unchanged" };
|
|
@@ -878,12 +462,13 @@ var PostgresMemoryRepository = class {
|
|
|
878
462
|
* item twice. Expired leases are re-claimable, so a crashed worker's work is
|
|
879
463
|
* picked up again instead of being stuck forever.
|
|
880
464
|
*/
|
|
881
|
-
async claimPendingEmbeddings(limit) {
|
|
465
|
+
async claimPendingEmbeddings(limit, namespaceId) {
|
|
882
466
|
const rows = await this.client.sql`
|
|
883
467
|
with claimed as (
|
|
884
468
|
select id from embeddings
|
|
885
|
-
where status = 'pending'
|
|
886
|
-
or (status = 'processing' and (lease_until is null or lease_until < now()))
|
|
469
|
+
where (status = 'pending'
|
|
470
|
+
or (status = 'processing' and (lease_until is null or lease_until < now())))
|
|
471
|
+
and (${namespaceId ?? null}::text is null or namespace_id = ${namespaceId ?? null})
|
|
887
472
|
order by created_at asc
|
|
888
473
|
for update skip locked
|
|
889
474
|
limit ${limit}
|
|
@@ -922,33 +507,33 @@ var PostgresMemoryRepository = class {
|
|
|
922
507
|
}));
|
|
923
508
|
}
|
|
924
509
|
async renewEmbeddingLease(id, leaseToken) {
|
|
925
|
-
const rows = await this.db.update(embeddings).set({ leaseUntil:
|
|
510
|
+
const rows = await this.db.update(embeddings).set({ leaseUntil: sql`now() + interval '120 seconds'` }).where(
|
|
926
511
|
and(
|
|
927
512
|
eq(embeddings.id, id),
|
|
928
513
|
eq(embeddings.leaseToken, leaseToken),
|
|
929
514
|
eq(embeddings.status, "processing"),
|
|
930
|
-
|
|
515
|
+
sql`${embeddings.leaseUntil} > now()`
|
|
931
516
|
)
|
|
932
517
|
).returning({ id: embeddings.id });
|
|
933
518
|
return rows.length > 0;
|
|
934
519
|
}
|
|
935
|
-
async markEmbeddingReady(id, leaseToken,
|
|
936
|
-
const literal = `[${
|
|
520
|
+
async markEmbeddingReady(id, leaseToken, vector, model, space) {
|
|
521
|
+
const literal = `[${vector.join(",")}]`;
|
|
937
522
|
const rows = await this.db.update(embeddings).set({
|
|
938
523
|
status: "ready",
|
|
939
524
|
vector: literal,
|
|
940
525
|
embeddingModel: model,
|
|
941
|
-
embeddingSpace: space ?? embeddingSpaceKey(embeddingSpace({ model, dimensions:
|
|
526
|
+
embeddingSpace: space ?? embeddingSpaceKey(embeddingSpace({ model, dimensions: vector.length })),
|
|
942
527
|
lastError: null,
|
|
943
528
|
leaseUntil: null,
|
|
944
529
|
leaseToken: null,
|
|
945
|
-
updatedAt:
|
|
530
|
+
updatedAt: sql`now()`
|
|
946
531
|
}).where(
|
|
947
532
|
and(
|
|
948
533
|
eq(embeddings.id, id),
|
|
949
534
|
eq(embeddings.leaseToken, leaseToken),
|
|
950
535
|
eq(embeddings.status, "processing"),
|
|
951
|
-
|
|
536
|
+
sql`${embeddings.leaseUntil} > now()`
|
|
952
537
|
)
|
|
953
538
|
).returning({ id: embeddings.id });
|
|
954
539
|
return rows.length > 0;
|
|
@@ -964,18 +549,18 @@ var PostgresMemoryRepository = class {
|
|
|
964
549
|
const maxAttempts = options?.maxAttempts ?? 5;
|
|
965
550
|
const backoffSeconds = Math.min(60, 2 ** Math.max(0, attempts - 1));
|
|
966
551
|
const rows = await this.db.update(embeddings).set({
|
|
967
|
-
status:
|
|
968
|
-
leaseUntil:
|
|
552
|
+
status: sql`case when ${attempts} >= ${maxAttempts} then 'failed'::embedding_status else 'processing'::embedding_status end`,
|
|
553
|
+
leaseUntil: sql`case when ${attempts} >= ${maxAttempts} then null else now() + (${backoffSeconds} || ' seconds')::interval end`,
|
|
969
554
|
leaseToken: null,
|
|
970
555
|
attempts,
|
|
971
556
|
lastError: error,
|
|
972
|
-
updatedAt:
|
|
557
|
+
updatedAt: sql`now()`
|
|
973
558
|
}).where(
|
|
974
559
|
and(
|
|
975
560
|
eq(embeddings.id, id),
|
|
976
561
|
eq(embeddings.leaseToken, leaseToken),
|
|
977
562
|
eq(embeddings.status, "processing"),
|
|
978
|
-
|
|
563
|
+
sql`${embeddings.leaseUntil} > now()`
|
|
979
564
|
)
|
|
980
565
|
).returning({ id: embeddings.id });
|
|
981
566
|
return rows.length > 0;
|
|
@@ -1192,12 +777,12 @@ var PostgresMemoryRepository = class {
|
|
|
1192
777
|
lastError: null,
|
|
1193
778
|
leaseUntil: null,
|
|
1194
779
|
leaseToken: null,
|
|
1195
|
-
updatedAt:
|
|
780
|
+
updatedAt: sql`now()`
|
|
1196
781
|
}).where(
|
|
1197
782
|
and(
|
|
1198
783
|
eq(embeddings.namespaceId, namespaceId),
|
|
1199
784
|
inArray(embeddings.status, ["ready", "failed"]),
|
|
1200
|
-
|
|
785
|
+
sql`${embeddings.embeddingSpace} is distinct from ${space}`
|
|
1201
786
|
)
|
|
1202
787
|
).returning({ id: embeddings.id });
|
|
1203
788
|
return rows.length;
|
|
@@ -1205,7 +790,7 @@ var PostgresMemoryRepository = class {
|
|
|
1205
790
|
async searchSimilarEmbeddings(input) {
|
|
1206
791
|
if (!validSearchVector(input.vector))
|
|
1207
792
|
return { method: "exact_pgvector", candidates: [], compatibleReady: 0 };
|
|
1208
|
-
const rows = await this.db.execute(
|
|
793
|
+
const rows = await this.db.execute(sql`
|
|
1209
794
|
with compatible as materialized (
|
|
1210
795
|
select id, owner_type, owner_id, embedding_model, embedding_space, vector
|
|
1211
796
|
from embeddings
|
|
@@ -1214,7 +799,7 @@ var PostgresMemoryRepository = class {
|
|
|
1214
799
|
namespace_id = ${input.namespaceId}
|
|
1215
800
|
or (
|
|
1216
801
|
owner_type = 'fact'
|
|
1217
|
-
and ${liveReference(input.namespaceId,
|
|
802
|
+
and ${liveReference(input.namespaceId, sql`embeddings.namespace_id`, sql`embeddings.owner_id`)}
|
|
1218
803
|
)
|
|
1219
804
|
)
|
|
1220
805
|
and embedding_space = ${input.embeddingSpace}
|
|
@@ -1250,7 +835,7 @@ var PostgresMemoryRepository = class {
|
|
|
1250
835
|
async searchFactsLexically(namespaceId, query, limit) {
|
|
1251
836
|
const terms = lexicalTerms(query);
|
|
1252
837
|
if (!terms.length) return { candidates: [], matchedFacts: 0 };
|
|
1253
|
-
const rows = await this.db.execute(
|
|
838
|
+
const rows = await this.db.execute(sql`
|
|
1254
839
|
with scored as materialized (
|
|
1255
840
|
select id, content, subject, predicate, object, proposition_key, confidence,
|
|
1256
841
|
created_by_agent, created_at,
|
|
@@ -1264,7 +849,7 @@ var PostgresMemoryRepository = class {
|
|
|
1264
849
|
where not source_deleted and superseded_by is null
|
|
1265
850
|
and (
|
|
1266
851
|
namespace_id = ${namespaceId}
|
|
1267
|
-
or ${liveReference(namespaceId,
|
|
852
|
+
or ${liveReference(namespaceId, sql`facts.namespace_id`, sql`facts.id`)}
|
|
1268
853
|
)
|
|
1269
854
|
)
|
|
1270
855
|
select *, count(*) over () as total from scored where score > 0
|
|
@@ -1343,7 +928,7 @@ var PostgresMemoryRepository = class {
|
|
|
1343
928
|
isNull(facts.supersededBy),
|
|
1344
929
|
or(
|
|
1345
930
|
eq(facts.namespaceId, namespaceId),
|
|
1346
|
-
liveReference(namespaceId,
|
|
931
|
+
liveReference(namespaceId, sql`${facts.namespaceId}`, sql`${facts.id}`)
|
|
1347
932
|
)
|
|
1348
933
|
)
|
|
1349
934
|
);
|
|
@@ -1602,11 +1187,11 @@ var PostgresMemoryRepository = class {
|
|
|
1602
1187
|
limit 1
|
|
1603
1188
|
`;
|
|
1604
1189
|
const vectorCount = Number(countRows[0]?.count ?? 0);
|
|
1605
|
-
const
|
|
1190
|
+
const index = indexRows[0];
|
|
1606
1191
|
return {
|
|
1607
1192
|
vectorCount,
|
|
1608
|
-
indexName:
|
|
1609
|
-
indexDefinition:
|
|
1193
|
+
indexName: index?.indexname ?? null,
|
|
1194
|
+
indexDefinition: index?.indexdef ?? null,
|
|
1610
1195
|
needsTuning: vectorCount >= 1e5
|
|
1611
1196
|
};
|
|
1612
1197
|
}
|
|
@@ -1650,7 +1235,7 @@ async function upsertPendingEmbedding(tx, embedding, reset) {
|
|
|
1650
1235
|
lastError: null,
|
|
1651
1236
|
leaseUntil: null,
|
|
1652
1237
|
leaseToken: null,
|
|
1653
|
-
updatedAt:
|
|
1238
|
+
updatedAt: sql`now()`
|
|
1654
1239
|
}
|
|
1655
1240
|
});
|
|
1656
1241
|
}
|
|
@@ -1682,7 +1267,7 @@ var recentFactColumns = {
|
|
|
1682
1267
|
createdAt: facts.createdAt
|
|
1683
1268
|
};
|
|
1684
1269
|
function liveReference(namespaceId, ownerNamespace, factId) {
|
|
1685
|
-
return
|
|
1270
|
+
return sql`exists (
|
|
1686
1271
|
select 1 from shares s
|
|
1687
1272
|
where s.to_namespace_id = ${namespaceId}
|
|
1688
1273
|
and s.from_namespace_id = ${ownerNamespace}
|
|
@@ -1695,14 +1280,6 @@ function liveReference(namespaceId, ownerNamespace, factId) {
|
|
|
1695
1280
|
// src/core/codebuddy.ts
|
|
1696
1281
|
import pTimeout2 from "p-timeout";
|
|
1697
1282
|
|
|
1698
|
-
// src/core/memory-lifecycle.ts
|
|
1699
|
-
function isEligibleMemory(fact, options = {}) {
|
|
1700
|
-
if (options.namespace && fact.namespace !== options.namespace) return false;
|
|
1701
|
-
if (fact.sourceDeleted || fact.supersededBy) return false;
|
|
1702
|
-
if (fact.category === "incident" && fact.resolvedBy && !options.includeResolved) return false;
|
|
1703
|
-
return true;
|
|
1704
|
-
}
|
|
1705
|
-
|
|
1706
1283
|
// src/planner/default-policy.ts
|
|
1707
1284
|
var DefaultPolicy = class {
|
|
1708
1285
|
recencyFloorSize;
|
|
@@ -1791,8 +1368,10 @@ var DefaultPolicy = class {
|
|
|
1791
1368
|
if (!isEligibleMemory(fact)) continue;
|
|
1792
1369
|
const candidate = factCandidate(fact, useFallback ? fact.score : 0.35 * fact.score);
|
|
1793
1370
|
const existing = byKey.get(candidate.key);
|
|
1794
|
-
if (existing)
|
|
1795
|
-
|
|
1371
|
+
if (existing) {
|
|
1372
|
+
existing.score = Math.max(existing.score, candidate.score);
|
|
1373
|
+
existing.lexical = true;
|
|
1374
|
+
} else byKey.set(candidate.key, { ...candidate, lexical: true });
|
|
1796
1375
|
}
|
|
1797
1376
|
candidates = [...byKey.values()];
|
|
1798
1377
|
candidates = this.applyRecencyFloor(candidates, recencyInteractions);
|
|
@@ -1812,7 +1391,8 @@ var DefaultPolicy = class {
|
|
|
1812
1391
|
itemsSkipped += 1;
|
|
1813
1392
|
continue;
|
|
1814
1393
|
}
|
|
1815
|
-
|
|
1394
|
+
const floor = candidate.lexical || input.relevanceFloor === void 0 ? this.minVectorScore : Math.max(this.minVectorScore, input.relevanceFloor);
|
|
1395
|
+
if (!candidate.isRecencyFloor && candidate.score < floor && !useFallback) {
|
|
1816
1396
|
bump(skipReasons, "low_score");
|
|
1817
1397
|
itemsSkipped += 1;
|
|
1818
1398
|
continue;
|
|
@@ -2108,8 +1688,8 @@ var HuggingFaceResilience = class {
|
|
|
2108
1688
|
async execute(input) {
|
|
2109
1689
|
const models = [input.model, ...input.fallbackModels ?? []];
|
|
2110
1690
|
let lastError;
|
|
2111
|
-
for (const [
|
|
2112
|
-
const fallbackTriggered =
|
|
1691
|
+
for (const [index, model] of models.entries()) {
|
|
1692
|
+
const fallbackTriggered = index > 0;
|
|
2113
1693
|
try {
|
|
2114
1694
|
return await this.executeForModel(model, input, fallbackTriggered);
|
|
2115
1695
|
} catch (error) {
|
|
@@ -2176,8 +1756,7 @@ var HuggingFaceResilience = class {
|
|
|
2176
1756
|
retries = error.attemptNumber;
|
|
2177
1757
|
}
|
|
2178
1758
|
}
|
|
2179
|
-
)
|
|
2180
|
-
{ throwOnTimeout: true }
|
|
1759
|
+
)
|
|
2181
1760
|
);
|
|
2182
1761
|
this.markWarm(model);
|
|
2183
1762
|
status = fallbackTriggered ? "fallback" : "success";
|
|
@@ -2310,7 +1889,7 @@ function readRetryAfterMs(error) {
|
|
|
2310
1889
|
return Number.isFinite(retryAfterSeconds) ? retryAfterSeconds * 1e3 : void 0;
|
|
2311
1890
|
}
|
|
2312
1891
|
function sleep(ms) {
|
|
2313
|
-
return new Promise((
|
|
1892
|
+
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
2314
1893
|
}
|
|
2315
1894
|
|
|
2316
1895
|
// src/providers/huggingface.ts
|
|
@@ -2415,7 +1994,7 @@ function normalizeEmbeddingResponse(response) {
|
|
|
2415
1994
|
return [response];
|
|
2416
1995
|
}
|
|
2417
1996
|
if (response.every(
|
|
2418
|
-
(
|
|
1997
|
+
(vector) => Array.isArray(vector) && vector.every((value) => typeof value === "number")
|
|
2419
1998
|
)) {
|
|
2420
1999
|
return response;
|
|
2421
2000
|
}
|
|
@@ -2483,7 +2062,8 @@ var providerSchema = z.object({
|
|
|
2483
2062
|
type: z.enum(["local", "huggingface", "openrouter"]),
|
|
2484
2063
|
apiKey: z.string().min(1).optional(),
|
|
2485
2064
|
model: z.string().min(1).optional(),
|
|
2486
|
-
relevanceFloor: z.number().min(-1).max(1).optional()
|
|
2065
|
+
relevanceFloor: z.number().min(-1).max(1).optional(),
|
|
2066
|
+
recallFloor: z.number().min(-1).max(1).optional()
|
|
2487
2067
|
});
|
|
2488
2068
|
var codeBuddyConfigSchema = z.object({
|
|
2489
2069
|
postgresUrl: z.string().min(1, "postgresUrl is required"),
|
|
@@ -2505,308 +2085,6 @@ function validateConfig(config) {
|
|
|
2505
2085
|
return codeBuddyConfigSchema.parse(config);
|
|
2506
2086
|
}
|
|
2507
2087
|
|
|
2508
|
-
// src/core/memory-file-store.ts
|
|
2509
|
-
import { mkdir, readdir, readFile, rm } from "fs/promises";
|
|
2510
|
-
import { dirname as dirname2, join, relative, resolve as resolve2, sep } from "path";
|
|
2511
|
-
import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
|
|
2512
|
-
import { z as z2 } from "zod";
|
|
2513
|
-
var FACT_CATEGORIES = ["general", "incident"];
|
|
2514
|
-
var INCIDENT_SEVERITIES = ["low", "medium", "high", "critical"];
|
|
2515
|
-
var factFrontMatterSchema = z2.object({
|
|
2516
|
-
schemaVersion: z2.literal(1),
|
|
2517
|
-
id: z2.string().regex(/^fact_[a-z0-9]+$/),
|
|
2518
|
-
namespace: z2.string().min(1),
|
|
2519
|
-
type: z2.literal("fact"),
|
|
2520
|
-
subject: z2.string(),
|
|
2521
|
-
predicate: z2.string(),
|
|
2522
|
-
object: z2.string(),
|
|
2523
|
-
/** Explicit single-valued claim identity; facts sharing it are rival assertions. */
|
|
2524
|
-
propositionKey: z2.string().min(1).nullable().default(null),
|
|
2525
|
-
confidence: z2.number().min(0).max(1),
|
|
2526
|
-
createdAt: z2.preprocess(
|
|
2527
|
-
(value) => value instanceof Date ? value.toISOString() : value,
|
|
2528
|
-
z2.string().datetime()
|
|
2529
|
-
),
|
|
2530
|
-
createdByAgent: z2.string().nullable(),
|
|
2531
|
-
sourceInteractionId: z2.string().nullable(),
|
|
2532
|
-
sourceDeleted: z2.boolean(),
|
|
2533
|
-
category: z2.enum(FACT_CATEGORIES).default("general"),
|
|
2534
|
-
paths: z2.array(z2.string().min(1)).default([]),
|
|
2535
|
-
severity: z2.enum(INCIDENT_SEVERITIES).nullable().default(null),
|
|
2536
|
-
introducedBy: z2.string().nullable().default(null),
|
|
2537
|
-
resolvedBy: z2.string().nullable().default(null),
|
|
2538
|
-
fingerprint: z2.string().regex(/^[a-f0-9]{64}$/).nullable().default(null),
|
|
2539
|
-
sourcePlanId: z2.string().nullable().default(null),
|
|
2540
|
-
sourceSessionId: z2.string().nullable().default(null),
|
|
2541
|
-
verification: z2.array(z2.string()).default([]),
|
|
2542
|
-
supersededBy: z2.string().nullable().default(null),
|
|
2543
|
-
/** Namespaces granted a live reference to this fact (durable form of reference shares). */
|
|
2544
|
-
sharedWith: z2.array(z2.string().min(1)).default([])
|
|
2545
|
-
});
|
|
2546
|
-
var summaryFrontMatterSchema = z2.object({
|
|
2547
|
-
schemaVersion: z2.literal(1),
|
|
2548
|
-
id: z2.string().regex(/^sum_[a-z0-9]+$/),
|
|
2549
|
-
namespace: z2.string().min(1),
|
|
2550
|
-
type: z2.literal("summary"),
|
|
2551
|
-
sessionId: z2.string().min(1),
|
|
2552
|
-
version: z2.number().int().positive(),
|
|
2553
|
-
tokenCount: z2.number().int().nonnegative(),
|
|
2554
|
-
createdAt: z2.preprocess(
|
|
2555
|
-
(value) => value instanceof Date ? value.toISOString() : value,
|
|
2556
|
-
z2.string().datetime()
|
|
2557
|
-
),
|
|
2558
|
-
createdByAgent: z2.string().nullable()
|
|
2559
|
-
});
|
|
2560
|
-
var MemoryFileStore = class {
|
|
2561
|
-
repositoryRoot;
|
|
2562
|
-
factsDirectory;
|
|
2563
|
-
summariesDirectory;
|
|
2564
|
-
constructor(repositoryRoot = process.cwd()) {
|
|
2565
|
-
this.repositoryRoot = resolve2(repositoryRoot);
|
|
2566
|
-
this.factsDirectory = join(this.repositoryRoot, ".codebuddy", "memory", "facts");
|
|
2567
|
-
this.summariesDirectory = join(this.repositoryRoot, ".codebuddy", "memory", "summaries");
|
|
2568
|
-
}
|
|
2569
|
-
async writeFact(raw) {
|
|
2570
|
-
const input = redactPersisted(raw);
|
|
2571
|
-
const path = this.factPath(input.id);
|
|
2572
|
-
await this.prepareDirectory(this.factsDirectory);
|
|
2573
|
-
await assertNoSymlinkBetween(this.repositoryRoot, dirname2(path));
|
|
2574
|
-
const metadata = {
|
|
2575
|
-
schemaVersion: 1,
|
|
2576
|
-
id: input.id,
|
|
2577
|
-
namespace: input.namespace,
|
|
2578
|
-
type: "fact",
|
|
2579
|
-
subject: input.subject,
|
|
2580
|
-
predicate: input.predicate,
|
|
2581
|
-
object: input.object,
|
|
2582
|
-
propositionKey: input.propositionKey,
|
|
2583
|
-
confidence: input.confidence,
|
|
2584
|
-
createdAt: input.createdAt,
|
|
2585
|
-
createdByAgent: input.createdByAgent,
|
|
2586
|
-
sourceInteractionId: input.sourceInteractionId,
|
|
2587
|
-
sourceDeleted: input.sourceDeleted,
|
|
2588
|
-
category: input.category,
|
|
2589
|
-
paths: input.paths,
|
|
2590
|
-
severity: input.severity,
|
|
2591
|
-
introducedBy: input.introducedBy,
|
|
2592
|
-
resolvedBy: input.resolvedBy,
|
|
2593
|
-
fingerprint: input.fingerprint,
|
|
2594
|
-
sourcePlanId: input.sourcePlanId,
|
|
2595
|
-
sourceSessionId: input.sourceSessionId,
|
|
2596
|
-
verification: input.verification,
|
|
2597
|
-
supersededBy: input.supersededBy,
|
|
2598
|
-
sharedWith: input.sharedWith
|
|
2599
|
-
};
|
|
2600
|
-
const frontMatter = stringifyYaml(stripUndefinedAndDefaultIncidentFields(metadata));
|
|
2601
|
-
await writeAtomic(path, composeMarkdown(frontMatter, input.content));
|
|
2602
|
-
return path;
|
|
2603
|
-
}
|
|
2604
|
-
async writeSummary(input) {
|
|
2605
|
-
const path = this.summaryPath(input.id);
|
|
2606
|
-
await this.prepareDirectory(this.summariesDirectory);
|
|
2607
|
-
await assertNoSymlinkBetween(this.repositoryRoot, dirname2(path));
|
|
2608
|
-
const metadata = stringifyYaml({
|
|
2609
|
-
schemaVersion: 1,
|
|
2610
|
-
id: input.id,
|
|
2611
|
-
namespace: input.namespace,
|
|
2612
|
-
type: "summary",
|
|
2613
|
-
sessionId: input.sessionId,
|
|
2614
|
-
version: input.version,
|
|
2615
|
-
tokenCount: input.tokenCount,
|
|
2616
|
-
createdAt: input.createdAt,
|
|
2617
|
-
createdByAgent: input.createdByAgent
|
|
2618
|
-
});
|
|
2619
|
-
await writeAtomic(path, composeMarkdown(metadata, input.content));
|
|
2620
|
-
return path;
|
|
2621
|
-
}
|
|
2622
|
-
async readFact(pathOrId) {
|
|
2623
|
-
const path = pathOrId.endsWith(".md") ? this.safePath(pathOrId) : this.factPath(pathOrId);
|
|
2624
|
-
await assertNoSymlinkBetween(this.repositoryRoot, path);
|
|
2625
|
-
const parsed = parseFrontMatter(await readFile(path, "utf8"));
|
|
2626
|
-
const metadata = factFrontMatterSchema.parse(parseYaml(parsed.frontMatter));
|
|
2627
|
-
return {
|
|
2628
|
-
...metadata,
|
|
2629
|
-
content: parsed.content.trim(),
|
|
2630
|
-
path
|
|
2631
|
-
};
|
|
2632
|
-
}
|
|
2633
|
-
async listFacts() {
|
|
2634
|
-
return (await this.scanFacts()).facts;
|
|
2635
|
-
}
|
|
2636
|
-
/**
|
|
2637
|
-
* Parse every managed fact file. Hand-written markdown is ignored, and a
|
|
2638
|
-
* schema-invalid/corrupt fact is reported in `invalid` (not thrown) so one
|
|
2639
|
-
* bad file can't break recall, capture or reindexing for the whole project.
|
|
2640
|
-
* Invalid files stay on disk for manual repair.
|
|
2641
|
-
*/
|
|
2642
|
-
async scanFacts() {
|
|
2643
|
-
const scan = await this.scan(
|
|
2644
|
-
this.factsDirectory,
|
|
2645
|
-
/^fact_[a-z0-9]+\.md$/,
|
|
2646
|
-
(path) => this.readFact(path)
|
|
2647
|
-
);
|
|
2648
|
-
return { facts: scan.items, invalid: scan.invalid };
|
|
2649
|
-
}
|
|
2650
|
-
/**
|
|
2651
|
-
* List incident facts for the namespace, ranked by severity then recency.
|
|
2652
|
-
* Unlike {@link findIncidentFactsForPaths} this does not filter by path, so
|
|
2653
|
-
* callers (e.g. the context engine's project bootstrap) can surface the
|
|
2654
|
-
* repository's incident hotspots before any specific edit target is known.
|
|
2655
|
-
*/
|
|
2656
|
-
async listIncidentFacts(input = {}) {
|
|
2657
|
-
const incidents = (await this.listFacts()).filter(
|
|
2658
|
-
(fact) => fact.category === "incident" && Boolean(fact.severity) && isEligibleMemory(fact, input)
|
|
2659
|
-
);
|
|
2660
|
-
return incidents.sort((left, right) => {
|
|
2661
|
-
const severity = severityRank(right.severity) - severityRank(left.severity);
|
|
2662
|
-
if (severity !== 0) return severity;
|
|
2663
|
-
return right.createdAt.localeCompare(left.createdAt);
|
|
2664
|
-
});
|
|
2665
|
-
}
|
|
2666
|
-
async findIncidentFactsForPaths(input) {
|
|
2667
|
-
const wanted = new Set(input.paths.map(normalizeMemoryPath));
|
|
2668
|
-
if (wanted.size === 0) return [];
|
|
2669
|
-
return (await this.listIncidentFacts({
|
|
2670
|
-
...input.namespace !== void 0 ? { namespace: input.namespace } : {},
|
|
2671
|
-
...input.includeResolved !== void 0 ? { includeResolved: input.includeResolved } : {}
|
|
2672
|
-
})).filter((fact) => fact.paths.map(normalizeMemoryPath).some((path) => wanted.has(path)));
|
|
2673
|
-
}
|
|
2674
|
-
async readSummary(pathOrId) {
|
|
2675
|
-
const path = pathOrId.endsWith(".md") ? this.safePath(pathOrId) : this.summaryPath(pathOrId);
|
|
2676
|
-
await assertNoSymlinkBetween(this.repositoryRoot, path);
|
|
2677
|
-
const parsed = parseFrontMatter(await readFile(path, "utf8"));
|
|
2678
|
-
return {
|
|
2679
|
-
...summaryFrontMatterSchema.parse(parseYaml(parsed.frontMatter)),
|
|
2680
|
-
content: parsed.content.trim(),
|
|
2681
|
-
path
|
|
2682
|
-
};
|
|
2683
|
-
}
|
|
2684
|
-
async listSummaries() {
|
|
2685
|
-
return (await this.scanSummaries()).summaries;
|
|
2686
|
-
}
|
|
2687
|
-
async scanSummaries() {
|
|
2688
|
-
const scan = await this.scan(
|
|
2689
|
-
this.summariesDirectory,
|
|
2690
|
-
/^sum_[a-z0-9]+\.md$/,
|
|
2691
|
-
(path) => this.readSummary(path)
|
|
2692
|
-
);
|
|
2693
|
-
return { summaries: scan.items, invalid: scan.invalid };
|
|
2694
|
-
}
|
|
2695
|
-
async scan(directory, managedName, read) {
|
|
2696
|
-
try {
|
|
2697
|
-
await this.prepareDirectory(directory);
|
|
2698
|
-
const entries = await readdir(directory, { withFileTypes: true });
|
|
2699
|
-
const result = { items: [], invalid: [] };
|
|
2700
|
-
for (const entry of entries.sort((left, right) => left.name.localeCompare(right.name))) {
|
|
2701
|
-
if (!entry.isFile() || !managedName.test(entry.name)) continue;
|
|
2702
|
-
const path = join(directory, entry.name);
|
|
2703
|
-
try {
|
|
2704
|
-
const item = await read(path);
|
|
2705
|
-
const id = entry.name.slice(0, -".md".length);
|
|
2706
|
-
if (item.id !== id)
|
|
2707
|
-
throw new Error(`file ID ${id} differs from front matter ID ${item.id}`);
|
|
2708
|
-
result.items.push(item);
|
|
2709
|
-
} catch (error) {
|
|
2710
|
-
result.invalid.push({
|
|
2711
|
-
id: entry.name.slice(0, -".md".length),
|
|
2712
|
-
path,
|
|
2713
|
-
error: error.message.split("\n")[0]?.slice(0, 300) ?? "unreadable"
|
|
2714
|
-
});
|
|
2715
|
-
}
|
|
2716
|
-
}
|
|
2717
|
-
return result;
|
|
2718
|
-
} catch (error) {
|
|
2719
|
-
if (error.code === "ENOENT") return { items: [], invalid: [] };
|
|
2720
|
-
throw error;
|
|
2721
|
-
}
|
|
2722
|
-
}
|
|
2723
|
-
async deleteFact(id) {
|
|
2724
|
-
await rm(this.factPath(id), { force: true });
|
|
2725
|
-
}
|
|
2726
|
-
async markIncidentResolved(id, resolvedBy) {
|
|
2727
|
-
const fact = await this.readFact(id);
|
|
2728
|
-
await this.writeFact(copyFact(fact, { resolvedBy }));
|
|
2729
|
-
}
|
|
2730
|
-
async markFactSuperseded(id, supersededBy) {
|
|
2731
|
-
const fact = await this.readFact(id);
|
|
2732
|
-
await this.writeFact(copyFact(fact, { supersededBy }));
|
|
2733
|
-
}
|
|
2734
|
-
async setFactSharedWith(id, sharedWith) {
|
|
2735
|
-
const fact = await this.readFact(id);
|
|
2736
|
-
await this.writeFact(copyFact(fact, { sharedWith: [...new Set(sharedWith)].sort() }));
|
|
2737
|
-
}
|
|
2738
|
-
/** Persist interaction deletion in Markdown before the derived DB is updated. */
|
|
2739
|
-
async markSourceInteractionDeleted(namespace, sourceInteractionId) {
|
|
2740
|
-
const { facts: facts2 } = await this.scanFacts();
|
|
2741
|
-
for (const fact of facts2) {
|
|
2742
|
-
if (fact.namespace !== namespace || fact.sourceInteractionId !== sourceInteractionId)
|
|
2743
|
-
continue;
|
|
2744
|
-
if (fact.sourceDeleted) continue;
|
|
2745
|
-
await this.writeFact(copyFact(fact, { sourceDeleted: true }));
|
|
2746
|
-
}
|
|
2747
|
-
}
|
|
2748
|
-
/** Persist that a fact's source interaction was forgotten, keeping its provenance id. */
|
|
2749
|
-
async markFactSourceDeleted(id) {
|
|
2750
|
-
const fact = await this.readFact(id);
|
|
2751
|
-
await this.writeFact(copyFact(fact, { sourceDeleted: true }));
|
|
2752
|
-
}
|
|
2753
|
-
factPath(id) {
|
|
2754
|
-
if (!/^fact_[a-z0-9]+$/.test(id)) {
|
|
2755
|
-
throw new Error(`Invalid fact id: ${id}`);
|
|
2756
|
-
}
|
|
2757
|
-
return this.safePath(join(this.factsDirectory, `${id}.md`));
|
|
2758
|
-
}
|
|
2759
|
-
summaryPath(id) {
|
|
2760
|
-
if (!/^sum_[a-z0-9]+$/.test(id)) throw new Error(`Invalid summary id: ${id}`);
|
|
2761
|
-
return this.safePath(join(this.summariesDirectory, `${id}.md`));
|
|
2762
|
-
}
|
|
2763
|
-
safePath(path) {
|
|
2764
|
-
const absolute = resolve2(path);
|
|
2765
|
-
const relativePath = relative(this.repositoryRoot, absolute);
|
|
2766
|
-
if (relativePath === ".." || relativePath.startsWith(`..${sep}`) || relativePath === "") {
|
|
2767
|
-
throw new Error(`Memory path escapes repository root: ${path}`);
|
|
2768
|
-
}
|
|
2769
|
-
return absolute;
|
|
2770
|
-
}
|
|
2771
|
-
async prepareDirectory(directory) {
|
|
2772
|
-
await assertNoSymlinkBetween(this.repositoryRoot, dirname2(directory));
|
|
2773
|
-
await mkdir(directory, { recursive: true, mode: 448 });
|
|
2774
|
-
}
|
|
2775
|
-
};
|
|
2776
|
-
function copyFact(fact, patch) {
|
|
2777
|
-
const { path: _path, schemaVersion: _schemaVersion, type: _type, ...write } = fact;
|
|
2778
|
-
return { ...write, ...patch };
|
|
2779
|
-
}
|
|
2780
|
-
function stripUndefinedAndDefaultIncidentFields(metadata) {
|
|
2781
|
-
return Object.fromEntries(
|
|
2782
|
-
Object.entries(metadata).filter(([key, value]) => {
|
|
2783
|
-
if (value === void 0) return false;
|
|
2784
|
-
if (key === "category" && value === "general") return false;
|
|
2785
|
-
if ((key === "paths" || key === "sharedWith") && Array.isArray(value) && value.length === 0)
|
|
2786
|
-
return false;
|
|
2787
|
-
if (["severity", "introducedBy", "resolvedBy", "propositionKey"].includes(key) && (value === null || value === void 0)) {
|
|
2788
|
-
return false;
|
|
2789
|
-
}
|
|
2790
|
-
return true;
|
|
2791
|
-
})
|
|
2792
|
-
);
|
|
2793
|
-
}
|
|
2794
|
-
function normalizeMemoryPath(path) {
|
|
2795
|
-
return path.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
2796
|
-
}
|
|
2797
|
-
function severityRank(severity) {
|
|
2798
|
-
switch (severity) {
|
|
2799
|
-
case "critical":
|
|
2800
|
-
return 4;
|
|
2801
|
-
case "high":
|
|
2802
|
-
return 3;
|
|
2803
|
-
case "medium":
|
|
2804
|
-
return 2;
|
|
2805
|
-
case "low":
|
|
2806
|
-
return 1;
|
|
2807
|
-
}
|
|
2808
|
-
}
|
|
2809
|
-
|
|
2810
2088
|
// src/core/worker.ts
|
|
2811
2089
|
var DEFAULT_POLL_MS = 250;
|
|
2812
2090
|
var DEFAULT_BATCH_SIZE = 8;
|
|
@@ -2819,6 +2097,7 @@ var EmbeddingWorker = class {
|
|
|
2819
2097
|
batchSize;
|
|
2820
2098
|
maxAttempts;
|
|
2821
2099
|
onDiagnostic;
|
|
2100
|
+
namespaceId;
|
|
2822
2101
|
state = "idle";
|
|
2823
2102
|
loopPromise = null;
|
|
2824
2103
|
wake = null;
|
|
@@ -2831,6 +2110,7 @@ var EmbeddingWorker = class {
|
|
|
2831
2110
|
this.batchSize = options.batchSize ?? DEFAULT_BATCH_SIZE;
|
|
2832
2111
|
this.maxAttempts = options.maxAttempts ?? DEFAULT_MAX_ATTEMPTS;
|
|
2833
2112
|
this.onDiagnostic = options.onDiagnostic;
|
|
2113
|
+
this.namespaceId = options.namespaceId;
|
|
2834
2114
|
}
|
|
2835
2115
|
getState() {
|
|
2836
2116
|
return this.state;
|
|
@@ -2860,8 +2140,8 @@ var EmbeddingWorker = class {
|
|
|
2860
2140
|
* wait out anymore.
|
|
2861
2141
|
*/
|
|
2862
2142
|
async drain() {
|
|
2863
|
-
await new Promise((
|
|
2864
|
-
this.idleResolvers.push(
|
|
2143
|
+
await new Promise((resolve2) => {
|
|
2144
|
+
this.idleResolvers.push(resolve2);
|
|
2865
2145
|
this.notify();
|
|
2866
2146
|
});
|
|
2867
2147
|
}
|
|
@@ -2887,13 +2167,13 @@ var EmbeddingWorker = class {
|
|
|
2887
2167
|
while (this.state === "running") {
|
|
2888
2168
|
let jobs;
|
|
2889
2169
|
try {
|
|
2890
|
-
jobs = await this.repo.claimPendingEmbeddings(this.batchSize);
|
|
2170
|
+
jobs = await this.repo.claimPendingEmbeddings(this.batchSize, this.namespaceId?.());
|
|
2891
2171
|
} catch (error) {
|
|
2892
2172
|
console.error(
|
|
2893
2173
|
`[worker] claimPendingEmbeddings failed: ${error instanceof Error ? error.message : String(error)}; retrying`
|
|
2894
2174
|
);
|
|
2895
2175
|
if (this.state !== "running") break;
|
|
2896
|
-
await new Promise((
|
|
2176
|
+
await new Promise((resolve2) => setTimeout(resolve2, ERROR_BACKOFF_MS));
|
|
2897
2177
|
continue;
|
|
2898
2178
|
}
|
|
2899
2179
|
if (jobs.length === 0) {
|
|
@@ -2920,7 +2200,7 @@ var EmbeddingWorker = class {
|
|
|
2920
2200
|
if (this.inFlight > 0) return;
|
|
2921
2201
|
const resolvers = this.idleResolvers;
|
|
2922
2202
|
this.idleResolvers = [];
|
|
2923
|
-
for (const
|
|
2203
|
+
for (const resolve2 of resolvers) resolve2();
|
|
2924
2204
|
}
|
|
2925
2205
|
async processJob(job) {
|
|
2926
2206
|
let leaseLost = false;
|
|
@@ -2940,14 +2220,14 @@ var EmbeddingWorker = class {
|
|
|
2940
2220
|
model: job.embeddingModel
|
|
2941
2221
|
});
|
|
2942
2222
|
validateEmbeddingResponse(response, 1);
|
|
2943
|
-
const
|
|
2944
|
-
if (!
|
|
2223
|
+
const vector = response.vectors[0];
|
|
2224
|
+
if (!vector || vector.length === 0) {
|
|
2945
2225
|
throw new Error("Provider returned no embedding vector.");
|
|
2946
2226
|
}
|
|
2947
2227
|
if (leaseLost || !await this.repo.markEmbeddingReady(
|
|
2948
2228
|
job.id,
|
|
2949
2229
|
job.leaseToken,
|
|
2950
|
-
|
|
2230
|
+
vector,
|
|
2951
2231
|
response.model,
|
|
2952
2232
|
embeddingSpaceKey(embeddingSpace(response))
|
|
2953
2233
|
))
|
|
@@ -3005,14 +2285,14 @@ var EmbeddingWorker = class {
|
|
|
3005
2285
|
});
|
|
3006
2286
|
}
|
|
3007
2287
|
sleep(ms) {
|
|
3008
|
-
return new Promise((
|
|
2288
|
+
return new Promise((resolve2) => {
|
|
3009
2289
|
const timer = setTimeout(() => {
|
|
3010
2290
|
this.wake = null;
|
|
3011
|
-
|
|
2291
|
+
resolve2();
|
|
3012
2292
|
}, ms ?? this.pollIntervalMs);
|
|
3013
2293
|
this.wake = () => {
|
|
3014
2294
|
clearTimeout(timer);
|
|
3015
|
-
|
|
2295
|
+
resolve2();
|
|
3016
2296
|
};
|
|
3017
2297
|
});
|
|
3018
2298
|
}
|
|
@@ -3043,6 +2323,7 @@ var CodeBuddy = class {
|
|
|
3043
2323
|
this.worker = deps.worker ?? new EmbeddingWorker({
|
|
3044
2324
|
repository: this.repo,
|
|
3045
2325
|
provider: this.provider,
|
|
2326
|
+
namespaceId: () => this.namespaceId ?? void 0,
|
|
3046
2327
|
...this.config.worker?.pollIntervalMs !== void 0 ? { pollIntervalMs: this.config.worker.pollIntervalMs } : {},
|
|
3047
2328
|
...this.config.worker?.batchSize !== void 0 ? { batchSize: this.config.worker.batchSize } : {},
|
|
3048
2329
|
...this.config.worker?.maxAttempts !== void 0 ? { maxAttempts: this.config.worker.maxAttempts } : {}
|
|
@@ -3076,6 +2357,9 @@ var CodeBuddy = class {
|
|
|
3076
2357
|
if (!content || content.length === 0) {
|
|
3077
2358
|
throw new Error("remember: content must not be empty.");
|
|
3078
2359
|
}
|
|
2360
|
+
if (input.idempotencyKey !== void 0 && (input.idempotencyKey.length === 0 || input.idempotencyKey.length > 200)) {
|
|
2361
|
+
throw new Error("remember: idempotencyKey must be 1\u2013200 characters.");
|
|
2362
|
+
}
|
|
3079
2363
|
const result = await this.repo.withSessionLock(
|
|
3080
2364
|
namespaceId,
|
|
3081
2365
|
sessionId,
|
|
@@ -3120,7 +2404,8 @@ var CodeBuddy = class {
|
|
|
3120
2404
|
conflictMode: input.conflictMode ?? "all",
|
|
3121
2405
|
queryEmbedding,
|
|
3122
2406
|
repository: this.repo,
|
|
3123
|
-
...input.callerModel !== void 0 ? { callerModel: input.callerModel } : {}
|
|
2407
|
+
...input.callerModel !== void 0 ? { callerModel: input.callerModel } : {},
|
|
2408
|
+
relevanceFloor: this.recallFloor(queryEmbedding?.space.model)
|
|
3124
2409
|
};
|
|
3125
2410
|
const planned = await this.tracer.withSpan(
|
|
3126
2411
|
"codebuddy.recall",
|
|
@@ -3139,6 +2424,11 @@ var CodeBuddy = class {
|
|
|
3139
2424
|
}
|
|
3140
2425
|
return planned;
|
|
3141
2426
|
}
|
|
2427
|
+
/** Cosine floor for an explicit `recall` of the query's embedding model. */
|
|
2428
|
+
recallFloor(model) {
|
|
2429
|
+
const definition = model ? getModelDefinition(model) : void 0;
|
|
2430
|
+
return this.config.provider.recallFloor ?? definition?.recallFloor ?? this.config.provider.relevanceFloor ?? definition?.relevanceFloor;
|
|
2431
|
+
}
|
|
3142
2432
|
/**
|
|
3143
2433
|
* Dense evidence for file facts supplied by the context engine: the cosine of
|
|
3144
2434
|
* each fact whose stored vector clears the embedding model's calibrated
|
|
@@ -3186,8 +2476,8 @@ var CodeBuddy = class {
|
|
|
3186
2476
|
})
|
|
3187
2477
|
);
|
|
3188
2478
|
validateEmbeddingResponse(response, 1);
|
|
3189
|
-
const
|
|
3190
|
-
return
|
|
2479
|
+
const vector = response.vectors[0];
|
|
2480
|
+
return vector ? { vector, space: embeddingSpace(response) } : null;
|
|
3191
2481
|
} catch {
|
|
3192
2482
|
return null;
|
|
3193
2483
|
}
|
|
@@ -3369,9 +2659,10 @@ var CodeBuddy = class {
|
|
|
3369
2659
|
};
|
|
3370
2660
|
const factResult = await this.repo.withSessionLock(namespaceId, REINDEX_LOCK_KEY, async () => {
|
|
3371
2661
|
const result = await this.repo.forgetFact(namespaceId, id, factAudit);
|
|
3372
|
-
|
|
3373
|
-
|
|
3374
|
-
|
|
2662
|
+
const file = await this.memoryFileStore?.readFact(id).catch(() => null);
|
|
2663
|
+
if (file?.namespace === this.config.namespace) {
|
|
2664
|
+
await this.memoryFileStore?.deleteFact(id);
|
|
2665
|
+
return { ...result, found: true };
|
|
3375
2666
|
}
|
|
3376
2667
|
return result;
|
|
3377
2668
|
});
|
|
@@ -3387,7 +2678,7 @@ var CodeBuddy = class {
|
|
|
3387
2678
|
* file is applied independently: malformed or conflicting files are reported
|
|
3388
2679
|
* in `failures` and never abort healthy imports (nor count as deletions).
|
|
3389
2680
|
*/
|
|
3390
|
-
async reindexMemory() {
|
|
2681
|
+
async reindexMemory(options = {}) {
|
|
3391
2682
|
const namespaceId = this.requireNamespaceId();
|
|
3392
2683
|
const store = this.memoryFileStore;
|
|
3393
2684
|
if (!store) {
|
|
@@ -3467,7 +2758,7 @@ var CodeBuddy = class {
|
|
|
3467
2758
|
failures: failures.slice(0, MAX_REPORTED_FAILURES)
|
|
3468
2759
|
};
|
|
3469
2760
|
});
|
|
3470
|
-
const space = this.provider.space;
|
|
2761
|
+
const space = options.respace === false ? void 0 : this.provider.space;
|
|
3471
2762
|
const canEmbed = space !== void 0 && await this.provider.embed({ input: "codebuddy" }).then(
|
|
3472
2763
|
() => true,
|
|
3473
2764
|
() => false
|
|
@@ -3495,7 +2786,7 @@ var CodeBuddy = class {
|
|
|
3495
2786
|
content: input.content
|
|
3496
2787
|
});
|
|
3497
2788
|
const content = input.type === "fact" ? claim.content : input.content;
|
|
3498
|
-
const contentHash = input.
|
|
2789
|
+
const contentHash = input.type === "fact" ? factContentHash(this.config.namespace, ownerId, content) : input.idempotencyKey ?? computeContentHash(this.config.namespace, input.sessionId, input.content);
|
|
3499
2790
|
const tokenCount = estimateTokenCount(content);
|
|
3500
2791
|
const embeddingId = generateEntityId("emb");
|
|
3501
2792
|
const embedding = {
|
|
@@ -3535,6 +2826,7 @@ var CodeBuddy = class {
|
|
|
3535
2826
|
subject: claim.subject,
|
|
3536
2827
|
predicate: claim.predicate,
|
|
3537
2828
|
object: claim.object,
|
|
2829
|
+
...input.idempotencyKey !== void 0 ? { idempotencyKey: input.idempotencyKey } : {},
|
|
3538
2830
|
...input.agentId !== void 0 ? { createdByAgent: input.agentId } : {}
|
|
3539
2831
|
};
|
|
3540
2832
|
const result2 = await this.repo.writeFact({ fact, embedding, audit });
|
|
@@ -3558,6 +2850,7 @@ var CodeBuddy = class {
|
|
|
3558
2850
|
createdByAgent: input.agentId ?? null,
|
|
3559
2851
|
sourceInteractionId: fact.sourceInteractionId ?? null,
|
|
3560
2852
|
sourceDeleted: false,
|
|
2853
|
+
...input.idempotencyKey !== void 0 ? { idempotencyKey: input.idempotencyKey } : {},
|
|
3561
2854
|
content: fact.content
|
|
3562
2855
|
});
|
|
3563
2856
|
}
|
|
@@ -3622,6 +2915,7 @@ var CodeBuddy = class {
|
|
|
3622
2915
|
sourceDeleted: file.sourceDeleted,
|
|
3623
2916
|
supersededBy: file.supersededBy,
|
|
3624
2917
|
propositionKey: file.propositionKey ?? null,
|
|
2918
|
+
idempotencyKey: file.idempotencyKey ?? null,
|
|
3625
2919
|
createdByAgent: file.createdByAgent ?? void 0,
|
|
3626
2920
|
createdAt: new Date(file.createdAt)
|
|
3627
2921
|
},
|
|
@@ -3813,8 +3107,8 @@ function decodeFactCursor(cursor) {
|
|
|
3813
3107
|
if (!Array.isArray(parsed) || parsed.length !== 2 || typeof parsed[0] !== "string" || typeof parsed[1] !== "string" || Number.isNaN(Date.parse(parsed[0]))) {
|
|
3814
3108
|
throw new Error("Invalid cursor: expected a base64url-encoded [createdAt, id] pair.");
|
|
3815
3109
|
}
|
|
3816
|
-
const [
|
|
3817
|
-
return { createdAt: new Date(
|
|
3110
|
+
const [createdAt, id] = parsed;
|
|
3111
|
+
return { createdAt: new Date(createdAt), id };
|
|
3818
3112
|
}
|
|
3819
3113
|
async function listFactsPage(memory, repository, input) {
|
|
3820
3114
|
const namespace = await repository.ensureNamespace(memory.config.namespace);
|
|
@@ -3850,6 +3144,7 @@ async function inspectNamespace(repository, namespace) {
|
|
|
3850
3144
|
};
|
|
3851
3145
|
}
|
|
3852
3146
|
async function runDoctor(options = {}) {
|
|
3147
|
+
const migrated = await migrateLegacyNamespace();
|
|
3853
3148
|
const fileConfig = await readConfigFile();
|
|
3854
3149
|
const scaffold = await checkProjectScaffold();
|
|
3855
3150
|
const configPermissions = await checkConfigPermissions();
|
|
@@ -3859,6 +3154,7 @@ async function runDoctor(options = {}) {
|
|
|
3859
3154
|
expectedDefault: expectedDefaultNamespace,
|
|
3860
3155
|
configured: fileConfig.namespace ?? null,
|
|
3861
3156
|
runtime: runtimeNamespace,
|
|
3157
|
+
...migrated ? { migrated } : {},
|
|
3862
3158
|
ok: !fileConfig.namespace || fileConfig.namespace === expectedDefaultNamespace || process.env.CODEBUDDY_NAMESPACE !== void 0,
|
|
3863
3159
|
...!fileConfig.namespace || fileConfig.namespace === expectedDefaultNamespace || process.env.CODEBUDDY_NAMESPACE !== void 0 ? {} : {
|
|
3864
3160
|
warning: `Configured namespace "${fileConfig.namespace}" differs from this folder's default "${expectedDefaultNamespace}". Confirm your MCP entries point at the intended namespace.`
|
|
@@ -4003,18 +3299,12 @@ async function checkModels(provider, skip = false) {
|
|
|
4003
3299
|
}
|
|
4004
3300
|
|
|
4005
3301
|
export {
|
|
4006
|
-
isEligibleMemory,
|
|
4007
|
-
lexicalTerms,
|
|
4008
|
-
termOverlap,
|
|
4009
3302
|
embeddingSpaceKey,
|
|
4010
3303
|
DefaultPolicy,
|
|
4011
3304
|
HuggingFaceProvider,
|
|
4012
3305
|
isLangSmithTracingEnabled,
|
|
4013
3306
|
createTracer,
|
|
4014
3307
|
codeBuddyConfigSchema,
|
|
4015
|
-
FACT_CATEGORIES,
|
|
4016
|
-
INCIDENT_SEVERITIES,
|
|
4017
|
-
MemoryFileStore,
|
|
4018
3308
|
EmbeddingWorker,
|
|
4019
3309
|
CodeBuddy,
|
|
4020
3310
|
buildProviderFromConfig,
|
|
@@ -4030,4 +3320,4 @@ export {
|
|
|
4030
3320
|
inspectNamespace,
|
|
4031
3321
|
runDoctor
|
|
4032
3322
|
};
|
|
4033
|
-
//# sourceMappingURL=chunk-
|
|
3323
|
+
//# sourceMappingURL=chunk-AK2UAE32.js.map
|