@orquentra/codebuddy 2.5.0 → 2.6.2

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 (89) hide show
  1. package/CLAUDE_CODEX.md +6 -4
  2. package/README.md +8 -0
  3. package/WORKFLOW.md +5 -5
  4. package/dist/{chunk-JDZ3VK26.js → chunk-2XDPKFUW.js} +114 -823
  5. package/dist/chunk-2XDPKFUW.js.map +1 -0
  6. package/dist/{chunk-QDBDYCM2.js → chunk-5FW3PMNP.js} +2 -2
  7. package/dist/{chunk-776EQXDV.js → chunk-5KZY6OTG.js} +82 -5
  8. package/dist/chunk-5KZY6OTG.js.map +1 -0
  9. package/dist/{chunk-UFZD3JAA.js → chunk-5MQOE4J6.js} +3 -3
  10. package/dist/chunk-5UH3BEQR.js +32 -0
  11. package/dist/chunk-5UH3BEQR.js.map +1 -0
  12. package/dist/{chunk-ROQTT5ZO.js → chunk-B6CO63SQ.js} +8 -7
  13. package/dist/chunk-B6CO63SQ.js.map +1 -0
  14. package/dist/chunk-CSRD5OQA.js +79 -0
  15. package/dist/chunk-CSRD5OQA.js.map +1 -0
  16. package/dist/{chunk-PYLH4MH5.js → chunk-FJOTP632.js} +1 -1
  17. package/dist/{chunk-3XYQGFPB.js → chunk-IBLJOHJL.js} +16 -3
  18. package/dist/chunk-IBLJOHJL.js.map +1 -0
  19. package/dist/chunk-IMFUYEZA.js +1607 -0
  20. package/dist/chunk-IMFUYEZA.js.map +1 -0
  21. package/dist/{chunk-CQPCHMBS.js → chunk-J2EAZ4MA.js} +21 -8
  22. package/dist/chunk-J2EAZ4MA.js.map +1 -0
  23. package/dist/{chunk-KYK6RPAJ.js → chunk-JFDRBPYF.js} +2 -2
  24. package/dist/{chunk-JMWJ7V6Y.js → chunk-JPHJBCZB.js} +2 -2
  25. package/dist/{chunk-JMWJ7V6Y.js.map → chunk-JPHJBCZB.js.map} +1 -1
  26. package/dist/{chunk-L5AYZC3F.js → chunk-NERKZSSE.js} +7 -7
  27. package/dist/{chunk-PCE4STAY.js → chunk-OWWCDRXW.js} +5 -5
  28. package/dist/{chunk-WHZSVY2L.js → chunk-QBDJXGG5.js} +2 -2
  29. package/dist/{chunk-Z5QB367W.js → chunk-R7CD6EXS.js} +367 -1798
  30. package/dist/chunk-R7CD6EXS.js.map +1 -0
  31. package/dist/{chunk-6MKEIDX5.js → chunk-RAJJSCIP.js} +2 -2
  32. package/dist/chunk-S6VEG4S2.js +991 -0
  33. package/dist/chunk-S6VEG4S2.js.map +1 -0
  34. package/dist/{chunk-4BYGTEJT.js → chunk-XBJQKCLU.js} +2 -2
  35. package/dist/cli/index.js +14 -3
  36. package/dist/cli/index.js.map +1 -1
  37. package/dist/{cloud-GP6G3EAK.js → cloud-7WM3YLI4.js} +3 -3
  38. package/dist/{codebuddy-DM-i0Fu5.d.ts → codebuddy-B_pCaQrV.d.ts} +22 -2
  39. package/dist/{config-KFEHLV6W.js → config-DEVHUDIU.js} +3 -3
  40. package/dist/docker-autostart-KDME7TVC.js +12 -0
  41. package/dist/index.d.ts +4 -2
  42. package/dist/index.js +22 -18
  43. package/dist/langgraph/index.d.ts +1 -1
  44. package/dist/lean-command-HTQCPH76.js +15 -0
  45. package/dist/local-5MZURLCZ.js +17 -0
  46. package/dist/{main-BDVQAGAS.js → main-MTBQZGJV.js} +333 -219
  47. package/dist/main-MTBQZGJV.js.map +1 -0
  48. package/dist/{memory-4RGVLPKG.js → memory-KIC5FJVW.js} +4 -4
  49. package/dist/{operations-EQGOFCBD.js → operations-POCHY3WR.js} +8 -7
  50. package/dist/postgres-docker-QQZQ2ABK.js +16 -0
  51. package/dist/{pretool-command-MQQ2UNQM.js → pretool-command-AX6JXP25.js} +3 -3
  52. package/dist/{rerank-3P3C2UHN.js → rerank-5QZ3PNUW.js} +4 -4
  53. package/dist/runtime-CUBOOPZO.js +43 -0
  54. package/dist/runtime-CUBOOPZO.js.map +1 -0
  55. package/dist/stdio-HOV4P3DQ.js +12 -0
  56. package/dist/stdio-HOV4P3DQ.js.map +1 -0
  57. package/dist/{summaries-ZBV5IN7Y.js → summaries-SB5L2TSW.js} +6 -6
  58. package/dist/summaries-SB5L2TSW.js.map +1 -0
  59. package/dist/{zvec-MAPSJQSR.js → zvec-MSRKT7IV.js} +3 -3
  60. package/dist/zvec-MSRKT7IV.js.map +1 -0
  61. package/package.json +7 -10
  62. package/dist/chunk-3XYQGFPB.js.map +0 -1
  63. package/dist/chunk-776EQXDV.js.map +0 -1
  64. package/dist/chunk-CQPCHMBS.js.map +0 -1
  65. package/dist/chunk-JDZ3VK26.js.map +0 -1
  66. package/dist/chunk-ROQTT5ZO.js.map +0 -1
  67. package/dist/chunk-Z5QB367W.js.map +0 -1
  68. package/dist/lean-command-DA4ZNKG3.js +0 -15
  69. package/dist/local-3NGNESNB.js +0 -13
  70. package/dist/main-BDVQAGAS.js.map +0 -1
  71. /package/dist/{chunk-QDBDYCM2.js.map → chunk-5FW3PMNP.js.map} +0 -0
  72. /package/dist/{chunk-UFZD3JAA.js.map → chunk-5MQOE4J6.js.map} +0 -0
  73. /package/dist/{chunk-PYLH4MH5.js.map → chunk-FJOTP632.js.map} +0 -0
  74. /package/dist/{chunk-KYK6RPAJ.js.map → chunk-JFDRBPYF.js.map} +0 -0
  75. /package/dist/{chunk-L5AYZC3F.js.map → chunk-NERKZSSE.js.map} +0 -0
  76. /package/dist/{chunk-PCE4STAY.js.map → chunk-OWWCDRXW.js.map} +0 -0
  77. /package/dist/{chunk-WHZSVY2L.js.map → chunk-QBDJXGG5.js.map} +0 -0
  78. /package/dist/{chunk-6MKEIDX5.js.map → chunk-RAJJSCIP.js.map} +0 -0
  79. /package/dist/{chunk-4BYGTEJT.js.map → chunk-XBJQKCLU.js.map} +0 -0
  80. /package/dist/{cloud-GP6G3EAK.js.map → cloud-7WM3YLI4.js.map} +0 -0
  81. /package/dist/{config-KFEHLV6W.js.map → config-DEVHUDIU.js.map} +0 -0
  82. /package/dist/{lean-command-DA4ZNKG3.js.map → docker-autostart-KDME7TVC.js.map} +0 -0
  83. /package/dist/{local-3NGNESNB.js.map → lean-command-HTQCPH76.js.map} +0 -0
  84. /package/dist/{operations-EQGOFCBD.js.map → local-5MZURLCZ.js.map} +0 -0
  85. /package/dist/{memory-4RGVLPKG.js.map → memory-KIC5FJVW.js.map} +0 -0
  86. /package/dist/{pretool-command-MQQ2UNQM.js.map → operations-POCHY3WR.js.map} +0 -0
  87. /package/dist/{summaries-ZBV5IN7Y.js.map → postgres-docker-QQZQ2ABK.js.map} +0 -0
  88. /package/dist/{zvec-MAPSJQSR.js.map → pretool-command-AX6JXP25.js.map} +0 -0
  89. /package/dist/{rerank-3P3C2UHN.js.map → rerank-5QZ3PNUW.js.map} +0 -0
@@ -1,3 +1,19 @@
1
+ import {
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-S6VEG4S2.js";
1
17
  import {
2
18
  REDACTED,
3
19
  redactPersisted
@@ -9,13 +25,13 @@ import {
9
25
  countTokens,
10
26
  generateEntityId,
11
27
  generateSessionId
12
- } from "./chunk-4BYGTEJT.js";
28
+ } from "./chunk-XBJQKCLU.js";
13
29
  import {
14
30
  OpenRouterEmbeddingProvider
15
- } from "./chunk-KYK6RPAJ.js";
31
+ } from "./chunk-JFDRBPYF.js";
16
32
  import {
17
33
  LocalModelProvider
18
- } from "./chunk-3XYQGFPB.js";
34
+ } from "./chunk-IBLJOHJL.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-JMWJ7V6Y.js";
42
+ } from "./chunk-JPHJBCZB.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-5KZY6OTG.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,139 +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
- }
151
+ import { and, desc, eq, inArray, isNull, lt, ne, or, sql } from "drizzle-orm";
552
152
 
553
153
  // src/providers/embedding-space.ts
554
154
  function embeddingSpace(response) {
@@ -566,12 +166,12 @@ function embeddingSpace(response) {
566
166
  function embeddingSpaceKey(space) {
567
167
  return JSON.stringify([space.model, space.revision, space.schema, space.dimensions]);
568
168
  }
569
- function validEmbeddingVector(vector2, dimensions) {
570
- 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);
571
171
  }
572
172
  function validateEmbeddingResponse(response, count) {
573
173
  embeddingSpace(response);
574
- 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))) {
575
175
  throw new Error("Provider returned invalid embedding dimensions, values, or batch size.");
576
176
  }
577
177
  }
@@ -602,7 +202,7 @@ var PostgresMemoryRepository = class {
602
202
  async withSessionLock(namespaceId, sessionId, fn) {
603
203
  const [k1, k2] = advisoryLockKeys(namespaceId, sessionId);
604
204
  return this.client.db.transaction(async (tx) => {
605
- await tx.execute(sql2`select pg_advisory_xact_lock(${k1}, ${k2})`);
205
+ await tx.execute(sql`select pg_advisory_xact_lock(${k1}, ${k2})`);
606
206
  return this.sessionTransaction.run(tx, fn);
607
207
  });
608
208
  }
@@ -783,7 +383,7 @@ var PostgresMemoryRepository = class {
783
383
  const contentChanged = existing.content !== fact.content;
784
384
  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();
785
385
  if (contentChanged || metadataChanged || !existing.fileBacked) {
786
- await tx.update(facts).set({ ...values, updatedAt: sql2`now()` }).where(and(eq(facts.namespaceId, fact.namespaceId), eq(facts.id, fact.id)));
386
+ await tx.update(facts).set({ ...values, updatedAt: sql`now()` }).where(and(eq(facts.namespaceId, fact.namespaceId), eq(facts.id, fact.id)));
787
387
  }
788
388
  await upsertPendingEmbedding(tx, input.embedding, contentChanged);
789
389
  if (!contentChanged && !metadataChanged) return { status: "unchanged" };
@@ -852,12 +452,13 @@ var PostgresMemoryRepository = class {
852
452
  * item twice. Expired leases are re-claimable, so a crashed worker's work is
853
453
  * picked up again instead of being stuck forever.
854
454
  */
855
- async claimPendingEmbeddings(limit) {
455
+ async claimPendingEmbeddings(limit, namespaceId) {
856
456
  const rows = await this.client.sql`
857
457
  with claimed as (
858
458
  select id from embeddings
859
- where status = 'pending'
860
- or (status = 'processing' and (lease_until is null or lease_until < now()))
459
+ where (status = 'pending'
460
+ or (status = 'processing' and (lease_until is null or lease_until < now())))
461
+ and (${namespaceId ?? null}::text is null or namespace_id = ${namespaceId ?? null})
861
462
  order by created_at asc
862
463
  for update skip locked
863
464
  limit ${limit}
@@ -896,33 +497,33 @@ var PostgresMemoryRepository = class {
896
497
  }));
897
498
  }
898
499
  async renewEmbeddingLease(id, leaseToken) {
899
- const rows = await this.db.update(embeddings).set({ leaseUntil: sql2`now() + interval '120 seconds'` }).where(
500
+ const rows = await this.db.update(embeddings).set({ leaseUntil: sql`now() + interval '120 seconds'` }).where(
900
501
  and(
901
502
  eq(embeddings.id, id),
902
503
  eq(embeddings.leaseToken, leaseToken),
903
504
  eq(embeddings.status, "processing"),
904
- sql2`${embeddings.leaseUntil} > now()`
505
+ sql`${embeddings.leaseUntil} > now()`
905
506
  )
906
507
  ).returning({ id: embeddings.id });
907
508
  return rows.length > 0;
908
509
  }
909
- async markEmbeddingReady(id, leaseToken, vector2, model, space) {
910
- const literal = `[${vector2.join(",")}]`;
510
+ async markEmbeddingReady(id, leaseToken, vector, model, space) {
511
+ const literal = `[${vector.join(",")}]`;
911
512
  const rows = await this.db.update(embeddings).set({
912
513
  status: "ready",
913
514
  vector: literal,
914
515
  embeddingModel: model,
915
- embeddingSpace: space ?? embeddingSpaceKey(embeddingSpace({ model, dimensions: vector2.length })),
516
+ embeddingSpace: space ?? embeddingSpaceKey(embeddingSpace({ model, dimensions: vector.length })),
916
517
  lastError: null,
917
518
  leaseUntil: null,
918
519
  leaseToken: null,
919
- updatedAt: sql2`now()`
520
+ updatedAt: sql`now()`
920
521
  }).where(
921
522
  and(
922
523
  eq(embeddings.id, id),
923
524
  eq(embeddings.leaseToken, leaseToken),
924
525
  eq(embeddings.status, "processing"),
925
- sql2`${embeddings.leaseUntil} > now()`
526
+ sql`${embeddings.leaseUntil} > now()`
926
527
  )
927
528
  ).returning({ id: embeddings.id });
928
529
  return rows.length > 0;
@@ -938,18 +539,18 @@ var PostgresMemoryRepository = class {
938
539
  const maxAttempts = options?.maxAttempts ?? 5;
939
540
  const backoffSeconds = Math.min(60, 2 ** Math.max(0, attempts - 1));
940
541
  const rows = await this.db.update(embeddings).set({
941
- status: sql2`case when ${attempts} >= ${maxAttempts} then 'failed'::embedding_status else 'processing'::embedding_status end`,
942
- leaseUntil: sql2`case when ${attempts} >= ${maxAttempts} then null else now() + (${backoffSeconds} || ' seconds')::interval end`,
542
+ status: sql`case when ${attempts} >= ${maxAttempts} then 'failed'::embedding_status else 'processing'::embedding_status end`,
543
+ leaseUntil: sql`case when ${attempts} >= ${maxAttempts} then null else now() + (${backoffSeconds} || ' seconds')::interval end`,
943
544
  leaseToken: null,
944
545
  attempts,
945
546
  lastError: error,
946
- updatedAt: sql2`now()`
547
+ updatedAt: sql`now()`
947
548
  }).where(
948
549
  and(
949
550
  eq(embeddings.id, id),
950
551
  eq(embeddings.leaseToken, leaseToken),
951
552
  eq(embeddings.status, "processing"),
952
- sql2`${embeddings.leaseUntil} > now()`
553
+ sql`${embeddings.leaseUntil} > now()`
953
554
  )
954
555
  ).returning({ id: embeddings.id });
955
556
  return rows.length > 0;
@@ -1166,12 +767,12 @@ var PostgresMemoryRepository = class {
1166
767
  lastError: null,
1167
768
  leaseUntil: null,
1168
769
  leaseToken: null,
1169
- updatedAt: sql2`now()`
770
+ updatedAt: sql`now()`
1170
771
  }).where(
1171
772
  and(
1172
773
  eq(embeddings.namespaceId, namespaceId),
1173
774
  inArray(embeddings.status, ["ready", "failed"]),
1174
- sql2`${embeddings.embeddingSpace} is distinct from ${space}`
775
+ sql`${embeddings.embeddingSpace} is distinct from ${space}`
1175
776
  )
1176
777
  ).returning({ id: embeddings.id });
1177
778
  return rows.length;
@@ -1179,7 +780,7 @@ var PostgresMemoryRepository = class {
1179
780
  async searchSimilarEmbeddings(input) {
1180
781
  if (!validSearchVector(input.vector))
1181
782
  return { method: "exact_pgvector", candidates: [], compatibleReady: 0 };
1182
- const rows = await this.db.execute(sql2`
783
+ const rows = await this.db.execute(sql`
1183
784
  with compatible as materialized (
1184
785
  select id, owner_type, owner_id, embedding_model, embedding_space, vector
1185
786
  from embeddings
@@ -1188,7 +789,7 @@ var PostgresMemoryRepository = class {
1188
789
  namespace_id = ${input.namespaceId}
1189
790
  or (
1190
791
  owner_type = 'fact'
1191
- and ${liveReference(input.namespaceId, sql2`embeddings.namespace_id`, sql2`embeddings.owner_id`)}
792
+ and ${liveReference(input.namespaceId, sql`embeddings.namespace_id`, sql`embeddings.owner_id`)}
1192
793
  )
1193
794
  )
1194
795
  and embedding_space = ${input.embeddingSpace}
@@ -1224,7 +825,7 @@ var PostgresMemoryRepository = class {
1224
825
  async searchFactsLexically(namespaceId, query, limit) {
1225
826
  const terms = lexicalTerms(query);
1226
827
  if (!terms.length) return { candidates: [], matchedFacts: 0 };
1227
- const rows = await this.db.execute(sql2`
828
+ const rows = await this.db.execute(sql`
1228
829
  with scored as materialized (
1229
830
  select id, content, subject, predicate, object, proposition_key, confidence,
1230
831
  created_by_agent, created_at,
@@ -1238,7 +839,7 @@ var PostgresMemoryRepository = class {
1238
839
  where not source_deleted and superseded_by is null
1239
840
  and (
1240
841
  namespace_id = ${namespaceId}
1241
- or ${liveReference(namespaceId, sql2`facts.namespace_id`, sql2`facts.id`)}
842
+ or ${liveReference(namespaceId, sql`facts.namespace_id`, sql`facts.id`)}
1242
843
  )
1243
844
  )
1244
845
  select *, count(*) over () as total from scored where score > 0
@@ -1317,7 +918,7 @@ var PostgresMemoryRepository = class {
1317
918
  isNull(facts.supersededBy),
1318
919
  or(
1319
920
  eq(facts.namespaceId, namespaceId),
1320
- liveReference(namespaceId, sql2`${facts.namespaceId}`, sql2`${facts.id}`)
921
+ liveReference(namespaceId, sql`${facts.namespaceId}`, sql`${facts.id}`)
1321
922
  )
1322
923
  )
1323
924
  );
@@ -1576,11 +1177,11 @@ var PostgresMemoryRepository = class {
1576
1177
  limit 1
1577
1178
  `;
1578
1179
  const vectorCount = Number(countRows[0]?.count ?? 0);
1579
- const index2 = indexRows[0];
1180
+ const index = indexRows[0];
1580
1181
  return {
1581
1182
  vectorCount,
1582
- indexName: index2?.indexname ?? null,
1583
- indexDefinition: index2?.indexdef ?? null,
1183
+ indexName: index?.indexname ?? null,
1184
+ indexDefinition: index?.indexdef ?? null,
1584
1185
  needsTuning: vectorCount >= 1e5
1585
1186
  };
1586
1187
  }
@@ -1624,7 +1225,7 @@ async function upsertPendingEmbedding(tx, embedding, reset) {
1624
1225
  lastError: null,
1625
1226
  leaseUntil: null,
1626
1227
  leaseToken: null,
1627
- updatedAt: sql2`now()`
1228
+ updatedAt: sql`now()`
1628
1229
  }
1629
1230
  });
1630
1231
  }
@@ -1656,7 +1257,7 @@ var recentFactColumns = {
1656
1257
  createdAt: facts.createdAt
1657
1258
  };
1658
1259
  function liveReference(namespaceId, ownerNamespace, factId) {
1659
- return sql2`exists (
1260
+ return sql`exists (
1660
1261
  select 1 from shares s
1661
1262
  where s.to_namespace_id = ${namespaceId}
1662
1263
  and s.from_namespace_id = ${ownerNamespace}
@@ -1669,14 +1270,6 @@ function liveReference(namespaceId, ownerNamespace, factId) {
1669
1270
  // src/core/codebuddy.ts
1670
1271
  import pTimeout2 from "p-timeout";
1671
1272
 
1672
- // src/core/memory-lifecycle.ts
1673
- function isEligibleMemory(fact, options = {}) {
1674
- if (options.namespace && fact.namespace !== options.namespace) return false;
1675
- if (fact.sourceDeleted || fact.supersededBy) return false;
1676
- if (fact.category === "incident" && fact.resolvedBy && !options.includeResolved) return false;
1677
- return true;
1678
- }
1679
-
1680
1273
  // src/planner/default-policy.ts
1681
1274
  var DefaultPolicy = class {
1682
1275
  recencyFloorSize;
@@ -2082,8 +1675,8 @@ var HuggingFaceResilience = class {
2082
1675
  async execute(input) {
2083
1676
  const models = [input.model, ...input.fallbackModels ?? []];
2084
1677
  let lastError;
2085
- for (const [index2, model] of models.entries()) {
2086
- const fallbackTriggered = index2 > 0;
1678
+ for (const [index, model] of models.entries()) {
1679
+ const fallbackTriggered = index > 0;
2087
1680
  try {
2088
1681
  return await this.executeForModel(model, input, fallbackTriggered);
2089
1682
  } catch (error) {
@@ -2150,8 +1743,7 @@ var HuggingFaceResilience = class {
2150
1743
  retries = error.attemptNumber;
2151
1744
  }
2152
1745
  }
2153
- ),
2154
- { throwOnTimeout: true }
1746
+ )
2155
1747
  );
2156
1748
  this.markWarm(model);
2157
1749
  status = fallbackTriggered ? "fallback" : "success";
@@ -2284,7 +1876,7 @@ function readRetryAfterMs(error) {
2284
1876
  return Number.isFinite(retryAfterSeconds) ? retryAfterSeconds * 1e3 : void 0;
2285
1877
  }
2286
1878
  function sleep(ms) {
2287
- return new Promise((resolve3) => setTimeout(resolve3, ms));
1879
+ return new Promise((resolve2) => setTimeout(resolve2, ms));
2288
1880
  }
2289
1881
 
2290
1882
  // src/providers/huggingface.ts
@@ -2389,7 +1981,7 @@ function normalizeEmbeddingResponse(response) {
2389
1981
  return [response];
2390
1982
  }
2391
1983
  if (response.every(
2392
- (vector2) => Array.isArray(vector2) && vector2.every((value) => typeof value === "number")
1984
+ (vector) => Array.isArray(vector) && vector.every((value) => typeof value === "number")
2393
1985
  )) {
2394
1986
  return response;
2395
1987
  }
@@ -2479,308 +2071,6 @@ function validateConfig(config) {
2479
2071
  return codeBuddyConfigSchema.parse(config);
2480
2072
  }
2481
2073
 
2482
- // src/core/memory-file-store.ts
2483
- import { mkdir, readdir, readFile, rm } from "fs/promises";
2484
- import { dirname as dirname2, join, relative, resolve as resolve2, sep } from "path";
2485
- import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
2486
- import { z as z2 } from "zod";
2487
- var FACT_CATEGORIES = ["general", "incident"];
2488
- var INCIDENT_SEVERITIES = ["low", "medium", "high", "critical"];
2489
- var factFrontMatterSchema = z2.object({
2490
- schemaVersion: z2.literal(1),
2491
- id: z2.string().regex(/^fact_[a-z0-9]+$/),
2492
- namespace: z2.string().min(1),
2493
- type: z2.literal("fact"),
2494
- subject: z2.string(),
2495
- predicate: z2.string(),
2496
- object: z2.string(),
2497
- /** Explicit single-valued claim identity; facts sharing it are rival assertions. */
2498
- propositionKey: z2.string().min(1).nullable().default(null),
2499
- confidence: z2.number().min(0).max(1),
2500
- createdAt: z2.preprocess(
2501
- (value) => value instanceof Date ? value.toISOString() : value,
2502
- z2.string().datetime()
2503
- ),
2504
- createdByAgent: z2.string().nullable(),
2505
- sourceInteractionId: z2.string().nullable(),
2506
- sourceDeleted: z2.boolean(),
2507
- category: z2.enum(FACT_CATEGORIES).default("general"),
2508
- paths: z2.array(z2.string().min(1)).default([]),
2509
- severity: z2.enum(INCIDENT_SEVERITIES).nullable().default(null),
2510
- introducedBy: z2.string().nullable().default(null),
2511
- resolvedBy: z2.string().nullable().default(null),
2512
- fingerprint: z2.string().regex(/^[a-f0-9]{64}$/).nullable().default(null),
2513
- sourcePlanId: z2.string().nullable().default(null),
2514
- sourceSessionId: z2.string().nullable().default(null),
2515
- verification: z2.array(z2.string()).default([]),
2516
- supersededBy: z2.string().nullable().default(null),
2517
- /** Namespaces granted a live reference to this fact (durable form of reference shares). */
2518
- sharedWith: z2.array(z2.string().min(1)).default([])
2519
- });
2520
- var summaryFrontMatterSchema = z2.object({
2521
- schemaVersion: z2.literal(1),
2522
- id: z2.string().regex(/^sum_[a-z0-9]+$/),
2523
- namespace: z2.string().min(1),
2524
- type: z2.literal("summary"),
2525
- sessionId: z2.string().min(1),
2526
- version: z2.number().int().positive(),
2527
- tokenCount: z2.number().int().nonnegative(),
2528
- createdAt: z2.preprocess(
2529
- (value) => value instanceof Date ? value.toISOString() : value,
2530
- z2.string().datetime()
2531
- ),
2532
- createdByAgent: z2.string().nullable()
2533
- });
2534
- var MemoryFileStore = class {
2535
- repositoryRoot;
2536
- factsDirectory;
2537
- summariesDirectory;
2538
- constructor(repositoryRoot = process.cwd()) {
2539
- this.repositoryRoot = resolve2(repositoryRoot);
2540
- this.factsDirectory = join(this.repositoryRoot, ".codebuddy", "memory", "facts");
2541
- this.summariesDirectory = join(this.repositoryRoot, ".codebuddy", "memory", "summaries");
2542
- }
2543
- async writeFact(raw) {
2544
- const input = redactPersisted(raw);
2545
- const path = this.factPath(input.id);
2546
- await this.prepareDirectory(this.factsDirectory);
2547
- await assertNoSymlinkBetween(this.repositoryRoot, dirname2(path));
2548
- const metadata = {
2549
- schemaVersion: 1,
2550
- id: input.id,
2551
- namespace: input.namespace,
2552
- type: "fact",
2553
- subject: input.subject,
2554
- predicate: input.predicate,
2555
- object: input.object,
2556
- propositionKey: input.propositionKey,
2557
- confidence: input.confidence,
2558
- createdAt: input.createdAt,
2559
- createdByAgent: input.createdByAgent,
2560
- sourceInteractionId: input.sourceInteractionId,
2561
- sourceDeleted: input.sourceDeleted,
2562
- category: input.category,
2563
- paths: input.paths,
2564
- severity: input.severity,
2565
- introducedBy: input.introducedBy,
2566
- resolvedBy: input.resolvedBy,
2567
- fingerprint: input.fingerprint,
2568
- sourcePlanId: input.sourcePlanId,
2569
- sourceSessionId: input.sourceSessionId,
2570
- verification: input.verification,
2571
- supersededBy: input.supersededBy,
2572
- sharedWith: input.sharedWith
2573
- };
2574
- const frontMatter = stringifyYaml(stripUndefinedAndDefaultIncidentFields(metadata));
2575
- await writeAtomic(path, composeMarkdown(frontMatter, input.content));
2576
- return path;
2577
- }
2578
- async writeSummary(input) {
2579
- const path = this.summaryPath(input.id);
2580
- await this.prepareDirectory(this.summariesDirectory);
2581
- await assertNoSymlinkBetween(this.repositoryRoot, dirname2(path));
2582
- const metadata = stringifyYaml({
2583
- schemaVersion: 1,
2584
- id: input.id,
2585
- namespace: input.namespace,
2586
- type: "summary",
2587
- sessionId: input.sessionId,
2588
- version: input.version,
2589
- tokenCount: input.tokenCount,
2590
- createdAt: input.createdAt,
2591
- createdByAgent: input.createdByAgent
2592
- });
2593
- await writeAtomic(path, composeMarkdown(metadata, input.content));
2594
- return path;
2595
- }
2596
- async readFact(pathOrId) {
2597
- const path = pathOrId.endsWith(".md") ? this.safePath(pathOrId) : this.factPath(pathOrId);
2598
- await assertNoSymlinkBetween(this.repositoryRoot, path);
2599
- const parsed = parseFrontMatter(await readFile(path, "utf8"));
2600
- const metadata = factFrontMatterSchema.parse(parseYaml(parsed.frontMatter));
2601
- return {
2602
- ...metadata,
2603
- content: parsed.content.trim(),
2604
- path
2605
- };
2606
- }
2607
- async listFacts() {
2608
- return (await this.scanFacts()).facts;
2609
- }
2610
- /**
2611
- * Parse every managed fact file. Hand-written markdown is ignored, and a
2612
- * schema-invalid/corrupt fact is reported in `invalid` (not thrown) so one
2613
- * bad file can't break recall, capture or reindexing for the whole project.
2614
- * Invalid files stay on disk for manual repair.
2615
- */
2616
- async scanFacts() {
2617
- const scan = await this.scan(
2618
- this.factsDirectory,
2619
- /^fact_[a-z0-9]+\.md$/,
2620
- (path) => this.readFact(path)
2621
- );
2622
- return { facts: scan.items, invalid: scan.invalid };
2623
- }
2624
- /**
2625
- * List incident facts for the namespace, ranked by severity then recency.
2626
- * Unlike {@link findIncidentFactsForPaths} this does not filter by path, so
2627
- * callers (e.g. the context engine's project bootstrap) can surface the
2628
- * repository's incident hotspots before any specific edit target is known.
2629
- */
2630
- async listIncidentFacts(input = {}) {
2631
- const incidents = (await this.listFacts()).filter(
2632
- (fact) => fact.category === "incident" && Boolean(fact.severity) && isEligibleMemory(fact, input)
2633
- );
2634
- return incidents.sort((left, right) => {
2635
- const severity = severityRank(right.severity) - severityRank(left.severity);
2636
- if (severity !== 0) return severity;
2637
- return right.createdAt.localeCompare(left.createdAt);
2638
- });
2639
- }
2640
- async findIncidentFactsForPaths(input) {
2641
- const wanted = new Set(input.paths.map(normalizeMemoryPath));
2642
- if (wanted.size === 0) return [];
2643
- return (await this.listIncidentFacts({
2644
- ...input.namespace !== void 0 ? { namespace: input.namespace } : {},
2645
- ...input.includeResolved !== void 0 ? { includeResolved: input.includeResolved } : {}
2646
- })).filter((fact) => fact.paths.map(normalizeMemoryPath).some((path) => wanted.has(path)));
2647
- }
2648
- async readSummary(pathOrId) {
2649
- const path = pathOrId.endsWith(".md") ? this.safePath(pathOrId) : this.summaryPath(pathOrId);
2650
- await assertNoSymlinkBetween(this.repositoryRoot, path);
2651
- const parsed = parseFrontMatter(await readFile(path, "utf8"));
2652
- return {
2653
- ...summaryFrontMatterSchema.parse(parseYaml(parsed.frontMatter)),
2654
- content: parsed.content.trim(),
2655
- path
2656
- };
2657
- }
2658
- async listSummaries() {
2659
- return (await this.scanSummaries()).summaries;
2660
- }
2661
- async scanSummaries() {
2662
- const scan = await this.scan(
2663
- this.summariesDirectory,
2664
- /^sum_[a-z0-9]+\.md$/,
2665
- (path) => this.readSummary(path)
2666
- );
2667
- return { summaries: scan.items, invalid: scan.invalid };
2668
- }
2669
- async scan(directory, managedName, read) {
2670
- try {
2671
- await this.prepareDirectory(directory);
2672
- const entries = await readdir(directory, { withFileTypes: true });
2673
- const result = { items: [], invalid: [] };
2674
- for (const entry of entries.sort((left, right) => left.name.localeCompare(right.name))) {
2675
- if (!entry.isFile() || !managedName.test(entry.name)) continue;
2676
- const path = join(directory, entry.name);
2677
- try {
2678
- const item = await read(path);
2679
- const id = entry.name.slice(0, -".md".length);
2680
- if (item.id !== id)
2681
- throw new Error(`file ID ${id} differs from front matter ID ${item.id}`);
2682
- result.items.push(item);
2683
- } catch (error) {
2684
- result.invalid.push({
2685
- id: entry.name.slice(0, -".md".length),
2686
- path,
2687
- error: error.message.split("\n")[0]?.slice(0, 300) ?? "unreadable"
2688
- });
2689
- }
2690
- }
2691
- return result;
2692
- } catch (error) {
2693
- if (error.code === "ENOENT") return { items: [], invalid: [] };
2694
- throw error;
2695
- }
2696
- }
2697
- async deleteFact(id) {
2698
- await rm(this.factPath(id), { force: true });
2699
- }
2700
- async markIncidentResolved(id, resolvedBy) {
2701
- const fact = await this.readFact(id);
2702
- await this.writeFact(copyFact(fact, { resolvedBy }));
2703
- }
2704
- async markFactSuperseded(id, supersededBy) {
2705
- const fact = await this.readFact(id);
2706
- await this.writeFact(copyFact(fact, { supersededBy }));
2707
- }
2708
- async setFactSharedWith(id, sharedWith) {
2709
- const fact = await this.readFact(id);
2710
- await this.writeFact(copyFact(fact, { sharedWith: [...new Set(sharedWith)].sort() }));
2711
- }
2712
- /** Persist interaction deletion in Markdown before the derived DB is updated. */
2713
- async markSourceInteractionDeleted(namespace, sourceInteractionId) {
2714
- const { facts: facts2 } = await this.scanFacts();
2715
- for (const fact of facts2) {
2716
- if (fact.namespace !== namespace || fact.sourceInteractionId !== sourceInteractionId)
2717
- continue;
2718
- if (fact.sourceDeleted) continue;
2719
- await this.writeFact(copyFact(fact, { sourceDeleted: true }));
2720
- }
2721
- }
2722
- /** Persist that a fact's source interaction was forgotten, keeping its provenance id. */
2723
- async markFactSourceDeleted(id) {
2724
- const fact = await this.readFact(id);
2725
- await this.writeFact(copyFact(fact, { sourceDeleted: true }));
2726
- }
2727
- factPath(id) {
2728
- if (!/^fact_[a-z0-9]+$/.test(id)) {
2729
- throw new Error(`Invalid fact id: ${id}`);
2730
- }
2731
- return this.safePath(join(this.factsDirectory, `${id}.md`));
2732
- }
2733
- summaryPath(id) {
2734
- if (!/^sum_[a-z0-9]+$/.test(id)) throw new Error(`Invalid summary id: ${id}`);
2735
- return this.safePath(join(this.summariesDirectory, `${id}.md`));
2736
- }
2737
- safePath(path) {
2738
- const absolute = resolve2(path);
2739
- const relativePath = relative(this.repositoryRoot, absolute);
2740
- if (relativePath === ".." || relativePath.startsWith(`..${sep}`) || relativePath === "") {
2741
- throw new Error(`Memory path escapes repository root: ${path}`);
2742
- }
2743
- return absolute;
2744
- }
2745
- async prepareDirectory(directory) {
2746
- await assertNoSymlinkBetween(this.repositoryRoot, dirname2(directory));
2747
- await mkdir(directory, { recursive: true, mode: 448 });
2748
- }
2749
- };
2750
- function copyFact(fact, patch) {
2751
- const { path: _path, schemaVersion: _schemaVersion, type: _type, ...write } = fact;
2752
- return { ...write, ...patch };
2753
- }
2754
- function stripUndefinedAndDefaultIncidentFields(metadata) {
2755
- return Object.fromEntries(
2756
- Object.entries(metadata).filter(([key, value]) => {
2757
- if (value === void 0) return false;
2758
- if (key === "category" && value === "general") return false;
2759
- if ((key === "paths" || key === "sharedWith") && Array.isArray(value) && value.length === 0)
2760
- return false;
2761
- if (["severity", "introducedBy", "resolvedBy", "propositionKey"].includes(key) && (value === null || value === void 0)) {
2762
- return false;
2763
- }
2764
- return true;
2765
- })
2766
- );
2767
- }
2768
- function normalizeMemoryPath(path) {
2769
- return path.replace(/\\/g, "/").replace(/^\.\//, "");
2770
- }
2771
- function severityRank(severity) {
2772
- switch (severity) {
2773
- case "critical":
2774
- return 4;
2775
- case "high":
2776
- return 3;
2777
- case "medium":
2778
- return 2;
2779
- case "low":
2780
- return 1;
2781
- }
2782
- }
2783
-
2784
2074
  // src/core/worker.ts
2785
2075
  var DEFAULT_POLL_MS = 250;
2786
2076
  var DEFAULT_BATCH_SIZE = 8;
@@ -2793,6 +2083,7 @@ var EmbeddingWorker = class {
2793
2083
  batchSize;
2794
2084
  maxAttempts;
2795
2085
  onDiagnostic;
2086
+ namespaceId;
2796
2087
  state = "idle";
2797
2088
  loopPromise = null;
2798
2089
  wake = null;
@@ -2805,6 +2096,7 @@ var EmbeddingWorker = class {
2805
2096
  this.batchSize = options.batchSize ?? DEFAULT_BATCH_SIZE;
2806
2097
  this.maxAttempts = options.maxAttempts ?? DEFAULT_MAX_ATTEMPTS;
2807
2098
  this.onDiagnostic = options.onDiagnostic;
2099
+ this.namespaceId = options.namespaceId;
2808
2100
  }
2809
2101
  getState() {
2810
2102
  return this.state;
@@ -2834,8 +2126,8 @@ var EmbeddingWorker = class {
2834
2126
  * wait out anymore.
2835
2127
  */
2836
2128
  async drain() {
2837
- await new Promise((resolve3) => {
2838
- this.idleResolvers.push(resolve3);
2129
+ await new Promise((resolve2) => {
2130
+ this.idleResolvers.push(resolve2);
2839
2131
  this.notify();
2840
2132
  });
2841
2133
  }
@@ -2861,13 +2153,13 @@ var EmbeddingWorker = class {
2861
2153
  while (this.state === "running") {
2862
2154
  let jobs;
2863
2155
  try {
2864
- jobs = await this.repo.claimPendingEmbeddings(this.batchSize);
2156
+ jobs = await this.repo.claimPendingEmbeddings(this.batchSize, this.namespaceId?.());
2865
2157
  } catch (error) {
2866
2158
  console.error(
2867
2159
  `[worker] claimPendingEmbeddings failed: ${error instanceof Error ? error.message : String(error)}; retrying`
2868
2160
  );
2869
2161
  if (this.state !== "running") break;
2870
- await new Promise((resolve3) => setTimeout(resolve3, ERROR_BACKOFF_MS));
2162
+ await new Promise((resolve2) => setTimeout(resolve2, ERROR_BACKOFF_MS));
2871
2163
  continue;
2872
2164
  }
2873
2165
  if (jobs.length === 0) {
@@ -2894,7 +2186,7 @@ var EmbeddingWorker = class {
2894
2186
  if (this.inFlight > 0) return;
2895
2187
  const resolvers = this.idleResolvers;
2896
2188
  this.idleResolvers = [];
2897
- for (const resolve3 of resolvers) resolve3();
2189
+ for (const resolve2 of resolvers) resolve2();
2898
2190
  }
2899
2191
  async processJob(job) {
2900
2192
  let leaseLost = false;
@@ -2914,14 +2206,14 @@ var EmbeddingWorker = class {
2914
2206
  model: job.embeddingModel
2915
2207
  });
2916
2208
  validateEmbeddingResponse(response, 1);
2917
- const vector2 = response.vectors[0];
2918
- if (!vector2 || vector2.length === 0) {
2209
+ const vector = response.vectors[0];
2210
+ if (!vector || vector.length === 0) {
2919
2211
  throw new Error("Provider returned no embedding vector.");
2920
2212
  }
2921
2213
  if (leaseLost || !await this.repo.markEmbeddingReady(
2922
2214
  job.id,
2923
2215
  job.leaseToken,
2924
- vector2,
2216
+ vector,
2925
2217
  response.model,
2926
2218
  embeddingSpaceKey(embeddingSpace(response))
2927
2219
  ))
@@ -2979,14 +2271,14 @@ var EmbeddingWorker = class {
2979
2271
  });
2980
2272
  }
2981
2273
  sleep(ms) {
2982
- return new Promise((resolve3) => {
2274
+ return new Promise((resolve2) => {
2983
2275
  const timer = setTimeout(() => {
2984
2276
  this.wake = null;
2985
- resolve3();
2277
+ resolve2();
2986
2278
  }, ms ?? this.pollIntervalMs);
2987
2279
  this.wake = () => {
2988
2280
  clearTimeout(timer);
2989
- resolve3();
2281
+ resolve2();
2990
2282
  };
2991
2283
  });
2992
2284
  }
@@ -3017,6 +2309,7 @@ var CodeBuddy = class {
3017
2309
  this.worker = deps.worker ?? new EmbeddingWorker({
3018
2310
  repository: this.repo,
3019
2311
  provider: this.provider,
2312
+ namespaceId: () => this.namespaceId ?? void 0,
3020
2313
  ...this.config.worker?.pollIntervalMs !== void 0 ? { pollIntervalMs: this.config.worker.pollIntervalMs } : {},
3021
2314
  ...this.config.worker?.batchSize !== void 0 ? { batchSize: this.config.worker.batchSize } : {},
3022
2315
  ...this.config.worker?.maxAttempts !== void 0 ? { maxAttempts: this.config.worker.maxAttempts } : {}
@@ -3160,8 +2453,8 @@ var CodeBuddy = class {
3160
2453
  })
3161
2454
  );
3162
2455
  validateEmbeddingResponse(response, 1);
3163
- const vector2 = response.vectors[0];
3164
- return vector2 ? { vector: vector2, space: embeddingSpace(response) } : null;
2456
+ const vector = response.vectors[0];
2457
+ return vector ? { vector, space: embeddingSpace(response) } : null;
3165
2458
  } catch {
3166
2459
  return null;
3167
2460
  }
@@ -3343,9 +2636,10 @@ var CodeBuddy = class {
3343
2636
  };
3344
2637
  const factResult = await this.repo.withSessionLock(namespaceId, REINDEX_LOCK_KEY, async () => {
3345
2638
  const result = await this.repo.forgetFact(namespaceId, id, factAudit);
3346
- if (result.found) {
3347
- const file = await this.memoryFileStore?.readFact(id).catch(() => null);
3348
- if (file?.namespace === this.config.namespace) await this.memoryFileStore?.deleteFact(id);
2639
+ const file = await this.memoryFileStore?.readFact(id).catch(() => null);
2640
+ if (file?.namespace === this.config.namespace) {
2641
+ await this.memoryFileStore?.deleteFact(id);
2642
+ return { ...result, found: true };
3349
2643
  }
3350
2644
  return result;
3351
2645
  });
@@ -3361,7 +2655,7 @@ var CodeBuddy = class {
3361
2655
  * file is applied independently: malformed or conflicting files are reported
3362
2656
  * in `failures` and never abort healthy imports (nor count as deletions).
3363
2657
  */
3364
- async reindexMemory() {
2658
+ async reindexMemory(options = {}) {
3365
2659
  const namespaceId = this.requireNamespaceId();
3366
2660
  const store = this.memoryFileStore;
3367
2661
  if (!store) {
@@ -3441,7 +2735,7 @@ var CodeBuddy = class {
3441
2735
  failures: failures.slice(0, MAX_REPORTED_FAILURES)
3442
2736
  };
3443
2737
  });
3444
- const space = this.provider.space;
2738
+ const space = options.respace === false ? void 0 : this.provider.space;
3445
2739
  const canEmbed = space !== void 0 && await this.provider.embed({ input: "codebuddy" }).then(
3446
2740
  () => true,
3447
2741
  () => false
@@ -3787,8 +3081,8 @@ function decodeFactCursor(cursor) {
3787
3081
  if (!Array.isArray(parsed) || parsed.length !== 2 || typeof parsed[0] !== "string" || typeof parsed[1] !== "string" || Number.isNaN(Date.parse(parsed[0]))) {
3788
3082
  throw new Error("Invalid cursor: expected a base64url-encoded [createdAt, id] pair.");
3789
3083
  }
3790
- const [createdAt2, id] = parsed;
3791
- return { createdAt: new Date(createdAt2), id };
3084
+ const [createdAt, id] = parsed;
3085
+ return { createdAt: new Date(createdAt), id };
3792
3086
  }
3793
3087
  async function listFactsPage(memory, repository, input) {
3794
3088
  const namespace = await repository.ensureNamespace(memory.config.namespace);
@@ -3824,6 +3118,7 @@ async function inspectNamespace(repository, namespace) {
3824
3118
  };
3825
3119
  }
3826
3120
  async function runDoctor(options = {}) {
3121
+ const migrated = await migrateLegacyNamespace();
3827
3122
  const fileConfig = await readConfigFile();
3828
3123
  const scaffold = await checkProjectScaffold();
3829
3124
  const configPermissions = await checkConfigPermissions();
@@ -3833,6 +3128,7 @@ async function runDoctor(options = {}) {
3833
3128
  expectedDefault: expectedDefaultNamespace,
3834
3129
  configured: fileConfig.namespace ?? null,
3835
3130
  runtime: runtimeNamespace,
3131
+ ...migrated ? { migrated } : {},
3836
3132
  ok: !fileConfig.namespace || fileConfig.namespace === expectedDefaultNamespace || process.env.CODEBUDDY_NAMESPACE !== void 0,
3837
3133
  ...!fileConfig.namespace || fileConfig.namespace === expectedDefaultNamespace || process.env.CODEBUDDY_NAMESPACE !== void 0 ? {} : {
3838
3134
  warning: `Configured namespace "${fileConfig.namespace}" differs from this folder's default "${expectedDefaultNamespace}". Confirm your MCP entries point at the intended namespace.`
@@ -3977,17 +3273,12 @@ async function checkModels(provider, skip = false) {
3977
3273
  }
3978
3274
 
3979
3275
  export {
3980
- isEligibleMemory,
3981
- lexicalTerms,
3982
3276
  embeddingSpaceKey,
3983
3277
  DefaultPolicy,
3984
3278
  HuggingFaceProvider,
3985
3279
  isLangSmithTracingEnabled,
3986
3280
  createTracer,
3987
3281
  codeBuddyConfigSchema,
3988
- FACT_CATEGORIES,
3989
- INCIDENT_SEVERITIES,
3990
- MemoryFileStore,
3991
3282
  EmbeddingWorker,
3992
3283
  CodeBuddy,
3993
3284
  buildProviderFromConfig,
@@ -4003,4 +3294,4 @@ export {
4003
3294
  inspectNamespace,
4004
3295
  runDoctor
4005
3296
  };
4006
- //# sourceMappingURL=chunk-JDZ3VK26.js.map
3297
+ //# sourceMappingURL=chunk-2XDPKFUW.js.map