@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
|
@@ -2,12 +2,7 @@ import type { OwnershipDispatchContext, PlanNodeSpec, PluginPlanNodeOwner } from
|
|
|
2
2
|
import { Context, Effect, Layer } from 'effect'
|
|
3
3
|
|
|
4
4
|
import { BadRequestError, ServiceError } from '../effect/errors'
|
|
5
|
-
import { makeEffectTryPromiseWithMessage } from '../effect/helpers'
|
|
6
5
|
import { RuntimeConfigServiceTag } from '../effect/services'
|
|
7
|
-
|
|
8
|
-
const tryPluginExecutorPromise = makeEffectTryPromiseWithMessage(
|
|
9
|
-
(message, cause) => new ServiceError({ message, cause }),
|
|
10
|
-
)
|
|
11
6
|
import type { LotaPlugin, PluginNodeExecutionParams } from '../runtime/plugin-types'
|
|
12
7
|
import type { ResolvedLotaRuntimeConfig } from '../runtime/runtime-config'
|
|
13
8
|
import type { PlanValidationIssueInput } from './plan/plan-validator.service'
|
|
@@ -107,8 +102,8 @@ export function makePluginExecutorService(config: ResolvedLotaRuntimeConfig) {
|
|
|
107
102
|
})
|
|
108
103
|
}
|
|
109
104
|
|
|
110
|
-
return yield*
|
|
111
|
-
() =>
|
|
105
|
+
return yield* Effect.tryPromise({
|
|
106
|
+
try: () =>
|
|
112
107
|
nodeExecutor.executeNode(
|
|
113
108
|
buildPluginExecutionParams({
|
|
114
109
|
owner,
|
|
@@ -117,8 +112,9 @@ export function makePluginExecutorService(config: ResolvedLotaRuntimeConfig) {
|
|
|
117
112
|
context: params.context,
|
|
118
113
|
}),
|
|
119
114
|
),
|
|
120
|
-
|
|
121
|
-
|
|
115
|
+
catch: (cause) =>
|
|
116
|
+
new ServiceError({ message: `Plugin executor "${owner.ref}.${owner.operation}" failed.`, cause }),
|
|
117
|
+
}).pipe(Effect.withSpan('PluginExecutor.invoke'))
|
|
122
118
|
}),
|
|
123
119
|
}
|
|
124
120
|
}
|
|
@@ -14,7 +14,6 @@ import type { SurrealDBService } from '../db/service'
|
|
|
14
14
|
import { TABLES } from '../db/tables'
|
|
15
15
|
import { isRetriableTransactionConflict } from '../db/transaction-conflict'
|
|
16
16
|
import { BadRequestError, DatabaseError } from '../effect/errors'
|
|
17
|
-
import { makeEffectTryPromiseWithMessage } from '../effect/helpers'
|
|
18
17
|
import { DatabaseServiceTag } from '../effect/services'
|
|
19
18
|
import { createDeterministicRecordId } from '../utils/crypto'
|
|
20
19
|
import { nowDate, unsafeDateFrom } from '../utils/date-time'
|
|
@@ -185,17 +184,6 @@ function getQueuedStatus(job: TrackedBullJobLike): QueueJobStatus {
|
|
|
185
184
|
return typeof delay === 'number' && delay > 0 ? 'delayed' : 'waiting'
|
|
186
185
|
}
|
|
187
186
|
|
|
188
|
-
const effectTryQueueJobPersistence = makeEffectTryPromiseWithMessage(
|
|
189
|
-
(message, cause) => new DatabaseError({ message, cause }),
|
|
190
|
-
)
|
|
191
|
-
|
|
192
|
-
function tryQueueJobPersistence<A>(
|
|
193
|
-
message: string,
|
|
194
|
-
evaluate: () => PromiseLike<A> | Effect.Effect<A, unknown>,
|
|
195
|
-
): Effect.Effect<A, DatabaseError> {
|
|
196
|
-
return effectTryQueueJobPersistence(evaluate, message)
|
|
197
|
-
}
|
|
198
|
-
|
|
199
187
|
function withPersistenceRetry<T>(work: Effect.Effect<T, DatabaseError>): Effect.Effect<T, DatabaseError> {
|
|
200
188
|
return Effect.retry(work, {
|
|
201
189
|
times: PERSISTENCE_MAX_ATTEMPTS - 1,
|
|
@@ -219,7 +207,14 @@ export function makeQueueJobService(db: SurrealDBService) {
|
|
|
219
207
|
|
|
220
208
|
recordEnqueued(job: TrackedBullJobLike, context?: Record<string, unknown>) {
|
|
221
209
|
return Effect.gen(function* () {
|
|
222
|
-
yield*
|
|
210
|
+
yield* db
|
|
211
|
+
.connect()
|
|
212
|
+
.pipe(
|
|
213
|
+
Effect.mapError(
|
|
214
|
+
(cause) =>
|
|
215
|
+
new DatabaseError({ message: 'Failed to connect before recording queued job metadata', cause }),
|
|
216
|
+
),
|
|
217
|
+
)
|
|
223
218
|
|
|
224
219
|
const bullmqJobId = yield* getBullmqJobId(job)
|
|
225
220
|
const queueJobId = yield* getQueueJobRecordId(job)
|
|
@@ -228,8 +223,8 @@ export function makeQueueJobService(db: SurrealDBService) {
|
|
|
228
223
|
|
|
229
224
|
return yield* withPersistenceRetry(
|
|
230
225
|
Effect.gen(function* () {
|
|
231
|
-
yield*
|
|
232
|
-
|
|
226
|
+
yield* db
|
|
227
|
+
.upsert(
|
|
233
228
|
TABLES.QUEUE_JOB,
|
|
234
229
|
queueJobId,
|
|
235
230
|
compactRecord({
|
|
@@ -245,8 +240,12 @@ export function makeQueueJobService(db: SurrealDBService) {
|
|
|
245
240
|
queuedAt,
|
|
246
241
|
}),
|
|
247
242
|
QueueJobRowSchema,
|
|
248
|
-
)
|
|
249
|
-
|
|
243
|
+
)
|
|
244
|
+
.pipe(
|
|
245
|
+
Effect.mapError(
|
|
246
|
+
(cause) => new DatabaseError({ message: 'Failed to upsert queued job metadata', cause }),
|
|
247
|
+
),
|
|
248
|
+
)
|
|
250
249
|
|
|
251
250
|
return recordIdToString(queueJobId, TABLES.QUEUE_JOB)
|
|
252
251
|
}),
|
|
@@ -256,7 +255,14 @@ export function makeQueueJobService(db: SurrealDBService) {
|
|
|
256
255
|
|
|
257
256
|
markAttemptStarted(job: TrackedBullJobLike) {
|
|
258
257
|
return Effect.gen(function* () {
|
|
259
|
-
yield*
|
|
258
|
+
yield* db
|
|
259
|
+
.connect()
|
|
260
|
+
.pipe(
|
|
261
|
+
Effect.mapError(
|
|
262
|
+
(cause) =>
|
|
263
|
+
new DatabaseError({ message: 'Failed to connect before marking queue job attempt started', cause }),
|
|
264
|
+
),
|
|
265
|
+
)
|
|
260
266
|
|
|
261
267
|
const attemptNumber = resolveAttemptNumber(job)
|
|
262
268
|
const bullmqJobId = yield* getBullmqJobId(job).pipe(
|
|
@@ -273,8 +279,8 @@ export function makeQueueJobService(db: SurrealDBService) {
|
|
|
273
279
|
|
|
274
280
|
return yield* withPersistenceRetry(
|
|
275
281
|
Effect.gen(function* () {
|
|
276
|
-
yield*
|
|
277
|
-
|
|
282
|
+
yield* db
|
|
283
|
+
.upsert(
|
|
278
284
|
TABLES.QUEUE_JOB,
|
|
279
285
|
queueJobId,
|
|
280
286
|
compactRecord({
|
|
@@ -292,17 +298,25 @@ export function makeQueueJobService(db: SurrealDBService) {
|
|
|
292
298
|
startedAt,
|
|
293
299
|
}),
|
|
294
300
|
QueueJobRowSchema,
|
|
295
|
-
)
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
301
|
+
)
|
|
302
|
+
.pipe(
|
|
303
|
+
Effect.mapError(
|
|
304
|
+
(cause) => new DatabaseError({ message: 'Failed to upsert queue job start metadata', cause }),
|
|
305
|
+
),
|
|
306
|
+
)
|
|
307
|
+
|
|
308
|
+
yield* db
|
|
309
|
+
.upsert(
|
|
300
310
|
TABLES.QUEUE_JOB_ATTEMPT,
|
|
301
311
|
attemptId,
|
|
302
312
|
{ queueJobId, attemptNumber, status: 'active', startedAt },
|
|
303
313
|
QueueJobAttemptRowSchema,
|
|
304
|
-
)
|
|
305
|
-
|
|
314
|
+
)
|
|
315
|
+
.pipe(
|
|
316
|
+
Effect.mapError(
|
|
317
|
+
(cause) => new DatabaseError({ message: 'Failed to upsert queue job attempt start metadata', cause }),
|
|
318
|
+
),
|
|
319
|
+
)
|
|
306
320
|
|
|
307
321
|
return recordIdToString(queueJobId, TABLES.QUEUE_JOB)
|
|
308
322
|
}),
|
|
@@ -312,9 +326,14 @@ export function makeQueueJobService(db: SurrealDBService) {
|
|
|
312
326
|
|
|
313
327
|
markAttemptCompleted(job: TrackedBullJobLike, result: unknown) {
|
|
314
328
|
return Effect.gen(function* () {
|
|
315
|
-
yield*
|
|
316
|
-
|
|
317
|
-
|
|
329
|
+
yield* db
|
|
330
|
+
.connect()
|
|
331
|
+
.pipe(
|
|
332
|
+
Effect.mapError(
|
|
333
|
+
(cause) =>
|
|
334
|
+
new DatabaseError({ message: 'Failed to connect before marking queue job attempt completed', cause }),
|
|
335
|
+
),
|
|
336
|
+
)
|
|
318
337
|
|
|
319
338
|
const attemptNumber = resolveAttemptNumber(job)
|
|
320
339
|
const bullmqJobId = yield* getBullmqJobId(job)
|
|
@@ -325,13 +344,20 @@ export function makeQueueJobService(db: SurrealDBService) {
|
|
|
325
344
|
|
|
326
345
|
yield* withPersistenceRetry(
|
|
327
346
|
Effect.gen(function* () {
|
|
328
|
-
const existingAttempt = yield*
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
347
|
+
const existingAttempt = yield* db
|
|
348
|
+
.findOne(TABLES.QUEUE_JOB_ATTEMPT, { id: attemptId }, QueueJobAttemptRowSchema)
|
|
349
|
+
.pipe(
|
|
350
|
+
Effect.mapError(
|
|
351
|
+
(cause) =>
|
|
352
|
+
new DatabaseError({
|
|
353
|
+
message: 'Failed to load existing queue job attempt before completion update',
|
|
354
|
+
cause,
|
|
355
|
+
}),
|
|
356
|
+
),
|
|
357
|
+
)
|
|
358
|
+
|
|
359
|
+
yield* db
|
|
360
|
+
.upsert(
|
|
335
361
|
TABLES.QUEUE_JOB_ATTEMPT,
|
|
336
362
|
attemptId,
|
|
337
363
|
compactRecord({
|
|
@@ -346,11 +372,16 @@ export function makeQueueJobService(db: SurrealDBService) {
|
|
|
346
372
|
: 0,
|
|
347
373
|
}),
|
|
348
374
|
QueueJobAttemptRowSchema,
|
|
349
|
-
)
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
375
|
+
)
|
|
376
|
+
.pipe(
|
|
377
|
+
Effect.mapError(
|
|
378
|
+
(cause) =>
|
|
379
|
+
new DatabaseError({ message: 'Failed to upsert completed queue job attempt metadata', cause }),
|
|
380
|
+
),
|
|
381
|
+
)
|
|
382
|
+
|
|
383
|
+
yield* db
|
|
384
|
+
.upsert(
|
|
354
385
|
TABLES.QUEUE_JOB,
|
|
355
386
|
queueJobId,
|
|
356
387
|
compactRecord({
|
|
@@ -370,8 +401,12 @@ export function makeQueueJobService(db: SurrealDBService) {
|
|
|
370
401
|
lastError: undefined,
|
|
371
402
|
}),
|
|
372
403
|
QueueJobRowSchema,
|
|
373
|
-
)
|
|
374
|
-
|
|
404
|
+
)
|
|
405
|
+
.pipe(
|
|
406
|
+
Effect.mapError(
|
|
407
|
+
(cause) => new DatabaseError({ message: 'Failed to upsert completed queue job metadata', cause }),
|
|
408
|
+
),
|
|
409
|
+
)
|
|
375
410
|
}),
|
|
376
411
|
)
|
|
377
412
|
})
|
|
@@ -379,7 +414,14 @@ export function makeQueueJobService(db: SurrealDBService) {
|
|
|
379
414
|
|
|
380
415
|
markAttemptFailed(job: TrackedBullJobLike, error: unknown) {
|
|
381
416
|
return Effect.gen(function* () {
|
|
382
|
-
yield*
|
|
417
|
+
yield* db
|
|
418
|
+
.connect()
|
|
419
|
+
.pipe(
|
|
420
|
+
Effect.mapError(
|
|
421
|
+
(cause) =>
|
|
422
|
+
new DatabaseError({ message: 'Failed to connect before marking queue job attempt failed', cause }),
|
|
423
|
+
),
|
|
424
|
+
)
|
|
383
425
|
|
|
384
426
|
const attemptNumber = resolveAttemptNumber(job)
|
|
385
427
|
const bullmqJobId = yield* getBullmqJobId(job)
|
|
@@ -393,13 +435,20 @@ export function makeQueueJobService(db: SurrealDBService) {
|
|
|
393
435
|
|
|
394
436
|
yield* withPersistenceRetry(
|
|
395
437
|
Effect.gen(function* () {
|
|
396
|
-
const existingAttempt = yield*
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
438
|
+
const existingAttempt = yield* db
|
|
439
|
+
.findOne(TABLES.QUEUE_JOB_ATTEMPT, { id: attemptId }, QueueJobAttemptRowSchema)
|
|
440
|
+
.pipe(
|
|
441
|
+
Effect.mapError(
|
|
442
|
+
(cause) =>
|
|
443
|
+
new DatabaseError({
|
|
444
|
+
message: 'Failed to load existing queue job attempt before failure update',
|
|
445
|
+
cause,
|
|
446
|
+
}),
|
|
447
|
+
),
|
|
448
|
+
)
|
|
449
|
+
|
|
450
|
+
yield* db
|
|
451
|
+
.upsert(
|
|
403
452
|
TABLES.QUEUE_JOB_ATTEMPT,
|
|
404
453
|
attemptId,
|
|
405
454
|
compactRecord({
|
|
@@ -414,11 +463,16 @@ export function makeQueueJobService(db: SurrealDBService) {
|
|
|
414
463
|
: 0,
|
|
415
464
|
}),
|
|
416
465
|
QueueJobAttemptRowSchema,
|
|
417
|
-
)
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
466
|
+
)
|
|
467
|
+
.pipe(
|
|
468
|
+
Effect.mapError(
|
|
469
|
+
(cause) =>
|
|
470
|
+
new DatabaseError({ message: 'Failed to upsert failed queue job attempt metadata', cause }),
|
|
471
|
+
),
|
|
472
|
+
)
|
|
473
|
+
|
|
474
|
+
yield* db
|
|
475
|
+
.upsert(
|
|
422
476
|
TABLES.QUEUE_JOB,
|
|
423
477
|
queueJobId,
|
|
424
478
|
compactRecord({
|
|
@@ -437,8 +491,12 @@ export function makeQueueJobService(db: SurrealDBService) {
|
|
|
437
491
|
lastError: normalizedError,
|
|
438
492
|
}),
|
|
439
493
|
QueueJobRowSchema,
|
|
440
|
-
)
|
|
441
|
-
|
|
494
|
+
)
|
|
495
|
+
.pipe(
|
|
496
|
+
Effect.mapError(
|
|
497
|
+
(cause) => new DatabaseError({ message: 'Failed to upsert failed queue job metadata', cause }),
|
|
498
|
+
),
|
|
499
|
+
)
|
|
442
500
|
}),
|
|
443
501
|
)
|
|
444
502
|
})
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import { Context, Effect, Layer } from 'effect'
|
|
2
2
|
|
|
3
|
+
import { AiGatewayModelsTag } from '../ai-gateway/ai-gateway'
|
|
4
|
+
import type { AiGatewayModels } from '../ai-gateway/ai-gateway'
|
|
3
5
|
import type { ResolvedAgentConfig } from '../config/agent-defaults'
|
|
4
6
|
import { ServiceError } from '../effect/errors'
|
|
5
|
-
import { makeEffectTryPromiseWithMessage } from '../effect/helpers'
|
|
6
7
|
import { AgentConfigServiceTag } from '../effect/services'
|
|
7
8
|
import type { HelperModelRuntime } from '../runtime/helper-model'
|
|
8
9
|
import { HelperModelTag } from '../runtime/helper-model'
|
|
9
10
|
import { normalizeTitle } from '../runtime/title-helpers'
|
|
10
11
|
import {
|
|
11
|
-
|
|
12
|
+
makeRecentActivityTitleRefinerAgentFactory,
|
|
12
13
|
RECENT_ACTIVITY_TITLE_REFINER_PROMPT,
|
|
13
14
|
} from '../system-agents/recent-activity-title-refiner.agent'
|
|
14
15
|
import type { makeRecentActivityService } from './recent-activity.service'
|
|
@@ -16,10 +17,6 @@ import { RecentActivityServiceTag } from './recent-activity.service'
|
|
|
16
17
|
|
|
17
18
|
const RECENT_ACTIVITY_TITLE_TIMEOUT_MS = 60_000
|
|
18
19
|
|
|
19
|
-
const tryRecentActivityTitlePromise = makeEffectTryPromiseWithMessage(
|
|
20
|
-
(message, cause) => new ServiceError({ message, cause }),
|
|
21
|
-
)
|
|
22
|
-
|
|
23
20
|
function buildRefinementPromptInput(
|
|
24
21
|
candidate: {
|
|
25
22
|
id: string
|
|
@@ -48,9 +45,11 @@ function buildRefinementPromptInput(
|
|
|
48
45
|
|
|
49
46
|
export function makeRecentActivityTitleService(
|
|
50
47
|
agentConfig: ResolvedAgentConfig,
|
|
48
|
+
aiGatewayModels: AiGatewayModels,
|
|
51
49
|
recentActivityService: ReturnType<typeof makeRecentActivityService>,
|
|
52
50
|
helperModelRuntime: HelperModelRuntime,
|
|
53
51
|
) {
|
|
52
|
+
const refinerAgentFactory = makeRecentActivityTitleRefinerAgentFactory(aiGatewayModels, agentConfig)
|
|
54
53
|
const refineRecentActivityTitle = (activityId: string) =>
|
|
55
54
|
Effect.gen(function* () {
|
|
56
55
|
const candidate = yield* recentActivityService.getRefinementCandidate(activityId)
|
|
@@ -64,17 +63,18 @@ export function makeRecentActivityTitleService(
|
|
|
64
63
|
}
|
|
65
64
|
|
|
66
65
|
const refinedTitle = normalizeTitle(
|
|
67
|
-
yield*
|
|
68
|
-
() =>
|
|
66
|
+
yield* Effect.tryPromise({
|
|
67
|
+
try: () =>
|
|
69
68
|
helperModelRuntime.generateHelperText({
|
|
70
69
|
tag: 'recent-activity-title-refinement',
|
|
71
|
-
createAgent:
|
|
70
|
+
createAgent: refinerAgentFactory,
|
|
72
71
|
defaultSystemPrompt: RECENT_ACTIVITY_TITLE_REFINER_PROMPT,
|
|
73
72
|
timeoutMs: RECENT_ACTIVITY_TITLE_TIMEOUT_MS,
|
|
74
73
|
messages: [{ role: 'user', content: promptInput }],
|
|
75
74
|
}),
|
|
76
|
-
|
|
77
|
-
|
|
75
|
+
catch: (cause) =>
|
|
76
|
+
new ServiceError({ message: 'Failed to generate recent activity title refinement.', cause }),
|
|
77
|
+
}),
|
|
78
78
|
)
|
|
79
79
|
if (
|
|
80
80
|
!recentActivityService.isAgentTitleUseful({
|
|
@@ -101,8 +101,9 @@ export const RecentActivityTitleServiceLive = Layer.effect(
|
|
|
101
101
|
RecentActivityTitleServiceTag,
|
|
102
102
|
Effect.gen(function* () {
|
|
103
103
|
const agentConfig = yield* AgentConfigServiceTag
|
|
104
|
+
const aiGatewayModels = yield* AiGatewayModelsTag
|
|
104
105
|
const recentActivityService = yield* RecentActivityServiceTag
|
|
105
106
|
const helperModelRuntime = yield* HelperModelTag
|
|
106
|
-
return makeRecentActivityTitleService(agentConfig, recentActivityService, helperModelRuntime)
|
|
107
|
+
return makeRecentActivityTitleService(agentConfig, aiGatewayModels, recentActivityService, helperModelRuntime)
|
|
107
108
|
}),
|
|
108
109
|
)
|
|
@@ -22,6 +22,7 @@ import type { RecordIdInput, RecordIdRef } from '../db/record-id'
|
|
|
22
22
|
import type { SurrealDBService } from '../db/service'
|
|
23
23
|
import { TABLES } from '../db/tables'
|
|
24
24
|
import { DatabaseServiceTag } from '../effect/services'
|
|
25
|
+
import { toValidationError } from '../effect/zod'
|
|
25
26
|
import { createDeterministicRecordId } from '../utils/crypto'
|
|
26
27
|
import { nowDate, toIsoDateTimeString, toOptionalIsoDateTimeString, unsafeDateFrom } from '../utils/date-time'
|
|
27
28
|
import { compactRecord, compactWhitespace, truncateText } from '../utils/string'
|
|
@@ -156,7 +157,11 @@ export function makeRecentActivityService(db: SurrealDBService) {
|
|
|
156
157
|
event: RecentActivityEventInput
|
|
157
158
|
}) {
|
|
158
159
|
return Effect.gen(function* () {
|
|
159
|
-
const
|
|
160
|
+
const parsedInput = yield* Effect.try({
|
|
161
|
+
try: () => RecentActivityEventInputSchema.parse(params.event),
|
|
162
|
+
catch: (cause) => toValidationError(cause, 'Failed to validate recent activity event.'),
|
|
163
|
+
})
|
|
164
|
+
const parsedEvent = sanitizeEvent(parsedInput)
|
|
160
165
|
const orgIdString = recordIdToString(params.orgId, TABLES.ORGANIZATION)
|
|
161
166
|
const userIdString = recordIdToString(params.userId, TABLES.USER)
|
|
162
167
|
const eventRecordId = createDeterministicRecordId(
|
|
@@ -2,21 +2,16 @@ import { normalizeMessageBatch } from '@lota-sdk/shared'
|
|
|
2
2
|
import { Context, Effect, Layer, Schema } from 'effect'
|
|
3
3
|
import { z } from 'zod'
|
|
4
4
|
|
|
5
|
-
import { makeEffectTryPromiseWithMessage } from '../effect/helpers'
|
|
6
5
|
import { RedisServiceTag, RuntimeConfigServiceTag } from '../effect/services'
|
|
7
6
|
import type { RedisConnectionManager } from '../redis/connection'
|
|
8
7
|
import type { ResolvedLotaRuntimeConfig } from '../runtime/runtime-config'
|
|
9
8
|
import type { LotaRuntimeBackgroundCursor, LotaRuntimeBackgroundCursorKind } from '../runtime/runtime-extensions'
|
|
10
9
|
|
|
11
10
|
export class SocialChatHistoryError extends Schema.TaggedErrorClass<SocialChatHistoryError>()(
|
|
12
|
-
'SocialChatHistoryError',
|
|
11
|
+
'@lota-sdk/core/SocialChatHistoryError',
|
|
13
12
|
{ message: Schema.String, cause: Schema.optional(Schema.Defect) },
|
|
14
13
|
) {}
|
|
15
14
|
|
|
16
|
-
const tryRedisPromise = makeEffectTryPromiseWithMessage(
|
|
17
|
-
(message, cause) => new SocialChatHistoryError({ message, cause }),
|
|
18
|
-
)
|
|
19
|
-
|
|
20
15
|
const DEFAULT_SOCIAL_CHAT_HISTORY_PREFIX = 'lota:social:history'
|
|
21
16
|
|
|
22
17
|
const SocialChatMessageRoleSchema = z.enum(['user', 'assistant'])
|
|
@@ -157,20 +152,26 @@ export function makeSocialChatHistoryService(
|
|
|
157
152
|
multi.zadd(workspaceIndexKey(message.workspaceId), score, storageKey)
|
|
158
153
|
}
|
|
159
154
|
|
|
160
|
-
yield*
|
|
155
|
+
yield* Effect.tryPromise({
|
|
156
|
+
try: () => multi.exec(),
|
|
157
|
+
catch: (cause) => new SocialChatHistoryError({ message: 'Failed to upsert social chat messages.', cause }),
|
|
158
|
+
})
|
|
161
159
|
return normalizedMessages
|
|
162
160
|
})
|
|
163
161
|
|
|
164
162
|
const listThreadMessagesEffect = (params: { workspaceId: string; threadId: string }) =>
|
|
165
163
|
Effect.gen(function* () {
|
|
166
164
|
const conn = redis.getConnection()
|
|
167
|
-
const storageKeys = yield*
|
|
168
|
-
() => conn.zrange(threadIndexKey(params.workspaceId, params.threadId), 0, -1),
|
|
169
|
-
'Failed to list thread message keys.',
|
|
170
|
-
)
|
|
165
|
+
const storageKeys = yield* Effect.tryPromise({
|
|
166
|
+
try: () => conn.zrange(threadIndexKey(params.workspaceId, params.threadId), 0, -1),
|
|
167
|
+
catch: (cause) => new SocialChatHistoryError({ message: 'Failed to list thread message keys.', cause }),
|
|
168
|
+
})
|
|
171
169
|
if (storageKeys.length === 0) return [] as SocialChatHistoryMessage[]
|
|
172
170
|
|
|
173
|
-
const storedValues = yield*
|
|
171
|
+
const storedValues = yield* Effect.tryPromise({
|
|
172
|
+
try: () => conn.mget(storageKeys),
|
|
173
|
+
catch: (cause) => new SocialChatHistoryError({ message: 'Failed to read thread messages.', cause }),
|
|
174
|
+
})
|
|
174
175
|
const parsedMessages = yield* Effect.forEach(storedValues, parseStoredMessageEffect)
|
|
175
176
|
return parsedMessages
|
|
176
177
|
.filter((message): message is SocialChatHistoryMessage => message !== null)
|
|
@@ -188,16 +189,19 @@ export function makeSocialChatHistoryService(
|
|
|
188
189
|
const scoreStart =
|
|
189
190
|
params.cursor?.createdAt.getTime() ??
|
|
190
191
|
(params.onboardingCutoff ? params.onboardingCutoff.getTime() : Number.NEGATIVE_INFINITY)
|
|
191
|
-
const storageKeys = yield*
|
|
192
|
-
() =>
|
|
192
|
+
const storageKeys = yield* Effect.tryPromise({
|
|
193
|
+
try: () =>
|
|
193
194
|
params.cursor || params.onboardingCutoff
|
|
194
195
|
? conn.zrangebyscore(indexKey, scoreStart, '+inf')
|
|
195
196
|
: conn.zrange(indexKey, 0, -1),
|
|
196
|
-
'Failed to list workspace message keys.',
|
|
197
|
-
)
|
|
197
|
+
catch: (cause) => new SocialChatHistoryError({ message: 'Failed to list workspace message keys.', cause }),
|
|
198
|
+
})
|
|
198
199
|
if (storageKeys.length === 0) return [] as SocialChatHistoryMessage[]
|
|
199
200
|
|
|
200
|
-
const storedValues = yield*
|
|
201
|
+
const storedValues = yield* Effect.tryPromise({
|
|
202
|
+
try: () => conn.mget(storageKeys),
|
|
203
|
+
catch: (cause) => new SocialChatHistoryError({ message: 'Failed to read workspace messages.', cause }),
|
|
204
|
+
})
|
|
201
205
|
const parsedMessages = yield* Effect.forEach(storedValues, parseStoredMessageEffect)
|
|
202
206
|
return parsedMessages
|
|
203
207
|
.filter((message): message is SocialChatHistoryMessage => message !== null)
|
|
@@ -225,20 +229,20 @@ export function makeSocialChatHistoryService(
|
|
|
225
229
|
}).pipe(Effect.map((messages) => messages.length > 0))
|
|
226
230
|
|
|
227
231
|
const getBackgroundCursorEffect = (kind: LotaRuntimeBackgroundCursorKind, workspaceId: string) =>
|
|
228
|
-
|
|
229
|
-
() => redis.getConnection().get(cursorKey(kind, workspaceId)),
|
|
230
|
-
'Failed to read background cursor.',
|
|
231
|
-
).pipe(Effect.flatMap((value) => parseCursorEffect(value)))
|
|
232
|
+
Effect.tryPromise({
|
|
233
|
+
try: () => redis.getConnection().get(cursorKey(kind, workspaceId)),
|
|
234
|
+
catch: (cause) => new SocialChatHistoryError({ message: 'Failed to read background cursor.', cause }),
|
|
235
|
+
}).pipe(Effect.flatMap((value) => parseCursorEffect(value)))
|
|
232
236
|
|
|
233
237
|
const setBackgroundCursorEffect = (
|
|
234
238
|
kind: LotaRuntimeBackgroundCursorKind,
|
|
235
239
|
workspaceId: string,
|
|
236
240
|
cursor: LotaRuntimeBackgroundCursor,
|
|
237
241
|
) =>
|
|
238
|
-
|
|
239
|
-
() => redis.getConnection().set(cursorKey(kind, workspaceId), serializeCursor(cursor)),
|
|
240
|
-
'Failed to write background cursor.',
|
|
241
|
-
).pipe(Effect.asVoid)
|
|
242
|
+
Effect.tryPromise({
|
|
243
|
+
try: () => redis.getConnection().set(cursorKey(kind, workspaceId), serializeCursor(cursor)),
|
|
244
|
+
catch: (cause) => new SocialChatHistoryError({ message: 'Failed to write background cursor.', cause }),
|
|
245
|
+
}).pipe(Effect.asVoid)
|
|
242
246
|
|
|
243
247
|
return {
|
|
244
248
|
upsertMessages: upsertMessagesEffect,
|
|
@@ -2,12 +2,7 @@ import type { OwnershipDispatchContext, PlanNodeResult, PlanNodeSpec, SystemPlan
|
|
|
2
2
|
import { Context, Effect, Layer } from 'effect'
|
|
3
3
|
|
|
4
4
|
import { BadRequestError, ServiceError } from '../effect/errors'
|
|
5
|
-
import { makeEffectTryPromiseWithMessage } from '../effect/helpers'
|
|
6
5
|
import { RuntimeConfigServiceTag } from '../effect/services'
|
|
7
|
-
|
|
8
|
-
const trySystemExecutorPromise = makeEffectTryPromiseWithMessage(
|
|
9
|
-
(message, cause) => new ServiceError({ message, cause }),
|
|
10
|
-
)
|
|
11
6
|
import type { SystemNodeExecutor, PluginNodeExecutionParams } from '../runtime/plugin-types'
|
|
12
7
|
import type { ResolvedLotaRuntimeConfig } from '../runtime/runtime-config'
|
|
13
8
|
import type { PlanValidationIssueInput } from './plan/plan-validator.service'
|
|
@@ -110,8 +105,8 @@ export function makeSystemExecutorService(config: ResolvedLotaRuntimeConfig) {
|
|
|
110
105
|
})
|
|
111
106
|
}
|
|
112
107
|
|
|
113
|
-
return yield*
|
|
114
|
-
() =>
|
|
108
|
+
return yield* Effect.tryPromise({
|
|
109
|
+
try: () =>
|
|
115
110
|
executor.executeNode(
|
|
116
111
|
buildSystemExecutionParams({
|
|
117
112
|
owner,
|
|
@@ -120,8 +115,9 @@ export function makeSystemExecutorService(config: ResolvedLotaRuntimeConfig) {
|
|
|
120
115
|
context: params.context,
|
|
121
116
|
}),
|
|
122
117
|
),
|
|
123
|
-
|
|
124
|
-
|
|
118
|
+
catch: (cause) =>
|
|
119
|
+
new ServiceError({ message: `System executor "${owner.ref}.${owner.operation}" failed.`, cause }),
|
|
120
|
+
}).pipe(Effect.withSpan('SystemExecutor.invoke'))
|
|
125
121
|
}),
|
|
126
122
|
}
|
|
127
123
|
}
|
|
@@ -6,7 +6,7 @@ import type { RecordIdRef } from '../../db/record-id'
|
|
|
6
6
|
import { ensureRecordId, recordIdToString } from '../../db/record-id'
|
|
7
7
|
import type { SurrealDBService } from '../../db/service'
|
|
8
8
|
import { TABLES } from '../../db/tables'
|
|
9
|
-
import { ActiveThreadRunConflictError, DatabaseError, RedisError } from '../../effect/errors'
|
|
9
|
+
import { ERROR_TAGS, ActiveThreadRunConflictError, DatabaseError, RedisError } from '../../effect/errors'
|
|
10
10
|
import type { RedisConnectionManager } from '../../redis/connection'
|
|
11
11
|
import { withLeaseLock } from '../../redis/redis-lease-lock'
|
|
12
12
|
import type { ThreadRecordStore } from './thread-record-store'
|
|
@@ -176,7 +176,7 @@ export function createThreadActiveRunHelpers(deps: {
|
|
|
176
176
|
},
|
|
177
177
|
fn,
|
|
178
178
|
).pipe(
|
|
179
|
-
Effect.catchTag(
|
|
179
|
+
Effect.catchTag(ERROR_TAGS.LockAcquisitionError, () =>
|
|
180
180
|
Effect.gen(function* () {
|
|
181
181
|
const activeRunId = (yield* Effect.catch(getActiveRunIdEffect(threadId), () => Effect.succeed(null))) ?? ''
|
|
182
182
|
return yield* new ActiveThreadRunConflictError({
|