@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.
Files changed (117) hide show
  1. package/CLAUDE_CODEX.md +6 -4
  2. package/WORKFLOW.md +5 -5
  3. package/dist/{chunk-U4HAJLIB.js → chunk-3QGW53GD.js} +16 -3
  4. package/dist/chunk-3QGW53GD.js.map +1 -0
  5. package/dist/{chunk-L5AYZC3F.js → chunk-5EDGAZQN.js} +438 -68
  6. package/dist/chunk-5EDGAZQN.js.map +1 -0
  7. package/dist/chunk-5UH3BEQR.js +32 -0
  8. package/dist/chunk-5UH3BEQR.js.map +1 -0
  9. package/dist/{chunk-JFDRBPYF.js → chunk-6JCKMIQY.js} +2 -2
  10. package/dist/{chunk-UN57FKQX.js → chunk-AK2UAE32.js} +152 -862
  11. package/dist/chunk-AK2UAE32.js.map +1 -0
  12. package/dist/{chunk-GDJH7XJC.js → chunk-AL6VBTNA.js} +77 -3
  13. package/dist/chunk-AL6VBTNA.js.map +1 -0
  14. package/dist/{chunk-WHZSVY2L.js → chunk-AQHPEJR6.js} +94 -23
  15. package/dist/chunk-AQHPEJR6.js.map +1 -0
  16. package/dist/{chunk-PCE4STAY.js → chunk-BQM7FJ7D.js} +6 -6
  17. package/dist/chunk-BQM7FJ7D.js.map +1 -0
  18. package/dist/{chunk-XBJQKCLU.js → chunk-CAEPHLHC.js} +13 -9
  19. package/dist/chunk-CAEPHLHC.js.map +1 -0
  20. package/dist/chunk-CG5B3LYG.js +66 -0
  21. package/dist/chunk-CG5B3LYG.js.map +1 -0
  22. package/dist/chunk-CSRD5OQA.js +79 -0
  23. package/dist/chunk-CSRD5OQA.js.map +1 -0
  24. package/dist/{chunk-IHQM2ZDV.js → chunk-DFV2SK6C.js} +1 -1
  25. package/dist/{chunk-IHQM2ZDV.js.map → chunk-DFV2SK6C.js.map} +1 -1
  26. package/dist/chunk-EEUFU7KV.js +1634 -0
  27. package/dist/chunk-EEUFU7KV.js.map +1 -0
  28. package/dist/{chunk-UFZD3JAA.js → chunk-ESZXCZIN.js} +3 -3
  29. package/dist/{chunk-PYLH4MH5.js → chunk-FJOTP632.js} +1 -1
  30. package/dist/{chunk-SYWJG5NA.js → chunk-GKNYMNFM.js} +17 -7
  31. package/dist/{chunk-SYWJG5NA.js.map → chunk-GKNYMNFM.js.map} +1 -1
  32. package/dist/{chunk-JPHJBCZB.js → chunk-HPOR6YXA.js} +3 -2
  33. package/dist/chunk-HPOR6YXA.js.map +1 -0
  34. package/dist/chunk-JUAFCLRB.js +1014 -0
  35. package/dist/chunk-JUAFCLRB.js.map +1 -0
  36. package/dist/{chunk-776EQXDV.js → chunk-KNR2P2UY.js} +110 -83
  37. package/dist/chunk-KNR2P2UY.js.map +1 -0
  38. package/dist/{chunk-N34XHWBA.js → chunk-LCSBL5TT.js} +24 -11
  39. package/dist/chunk-LCSBL5TT.js.map +1 -0
  40. package/dist/{chunk-QDBDYCM2.js → chunk-MSUAVR3R.js} +76 -11
  41. package/dist/chunk-MSUAVR3R.js.map +1 -0
  42. package/dist/{chunk-6MKEIDX5.js → chunk-SFMIMLXR.js} +2 -2
  43. package/dist/chunk-TGFTKWOR.js +16 -0
  44. package/dist/chunk-TGFTKWOR.js.map +1 -0
  45. package/dist/chunk-TUR6YKEE.js +55 -0
  46. package/dist/chunk-TUR6YKEE.js.map +1 -0
  47. package/dist/chunk-Y7D7PUBE.js +321 -0
  48. package/dist/chunk-Y7D7PUBE.js.map +1 -0
  49. package/dist/{chunk-VAKBJV26.js → chunk-YJIA2MFL.js} +918 -2238
  50. package/dist/chunk-YJIA2MFL.js.map +1 -0
  51. package/dist/cli/index.js +14 -3
  52. package/dist/cli/index.js.map +1 -1
  53. package/dist/{cloud-7WM3YLI4.js → cloud-LXZUR3U2.js} +3 -3
  54. package/dist/{codebuddy-DM-i0Fu5.d.ts → codebuddy-DO7ZhoWn.d.ts} +37 -3
  55. package/dist/config-GEZFLXK6.js +12 -0
  56. package/dist/docker-autostart-KDME7TVC.js +12 -0
  57. package/dist/index.d.ts +48 -3
  58. package/dist/index.js +32 -25
  59. package/dist/langgraph/index.d.ts +1 -1
  60. package/dist/lean-command-CWJAVQ5G.js +18 -0
  61. package/dist/local-ST4WMRCG.js +17 -0
  62. package/dist/{main-HFHLGID4.js → main-AHNGHC4L.js} +327 -329
  63. package/dist/main-AHNGHC4L.js.map +1 -0
  64. package/dist/{memory-4RGVLPKG.js → memory-TGHVTBME.js} +9 -8
  65. package/dist/memory-TGHVTBME.js.map +1 -0
  66. package/dist/operations-7BTAAUHF.js +26 -0
  67. package/dist/postgres-docker-QQZQ2ABK.js +16 -0
  68. package/dist/pretool-command-3SXQSMAO.js +13 -0
  69. package/dist/{rerank-3P3C2UHN.js → rerank-IQAEWLQR.js} +6 -5
  70. package/dist/rerank-IQAEWLQR.js.map +1 -0
  71. package/dist/runtime-E7JQZ2BZ.js +46 -0
  72. package/dist/runtime-E7JQZ2BZ.js.map +1 -0
  73. package/dist/stdio-HOV4P3DQ.js +12 -0
  74. package/dist/stdio-HOV4P3DQ.js.map +1 -0
  75. package/dist/summaries-XV3JGGCC.js +20 -0
  76. package/dist/summaries-XV3JGGCC.js.map +1 -0
  77. package/dist/{zvec-MAPSJQSR.js → zvec-KP4KNB7Y.js} +10 -3
  78. package/dist/zvec-KP4KNB7Y.js.map +1 -0
  79. package/package.json +7 -10
  80. package/src/db/migrations/0006_fact_idempotency_key.sql +2 -0
  81. package/src/db/migrations/meta/0006_snapshot.json +1258 -0
  82. package/src/db/migrations/meta/_journal.json +7 -0
  83. package/dist/chunk-776EQXDV.js.map +0 -1
  84. package/dist/chunk-GDJH7XJC.js.map +0 -1
  85. package/dist/chunk-JPHJBCZB.js.map +0 -1
  86. package/dist/chunk-L5AYZC3F.js.map +0 -1
  87. package/dist/chunk-N34XHWBA.js.map +0 -1
  88. package/dist/chunk-PCE4STAY.js.map +0 -1
  89. package/dist/chunk-PQK3GKZH.js +0 -98
  90. package/dist/chunk-PQK3GKZH.js.map +0 -1
  91. package/dist/chunk-QDBDYCM2.js.map +0 -1
  92. package/dist/chunk-U4HAJLIB.js.map +0 -1
  93. package/dist/chunk-UN57FKQX.js.map +0 -1
  94. package/dist/chunk-VAKBJV26.js.map +0 -1
  95. package/dist/chunk-WHZSVY2L.js.map +0 -1
  96. package/dist/chunk-XBJQKCLU.js.map +0 -1
  97. package/dist/config-KFEHLV6W.js +0 -11
  98. package/dist/lean-command-C5YPFKHP.js +0 -15
  99. package/dist/local-OTOY56B4.js +0 -13
  100. package/dist/main-HFHLGID4.js.map +0 -1
  101. package/dist/memory-4RGVLPKG.js.map +0 -1
  102. package/dist/operations-LOBAQRLO.js +0 -24
  103. package/dist/pretool-command-MQQ2UNQM.js +0 -10
  104. package/dist/rerank-3P3C2UHN.js.map +0 -1
  105. package/dist/summaries-ZBV5IN7Y.js +0 -18
  106. /package/dist/{chunk-JFDRBPYF.js.map → chunk-6JCKMIQY.js.map} +0 -0
  107. /package/dist/{chunk-UFZD3JAA.js.map → chunk-ESZXCZIN.js.map} +0 -0
  108. /package/dist/{chunk-PYLH4MH5.js.map → chunk-FJOTP632.js.map} +0 -0
  109. /package/dist/{chunk-6MKEIDX5.js.map → chunk-SFMIMLXR.js.map} +0 -0
  110. /package/dist/{cloud-7WM3YLI4.js.map → cloud-LXZUR3U2.js.map} +0 -0
  111. /package/dist/{config-KFEHLV6W.js.map → config-GEZFLXK6.js.map} +0 -0
  112. /package/dist/{lean-command-C5YPFKHP.js.map → docker-autostart-KDME7TVC.js.map} +0 -0
  113. /package/dist/{local-OTOY56B4.js.map → lean-command-CWJAVQ5G.js.map} +0 -0
  114. /package/dist/{operations-LOBAQRLO.js.map → local-ST4WMRCG.js.map} +0 -0
  115. /package/dist/{pretool-command-MQQ2UNQM.js.map → operations-7BTAAUHF.js.map} +0 -0
  116. /package/dist/{summaries-ZBV5IN7Y.js.map → postgres-docker-QQZQ2ABK.js.map} +0 -0
  117. /package/dist/{zvec-MAPSJQSR.js.map → pretool-command-3SXQSMAO.js.map} +0 -0
@@ -1,7 +1,19 @@
1
1
  import {
2
- REDACTED,
3
- redactPersisted
4
- } from "./chunk-GDJH7XJC.js";
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-XBJQKCLU.js";
24
+ } from "./chunk-CAEPHLHC.js";
13
25
  import {
14
26
  OpenRouterEmbeddingProvider
15
- } from "./chunk-JFDRBPYF.js";
27
+ } from "./chunk-6JCKMIQY.js";
16
28
  import {
17
29
  LocalModelProvider
18
- } from "./chunk-U4HAJLIB.js";
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-JPHJBCZB.js";
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
- parseFrontMatter,
49
+ migrateLegacyNamespace,
36
50
  readConfigFile,
37
- redactSecrets,
38
- writeAtomic
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(sql3) {
344
- await sql3`create extension if not exists vector`;
55
+ async function enablePgvector(sql2) {
56
+ await sql2`create extension if not exists vector`;
345
57
  }
346
- async function checkPgvector(sql3) {
347
- const result = await sql3`
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(sql3) {
356
- await enablePgvector(sql3);
357
- return checkPgvector(sql3);
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 sql3 = postgres(config.postgresUrl, {
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: sql3,
379
- db: drizzle(sql3, { schema: schema_exports }),
104
+ sql: sql2,
105
+ db: drizzle(sql2, { schema: schema_exports }),
380
106
  async close() {
381
- await sql3.end();
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 as sql2 } from "drizzle-orm";
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(vector2, dimensions) {
596
- return dimensions > 0 && vector2.length === dimensions && vector2.every(Number.isFinite);
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((vector2) => !validEmbeddingVector(vector2, response.dimensions))) {
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(sql2`select pg_advisory_xact_lock(${k1}, ${k2})`);
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
- eq(facts.contentHash, input.fact.contentHash)
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: sql2`now()` }).where(and(eq(facts.namespaceId, fact.namespaceId), eq(facts.id, fact.id)));
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: sql2`now() + interval '120 seconds'` }).where(
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
- sql2`${embeddings.leaseUntil} > now()`
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, vector2, model, space) {
936
- const literal = `[${vector2.join(",")}]`;
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: vector2.length })),
526
+ embeddingSpace: space ?? embeddingSpaceKey(embeddingSpace({ model, dimensions: vector.length })),
942
527
  lastError: null,
943
528
  leaseUntil: null,
944
529
  leaseToken: null,
945
- updatedAt: sql2`now()`
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
- sql2`${embeddings.leaseUntil} > now()`
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: sql2`case when ${attempts} >= ${maxAttempts} then 'failed'::embedding_status else 'processing'::embedding_status end`,
968
- leaseUntil: sql2`case when ${attempts} >= ${maxAttempts} then null else now() + (${backoffSeconds} || ' seconds')::interval end`,
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: sql2`now()`
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
- sql2`${embeddings.leaseUntil} > now()`
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: sql2`now()`
780
+ updatedAt: sql`now()`
1196
781
  }).where(
1197
782
  and(
1198
783
  eq(embeddings.namespaceId, namespaceId),
1199
784
  inArray(embeddings.status, ["ready", "failed"]),
1200
- sql2`${embeddings.embeddingSpace} is distinct from ${space}`
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(sql2`
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, sql2`embeddings.namespace_id`, sql2`embeddings.owner_id`)}
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(sql2`
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, sql2`facts.namespace_id`, sql2`facts.id`)}
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, sql2`${facts.namespaceId}`, sql2`${facts.id}`)
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 index2 = indexRows[0];
1190
+ const index = indexRows[0];
1606
1191
  return {
1607
1192
  vectorCount,
1608
- indexName: index2?.indexname ?? null,
1609
- indexDefinition: index2?.indexdef ?? null,
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: sql2`now()`
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 sql2`exists (
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) existing.score = Math.max(existing.score, candidate.score);
1795
- else byKey.set(candidate.key, candidate);
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
- if (!candidate.isRecencyFloor && candidate.score < this.minVectorScore && !useFallback) {
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 [index2, model] of models.entries()) {
2112
- const fallbackTriggered = index2 > 0;
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((resolve3) => setTimeout(resolve3, ms));
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
- (vector2) => Array.isArray(vector2) && vector2.every((value) => typeof value === "number")
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((resolve3) => {
2864
- this.idleResolvers.push(resolve3);
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((resolve3) => setTimeout(resolve3, ERROR_BACKOFF_MS));
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 resolve3 of resolvers) resolve3();
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 vector2 = response.vectors[0];
2944
- if (!vector2 || vector2.length === 0) {
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
- vector2,
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((resolve3) => {
2288
+ return new Promise((resolve2) => {
3009
2289
  const timer = setTimeout(() => {
3010
2290
  this.wake = null;
3011
- resolve3();
2291
+ resolve2();
3012
2292
  }, ms ?? this.pollIntervalMs);
3013
2293
  this.wake = () => {
3014
2294
  clearTimeout(timer);
3015
- resolve3();
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 vector2 = response.vectors[0];
3190
- return vector2 ? { vector: vector2, space: embeddingSpace(response) } : null;
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
- if (result.found) {
3373
- const file = await this.memoryFileStore?.readFact(id).catch(() => null);
3374
- if (file?.namespace === this.config.namespace) await this.memoryFileStore?.deleteFact(id);
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.idempotencyKey ?? (input.type === "fact" ? factContentHash(this.config.namespace, ownerId, content) : computeContentHash(this.config.namespace, input.sessionId, input.content));
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 [createdAt2, id] = parsed;
3817
- return { createdAt: new Date(createdAt2), id };
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-UN57FKQX.js.map
3323
+ //# sourceMappingURL=chunk-AK2UAE32.js.map