@lota-sdk/core 0.1.14 → 0.1.16

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 (174) hide show
  1. package/infrastructure/schema/00_identity.surql +0 -2
  2. package/infrastructure/schema/01_memory.surql +1 -1
  3. package/infrastructure/schema/02_execution_plan.surql +62 -1
  4. package/infrastructure/schema/03_learned_skill.surql +1 -1
  5. package/infrastructure/schema/06_playbook.surql +25 -0
  6. package/infrastructure/schema/07_institutional_memory.surql +13 -0
  7. package/infrastructure/schema/08_quality_metrics.surql +17 -0
  8. package/package.json +9 -8
  9. package/src/ai/definitions.ts +80 -2
  10. package/src/ai/embedding-cache.ts +7 -6
  11. package/src/ai/index.ts +0 -1
  12. package/src/bifrost/bifrost.ts +14 -14
  13. package/src/config/agent-defaults.ts +32 -22
  14. package/src/config/agent-types.ts +11 -0
  15. package/src/config/constants.ts +2 -14
  16. package/src/config/debug-logger.ts +5 -1
  17. package/src/config/index.ts +3 -0
  18. package/src/config/logger.ts +7 -9
  19. package/src/config/model-constants.ts +16 -34
  20. package/src/config/search.ts +1 -15
  21. package/src/create-runtime.ts +453 -0
  22. package/src/db/cursor-pagination.ts +3 -6
  23. package/src/db/index.ts +2 -0
  24. package/src/db/memory-store.rows.ts +7 -7
  25. package/src/db/memory-store.ts +24 -24
  26. package/src/db/memory.ts +18 -16
  27. package/src/db/schema-fingerprint.ts +1 -0
  28. package/src/db/service.ts +193 -122
  29. package/src/db/startup.ts +9 -13
  30. package/src/db/surreal-mutation.ts +43 -0
  31. package/src/db/tables.ts +7 -0
  32. package/src/db/workstream-message-row.ts +15 -0
  33. package/src/embeddings/provider.ts +1 -1
  34. package/src/index.ts +1 -1
  35. package/src/queues/context-compaction.queue.ts +17 -52
  36. package/src/queues/delayed-node-promotion.queue.ts +41 -0
  37. package/src/queues/document-processor.queue.ts +7 -7
  38. package/src/queues/index.ts +3 -0
  39. package/src/queues/memory-consolidation.queue.ts +18 -54
  40. package/src/queues/plan-scheduler.queue.ts +97 -0
  41. package/src/queues/post-chat-memory.queue.ts +15 -60
  42. package/src/queues/queue-factory.ts +100 -0
  43. package/src/queues/recent-activity-title-refinement.queue.ts +15 -54
  44. package/src/queues/regular-chat-memory-digest.queue.ts +16 -55
  45. package/src/queues/skill-extraction.queue.ts +15 -50
  46. package/src/queues/workstream-title-generation.queue.ts +15 -51
  47. package/src/redis/connection.ts +12 -3
  48. package/src/redis/index.ts +2 -1
  49. package/src/redis/org-memory-lock.ts +1 -1
  50. package/src/redis/redis-lease-lock.ts +41 -8
  51. package/src/redis/stream-context.ts +11 -0
  52. package/src/runtime/agent-runtime-policy.ts +106 -21
  53. package/src/runtime/agent-stream-helpers.ts +2 -1
  54. package/src/runtime/approval-continuation.ts +12 -6
  55. package/src/runtime/context-compaction-constants.ts +1 -1
  56. package/src/runtime/context-compaction-runtime.ts +7 -5
  57. package/src/runtime/context-compaction.ts +40 -97
  58. package/src/runtime/execution-plan.ts +23 -19
  59. package/src/runtime/graph-designer.ts +15 -0
  60. package/src/runtime/helper-model.ts +10 -196
  61. package/src/runtime/index.ts +14 -1
  62. package/src/runtime/llm-content.ts +1 -1
  63. package/src/runtime/memory-block.ts +11 -12
  64. package/src/runtime/memory-pipeline.ts +26 -10
  65. package/src/runtime/plugin-resolution.ts +35 -0
  66. package/src/runtime/plugin-types.ts +73 -1
  67. package/src/runtime/retrieval-adapters.ts +1 -1
  68. package/src/runtime/runtime-config.ts +25 -12
  69. package/src/runtime/runtime-extensions.ts +91 -15
  70. package/src/runtime/runtime-worker-registry.ts +6 -0
  71. package/src/runtime/team-consultation-orchestrator.ts +45 -28
  72. package/src/runtime/team-consultation-prompts.ts +11 -2
  73. package/src/runtime/title-helpers.ts +11 -4
  74. package/src/runtime/workstream-chat-helpers.ts +6 -7
  75. package/src/runtime/workstream-routing-policy.ts +0 -30
  76. package/src/runtime/workstream-state.ts +17 -7
  77. package/src/services/adaptive-playbook.service.ts +152 -0
  78. package/src/services/agent-executor.service.ts +293 -0
  79. package/src/services/artifact-provenance.service.ts +172 -0
  80. package/src/services/attachment.service.ts +7 -12
  81. package/src/services/context-compaction.service.ts +75 -58
  82. package/src/services/context-enrichment.service.ts +33 -0
  83. package/src/services/coordination-registry.service.ts +117 -0
  84. package/src/services/document-chunk.service.ts +38 -33
  85. package/src/services/domain-agent-executor.service.ts +71 -0
  86. package/src/services/execution-plan.service.ts +271 -50
  87. package/src/services/feedback-loop.service.ts +96 -0
  88. package/src/services/global-orchestrator.service.ts +148 -0
  89. package/src/services/index.ts +26 -0
  90. package/src/services/institutional-memory.service.ts +145 -0
  91. package/src/services/learned-skill.service.ts +30 -15
  92. package/src/services/memory-assessment.service.ts +3 -2
  93. package/src/services/{memory.utils.ts → memory-utils.ts} +4 -13
  94. package/src/services/memory.service.ts +55 -69
  95. package/src/services/monitoring-window.service.ts +86 -0
  96. package/src/services/mutating-approval.service.ts +1 -1
  97. package/src/services/node-workspace.service.ts +155 -0
  98. package/src/services/notification.service.ts +39 -0
  99. package/src/services/organization-member.service.ts +12 -5
  100. package/src/services/organization.service.ts +5 -5
  101. package/src/services/ownership-dispatcher.service.ts +403 -0
  102. package/src/services/plan-approval.service.ts +1 -1
  103. package/src/services/plan-artifact.service.ts +1 -0
  104. package/src/services/plan-builder.service.ts +1 -0
  105. package/src/services/plan-checkpoint.service.ts +30 -2
  106. package/src/services/plan-compiler.service.ts +5 -0
  107. package/src/services/plan-coordination.service.ts +152 -0
  108. package/src/services/plan-cycle.service.ts +284 -0
  109. package/src/services/plan-deadline.service.ts +287 -0
  110. package/src/services/plan-executor.service.ts +386 -58
  111. package/src/services/plan-helpers.ts +15 -0
  112. package/src/services/plan-run.service.ts +41 -7
  113. package/src/services/plan-scheduler.service.ts +240 -0
  114. package/src/services/plan-template.service.ts +117 -0
  115. package/src/services/plan-validator.service.ts +87 -20
  116. package/src/services/plan-workspace.service.ts +83 -0
  117. package/src/services/playbook-registry.service.ts +67 -0
  118. package/src/services/plugin-executor.service.ts +103 -0
  119. package/src/services/quality-metrics.service.ts +132 -0
  120. package/src/services/recent-activity-title.service.ts +3 -10
  121. package/src/services/recent-activity.service.ts +33 -43
  122. package/src/services/skill-resolver.service.ts +19 -0
  123. package/src/services/system-executor.service.ts +105 -0
  124. package/src/services/workstream-message.service.ts +29 -41
  125. package/src/services/workstream-plan-registry.service.ts +22 -0
  126. package/src/services/workstream-title.service.ts +3 -9
  127. package/src/services/{workstream-turn-preparation.ts → workstream-turn-preparation.service.ts} +428 -373
  128. package/src/services/workstream-turn.ts +2 -2
  129. package/src/services/workstream.service.ts +55 -65
  130. package/src/services/workstream.types.ts +10 -19
  131. package/src/services/write-intent-validator.service.ts +81 -0
  132. package/src/storage/attachment-parser.ts +1 -1
  133. package/src/storage/attachment-storage.service.ts +4 -4
  134. package/src/storage/{attachments.utils.ts → attachment-utils.ts} +2 -5
  135. package/src/storage/generated-document-storage.service.ts +3 -2
  136. package/src/storage/index.ts +2 -2
  137. package/src/system-agents/{context-compacter.agent.ts → context-compaction.agent.ts} +4 -4
  138. package/src/system-agents/delegated-agent-factory.ts +5 -2
  139. package/src/system-agents/index.ts +8 -0
  140. package/src/system-agents/memory-reranker.agent.ts +1 -1
  141. package/src/system-agents/memory.agent.ts +1 -1
  142. package/src/system-agents/recent-activity-title-refiner.agent.ts +1 -1
  143. package/src/tools/execution-plan.tool.ts +17 -19
  144. package/src/tools/fetch-webpage.tool.ts +20 -18
  145. package/src/tools/index.ts +2 -3
  146. package/src/tools/read-file-parts.tool.ts +1 -1
  147. package/src/tools/search-web.tool.ts +18 -15
  148. package/src/tools/{search-tools.ts → search.tool.ts} +1 -1
  149. package/src/tools/team-think.tool.ts +14 -8
  150. package/src/tools/{tool-contract.ts → tool-contracts.ts} +9 -2
  151. package/src/utils/async.ts +3 -2
  152. package/src/utils/date-time.ts +4 -32
  153. package/src/utils/env.ts +8 -0
  154. package/src/utils/errors.ts +47 -0
  155. package/src/utils/hono-error-handler.ts +1 -2
  156. package/src/utils/index.ts +19 -2
  157. package/src/utils/string.ts +128 -1
  158. package/src/workers/bootstrap.ts +2 -2
  159. package/src/workers/index.ts +1 -0
  160. package/src/workers/memory-consolidation.worker.ts +12 -12
  161. package/src/workers/regular-chat-memory-digest.helpers.ts +2 -7
  162. package/src/workers/regular-chat-memory-digest.runner.ts +11 -105
  163. package/src/workers/skill-extraction.runner.ts +8 -102
  164. package/src/workers/utils/file-section-chunker.ts +6 -3
  165. package/src/workers/utils/repomix-file-sections.ts +2 -2
  166. package/src/workers/utils/sandbox-error.ts +11 -2
  167. package/src/workers/utils/workstream-message-query.ts +97 -0
  168. package/src/workers/worker-utils.ts +6 -2
  169. package/src/runtime/retrieval-pipeline.ts +0 -3
  170. package/src/runtime.ts +0 -387
  171. package/src/tools/log-hello-world.tool.ts +0 -17
  172. package/src/utils/error.ts +0 -10
  173. /package/src/services/{context-compaction-runtime.ts → context-compaction-runtime.singleton.ts} +0 -0
  174. /package/src/storage/{attachments.types.ts → attachment-types.ts} +0 -0
@@ -3,6 +3,8 @@ import { BoundQuery, eq, inside } from 'surrealdb'
3
3
  import { aiLogger } from '../config/logger'
4
4
  import { DEFAULT_MEMORY_SEARCH_LIMIT } from '../config/search'
5
5
  import { getDefaultEmbeddings } from '../embeddings/provider'
6
+ import { withTimeout } from '../utils/async'
7
+ import { clampImportance, truncateText } from '../utils/string'
6
8
  import { memoryQueryBuilder } from './memory-query-builder'
7
9
  import type { RelationCounts } from './memory-store.helpers'
8
10
  import { hashContent, mapRowToMemoryRecord, processGraphAwareRows } from './memory-store.helpers'
@@ -24,6 +26,10 @@ const MEMORY_TABLE = TABLES.MEMORY
24
26
  const MEMORY_HISTORY_TABLE = TABLES.MEMORY_HISTORY
25
27
  const MEMORY_RELATION_TABLE = TABLES.MEMORY_RELATION
26
28
  const MIN_RELEVANCE_SCORE = 0.25
29
+ const STRONG_GRAPH_BOOSTS = { support: 0.1, contradict: 0.2 } as const
30
+ const WEAK_GRAPH_BOOSTS = { support: 0.05, contradict: 0.1 } as const
31
+ const CANDIDATE_FANOUT_MULTIPLIER = 4
32
+ const CANDIDATE_SLICE_FLOOR = 50
27
33
  const TOUCH_MEMORIES_MAX_ATTEMPTS = 4
28
34
  const TOUCH_MEMORIES_RETRY_BASE_DELAY_MS = 25
29
35
  const TOUCH_MEMORIES_RETRY_JITTER_MS = 20
@@ -88,7 +94,7 @@ export class SurrealMemoryStore {
88
94
  LIMIT $limit
89
95
  `
90
96
 
91
- return await databaseService.query<BasicSearchRow>(
97
+ return databaseService.query<BasicSearchRow>(
92
98
  new BoundQuery(sql, { scopeId: options.scopeId, memoryType: options.memoryType, limit: options.limit }),
93
99
  )
94
100
  }
@@ -153,7 +159,7 @@ export class SurrealMemoryStore {
153
159
  relationCounts,
154
160
  options.limit,
155
161
  (row) => 1 / (1 + row.distance),
156
- { support: 0.1, contradict: 0.2 },
162
+ STRONG_GRAPH_BOOSTS,
157
163
  MIN_RELEVANCE_SCORE,
158
164
  )
159
165
 
@@ -180,7 +186,7 @@ export class SurrealMemoryStore {
180
186
  const normalized = content.trim()
181
187
  if (!normalized) return []
182
188
 
183
- return await this.embeddings.embedQuery(normalized)
189
+ return this.embeddings.embedQuery(normalized)
184
190
  }
185
191
 
186
192
  async warmEmbedding(content: string): Promise<void> {
@@ -317,7 +323,7 @@ export class SurrealMemoryStore {
317
323
  if (b.textScore !== a.textScore) return b.textScore - a.textScore
318
324
  return a.index - b.index
319
325
  })
320
- .slice(0, Math.max(options.limit * 4, 50))
326
+ .slice(0, Math.max(options.limit * CANDIDATE_FANOUT_MULTIPLIER, CANDIDATE_SLICE_FLOOR))
321
327
 
322
328
  if (options.fastMode) {
323
329
  return this.mapFastRows(scoredRows, options.limit, (row) => row.textScore)
@@ -330,7 +336,7 @@ export class SurrealMemoryStore {
330
336
  recentRelationCounts,
331
337
  options.limit,
332
338
  (row) => row.textScore,
333
- { support: 0.05, contradict: 0.1 },
339
+ WEAK_GRAPH_BOOSTS,
334
340
  MIN_RELEVANCE_SCORE,
335
341
  )
336
342
 
@@ -351,11 +357,11 @@ export class SurrealMemoryStore {
351
357
  const hash = hashContent(content, scopeId, memoryType)
352
358
  const embedding = await this.generateEmbedding(content)
353
359
 
354
- importance = Math.max(0, Math.min(1, importance))
360
+ importance = clampImportance(importance)
355
361
 
356
362
  const nearDup = await this.findNearDuplicate(embedding, scopeId, content)
357
363
  if (nearDup) {
358
- const mergedImportance = Math.max(0, Math.min(1, Math.max(nearDup.importance, importance)))
364
+ const mergedImportance = clampImportance(Math.max(nearDup.importance, importance))
359
365
  const keepNew = content.length >= nearDup.content.length
360
366
  const winnerContent = keepNew ? content : nearDup.content
361
367
  await this.update(nearDup.id, winnerContent, { importance: mergedImportance })
@@ -483,7 +489,7 @@ export class SurrealMemoryStore {
483
489
  relationCounts,
484
490
  limit,
485
491
  (row) => 1 / (1 + row.distance),
486
- { support: 0.1, contradict: 0.2 },
492
+ STRONG_GRAPH_BOOSTS,
487
493
  MIN_RELEVANCE_SCORE,
488
494
  )
489
495
 
@@ -520,7 +526,7 @@ export class SurrealMemoryStore {
520
526
  relationCounts,
521
527
  limit,
522
528
  (row) => row.rrfScore,
523
- { support: 0.05, contradict: 0.1 },
529
+ WEAK_GRAPH_BOOSTS,
524
530
  MIN_RELEVANCE_SCORE,
525
531
  )
526
532
 
@@ -576,12 +582,11 @@ export class SurrealMemoryStore {
576
582
 
577
583
  let results: LinearRow[]
578
584
  try {
579
- results = await Promise.race([
585
+ results = await withTimeout(
580
586
  this.queryFinalStatement<LinearRow>(sql, bindVars),
581
- new Promise<never>((_, reject) =>
582
- setTimeout(() => reject(new Error('Hybrid search timeout')), SurrealMemoryStore.HYBRID_SEARCH_TIMEOUT_MS),
583
- ),
584
- ])
587
+ SurrealMemoryStore.HYBRID_SEARCH_TIMEOUT_MS,
588
+ 'Hybrid search',
589
+ )
585
590
  } catch {
586
591
  const elapsed = performance.now() - searchStart
587
592
  aiLogger.warn`Hybrid search timed out after ${elapsed.toFixed(0)}ms (scopeId: ${options.scopeId}). Falling back to vector-only.`
@@ -619,7 +624,7 @@ export class SurrealMemoryStore {
619
624
  relationCounts,
620
625
  options.limit,
621
626
  (row) => row.linearScore,
622
- { support: 0.05, contradict: 0.1 },
627
+ WEAK_GRAPH_BOOSTS,
623
628
  MIN_RELEVANCE_SCORE,
624
629
  )
625
630
 
@@ -665,18 +670,13 @@ export class SurrealMemoryStore {
665
670
 
666
671
  const importance =
667
672
  typeof options?.importance === 'number'
668
- ? Math.max(existing.importance, Math.max(0, Math.min(1, options.importance)))
673
+ ? Math.max(existing.importance, clampImportance(options.importance))
669
674
  : undefined
670
675
 
671
676
  const durability = options?.durability
672
677
  const metadata = options?.metadata ? { ...existing.metadata, ...options.metadata } : undefined
673
678
 
674
- const updatePayload: Record<string, unknown> = {
675
- content: newContent,
676
- embedding: newEmbedding,
677
- hash: newHash,
678
- updatedAt: new Date(),
679
- }
679
+ const updatePayload: Record<string, unknown> = { content: newContent, embedding: newEmbedding, hash: newHash }
680
680
  if (importance !== undefined) {
681
681
  updatePayload.importance = importance
682
682
  }
@@ -756,7 +756,7 @@ export class SurrealMemoryStore {
756
756
  }
757
757
 
758
758
  async addRelation(fromId: string, toId: string, relationType: RelationType, confidence: number = 1.0): Promise<void> {
759
- confidence = Math.max(0, Math.min(1, confidence))
759
+ confidence = clampImportance(confidence)
760
760
  const fromRef = ensureRecordId(fromId, TABLES.MEMORY)
761
761
  const toRef = ensureRecordId(toId, TABLES.MEMORY)
762
762
  await databaseService.relate(fromRef, MEMORY_RELATION_TABLE, toRef, { relationType, confidence })
@@ -949,7 +949,7 @@ export class SurrealMemoryStore {
949
949
  if (!neighbor.content || seen.has(neighborId)) continue
950
950
  seen.add(neighborId)
951
951
  const label = neighbor.relationType ? `[${neighbor.relationType}]` : ''
952
- const truncated = neighbor.content.length > 200 ? `${neighbor.content.slice(0, 197)}...` : neighbor.content
952
+ const truncated = truncateText(neighbor.content, 200)
953
953
  contexts.push(`${label} ${truncated}`.trim())
954
954
  }
955
955
  if (contexts.length > 0) {
package/src/db/memory.ts CHANGED
@@ -12,6 +12,7 @@ import { getFactRetrievalMessages } from '../runtime/memory-prompts-fact'
12
12
  import { parseMessages } from '../runtime/memory-prompts-parse'
13
13
  import { getClassifyMemoryDeltaPrompt } from '../runtime/memory-prompts-update'
14
14
  import { getRuntimeConfig } from '../runtime/runtime-config'
15
+ import { compactWhitespace, truncateText } from '../utils/string'
15
16
  import type { SurrealMemoryStore } from './memory-store'
16
17
  import { getDefaultMemoryStore } from './memory-store'
17
18
  import { hashContent, isUniqueIndexConflict } from './memory-store.helpers'
@@ -39,6 +40,7 @@ const MEMORY_DELTA_MAX_CANDIDATE_MEMORIES = 80
39
40
  const MEMORY_DELTA_MAX_CANDIDATES_PER_FACT = 10
40
41
  const MEMORY_DELTA_MIN_BASELINE_CANDIDATES = 12
41
42
  const MEMORY_DELTA_MEMORY_TEXT_MAX_CHARS = 400
43
+ const CANDIDATE_FANOUT_MULTIPLIER = 4
42
44
  const helperModelRuntime = createHelperModelRuntime()
43
45
 
44
46
  interface PreparedScopeUpdate {
@@ -80,7 +82,7 @@ export class Memory {
80
82
  durability?: Durability
81
83
  },
82
84
  ): Promise<string> {
83
- return await this.store.insert(
85
+ return this.store.insert(
84
86
  content,
85
87
  options.scopeId,
86
88
  options.memoryType,
@@ -135,7 +137,7 @@ export class Memory {
135
137
  return results
136
138
  }
137
139
 
138
- return await this.store.enrichWithNeighbors(results)
140
+ return this.store.enrichWithNeighbors(results)
139
141
  }
140
142
 
141
143
  async listTopMemories(options: {
@@ -145,11 +147,11 @@ export class Memory {
145
147
  durability?: Durability
146
148
  minImportance?: number
147
149
  }): Promise<MemoryRecord[]> {
148
- return await this.store.listTopMemories(options)
150
+ return this.store.listTopMemories(options)
149
151
  }
150
152
 
151
153
  async list(options: MemoryListOptions): Promise<MemoryRecord[]> {
152
- return await this.store.list(options)
154
+ return this.store.list(options)
153
155
  }
154
156
 
155
157
  async updateMemory(id: string, newContent: string): Promise<void> {
@@ -161,7 +163,7 @@ export class Memory {
161
163
  }
162
164
 
163
165
  async getStaleMemories(scopeId: string, limit?: number): Promise<MemorySearchResult[]> {
164
- return await this.store.getStaleMemories(scopeId, limit)
166
+ return this.store.getStaleMemories(scopeId, limit)
165
167
  }
166
168
 
167
169
  async add(
@@ -242,12 +244,12 @@ export class Memory {
242
244
  const factMaps = buildMemoryFactMaps(facts)
243
245
 
244
246
  const existingMemories = await this.store.list({ scopeId: options.scopeId, memoryType: options.memoryType })
245
- const oldMemoryFormat = existingMemories.map((m) => ({ id: m.id, text: m.content }))
247
+ const memoryDeltaInput = existingMemories.map((m) => ({ id: m.id, text: m.content }))
246
248
 
247
249
  const factContents = facts.map((f) => f.content)
248
- const updates = await this.determineUpdates(oldMemoryFormat, factContents)
250
+ const updates = await this.determineUpdates(memoryDeltaInput, factContents)
249
251
 
250
- return { options, updates, factMaps, existingMemories: oldMemoryFormat }
252
+ return { options, updates, factMaps, existingMemories: memoryDeltaInput }
251
253
  }
252
254
 
253
255
  private async extractFacts(
@@ -277,7 +279,7 @@ export class Memory {
277
279
  typeof extractionOptions?.maxFacts === 'number' ? { maxFacts: extractionOptions.maxFacts } : {},
278
280
  )
279
281
  } catch (error) {
280
- aiLogger.error`Failed to extract facts: ${error}`
282
+ aiLogger.warn`Failed to extract facts: ${error}`
281
283
  return []
282
284
  }
283
285
  }
@@ -313,10 +315,10 @@ export class Memory {
313
315
  }
314
316
 
315
317
  private normalizeMemoryDeltaText(value: string, maxChars?: number): string {
316
- const normalized = value.replace(/\s+/g, ' ').trim()
318
+ const normalized = compactWhitespace(value)
317
319
  if (!normalized) return ''
318
- if (typeof maxChars !== 'number' || normalized.length <= maxChars) return normalized
319
- return `${normalized.slice(0, maxChars - 3)}...`
320
+ if (typeof maxChars !== 'number') return normalized
321
+ return truncateText(normalized, maxChars)
320
322
  }
321
323
 
322
324
  private tokenizeMemoryDeltaText(value: string): string[] {
@@ -384,7 +386,7 @@ export class Memory {
384
386
  const targetCandidateCount = Math.min(MEMORY_DELTA_MAX_CANDIDATE_MEMORIES, normalizedExisting.length)
385
387
  const baselineCount = Math.min(
386
388
  targetCandidateCount,
387
- Math.max(MEMORY_DELTA_MIN_BASELINE_CANDIDATES, newFacts.length * 4),
389
+ Math.max(MEMORY_DELTA_MIN_BASELINE_CANDIDATES, newFacts.length * CANDIDATE_FANOUT_MULTIPLIER),
388
390
  )
389
391
 
390
392
  const selectedIds = new Set<string>(
@@ -440,7 +442,7 @@ export class Memory {
440
442
  for (const action of plan.actions) {
441
443
  switch (action.type) {
442
444
  case 'add': {
443
- const truncatedContent = action.text.length > 50 ? `${action.text.slice(0, 50)}...` : action.text
445
+ const truncatedContent = truncateText(action.text, 50)
444
446
  const metadata = { ...options.metadata, memoryCategory: action.category }
445
447
  const hash = hashContent(action.text, options.scopeId, options.memoryType)
446
448
  let newId: string
@@ -481,7 +483,7 @@ export class Memory {
481
483
  metadata: Object.keys(metadata).length > 0 ? metadata : undefined,
482
484
  })
483
485
  idMap.set(action.refId, action.refId)
484
- aiLogger.debug`Updated memory ${action.refId}: ${action.text.slice(0, 50)}...`
486
+ aiLogger.debug`Updated memory ${action.refId}: ${truncateText(action.text, 50)}`
485
487
  break
486
488
  }
487
489
 
@@ -502,7 +504,7 @@ export class Memory {
502
504
  await this.store.addRelation(fromId, toId, relation.relation)
503
505
  aiLogger.debug`Created ${relation.relation} relation: ${fromId} -> ${toId}`
504
506
  } catch (error) {
505
- aiLogger.warn`Failed to create relation: ${error}`
507
+ aiLogger.warn`Failed to create memory relation (non-fatal, graph may be incomplete): ${error}`
506
508
  }
507
509
  }
508
510
  }
@@ -7,6 +7,7 @@ function toSchemaFilePath(value: string | URL): string {
7
7
  export async function computeSchemaFingerprint(schemaFiles: readonly (string | URL)[]): Promise<string> {
8
8
  const hash = createHash('sha256')
9
9
 
10
+ // Sequential reads required: hash must be computed in deterministic file order
10
11
  for (const schemaFile of schemaFiles) {
11
12
  const sortKey = toSchemaFilePath(schemaFile)
12
13
  const file = schemaFile instanceof URL ? Bun.file(schemaFile.pathname) : Bun.file(schemaFile)