@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.
- package/package.json +4 -4
- package/src/ai/embedding-cache.ts +17 -11
- package/src/ai-gateway/ai-gateway.ts +164 -94
- package/src/ai-gateway/index.ts +4 -1
- package/src/config/agent-defaults.ts +2 -2
- package/src/config/agent-types.ts +1 -1
- package/src/config/constants.ts +1 -1
- package/src/create-runtime.ts +259 -200
- package/src/db/cursor-pagination.ts +2 -9
- package/src/db/memory-store.ts +194 -175
- package/src/db/memory.ts +125 -71
- package/src/db/schema-fingerprint.ts +5 -4
- package/src/db/service-normalization.ts +4 -3
- package/src/db/service.ts +3 -2
- package/src/db/startup.ts +15 -16
- package/src/effect/errors.ts +161 -21
- package/src/effect/index.ts +0 -1
- package/src/embeddings/provider.ts +15 -7
- package/src/queues/autonomous-job.queue.ts +10 -22
- package/src/queues/delayed-node-promotion.queue.ts +8 -14
- package/src/queues/document-processor.queue.ts +13 -4
- package/src/queues/memory-consolidation.queue.ts +26 -14
- package/src/queues/plan-agent-heartbeat.queue.ts +48 -31
- package/src/queues/plan-scheduler.queue.ts +37 -15
- package/src/queues/queue-factory.ts +59 -35
- package/src/queues/standalone-worker.ts +3 -2
- package/src/redis/connection.ts +10 -3
- package/src/redis/org-memory-lock.ts +1 -1
- package/src/redis/redis-lease-lock.ts +5 -5
- package/src/redis/stream-context.ts +1 -1
- package/src/runtime/chat-message.ts +64 -1
- package/src/runtime/chat-run-orchestration.ts +33 -20
- package/src/runtime/context-compaction/context-compaction-runtime.ts +14 -7
- package/src/runtime/context-compaction/context-compaction.ts +78 -66
- package/src/runtime/domain-layer.ts +19 -13
- package/src/runtime/execution-plan.ts +7 -3
- package/src/runtime/memory/memory-block.ts +3 -9
- package/src/runtime/memory/memory-scope.ts +3 -1
- package/src/runtime/plugin-resolution.ts +2 -1
- package/src/runtime/post-turn-side-effects.ts +6 -5
- package/src/runtime/retrieval-adapters.ts +8 -20
- package/src/runtime/runtime-config.ts +3 -9
- package/src/runtime/runtime-extensions.ts +2 -4
- package/src/runtime/runtime-lifecycle.ts +56 -16
- package/src/runtime/runtime-services.ts +180 -102
- package/src/runtime/runtime-worker-registry.ts +3 -1
- package/src/runtime/social-chat/social-chat-agent-runner.ts +1 -1
- package/src/runtime/social-chat/social-chat-history.ts +21 -18
- package/src/runtime/social-chat/social-chat.ts +356 -223
- package/src/runtime/specialist-runner.ts +3 -1
- package/src/runtime/team-consultation/team-consultation-orchestrator.ts +3 -2
- package/src/runtime/thread-turn-context.ts +142 -102
- package/src/runtime/turn-lifecycle.ts +15 -46
- package/src/services/agent-activity.service.ts +1 -1
- package/src/services/agent-executor.service.ts +107 -77
- package/src/services/autonomous-job.service.ts +354 -293
- package/src/services/background-work.service.ts +3 -3
- package/src/services/context-compaction.service.ts +7 -2
- package/src/services/document-chunk.service.ts +50 -32
- package/src/services/execution-plan/execution-plan-schedule.ts +5 -3
- package/src/services/execution-plan/execution-plan.service.ts +162 -179
- package/src/services/feedback-loop.service.ts +5 -4
- package/src/services/graph-full-routing.ts +37 -36
- package/src/services/institutional-memory.service.ts +28 -30
- package/src/services/learned-skill.service.ts +107 -72
- package/src/services/memory/memory-errors.ts +4 -23
- package/src/services/memory/memory-org-memory.ts +10 -5
- package/src/services/memory/memory-rerank.ts +18 -6
- package/src/services/memory/memory.service.ts +170 -111
- package/src/services/memory/rerank.service.ts +29 -20
- package/src/services/organization-member.service.ts +1 -1
- package/src/services/organization.service.ts +69 -75
- package/src/services/ownership-dispatcher.service.ts +40 -39
- package/src/services/plan/plan-agent-heartbeat.service.ts +22 -24
- package/src/services/plan/plan-agent-query.service.ts +39 -31
- package/src/services/plan/plan-completion-side-effects.ts +13 -17
- package/src/services/plan/plan-coordination.service.ts +2 -1
- package/src/services/plan/plan-cycle.service.ts +6 -5
- package/src/services/plan/plan-deadline.service.ts +57 -54
- package/src/services/plan/plan-event-delivery.service.ts +5 -4
- package/src/services/plan/plan-executor-graph.ts +18 -15
- package/src/services/plan/plan-executor.service.ts +235 -262
- package/src/services/plan/plan-run.service.ts +169 -93
- package/src/services/plan/plan-scheduler.service.ts +192 -202
- package/src/services/plan/plan-template.service.ts +1 -1
- package/src/services/plan/plan-transaction-events.ts +1 -1
- package/src/services/plan/plan-workspace.service.ts +23 -14
- package/src/services/plugin-executor.service.ts +5 -9
- package/src/services/queue-job.service.ts +117 -59
- package/src/services/recent-activity-title.service.ts +13 -12
- package/src/services/recent-activity.service.ts +6 -1
- package/src/services/social-chat-history.service.ts +29 -25
- package/src/services/system-executor.service.ts +5 -9
- package/src/services/thread/thread-active-run.ts +2 -2
- package/src/services/thread/thread-listing.ts +61 -57
- package/src/services/thread/thread-memory-block.ts +73 -48
- package/src/services/thread/thread-message.service.ts +76 -65
- package/src/services/thread/thread-record-store.ts +8 -8
- package/src/services/thread/thread-title.service.ts +10 -4
- package/src/services/thread/thread-turn-execution.ts +43 -45
- package/src/services/thread/thread-turn-preparation.service.ts +257 -135
- package/src/services/thread/thread-turn-streaming.ts +83 -92
- package/src/services/thread/thread-turn.ts +18 -16
- package/src/services/thread/thread.service.ts +135 -100
- package/src/services/user.service.ts +45 -48
- package/src/storage/attachment-parser.ts +6 -2
- package/src/storage/attachment-storage.service.ts +5 -6
- package/src/storage/generated-document-storage.service.ts +1 -1
- package/src/system-agents/context-compaction.agent.ts +10 -9
- package/src/system-agents/delegated-agent-factory.ts +30 -6
- package/src/system-agents/memory-reranker.agent.ts +10 -9
- package/src/system-agents/memory.agent.ts +10 -9
- package/src/system-agents/recent-activity-title-refiner.agent.ts +13 -15
- package/src/system-agents/regular-chat-memory-digest.agent.ts +13 -12
- package/src/system-agents/skill-extractor.agent.ts +13 -12
- package/src/system-agents/skill-manager.agent.ts +13 -12
- package/src/system-agents/thread-router.agent.ts +11 -7
- package/src/system-agents/title-generator.agent.ts +13 -12
- package/src/tools/fetch-webpage.tool.ts +13 -13
- package/src/tools/memory-block.tool.ts +3 -1
- package/src/tools/plan-approval.tool.ts +4 -2
- package/src/tools/read-file-parts.tool.ts +10 -4
- package/src/tools/remember-memory.tool.ts +3 -1
- package/src/tools/research-topic.tool.ts +9 -5
- package/src/tools/search-web.tool.ts +16 -16
- package/src/tools/search.tool.ts +20 -5
- package/src/tools/team-think.tool.ts +61 -38
- package/src/utils/async.ts +5 -5
- package/src/utils/errors.ts +19 -18
- package/src/utils/sse-keepalive.ts +28 -25
- package/src/workers/bootstrap.ts +75 -11
- package/src/workers/memory-consolidation.worker.ts +82 -91
- package/src/workers/organization-learning.worker.ts +14 -4
- package/src/workers/regular-chat-memory-digest.runner.ts +105 -67
- package/src/workers/skill-extraction.runner.ts +97 -61
- package/src/workers/utils/repo-structure-extractor.ts +13 -8
- package/src/workers/utils/thread-message-query.ts +24 -24
- package/src/workers/worker-utils.ts +23 -4
- package/src/effect/helpers.ts +0 -123
|
@@ -10,7 +10,6 @@ import type { RecordIdRef } from '../../db/record-id'
|
|
|
10
10
|
import type { SurrealDBService } from '../../db/service'
|
|
11
11
|
import { TABLES } from '../../db/tables'
|
|
12
12
|
import { BadRequestError, ServiceError } from '../../effect/errors'
|
|
13
|
-
import { makeEffectTryPromiseWithMessage } from '../../effect/helpers'
|
|
14
13
|
import {
|
|
15
14
|
AgentConfigServiceTag,
|
|
16
15
|
DatabaseServiceTag,
|
|
@@ -20,7 +19,7 @@ import {
|
|
|
20
19
|
import type { RedisConnectionManager } from '../../redis/connection'
|
|
21
20
|
import { CompactionCoordinationTag } from '../../runtime/chat-run-orchestration'
|
|
22
21
|
import { toIsoDateTimeString } from '../../utils/date-time'
|
|
23
|
-
import {
|
|
22
|
+
import { BackgroundWorkServiceTag } from '../background-work.service'
|
|
24
23
|
import { ChatRunRegistryTag } from '../chat-run-registry.service'
|
|
25
24
|
import { ContextCompactionServiceTag } from '../context-compaction.service'
|
|
26
25
|
import type { makeContextCompactionService } from '../context-compaction.service'
|
|
@@ -63,8 +62,6 @@ function toPublicThread(thread: NormalizedThread): PublicThread {
|
|
|
63
62
|
|
|
64
63
|
type ThreadServiceError = ServiceError | BadRequestError
|
|
65
64
|
|
|
66
|
-
const effectTryPromise = makeEffectTryPromiseWithMessage((message, cause) => new ServiceError({ message, cause }))
|
|
67
|
-
|
|
68
65
|
type ChatRunRegistry = Context.Service.Shape<typeof ChatRunRegistryTag>
|
|
69
66
|
|
|
70
67
|
type CompactionCoordination = Context.Service.Shape<typeof CompactionCoordinationTag>
|
|
@@ -78,7 +75,7 @@ interface ThreadServiceDeps {
|
|
|
78
75
|
threadMessageService: ReturnType<typeof makeThreadMessageService>
|
|
79
76
|
contextCompactionService: ReturnType<typeof makeContextCompactionService>
|
|
80
77
|
compactionCoordination: CompactionCoordination
|
|
81
|
-
background: Context.Service.Shape<typeof
|
|
78
|
+
background: Context.Service.Shape<typeof BackgroundWorkServiceTag>
|
|
82
79
|
}
|
|
83
80
|
|
|
84
81
|
export function makeThreadService(deps: ThreadServiceDeps) {
|
|
@@ -109,10 +106,11 @@ export function makeThreadService(deps: ThreadServiceDeps) {
|
|
|
109
106
|
return Effect.succeed(true)
|
|
110
107
|
}
|
|
111
108
|
|
|
112
|
-
return
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
109
|
+
return activeRun
|
|
110
|
+
.hasActiveRunLease(ensureRecordId(thread.id, TABLES.THREAD))
|
|
111
|
+
.pipe(
|
|
112
|
+
Effect.mapError((cause) => new ServiceError({ message: 'Failed to check active thread run lease.', cause })),
|
|
113
|
+
)
|
|
116
114
|
}
|
|
117
115
|
|
|
118
116
|
function toNormalizedThread(
|
|
@@ -174,68 +172,59 @@ export function makeThreadService(deps: ThreadServiceDeps) {
|
|
|
174
172
|
background: deps.background,
|
|
175
173
|
})
|
|
176
174
|
|
|
177
|
-
|
|
178
|
-
return
|
|
179
|
-
|
|
175
|
+
const getById = Effect.fn('ThreadService.getById')(function* (threadId: RecordIdRef) {
|
|
176
|
+
return yield* threadStore
|
|
177
|
+
.getById(threadId)
|
|
178
|
+
.pipe(Effect.mapError((cause) => new ServiceError({ message: 'Failed to load thread.', cause })))
|
|
179
|
+
})
|
|
180
180
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
})
|
|
186
|
-
}
|
|
181
|
+
const getThread = Effect.fn('ThreadService.getThread')(function* (threadId: RecordIdRef) {
|
|
182
|
+
const thread = yield* getById(threadId)
|
|
183
|
+
return yield* toNormalizedThread(thread)
|
|
184
|
+
})
|
|
187
185
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
return yield* toNormalizedThread(thread)
|
|
197
|
-
})
|
|
198
|
-
}
|
|
186
|
+
const updateTitle = Effect.fn('ThreadService.updateTitle')(function* (threadId: RecordIdRef, title: string) {
|
|
187
|
+
const existing = yield* getById(threadId)
|
|
188
|
+
yield* assertMutableThreadEffect(existing)
|
|
189
|
+
const thread = yield* threadStore
|
|
190
|
+
.update(threadId, { title, nameGenerated: true })
|
|
191
|
+
.pipe(Effect.mapError((cause) => new ServiceError({ message: 'Failed to update thread title.', cause })))
|
|
192
|
+
return yield* toNormalizedThread(thread)
|
|
193
|
+
})
|
|
199
194
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
return yield* toNormalizedThread(thread)
|
|
213
|
-
})
|
|
214
|
-
}
|
|
195
|
+
const updateStatus = Effect.fn('ThreadService.updateStatus')(function* (threadId: RecordIdRef, status: string) {
|
|
196
|
+
const parsedStatus = sdkThreadStatusSchema.safeParse(status)
|
|
197
|
+
if (!parsedStatus.success) {
|
|
198
|
+
return yield* new BadRequestError({ message: `Invalid thread status: ${status}` })
|
|
199
|
+
}
|
|
200
|
+
const existing = yield* getById(threadId)
|
|
201
|
+
yield* assertMutableThreadEffect(existing)
|
|
202
|
+
const thread = yield* threadStore
|
|
203
|
+
.update(threadId, { status: parsedStatus.data })
|
|
204
|
+
.pipe(Effect.mapError((cause) => new ServiceError({ message: 'Failed to update thread status.', cause })))
|
|
205
|
+
return yield* toNormalizedThread(thread)
|
|
206
|
+
})
|
|
215
207
|
|
|
216
|
-
|
|
208
|
+
const setCompacting = Effect.fn('ThreadService.setCompacting')(function* (threadId: RecordIdRef, value: boolean) {
|
|
217
209
|
const threadRef = ensureRecordId(threadId, TABLES.THREAD)
|
|
218
210
|
const threadIdString = recordIdToString(threadRef, TABLES.THREAD)
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
'Failed to update thread compaction flag.',
|
|
223
|
-
).pipe(Effect.tap(() => deps.compactionCoordination.signal(threadIdString, value))),
|
|
224
|
-
)
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
function clearThread(threadId: RecordIdRef) {
|
|
228
|
-
return Effect.gen(function* () {
|
|
229
|
-
const existing = yield* getById(threadId)
|
|
230
|
-
yield* assertMutableThreadEffect(existing)
|
|
231
|
-
const threadRef = ensureRecordId(threadId, TABLES.THREAD)
|
|
232
|
-
yield* effectTryPromise(
|
|
233
|
-
() => deps.db.deleteWhere(TABLES.THREAD_MESSAGE, { threadId: threadRef }),
|
|
234
|
-
'Failed to delete thread messages.',
|
|
211
|
+
yield* deps.db
|
|
212
|
+
.query<unknown>(surql`UPDATE ONLY ${threadRef} SET isCompacting = ${value}`)
|
|
213
|
+
.pipe(
|
|
214
|
+
Effect.mapError((cause) => new ServiceError({ message: 'Failed to update thread compaction flag.', cause })),
|
|
235
215
|
)
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
216
|
+
yield* deps.compactionCoordination.signal(threadIdString, value)
|
|
217
|
+
})
|
|
218
|
+
|
|
219
|
+
const clearThread = Effect.fn('ThreadService.clearThread')(function* (threadId: RecordIdRef) {
|
|
220
|
+
const existing = yield* getById(threadId)
|
|
221
|
+
yield* assertMutableThreadEffect(existing)
|
|
222
|
+
const threadRef = ensureRecordId(threadId, TABLES.THREAD)
|
|
223
|
+
yield* deps.db
|
|
224
|
+
.deleteWhere(TABLES.THREAD_MESSAGE, { threadId: threadRef })
|
|
225
|
+
.pipe(Effect.mapError((cause) => new ServiceError({ message: 'Failed to delete thread messages.', cause })))
|
|
226
|
+
yield* deps.db
|
|
227
|
+
.query<unknown>(surql`
|
|
239
228
|
UPDATE ONLY ${threadRef}
|
|
240
229
|
SET turnCount = 0,
|
|
241
230
|
compactionSummary = NONE,
|
|
@@ -243,68 +232,102 @@ export function makeThreadService(deps: ThreadServiceDeps) {
|
|
|
243
232
|
activeRunId = NONE,
|
|
244
233
|
activeStreamId = NONE,
|
|
245
234
|
isCompacting = false
|
|
246
|
-
`)
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
})
|
|
250
|
-
}
|
|
235
|
+
`)
|
|
236
|
+
.pipe(Effect.mapError((cause) => new ServiceError({ message: 'Failed to reset thread state.', cause })))
|
|
237
|
+
})
|
|
251
238
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
}
|
|
239
|
+
const deleteThread = Effect.fn('ThreadService.deleteThread')(function* (threadId: RecordIdRef) {
|
|
240
|
+
const existing = yield* getById(threadId)
|
|
241
|
+
yield* assertMutableThreadEffect(existing)
|
|
242
|
+
yield* threadStore
|
|
243
|
+
.deleteById(threadId)
|
|
244
|
+
.pipe(Effect.mapError((cause) => new ServiceError({ message: 'Failed to delete thread.', cause })))
|
|
245
|
+
})
|
|
259
246
|
|
|
260
247
|
function incrementTurnCount(threadId: RecordIdRef) {
|
|
261
248
|
const threadRef = ensureRecordId(threadId, TABLES.THREAD)
|
|
262
249
|
return Effect.gen(function* () {
|
|
263
|
-
const result = yield*
|
|
264
|
-
(
|
|
265
|
-
|
|
266
|
-
surql`
|
|
250
|
+
const result = yield* deps.db
|
|
251
|
+
.query<{ turnCount: number }>(
|
|
252
|
+
surql`
|
|
267
253
|
UPDATE ONLY ${threadRef}
|
|
268
254
|
SET turnCount += 1
|
|
269
255
|
RETURN turnCount
|
|
270
256
|
`,
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
257
|
+
)
|
|
258
|
+
.pipe(
|
|
259
|
+
Effect.mapError((cause) => new ServiceError({ message: 'Failed to increment thread turn count.', cause })),
|
|
260
|
+
)
|
|
274
261
|
return result[0].turnCount
|
|
275
262
|
})
|
|
276
263
|
}
|
|
277
264
|
|
|
278
265
|
return {
|
|
279
266
|
findById: (...args: Parameters<typeof threadStore.findById>) =>
|
|
280
|
-
|
|
267
|
+
threadStore.findById(...args).pipe(
|
|
268
|
+
Effect.mapError((cause) => new ServiceError({ message: 'Failed to find thread.', cause })),
|
|
269
|
+
Effect.withSpan('ThreadService.findById'),
|
|
270
|
+
),
|
|
281
271
|
getById,
|
|
282
272
|
findAll: (...args: Parameters<typeof threadStore.findAll>) =>
|
|
283
|
-
|
|
273
|
+
threadStore.findAll(...args).pipe(
|
|
274
|
+
Effect.mapError((cause) => new ServiceError({ message: 'Failed to find all threads.', cause })),
|
|
275
|
+
Effect.withSpan('ThreadService.findAll'),
|
|
276
|
+
),
|
|
284
277
|
create: (...args: Parameters<typeof threadStore.create>) =>
|
|
285
|
-
|
|
278
|
+
threadStore.create(...args).pipe(
|
|
279
|
+
Effect.mapError((cause) => new ServiceError({ message: 'Failed to create thread.', cause })),
|
|
280
|
+
Effect.withSpan('ThreadService.create'),
|
|
281
|
+
),
|
|
286
282
|
update: (...args: Parameters<typeof threadStore.update>) =>
|
|
287
|
-
|
|
283
|
+
threadStore.update(...args).pipe(
|
|
284
|
+
Effect.mapError((cause) => new ServiceError({ message: 'Failed to update thread.', cause })),
|
|
285
|
+
Effect.withSpan('ThreadService.update'),
|
|
286
|
+
),
|
|
288
287
|
delete: (...args: Parameters<typeof threadStore.deleteById>) =>
|
|
289
|
-
|
|
288
|
+
threadStore.deleteById(...args).pipe(
|
|
289
|
+
Effect.mapError((cause) => new ServiceError({ message: 'Failed to delete thread.', cause })),
|
|
290
|
+
Effect.withSpan('ThreadService.delete'),
|
|
291
|
+
),
|
|
290
292
|
getOrCreateDefault: (...args: Parameters<typeof bootstrap.getOrCreateDefault>) =>
|
|
291
|
-
|
|
293
|
+
bootstrap.getOrCreateDefault(...args).pipe(
|
|
294
|
+
Effect.mapError((cause) => new ServiceError({ message: 'Failed to get or create default thread.', cause })),
|
|
295
|
+
Effect.withSpan('ThreadService.getOrCreateDefault'),
|
|
296
|
+
),
|
|
292
297
|
getOrCreateThread: (...args: Parameters<typeof bootstrap.getOrCreateThread>) =>
|
|
293
|
-
|
|
298
|
+
bootstrap.getOrCreateThread(...args).pipe(
|
|
299
|
+
Effect.mapError((cause) => new ServiceError({ message: 'Failed to get or create thread.', cause })),
|
|
300
|
+
Effect.withSpan('ThreadService.getOrCreateThread'),
|
|
301
|
+
),
|
|
294
302
|
createThread: (...args: Parameters<typeof bootstrap.createThread>) =>
|
|
295
|
-
|
|
303
|
+
bootstrap.createThread(...args).pipe(
|
|
304
|
+
Effect.mapError((cause) => new ServiceError({ message: 'Failed to create thread.', cause })),
|
|
305
|
+
Effect.withSpan('ThreadService.createThread'),
|
|
306
|
+
),
|
|
296
307
|
ensureBootstrapThreads: (...args: Parameters<typeof bootstrap.ensureBootstrapThreads>) =>
|
|
297
|
-
|
|
308
|
+
bootstrap.ensureBootstrapThreads(...args).pipe(
|
|
309
|
+
Effect.mapError((cause) => new ServiceError({ message: 'Failed to ensure bootstrap threads.', cause })),
|
|
310
|
+
Effect.withSpan('ThreadService.ensureBootstrapThreads'),
|
|
311
|
+
),
|
|
298
312
|
listThreads: (...args: Parameters<typeof listing.listThreads>) =>
|
|
299
|
-
|
|
313
|
+
listing.listThreads(...args).pipe(
|
|
314
|
+
Effect.mapError((cause) => new ServiceError({ message: 'Failed to list threads.', cause })),
|
|
315
|
+
Effect.withSpan('ThreadService.listThreads'),
|
|
316
|
+
),
|
|
300
317
|
listOrganizationThreads: (...args: Parameters<typeof listing.listOrganizationThreads>) =>
|
|
301
|
-
|
|
318
|
+
listing.listOrganizationThreads(...args).pipe(
|
|
319
|
+
Effect.mapError((cause) => new ServiceError({ message: 'Failed to list organization threads.', cause })),
|
|
320
|
+
Effect.withSpan('ThreadService.listOrganizationThreads'),
|
|
321
|
+
),
|
|
302
322
|
getThread,
|
|
303
323
|
updateTitle,
|
|
304
324
|
updateStatus,
|
|
305
325
|
setActiveTurn: activeRun.setActiveTurn,
|
|
306
326
|
getActiveTurn: (...args: Parameters<typeof activeRun.getActiveTurn>) =>
|
|
307
|
-
|
|
327
|
+
activeRun.getActiveTurn(...args).pipe(
|
|
328
|
+
Effect.mapError((cause) => new ServiceError({ message: 'Failed to get active turn.', cause })),
|
|
329
|
+
Effect.withSpan('ThreadService.getActiveTurn'),
|
|
330
|
+
),
|
|
308
331
|
getActiveRunId: activeRun.getActiveRunId,
|
|
309
332
|
hasActiveRunLease: activeRun.hasActiveRunLease,
|
|
310
333
|
withActiveRunLease: activeRun.withActiveRunLease,
|
|
@@ -312,16 +335,28 @@ export function makeThreadService(deps: ThreadServiceDeps) {
|
|
|
312
335
|
clearActiveTurn: activeRun.clearActiveTurn,
|
|
313
336
|
clearStaleActiveRunIfMissingFromRegistry: activeRun.clearStaleActiveRunIfMissingFromRegistry,
|
|
314
337
|
stopActiveRun: (...args: Parameters<typeof activeRun.stopActiveRun>) =>
|
|
315
|
-
|
|
338
|
+
activeRun.stopActiveRun(...args).pipe(
|
|
339
|
+
Effect.mapError((cause) => new ServiceError({ message: 'Failed to stop active run.', cause })),
|
|
340
|
+
Effect.withSpan('ThreadService.stopActiveRun'),
|
|
341
|
+
),
|
|
316
342
|
setCompacting,
|
|
317
343
|
appendMemoryBlock: (...args: Parameters<typeof memory.appendMemoryBlock>) =>
|
|
318
|
-
|
|
344
|
+
memory.appendMemoryBlock(...args).pipe(
|
|
345
|
+
Effect.mapError((cause) => new ServiceError({ message: 'Failed to append memory block.', cause })),
|
|
346
|
+
Effect.withSpan('ThreadService.appendMemoryBlock'),
|
|
347
|
+
),
|
|
319
348
|
compactMemoryBlock: (...args: Parameters<typeof memory.compactMemoryBlock>) =>
|
|
320
|
-
|
|
349
|
+
memory.compactMemoryBlock(...args).pipe(
|
|
350
|
+
Effect.mapError((cause) => new ServiceError({ message: 'Failed to compact memory block.', cause })),
|
|
351
|
+
Effect.withSpan('ThreadService.compactMemoryBlock'),
|
|
352
|
+
),
|
|
321
353
|
clearThread,
|
|
322
354
|
deleteThread,
|
|
323
355
|
listRecentThreads: (...args: Parameters<typeof listing.listRecentThreads>) =>
|
|
324
|
-
|
|
356
|
+
listing.listRecentThreads(...args).pipe(
|
|
357
|
+
Effect.mapError((cause) => new ServiceError({ message: 'Failed to list recent threads.', cause })),
|
|
358
|
+
Effect.withSpan('ThreadService.listRecentThreads'),
|
|
359
|
+
),
|
|
325
360
|
formatMemoryBlockForPrompt,
|
|
326
361
|
toPublicThread,
|
|
327
362
|
incrementTurnCount,
|
|
@@ -343,7 +378,7 @@ export const ThreadServiceLive = Layer.effect(
|
|
|
343
378
|
const threadMessageService = yield* ThreadMessageServiceTag
|
|
344
379
|
const contextCompactionService = yield* ContextCompactionServiceTag
|
|
345
380
|
const compactionCoordination = yield* CompactionCoordinationTag
|
|
346
|
-
const background = yield*
|
|
381
|
+
const background = yield* BackgroundWorkServiceTag
|
|
347
382
|
return makeThreadService({
|
|
348
383
|
agentConfig,
|
|
349
384
|
threadBootstrapConfig,
|
|
@@ -6,7 +6,7 @@ import { ensureRecordId, recordIdToString } from '../db/record-id'
|
|
|
6
6
|
import type { RecordIdInput } from '../db/record-id'
|
|
7
7
|
import type { SurrealDBService } from '../db/service'
|
|
8
8
|
import { TABLES } from '../db/tables'
|
|
9
|
-
import { NotFoundError } from '../effect/errors'
|
|
9
|
+
import { ERROR_TAGS, NotFoundError } from '../effect/errors'
|
|
10
10
|
import { DatabaseServiceTag } from '../effect/services'
|
|
11
11
|
import { toIsoDateTimeString } from '../utils/date-time'
|
|
12
12
|
|
|
@@ -28,7 +28,7 @@ function userNotFoundError(userId: RecordIdInput): NotFoundError {
|
|
|
28
28
|
})
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
class UserServiceError extends Schema.TaggedErrorClass<UserServiceError>()(
|
|
31
|
+
class UserServiceError extends Schema.TaggedErrorClass<UserServiceError>()(ERROR_TAGS.UserServiceError, {
|
|
32
32
|
operation: Schema.String,
|
|
33
33
|
cause: Schema.Defect,
|
|
34
34
|
}) {}
|
|
@@ -38,59 +38,56 @@ function toUserServiceError(operation: string, cause: unknown): UserServiceError
|
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
export function makeUserService(db: SurrealDBService) {
|
|
41
|
-
function
|
|
41
|
+
const upsertUser = Effect.fn('UserService.upsertUser')(function* (params: {
|
|
42
|
+
id: RecordIdInput
|
|
43
|
+
name: string
|
|
44
|
+
email: string
|
|
45
|
+
}) {
|
|
42
46
|
const userRef = ensureRecordId(params.id, TABLES.USER)
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
Effect.
|
|
46
|
-
)
|
|
47
|
-
}
|
|
47
|
+
const record = yield* db
|
|
48
|
+
.upsert(TABLES.USER, userRef, { name: params.name, email: params.email }, sdkUserRecordSchema)
|
|
49
|
+
.pipe(Effect.mapError((cause) => toUserServiceError('upsertUser', cause)))
|
|
50
|
+
return toPublic(record)
|
|
51
|
+
})
|
|
48
52
|
|
|
49
|
-
function
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
Effect.
|
|
53
|
-
|
|
54
|
-
)
|
|
55
|
-
}
|
|
53
|
+
const getUser = Effect.fn('UserService.getUser')(function* (userId: RecordIdInput) {
|
|
54
|
+
const record = yield* db
|
|
55
|
+
.findOne(TABLES.USER, { id: ensureRecordId(userId, TABLES.USER) }, sdkUserRecordSchema)
|
|
56
|
+
.pipe(Effect.mapError((cause) => toUserServiceError('getUser', cause)))
|
|
57
|
+
if (!record) return yield* userNotFoundError(userId)
|
|
58
|
+
return toPublic(record)
|
|
59
|
+
})
|
|
56
60
|
|
|
57
|
-
function
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
Effect.
|
|
61
|
-
)
|
|
62
|
-
}
|
|
61
|
+
const listUsers = Effect.fn('UserService.listUsers')(function* () {
|
|
62
|
+
const records = yield* db
|
|
63
|
+
.findMany(TABLES.USER, {}, sdkUserRecordSchema, { orderBy: 'createdAt', orderDir: 'ASC' })
|
|
64
|
+
.pipe(Effect.mapError((cause) => toUserServiceError('listUsers', cause)))
|
|
65
|
+
return records.map(toPublic)
|
|
66
|
+
})
|
|
63
67
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
68
|
+
const updateUser = Effect.fn('UserService.updateUser')(function* (
|
|
69
|
+
userId: RecordIdInput,
|
|
70
|
+
params: { name?: string; email?: string },
|
|
71
|
+
) {
|
|
72
|
+
const updated = yield* db
|
|
73
|
+
.update(TABLES.USER, ensureRecordId(userId, TABLES.USER), params, sdkUserRecordSchema)
|
|
74
|
+
.pipe(Effect.mapError((cause) => toUserServiceError('updateUser', cause)))
|
|
75
|
+
if (!updated) return yield* userNotFoundError(userId)
|
|
76
|
+
return toPublic(updated)
|
|
77
|
+
})
|
|
71
78
|
|
|
72
|
-
function
|
|
79
|
+
const deleteUser = Effect.fn('UserService.deleteUser')(function* (userId: RecordIdInput) {
|
|
73
80
|
const userRef = ensureRecordId(userId, TABLES.USER)
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
return yield* userNotFoundError(userId)
|
|
83
|
-
}
|
|
84
|
-
})
|
|
85
|
-
}
|
|
81
|
+
yield* db
|
|
82
|
+
.deleteWhere(TABLES.ORGANIZATION_MEMBER, { in: userRef })
|
|
83
|
+
.pipe(Effect.mapError((cause) => toUserServiceError('deleteUser.deleteMemberships', cause)))
|
|
84
|
+
const deleted = yield* db
|
|
85
|
+
.deleteById(TABLES.USER, userRef)
|
|
86
|
+
.pipe(Effect.mapError((cause) => toUserServiceError('deleteUser.deleteUser', cause)))
|
|
87
|
+
if (!deleted) return yield* userNotFoundError(userId)
|
|
88
|
+
})
|
|
86
89
|
|
|
87
|
-
return {
|
|
88
|
-
upsertUser: upsertUserEffect,
|
|
89
|
-
getUser: getUserEffect,
|
|
90
|
-
listUsers: listUsersEffect,
|
|
91
|
-
updateUser: updateUserEffect,
|
|
92
|
-
deleteUser: deleteUserEffect,
|
|
93
|
-
}
|
|
90
|
+
return { upsertUser, getUser, listUsers, updateUser, deleteUser }
|
|
94
91
|
}
|
|
95
92
|
|
|
96
93
|
export class UserServiceTag extends Context.Service<UserServiceTag, ReturnType<typeof makeUserService>>()(
|
|
@@ -3,9 +3,11 @@ import { Schema, Effect } from 'effect'
|
|
|
3
3
|
import mammoth from 'mammoth'
|
|
4
4
|
import { PDFParse } from 'pdf-parse'
|
|
5
5
|
|
|
6
|
+
import { ERROR_TAGS } from '../effect/errors'
|
|
7
|
+
|
|
6
8
|
const READ_FILE_PARTS_CHARS_PER_PAGE = 3500
|
|
7
9
|
|
|
8
|
-
class AttachmentParserError extends Schema.TaggedErrorClass<AttachmentParserError>()(
|
|
10
|
+
class AttachmentParserError extends Schema.TaggedErrorClass<AttachmentParserError>()(ERROR_TAGS.AttachmentParserError, {
|
|
9
11
|
message: Schema.String,
|
|
10
12
|
cause: Schema.Defect,
|
|
11
13
|
}) {}
|
|
@@ -67,7 +69,9 @@ export function extractPdfPages(file: File): Promise<string[]> {
|
|
|
67
69
|
}).pipe(
|
|
68
70
|
Effect.ensuring(
|
|
69
71
|
tryAttachmentPromise('Failed to destroy PDF parser.', () => parser.destroy()).pipe(
|
|
70
|
-
Effect.
|
|
72
|
+
Effect.catchTag(ERROR_TAGS.AttachmentParserError, (error) =>
|
|
73
|
+
Effect.logWarning(`Attachment parser cleanup failed: ${error.message}`),
|
|
74
|
+
),
|
|
71
75
|
Effect.asVoid,
|
|
72
76
|
),
|
|
73
77
|
),
|
|
@@ -3,7 +3,7 @@ import { S3Client } from 'bun'
|
|
|
3
3
|
import { Context, Schema, Effect, Layer } from 'effect'
|
|
4
4
|
|
|
5
5
|
import { serverLogger } from '../config/logger'
|
|
6
|
-
import { BadRequestError, ForbiddenError } from '../effect/errors'
|
|
6
|
+
import { ERROR_TAGS, BadRequestError, ForbiddenError } from '../effect/errors'
|
|
7
7
|
import { RuntimeConfigServiceTag } from '../effect/services'
|
|
8
8
|
import type { ResolvedLotaRuntimeConfig } from '../runtime/runtime-config'
|
|
9
9
|
import { getErrorMessage } from '../utils/errors'
|
|
@@ -36,11 +36,10 @@ export type UploadedThreadAttachment = {
|
|
|
36
36
|
|
|
37
37
|
type AttachmentContextCandidate = { storageKey: string; name: string; contentType: string; sizeBytes: number | null }
|
|
38
38
|
|
|
39
|
-
class AttachmentStorageError extends Schema.TaggedErrorClass<AttachmentStorageError>()(
|
|
40
|
-
|
|
41
|
-
message: Schema.String,
|
|
42
|
-
|
|
43
|
-
}) {}
|
|
39
|
+
class AttachmentStorageError extends Schema.TaggedErrorClass<AttachmentStorageError>()(
|
|
40
|
+
ERROR_TAGS.AttachmentStorageError,
|
|
41
|
+
{ operation: Schema.String, message: Schema.String, cause: Schema.Defect },
|
|
42
|
+
) {}
|
|
44
43
|
|
|
45
44
|
function toAttachmentStorageError(operation: string, cause: unknown): AttachmentStorageError {
|
|
46
45
|
return new AttachmentStorageError({ operation, message: getErrorMessage(cause), cause })
|
|
@@ -26,7 +26,7 @@ function buildGeneratedDocumentStorageKey(params: {
|
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
class GeneratedDocumentStorageError extends Schema.TaggedErrorClass<GeneratedDocumentStorageError>()(
|
|
29
|
-
'GeneratedDocumentStorageError',
|
|
29
|
+
'@lota-sdk/core/GeneratedDocumentStorageError',
|
|
30
30
|
{ message: Schema.String, cause: Schema.Defect },
|
|
31
31
|
) {}
|
|
32
32
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { CreateHelperToolLoopAgentOptions } from '@lota-sdk/shared'
|
|
2
2
|
import { ToolLoopAgent } from 'ai'
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import type { AiGatewayModels } from '../ai-gateway/ai-gateway'
|
|
5
5
|
import { buildAiGatewayDirectCacheHeaders } from '../ai-gateway/cache-headers'
|
|
6
6
|
import {
|
|
7
7
|
OPENROUTER_STRUCTURED_HELPER_MODEL_ID,
|
|
@@ -31,12 +31,13 @@ The caller enforces a structured output schema with exactly one field:
|
|
|
31
31
|
</output-format>
|
|
32
32
|
</agent-instructions>`
|
|
33
33
|
|
|
34
|
-
export function
|
|
35
|
-
return
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
34
|
+
export function makeContextCompactionAgentFactory(models: AiGatewayModels) {
|
|
35
|
+
return (options: CreateHelperToolLoopAgentOptions) =>
|
|
36
|
+
new ToolLoopAgent({
|
|
37
|
+
id: 'context-compaction',
|
|
38
|
+
model: models.chatModel(OPENROUTER_STRUCTURED_HELPER_MODEL_ID),
|
|
39
|
+
headers: buildAiGatewayDirectCacheHeaders('lota-sdk'),
|
|
40
|
+
providerOptions: OPENROUTER_HIGH_REASONING_PROVIDER_OPTIONS,
|
|
41
|
+
...resolveHelperAgentOptions(options, { instructions: CONTEXT_COMPACTION_PROMPT }),
|
|
42
|
+
})
|
|
42
43
|
}
|