@lota-sdk/core 0.4.13 → 0.4.15

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 (139) hide show
  1. package/package.json +4 -4
  2. package/src/ai/embedding-cache.ts +17 -11
  3. package/src/ai-gateway/ai-gateway.ts +164 -94
  4. package/src/ai-gateway/index.ts +4 -1
  5. package/src/config/agent-defaults.ts +2 -2
  6. package/src/config/agent-types.ts +1 -1
  7. package/src/config/constants.ts +1 -1
  8. package/src/create-runtime.ts +259 -200
  9. package/src/db/cursor-pagination.ts +2 -9
  10. package/src/db/memory-store.ts +194 -175
  11. package/src/db/memory.ts +125 -71
  12. package/src/db/schema-fingerprint.ts +5 -4
  13. package/src/db/service-normalization.ts +4 -3
  14. package/src/db/service.ts +3 -2
  15. package/src/db/startup.ts +15 -16
  16. package/src/effect/errors.ts +161 -21
  17. package/src/effect/index.ts +0 -1
  18. package/src/embeddings/provider.ts +15 -7
  19. package/src/queues/autonomous-job.queue.ts +10 -22
  20. package/src/queues/delayed-node-promotion.queue.ts +8 -14
  21. package/src/queues/document-processor.queue.ts +13 -4
  22. package/src/queues/memory-consolidation.queue.ts +26 -14
  23. package/src/queues/plan-agent-heartbeat.queue.ts +48 -31
  24. package/src/queues/plan-scheduler.queue.ts +37 -15
  25. package/src/queues/queue-factory.ts +59 -35
  26. package/src/queues/standalone-worker.ts +3 -2
  27. package/src/redis/connection.ts +10 -3
  28. package/src/redis/org-memory-lock.ts +1 -1
  29. package/src/redis/redis-lease-lock.ts +5 -5
  30. package/src/redis/stream-context.ts +1 -1
  31. package/src/runtime/chat-message.ts +64 -1
  32. package/src/runtime/chat-run-orchestration.ts +33 -20
  33. package/src/runtime/context-compaction/context-compaction-runtime.ts +14 -7
  34. package/src/runtime/context-compaction/context-compaction.ts +78 -66
  35. package/src/runtime/domain-layer.ts +19 -13
  36. package/src/runtime/execution-plan.ts +7 -3
  37. package/src/runtime/memory/memory-block.ts +3 -9
  38. package/src/runtime/memory/memory-scope.ts +3 -1
  39. package/src/runtime/plugin-resolution.ts +2 -1
  40. package/src/runtime/post-turn-side-effects.ts +6 -5
  41. package/src/runtime/retrieval-adapters.ts +8 -20
  42. package/src/runtime/runtime-config.ts +3 -9
  43. package/src/runtime/runtime-extensions.ts +2 -4
  44. package/src/runtime/runtime-lifecycle.ts +56 -16
  45. package/src/runtime/runtime-services.ts +180 -102
  46. package/src/runtime/runtime-worker-registry.ts +3 -1
  47. package/src/runtime/social-chat/social-chat-agent-runner.ts +1 -1
  48. package/src/runtime/social-chat/social-chat-history.ts +21 -18
  49. package/src/runtime/social-chat/social-chat.ts +356 -223
  50. package/src/runtime/specialist-runner.ts +3 -1
  51. package/src/runtime/team-consultation/team-consultation-orchestrator.ts +3 -2
  52. package/src/runtime/thread-turn-context.ts +142 -102
  53. package/src/runtime/turn-lifecycle.ts +15 -46
  54. package/src/services/agent-activity.service.ts +1 -1
  55. package/src/services/agent-executor.service.ts +107 -77
  56. package/src/services/autonomous-job.service.ts +354 -293
  57. package/src/services/background-work.service.ts +3 -3
  58. package/src/services/context-compaction.service.ts +7 -2
  59. package/src/services/document-chunk.service.ts +50 -32
  60. package/src/services/execution-plan/execution-plan-schedule.ts +5 -3
  61. package/src/services/execution-plan/execution-plan.service.ts +162 -179
  62. package/src/services/feedback-loop.service.ts +5 -4
  63. package/src/services/graph-full-routing.ts +37 -36
  64. package/src/services/institutional-memory.service.ts +28 -30
  65. package/src/services/learned-skill.service.ts +107 -72
  66. package/src/services/memory/memory-errors.ts +4 -23
  67. package/src/services/memory/memory-org-memory.ts +10 -5
  68. package/src/services/memory/memory-rerank.ts +18 -6
  69. package/src/services/memory/memory.service.ts +170 -111
  70. package/src/services/memory/rerank.service.ts +29 -20
  71. package/src/services/organization-member.service.ts +1 -1
  72. package/src/services/organization.service.ts +69 -75
  73. package/src/services/ownership-dispatcher.service.ts +40 -39
  74. package/src/services/plan/plan-agent-heartbeat.service.ts +22 -24
  75. package/src/services/plan/plan-agent-query.service.ts +39 -31
  76. package/src/services/plan/plan-completion-side-effects.ts +13 -17
  77. package/src/services/plan/plan-coordination.service.ts +2 -1
  78. package/src/services/plan/plan-cycle.service.ts +6 -5
  79. package/src/services/plan/plan-deadline.service.ts +57 -54
  80. package/src/services/plan/plan-event-delivery.service.ts +5 -4
  81. package/src/services/plan/plan-executor-graph.ts +18 -15
  82. package/src/services/plan/plan-executor.service.ts +235 -262
  83. package/src/services/plan/plan-run.service.ts +169 -93
  84. package/src/services/plan/plan-scheduler.service.ts +192 -202
  85. package/src/services/plan/plan-template.service.ts +1 -1
  86. package/src/services/plan/plan-transaction-events.ts +1 -1
  87. package/src/services/plan/plan-workspace.service.ts +23 -14
  88. package/src/services/plugin-executor.service.ts +5 -9
  89. package/src/services/queue-job.service.ts +117 -59
  90. package/src/services/recent-activity-title.service.ts +13 -12
  91. package/src/services/recent-activity.service.ts +6 -1
  92. package/src/services/social-chat-history.service.ts +29 -25
  93. package/src/services/system-executor.service.ts +5 -9
  94. package/src/services/thread/thread-active-run.ts +2 -2
  95. package/src/services/thread/thread-listing.ts +61 -57
  96. package/src/services/thread/thread-memory-block.ts +73 -48
  97. package/src/services/thread/thread-message.service.ts +76 -65
  98. package/src/services/thread/thread-record-store.ts +8 -8
  99. package/src/services/thread/thread-title.service.ts +10 -4
  100. package/src/services/thread/thread-turn-execution.ts +43 -45
  101. package/src/services/thread/thread-turn-preparation.service.ts +257 -135
  102. package/src/services/thread/thread-turn-streaming.ts +83 -92
  103. package/src/services/thread/thread-turn.ts +18 -16
  104. package/src/services/thread/thread.service.ts +135 -100
  105. package/src/services/user.service.ts +45 -48
  106. package/src/storage/attachment-parser.ts +6 -2
  107. package/src/storage/attachment-storage.service.ts +5 -6
  108. package/src/storage/generated-document-storage.service.ts +1 -1
  109. package/src/system-agents/context-compaction.agent.ts +10 -9
  110. package/src/system-agents/delegated-agent-factory.ts +30 -6
  111. package/src/system-agents/memory-reranker.agent.ts +10 -9
  112. package/src/system-agents/memory.agent.ts +10 -9
  113. package/src/system-agents/recent-activity-title-refiner.agent.ts +13 -15
  114. package/src/system-agents/regular-chat-memory-digest.agent.ts +13 -12
  115. package/src/system-agents/skill-extractor.agent.ts +13 -12
  116. package/src/system-agents/skill-manager.agent.ts +13 -12
  117. package/src/system-agents/thread-router.agent.ts +11 -7
  118. package/src/system-agents/title-generator.agent.ts +13 -12
  119. package/src/tools/fetch-webpage.tool.ts +13 -13
  120. package/src/tools/memory-block.tool.ts +3 -1
  121. package/src/tools/plan-approval.tool.ts +4 -2
  122. package/src/tools/read-file-parts.tool.ts +10 -4
  123. package/src/tools/remember-memory.tool.ts +3 -1
  124. package/src/tools/research-topic.tool.ts +9 -5
  125. package/src/tools/search-web.tool.ts +16 -16
  126. package/src/tools/search.tool.ts +20 -5
  127. package/src/tools/team-think.tool.ts +61 -38
  128. package/src/utils/async.ts +5 -5
  129. package/src/utils/errors.ts +19 -18
  130. package/src/utils/sse-keepalive.ts +28 -25
  131. package/src/workers/bootstrap.ts +75 -11
  132. package/src/workers/memory-consolidation.worker.ts +82 -91
  133. package/src/workers/organization-learning.worker.ts +14 -4
  134. package/src/workers/regular-chat-memory-digest.runner.ts +105 -67
  135. package/src/workers/skill-extraction.runner.ts +97 -61
  136. package/src/workers/utils/repo-structure-extractor.ts +13 -8
  137. package/src/workers/utils/thread-message-query.ts +24 -24
  138. package/src/workers/worker-utils.ts +23 -4
  139. package/src/effect/helpers.ts +0 -123
package/src/db/memory.ts CHANGED
@@ -3,6 +3,7 @@ import { Effect } from 'effect'
3
3
  import type { z } from 'zod'
4
4
 
5
5
  import { aiLogger } from '../config/logger'
6
+ import { ERROR_TAGS } from '../effect/errors'
6
7
  import type { CreateHelperAgentFn, HelperModelRuntime } from '../runtime/helper-model'
7
8
  import { formatResults } from '../runtime/memory/memory-format'
8
9
  import {
@@ -16,8 +17,8 @@ import { getFactRetrievalMessages } from '../runtime/memory/memory-prompts-fact'
16
17
  import { parseMessages } from '../runtime/memory/memory-prompts-parse'
17
18
  import { getClassifyMemoryDeltaPrompt } from '../runtime/memory/memory-prompts-update'
18
19
  import type { ResolvedLotaRuntimeConfig } from '../runtime/runtime-config'
19
- import type { BackgroundWorkService } from '../services/background-work.service'
20
- import { MemoryServiceError, tryMemoryPromise } from '../services/memory/memory-errors'
20
+ import type { BackgroundWorkServiceTag } from '../services/background-work.service'
21
+ import { MemoryServiceError } from '../services/memory/memory-errors'
21
22
  import { sha256Hex } from '../utils/crypto'
22
23
  import { compactWhitespace, truncateText } from '../utils/string'
23
24
  import type { SurrealMemoryStore } from './memory-store'
@@ -70,7 +71,8 @@ export class Memory {
70
71
  db: SurrealDBService
71
72
  runtimeConfig: ResolvedLotaRuntimeConfig
72
73
  helperModelRuntime: HelperModelRuntime
73
- background: Context.Service.Shape<typeof BackgroundWorkService>
74
+ background: Context.Service.Shape<typeof BackgroundWorkServiceTag>
75
+ runPromise: <A, E>(effect: Effect.Effect<A, E>) => Promise<A>
74
76
  },
75
77
  agent: { createAgent: CreateHelperAgentFn; maxOutputTokens?: number },
76
78
  config: MemoryConfig = {},
@@ -80,6 +82,7 @@ export class Memory {
80
82
  {
81
83
  embeddingModel: deps.runtimeConfig.aiGateway.embeddingModel,
82
84
  openRouterApiKey: deps.runtimeConfig.aiGateway.openRouterApiKey,
85
+ runPromise: deps.runPromise,
83
86
  },
84
87
  deps.background,
85
88
  )
@@ -115,25 +118,25 @@ export class Memory {
115
118
  durability?: Durability
116
119
  },
117
120
  ): Effect.Effect<string, MemoryServiceError, never> {
118
- return tryMemoryPromise('Failed to insert memory.', () =>
119
- this.store.insert(
121
+ return this.store
122
+ .insert(
120
123
  content,
121
124
  options.scopeId,
122
125
  options.memoryType,
123
126
  options.metadata ?? {},
124
127
  options.importance ?? 1,
125
128
  options.durability ?? 'standard',
126
- ),
127
- )
129
+ )
130
+ .pipe(Effect.mapError((cause) => new MemoryServiceError({ message: 'Failed to insert memory.', cause })))
128
131
  }
129
132
 
130
133
  search(query: string, options: SearchOptions): Effect.Effect<string, MemoryServiceError, never> {
131
134
  return Effect.gen(
132
135
  function* (this: Memory) {
133
136
  const limit = options.limit ?? this.runtimeConfig.memory.searchK
134
- const results = yield* tryMemoryPromise('Failed to search memory.', () =>
135
- this.store.search(query, options.scopeId, limit, options.memoryType),
136
- )
137
+ const results = yield* this.store
138
+ .search(query, options.scopeId, limit, options.memoryType)
139
+ .pipe(Effect.mapError((cause) => new MemoryServiceError({ message: 'Failed to search memory.', cause })))
137
140
  return formatResults(results)
138
141
  }.bind(this),
139
142
  )
@@ -143,9 +146,11 @@ export class Memory {
143
146
  return Effect.gen(
144
147
  function* (this: Memory) {
145
148
  const limit = options.limit ?? this.runtimeConfig.memory.searchK
146
- const results = yield* tryMemoryPromise('Failed to perform hybrid search.', () =>
147
- this.store.hybridSearch(query, options.scopeId, limit, options.memoryType),
148
- )
149
+ const results = yield* this.store
150
+ .hybridSearch(query, options.scopeId, limit, options.memoryType)
151
+ .pipe(
152
+ Effect.mapError((cause) => new MemoryServiceError({ message: 'Failed to perform hybrid search.', cause })),
153
+ )
149
154
  return formatResults(results)
150
155
  }.bind(this),
151
156
  )
@@ -158,15 +163,19 @@ export class Memory {
158
163
  return Effect.gen(
159
164
  function* (this: Memory) {
160
165
  const limit = options.limit ?? this.runtimeConfig.memory.searchK
161
- const results = yield* tryMemoryPromise('Failed to perform weighted hybrid search.', () =>
162
- this.store.hybridSearchWeighted(query, {
166
+ const results = yield* this.store
167
+ .hybridSearchWeighted(query, {
163
168
  scopeId: options.scopeId,
164
169
  limit,
165
170
  memoryType: options.memoryType,
166
171
  weights: options.weights,
167
172
  normalization: options.normalization,
168
- }),
169
- )
173
+ })
174
+ .pipe(
175
+ Effect.mapError(
176
+ (cause) => new MemoryServiceError({ message: 'Failed to perform weighted hybrid search.', cause }),
177
+ ),
178
+ )
170
179
  return formatResults(results)
171
180
  }.bind(this),
172
181
  )
@@ -179,24 +188,32 @@ export class Memory {
179
188
  return Effect.gen(
180
189
  function* (this: Memory) {
181
190
  const limit = options.limit ?? this.runtimeConfig.memory.searchK
182
- const results = yield* tryMemoryPromise('Failed to search memory candidates.', () =>
183
- this.store.hybridSearchWeighted(query, {
191
+ const results = yield* this.store
192
+ .hybridSearchWeighted(query, {
184
193
  scopeId: options.scopeId,
185
194
  limit,
186
195
  memoryType: options.memoryType,
187
196
  weights: options.weights,
188
197
  normalization: options.normalization,
189
198
  fastMode: options.fastMode,
190
- }),
191
- )
199
+ })
200
+ .pipe(
201
+ Effect.mapError(
202
+ (cause) => new MemoryServiceError({ message: 'Failed to search memory candidates.', cause }),
203
+ ),
204
+ )
192
205
 
193
206
  if (options.fastMode || options.includeNeighborContext === false) {
194
207
  return results
195
208
  }
196
209
 
197
- return yield* tryMemoryPromise('Failed to enrich memory candidates.', () =>
198
- this.store.enrichWithNeighbors(results),
199
- )
210
+ return yield* this.store
211
+ .enrichWithNeighbors(results)
212
+ .pipe(
213
+ Effect.mapError(
214
+ (cause) => new MemoryServiceError({ message: 'Failed to enrich memory candidates.', cause }),
215
+ ),
216
+ )
200
217
  }.bind(this),
201
218
  )
202
219
  }
@@ -208,15 +225,21 @@ export class Memory {
208
225
  durability?: Durability
209
226
  minImportance?: number
210
227
  }): Effect.Effect<MemoryRecord[], MemoryServiceError, never> {
211
- return tryMemoryPromise('Failed to list top memories.', () => this.store.listTopMemories(options))
228
+ return this.store
229
+ .listTopMemories(options)
230
+ .pipe(Effect.mapError((cause) => new MemoryServiceError({ message: 'Failed to list top memories.', cause })))
212
231
  }
213
232
 
214
233
  list(options: MemoryListOptions): Effect.Effect<MemoryRecord[], MemoryServiceError, never> {
215
- return tryMemoryPromise('Failed to list memories.', () => this.store.list(options))
234
+ return this.store
235
+ .list(options)
236
+ .pipe(Effect.mapError((cause) => new MemoryServiceError({ message: 'Failed to list memories.', cause })))
216
237
  }
217
238
 
218
239
  updateMemory(id: string, newContent: string): Effect.Effect<void, MemoryServiceError, never> {
219
- return tryMemoryPromise(`Failed to update memory ${id}.`, () => this.store.update(id, newContent))
240
+ return this.store
241
+ .update(id, newContent)
242
+ .pipe(Effect.mapError((cause) => new MemoryServiceError({ message: `Failed to update memory ${id}.`, cause })))
220
243
  }
221
244
 
222
245
  addRelation(
@@ -225,13 +248,19 @@ export class Memory {
225
248
  relationType: RelationType,
226
249
  confidence = 1,
227
250
  ): Effect.Effect<void, MemoryServiceError, never> {
228
- return tryMemoryPromise(`Failed to add ${relationType} relation.`, () =>
229
- this.store.addRelation(fromId, toId, relationType, confidence),
230
- )
251
+ return this.store
252
+ .addRelation(fromId, toId, relationType, confidence)
253
+ .pipe(
254
+ Effect.mapError(
255
+ (cause) => new MemoryServiceError({ message: `Failed to add ${relationType} relation.`, cause }),
256
+ ),
257
+ )
231
258
  }
232
259
 
233
260
  getStaleMemories(scopeId: string, limit?: number): Effect.Effect<MemorySearchResult[], MemoryServiceError, never> {
234
- return tryMemoryPromise('Failed to load stale memories.', () => this.store.getStaleMemories(scopeId, limit))
261
+ return this.store
262
+ .getStaleMemories(scopeId, limit)
263
+ .pipe(Effect.mapError((cause) => new MemoryServiceError({ message: 'Failed to load stale memories.', cause })))
235
264
  }
236
265
 
237
266
  extractFactsFromMessages(
@@ -277,8 +306,8 @@ export class Memory {
277
306
  return Effect.forEach(
278
307
  prepared,
279
308
  (item) =>
280
- tryMemoryPromise('Failed to apply memory updates', () =>
281
- this.applyUpdates(item.updates, item.options, item.factMaps, item.existingMemories),
309
+ this.applyUpdates(item.updates, item.options, item.factMaps, item.existingMemories).pipe(
310
+ Effect.mapError((cause) => new MemoryServiceError({ message: 'Failed to apply memory updates', cause })),
282
311
  ),
283
312
  { concurrency: 2, discard: true },
284
313
  )
@@ -296,9 +325,14 @@ export class Memory {
296
325
  const factContents = facts.map((fact) => fact.content)
297
326
  const scopePayloads = yield* Effect.all(
298
327
  scopes.map((scopeOptions) =>
299
- tryMemoryPromise(`Failed to list memories for scope ${scopeOptions.scopeId}.`, () =>
300
- this.store.list({ scopeId: scopeOptions.scopeId, memoryType: scopeOptions.memoryType }),
301
- ).pipe(
328
+ this.store.list({ scopeId: scopeOptions.scopeId, memoryType: scopeOptions.memoryType }).pipe(
329
+ Effect.mapError(
330
+ (cause) =>
331
+ new MemoryServiceError({
332
+ message: `Failed to list memories for scope ${scopeOptions.scopeId}.`,
333
+ cause,
334
+ }),
335
+ ),
302
336
  Effect.map((existingMemories) => {
303
337
  const normalizedMemories = existingMemories.map((memory) => ({ id: memory.id, text: memory.content }))
304
338
  const scopeMemoryIdsByUnionId: Record<string, string[]> = {}
@@ -378,7 +412,7 @@ export class Memory {
378
412
  )
379
413
  }.bind(this),
380
414
  ).pipe(
381
- Effect.catchTag('MemoryServiceError', (error) =>
415
+ Effect.catchTag(ERROR_TAGS.MemoryServiceError, (error) =>
382
416
  Effect.sync(() => {
383
417
  aiLogger.warn`Failed to extract facts: ${error.message}`
384
418
  return []
@@ -564,37 +598,45 @@ export class Memory {
564
598
  const metadata = { ...options.metadata, memoryCategory: action.category }
565
599
  const hash = hashContent(action.text, options.scopeId, options.memoryType)
566
600
 
567
- const newId = yield* tryMemoryPromise(`Failed to insert memory for scope ${options.scopeId}.`, () =>
568
- this.store.insert(
601
+ const newId = yield* this.store
602
+ .insert(
569
603
  action.text,
570
604
  options.scopeId,
571
605
  options.memoryType,
572
606
  metadata,
573
607
  action.importance,
574
608
  action.durability as Durability,
575
- ),
576
- ).pipe(
577
- Effect.catchTag('MemoryServiceError', (error) => {
578
- if (!isUniqueIndexConflict(error.cause, 'memoryHashIdx')) {
579
- return Effect.fail(error)
580
- }
581
-
582
- return tryMemoryPromise(`Failed to look up memory hash ${hash}.`, () =>
583
- this.store.getByHash(hash),
584
- ).pipe(
585
- Effect.flatMap((existing) => {
586
- if (!existing) {
587
- return Effect.fail(error)
588
- }
589
-
590
- return Effect.sync(() => {
591
- aiLogger.debug`Skipped duplicate memory insert due to hash conflict: ${existing.id}`
592
- return existing.id
593
- })
594
- }),
595
- )
596
- }),
597
- )
609
+ )
610
+ .pipe(
611
+ Effect.mapError(
612
+ (cause) =>
613
+ new MemoryServiceError({
614
+ message: `Failed to insert memory for scope ${options.scopeId}.`,
615
+ cause,
616
+ }),
617
+ ),
618
+ Effect.catchTag(ERROR_TAGS.MemoryServiceError, (error) => {
619
+ if (!isUniqueIndexConflict(error.cause, 'memoryHashIdx')) {
620
+ return Effect.fail(error)
621
+ }
622
+
623
+ return this.store.getByHash(hash).pipe(
624
+ Effect.mapError(
625
+ (cause) => new MemoryServiceError({ message: `Failed to look up memory hash ${hash}.`, cause }),
626
+ ),
627
+ Effect.flatMap((existing) => {
628
+ if (!existing) {
629
+ return Effect.fail(error)
630
+ }
631
+
632
+ return Effect.sync(() => {
633
+ aiLogger.debug`Skipped duplicate memory insert due to hash conflict: ${existing.id}`
634
+ return existing.id
635
+ })
636
+ }),
637
+ )
638
+ }),
639
+ )
598
640
 
599
641
  idMap.set(action.refId, newId)
600
642
  aiLogger.debug`Added new memory (memoryType: ${options.memoryType}, category: ${action.category}, durability: ${action.durability}, importance: ${action.importance.toFixed(2)}, content: ${truncatedContent})`
@@ -603,20 +645,30 @@ export class Memory {
603
645
 
604
646
  case 'update': {
605
647
  const metadata = { ...options.metadata, ...(action.category ? { memoryCategory: action.category } : {}) }
606
- yield* tryMemoryPromise(`Failed to update memory ${action.refId}.`, () =>
607
- this.store.update(action.refId, action.text, {
648
+ yield* this.store
649
+ .update(action.refId, action.text, {
608
650
  importance: action.importance,
609
651
  durability: action.durability as Durability | undefined,
610
652
  metadata: Object.keys(metadata).length > 0 ? metadata : undefined,
611
- }),
612
- )
653
+ })
654
+ .pipe(
655
+ Effect.mapError(
656
+ (cause) => new MemoryServiceError({ message: `Failed to update memory ${action.refId}.`, cause }),
657
+ ),
658
+ )
613
659
  idMap.set(action.refId, action.refId)
614
660
  aiLogger.debug`Updated memory ${action.refId}: ${truncateText(action.text, 50)}`
615
661
  break
616
662
  }
617
663
 
618
664
  case 'delete':
619
- yield* tryMemoryPromise(`Failed to delete memory ${action.refId}.`, () => this.store.delete(action.refId))
665
+ yield* this.store
666
+ .delete(action.refId)
667
+ .pipe(
668
+ Effect.mapError(
669
+ (cause) => new MemoryServiceError({ message: `Failed to delete memory ${action.refId}.`, cause }),
670
+ ),
671
+ )
620
672
  aiLogger.debug`Deleted memory ${action.refId}`
621
673
  break
622
674
  }
@@ -627,15 +679,17 @@ export class Memory {
627
679
  if (!fromId) continue
628
680
 
629
681
  const toId = idMap.get(relation.toRefId) ?? relation.toRefId
630
- yield* tryMemoryPromise(`Failed to create memory relation ${relation.relation}.`, () =>
631
- this.store.addRelation(fromId, toId, relation.relation),
632
- ).pipe(
682
+ yield* this.store.addRelation(fromId, toId, relation.relation).pipe(
683
+ Effect.mapError(
684
+ (cause) =>
685
+ new MemoryServiceError({ message: `Failed to create memory relation ${relation.relation}.`, cause }),
686
+ ),
633
687
  Effect.tap(() =>
634
688
  Effect.sync(() => {
635
689
  aiLogger.debug`Created ${relation.relation} relation: ${fromId} -> ${toId}`
636
690
  }),
637
691
  ),
638
- Effect.catchTag('MemoryServiceError', (error) =>
692
+ Effect.catchTag(ERROR_TAGS.MemoryServiceError, (error) =>
639
693
  Effect.sync(() => {
640
694
  aiLogger.warn`Failed to create memory relation (non-fatal, graph may be incomplete): ${error.message}`
641
695
  }),
@@ -1,15 +1,16 @@
1
1
  import { Schema, Effect } from 'effect'
2
2
 
3
+ import { ERROR_TAGS } from '../effect/errors'
3
4
  import { createSha256Hasher } from '../utils/crypto'
4
5
 
5
6
  function toSchemaFilePath(value: string | URL): string {
6
7
  return value instanceof URL ? value.pathname : value
7
8
  }
8
9
 
9
- class SchemaFingerprintError extends Schema.TaggedErrorClass<SchemaFingerprintError>()('SchemaFingerprintError', {
10
- message: Schema.String,
11
- cause: Schema.Defect,
12
- }) {}
10
+ class SchemaFingerprintError extends Schema.TaggedErrorClass<SchemaFingerprintError>()(
11
+ ERROR_TAGS.SchemaFingerprintError,
12
+ { message: Schema.String, cause: Schema.Defect },
13
+ ) {}
13
14
 
14
15
  export function computeSchemaFingerprint(schemaFiles: readonly (string | URL)[]): Promise<string> {
15
16
  return Effect.runPromise(
@@ -2,7 +2,7 @@
2
2
  * Normalization helpers for SurrealDB service inputs.
3
3
  *
4
4
  * Helpers that can fail return `Effect<T, SurrealDBError>` so callers can
5
- * `yield*` them inside `Effect.gen` and use `catchTag('SurrealDBError')`
5
+ * `yield*` them inside `Effect.gen` and use `catchTag('@lota-sdk/core/SurrealDBError')`
6
6
  * without losing the typed failure channel.
7
7
  */
8
8
  import { Effect, Schema } from 'effect'
@@ -10,12 +10,13 @@ import { BoundQuery, RecordId, StringRecordId, Table, and, eq } from 'surrealdb'
10
10
  import type { ExprLike, Mutation } from 'surrealdb'
11
11
  import type { z } from 'zod'
12
12
 
13
+ import { ERROR_TAGS } from '../effect/errors'
13
14
  import { isRecord } from '../utils/string'
14
15
  import type { RecordIdInput } from './record-id'
15
16
  import { ensureRecordId, isRecordIdInput, isSurrealRecordIdValue } from './record-id'
16
17
  import type { DatabaseTable } from './tables'
17
18
 
18
- export class SurrealDBError extends Schema.TaggedErrorClass<SurrealDBError>()('SurrealDBError', {
19
+ export class SurrealDBError extends Schema.TaggedErrorClass<SurrealDBError>()(ERROR_TAGS.SurrealDBError, {
19
20
  message: Schema.String,
20
21
  query: Schema.optional(Schema.String),
21
22
  cause: Schema.optional(Schema.Defect),
@@ -28,7 +29,7 @@ type TransactionQueryDescriptor = { query: string; bindings?: Record<string, unk
28
29
  type ParseSchema = <TSchema extends z.ZodTypeAny>(schema: TSchema, value: unknown) => z.infer<TSchema>
29
30
 
30
31
  function isSurrealDBError(error: unknown): error is SurrealDBError {
31
- return isRecord(error) && error._tag === 'SurrealDBError'
32
+ return isRecord(error) && error._tag === ERROR_TAGS.SurrealDBError
32
33
  }
33
34
 
34
35
  function toSurrealDBError(error: unknown, fallbackMessage: string): SurrealDBError {
package/src/db/service.ts CHANGED
@@ -5,6 +5,7 @@ import { ZodError } from 'zod'
5
5
  import type { z } from 'zod'
6
6
 
7
7
  import { serverLogger } from '../config/logger'
8
+ import { ERROR_TAGS } from '../effect/errors'
8
9
  import { getErrorMessage } from '../utils/errors'
9
10
  import type { RecordIdInput, ensureRecordId } from './record-id'
10
11
  import { ensureRecordIdEffect } from './record-id'
@@ -107,7 +108,7 @@ function isRetriableConnectError(error: unknown): boolean {
107
108
  }
108
109
 
109
110
  function isSurrealDBError(error: unknown): error is SurrealDBError {
110
- return typeof error === 'object' && error !== null && (error as { _tag?: unknown })._tag === 'SurrealDBError'
111
+ return typeof error === 'object' && error !== null && (error as { _tag?: unknown })._tag === ERROR_TAGS.SurrealDBError
111
112
  }
112
113
 
113
114
  export class SurrealDBService {
@@ -926,7 +927,7 @@ export class SurrealDBService {
926
927
  const recordId = yield* this.normalizeRecordIdEffect(id, table)
927
928
  return yield* this.query<unknown>(new BoundQuery(`DELETE $recordId RETURN BEFORE`, { recordId })).pipe(
928
929
  Effect.map((result) => result.length > 0),
929
- Effect.catchTag('SurrealDBError', (error) => {
930
+ Effect.catchTag(ERROR_TAGS.SurrealDBError, (error) => {
930
931
  if (error.message.includes('does not exist')) {
931
932
  return Effect.succeed(false)
932
933
  }
package/src/db/startup.ts CHANGED
@@ -4,7 +4,6 @@ import { BoundQuery, RecordId } from 'surrealdb'
4
4
  import { z } from 'zod'
5
5
 
6
6
  import { DatabaseError } from '../effect/errors'
7
- import { effectTryPromise } from '../effect/helpers'
8
7
  import { nowDate } from '../utils/date-time'
9
8
  import { getErrorMessage } from '../utils/errors'
10
9
  import type { SurrealDBService, SurrealDatabaseLogger } from './service'
@@ -35,7 +34,7 @@ function shouldLogRetry(attempt: number): boolean {
35
34
  }
36
35
 
37
36
  function connectWithStartupRetryEffectInternal(params: {
38
- connect: () => PromiseLike<void> | Effect.Effect<void, SurrealDBError>
37
+ connect: () => Promise<void>
39
38
  label: string
40
39
  logger?: StartupLogger
41
40
  retryDelayMs?: number
@@ -46,10 +45,11 @@ function connectWithStartupRetryEffectInternal(params: {
46
45
 
47
46
  let attempt = 0
48
47
 
49
- const connectEffect = effectTryPromise(
50
- () => params.connect(),
51
- (error) => new DatabaseError({ message: `Waiting for ${params.label}: ${getErrorMessage(error)}`, cause: error }),
52
- )
48
+ const connectEffect = Effect.tryPromise({
49
+ try: () => params.connect(),
50
+ catch: (error) =>
51
+ new DatabaseError({ message: `Waiting for ${params.label}: ${getErrorMessage(error)}`, cause: error }),
52
+ })
53
53
 
54
54
  return connectEffect.pipe(
55
55
  Effect.tapError((error) =>
@@ -75,7 +75,7 @@ function connectWithStartupRetryEffectInternal(params: {
75
75
  }
76
76
 
77
77
  export function connectWithStartupRetry(params: {
78
- connect: () => PromiseLike<void> | Effect.Effect<void, SurrealDBError>
78
+ connect: () => Promise<void>
79
79
  label: string
80
80
  logger?: StartupLogger
81
81
  retryDelayMs?: number
@@ -104,7 +104,7 @@ function waitForDatabaseBootstrapEffectInternal(params: {
104
104
  expectedFingerprint?: string | null
105
105
  label: string
106
106
  logger?: StartupLogger
107
- connect?: () => PromiseLike<void> | Effect.Effect<void, SurrealDBError>
107
+ connect?: () => Promise<void>
108
108
  retryDelayMs?: number
109
109
  maxWaitMs?: number
110
110
  }): Effect.Effect<void, DatabaseError> {
@@ -120,15 +120,14 @@ function waitForDatabaseBootstrapEffectInternal(params: {
120
120
  const readBootstrapRecord = Effect.gen(function* () {
121
121
  const connect = params.connect
122
122
  if (connect) {
123
- yield* effectTryPromise(
124
- () => connect(),
125
- (error): BootstrapWaitFailure => ({ _tag: 'read-failed', cause: error }),
126
- )
123
+ yield* Effect.tryPromise({
124
+ try: () => connect(),
125
+ catch: (error): BootstrapWaitFailure => ({ _tag: 'read-failed', cause: error }),
126
+ })
127
127
  }
128
128
 
129
- return yield* effectTryPromise(
130
- () => readDatabaseBootstrapRecord(params.databaseService),
131
- (error): BootstrapWaitFailure => ({ _tag: 'read-failed', cause: error }),
129
+ return yield* readDatabaseBootstrapRecord(params.databaseService).pipe(
130
+ Effect.mapError((error): BootstrapWaitFailure => ({ _tag: 'read-failed', cause: error })),
132
131
  )
133
132
  })
134
133
 
@@ -194,7 +193,7 @@ export function waitForDatabaseBootstrap(params: {
194
193
  expectedFingerprint?: string | null
195
194
  label: string
196
195
  logger?: StartupLogger
197
- connect?: () => PromiseLike<void> | Effect.Effect<void, SurrealDBError>
196
+ connect?: () => Promise<void>
198
197
  retryDelayMs?: number
199
198
  maxWaitMs?: number
200
199
  }): Promise<void> {