@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
|
@@ -30,8 +30,8 @@ import { ensureRecordId, recordIdToString } from '../db/record-id'
|
|
|
30
30
|
import type { RecordIdInput } from '../db/record-id'
|
|
31
31
|
import type { SurrealDBService } from '../db/service'
|
|
32
32
|
import { TABLES } from '../db/tables'
|
|
33
|
-
import { makeEffectTryPromiseWithMessage } from '../effect/helpers'
|
|
34
33
|
import { DatabaseServiceTag, RuntimeConfigServiceTag } from '../effect/services'
|
|
34
|
+
import { toValidationError } from '../effect/zod'
|
|
35
35
|
import type { AutonomousJobQueuePayload, AutonomousJobQueueRuntime } from '../queues/autonomous-job.queue'
|
|
36
36
|
import { LotaQueuesServiceTag } from '../queues/queues.service'
|
|
37
37
|
import type { ResolvedLotaRuntimeConfig } from '../runtime/runtime-config'
|
|
@@ -56,13 +56,13 @@ import { ThreadServiceTag } from './thread/thread.service'
|
|
|
56
56
|
const AUTONOMOUS_JOB_QUEUE_NAME = 'autonomous-job'
|
|
57
57
|
|
|
58
58
|
class AutonomousJobServiceError extends Schema.TaggedErrorClass<AutonomousJobServiceError>()(
|
|
59
|
-
'AutonomousJobServiceError',
|
|
59
|
+
'@lota-sdk/core/AutonomousJobServiceError',
|
|
60
60
|
{ message: Schema.String, cause: Schema.optional(Schema.Defect) },
|
|
61
61
|
) {}
|
|
62
62
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
63
|
+
function toAutonomousJobServiceError(message: string, cause: unknown): AutonomousJobServiceError {
|
|
64
|
+
return new AutonomousJobServiceError({ message, cause })
|
|
65
|
+
}
|
|
66
66
|
|
|
67
67
|
function buildAutonomousManualJobId(autonomousJobId: string): string {
|
|
68
68
|
return `autonomous-manual-${encodeBullmqId(autonomousJobId)}-${nowEpochMillis()}`
|
|
@@ -221,7 +221,10 @@ function maybeNotifyEffect(
|
|
|
221
221
|
return Effect.void
|
|
222
222
|
}
|
|
223
223
|
|
|
224
|
-
return
|
|
224
|
+
return Effect.tryPromise({
|
|
225
|
+
try: () => notificationService.notify(params),
|
|
226
|
+
catch: (cause) => toAutonomousJobServiceError('Failed to send autonomous job notification.', cause),
|
|
227
|
+
})
|
|
225
228
|
}
|
|
226
229
|
|
|
227
230
|
function buildSyntheticUserMessage(prompt: string): ChatMessage {
|
|
@@ -243,22 +246,20 @@ function createRunRowEffect(
|
|
|
243
246
|
},
|
|
244
247
|
): Effect.Effect<AutonomousJobRunRow, AutonomousJobServiceError> {
|
|
245
248
|
const runId = new RecordId(TABLES.AUTONOMOUS_JOB_RUN, Bun.randomUUIDv7())
|
|
246
|
-
return
|
|
247
|
-
(
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
'Failed to create autonomous job run.',
|
|
261
|
-
)
|
|
249
|
+
return deps.db
|
|
250
|
+
.createWithId(
|
|
251
|
+
TABLES.AUTONOMOUS_JOB_RUN,
|
|
252
|
+
runId,
|
|
253
|
+
compactRecord({
|
|
254
|
+
autonomousJobId: ensureRecordId(params.autonomousJobId, TABLES.AUTONOMOUS_JOB),
|
|
255
|
+
threadId: ensureRecordId(params.threadId, TABLES.THREAD),
|
|
256
|
+
queueJobId: params.queueJobId ? ensureRecordId(params.queueJobId, TABLES.QUEUE_JOB) : undefined,
|
|
257
|
+
status: params.status ?? 'queued',
|
|
258
|
+
assistantMessageIds: [],
|
|
259
|
+
}),
|
|
260
|
+
AutonomousJobRunRowSchema,
|
|
261
|
+
)
|
|
262
|
+
.pipe(Effect.mapError((cause) => toAutonomousJobServiceError('Failed to create autonomous job run.', cause)))
|
|
262
263
|
}
|
|
263
264
|
|
|
264
265
|
function getRowEffect(
|
|
@@ -266,16 +267,14 @@ function getRowEffect(
|
|
|
266
267
|
jobId: RecordIdInput,
|
|
267
268
|
): Effect.Effect<AutonomousJobRow, AutonomousJobServiceError> {
|
|
268
269
|
return Effect.gen(function* () {
|
|
269
|
-
yield*
|
|
270
|
-
|
|
271
|
-
(
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
'Failed to load autonomous job.',
|
|
278
|
-
)
|
|
270
|
+
yield* deps.db
|
|
271
|
+
.connect()
|
|
272
|
+
.pipe(
|
|
273
|
+
Effect.mapError((cause) => toAutonomousJobServiceError('Failed to connect to autonomous job database.', cause)),
|
|
274
|
+
)
|
|
275
|
+
const row = yield* deps.db
|
|
276
|
+
.findOne(TABLES.AUTONOMOUS_JOB, { id: ensureRecordId(jobId, TABLES.AUTONOMOUS_JOB) }, AutonomousJobRowSchema)
|
|
277
|
+
.pipe(Effect.mapError((cause) => toAutonomousJobServiceError('Failed to load autonomous job.', cause)))
|
|
279
278
|
if (!row) {
|
|
280
279
|
return yield* new AutonomousJobServiceError({
|
|
281
280
|
message: `Autonomous job not found: ${recordIdToString(jobId, TABLES.AUTONOMOUS_JOB)}`,
|
|
@@ -290,16 +289,18 @@ function getRunRowEffect(
|
|
|
290
289
|
runId: RecordIdInput,
|
|
291
290
|
): Effect.Effect<AutonomousJobRunRow, AutonomousJobServiceError> {
|
|
292
291
|
return Effect.gen(function* () {
|
|
293
|
-
yield*
|
|
294
|
-
|
|
295
|
-
(
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
292
|
+
yield* deps.db
|
|
293
|
+
.connect()
|
|
294
|
+
.pipe(
|
|
295
|
+
Effect.mapError((cause) => toAutonomousJobServiceError('Failed to connect to autonomous job database.', cause)),
|
|
296
|
+
)
|
|
297
|
+
const row = yield* deps.db
|
|
298
|
+
.findOne(
|
|
299
|
+
TABLES.AUTONOMOUS_JOB_RUN,
|
|
300
|
+
{ id: ensureRecordId(runId, TABLES.AUTONOMOUS_JOB_RUN) },
|
|
301
|
+
AutonomousJobRunRowSchema,
|
|
302
|
+
)
|
|
303
|
+
.pipe(Effect.mapError((cause) => toAutonomousJobServiceError('Failed to load autonomous job run.', cause)))
|
|
303
304
|
if (!row) {
|
|
304
305
|
return yield* new AutonomousJobServiceError({
|
|
305
306
|
message: `Autonomous job run not found: ${recordIdToString(runId, TABLES.AUTONOMOUS_JOB_RUN)}`,
|
|
@@ -314,20 +315,20 @@ function unscheduleRowEffect(
|
|
|
314
315
|
row: AutonomousJobRow,
|
|
315
316
|
): Effect.Effect<void, AutonomousJobServiceError> {
|
|
316
317
|
if (row.schedule.kind === 'at') {
|
|
317
|
-
return Effect.
|
|
318
|
-
|
|
319
|
-
() => deps.autonomousJobQueue.removeAutonomousAtJob(recordIdToString(row.id, TABLES.AUTONOMOUS_JOB)),
|
|
320
|
-
'Failed to remove autonomous at-job.',
|
|
321
|
-
)
|
|
322
|
-
|
|
318
|
+
return Effect.asVoid(
|
|
319
|
+
Effect.tryPromise({
|
|
320
|
+
try: () => deps.autonomousJobQueue.removeAutonomousAtJob(recordIdToString(row.id, TABLES.AUTONOMOUS_JOB)),
|
|
321
|
+
catch: (cause) => toAutonomousJobServiceError('Failed to remove autonomous at-job.', cause),
|
|
322
|
+
}),
|
|
323
|
+
)
|
|
323
324
|
}
|
|
324
325
|
|
|
325
|
-
return Effect.
|
|
326
|
-
|
|
327
|
-
() => deps.autonomousJobQueue.removeAutonomousJobScheduler(recordIdToString(row.id, TABLES.AUTONOMOUS_JOB)),
|
|
328
|
-
'Failed to remove autonomous job scheduler.',
|
|
329
|
-
)
|
|
330
|
-
|
|
326
|
+
return Effect.asVoid(
|
|
327
|
+
Effect.tryPromise({
|
|
328
|
+
try: () => deps.autonomousJobQueue.removeAutonomousJobScheduler(recordIdToString(row.id, TABLES.AUTONOMOUS_JOB)),
|
|
329
|
+
catch: (cause) => toAutonomousJobServiceError('Failed to remove autonomous job scheduler.', cause),
|
|
330
|
+
}),
|
|
331
|
+
)
|
|
331
332
|
}
|
|
332
333
|
|
|
333
334
|
function findRecoverableRunRowEffect(
|
|
@@ -335,24 +336,23 @@ function findRecoverableRunRowEffect(
|
|
|
335
336
|
autonomousJobId: RecordIdInput,
|
|
336
337
|
): Effect.Effect<AutonomousJobRunRow | null, AutonomousJobServiceError> {
|
|
337
338
|
return Effect.gen(function* () {
|
|
338
|
-
const rows = yield*
|
|
339
|
-
(
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
`SELECT * FROM ${TABLES.AUTONOMOUS_JOB_RUN}
|
|
339
|
+
const rows = yield* deps.db
|
|
340
|
+
.queryMany(
|
|
341
|
+
new BoundQuery(
|
|
342
|
+
`SELECT * FROM ${TABLES.AUTONOMOUS_JOB_RUN}
|
|
343
343
|
WHERE autonomousJobId = $autonomousJobId
|
|
344
344
|
AND status IN $statuses
|
|
345
345
|
ORDER BY createdAt DESC
|
|
346
346
|
LIMIT 1`,
|
|
347
|
-
|
|
348
|
-
autonomousJobId: ensureRecordId(autonomousJobId, TABLES.AUTONOMOUS_JOB),
|
|
349
|
-
statuses: ['queued', 'running'],
|
|
350
|
-
},
|
|
351
|
-
),
|
|
352
|
-
AutonomousJobRunRowSchema,
|
|
347
|
+
{ autonomousJobId: ensureRecordId(autonomousJobId, TABLES.AUTONOMOUS_JOB), statuses: ['queued', 'running'] },
|
|
353
348
|
),
|
|
354
|
-
|
|
355
|
-
|
|
349
|
+
AutonomousJobRunRowSchema,
|
|
350
|
+
)
|
|
351
|
+
.pipe(
|
|
352
|
+
Effect.mapError((cause) =>
|
|
353
|
+
toAutonomousJobServiceError('Failed to find recoverable autonomous job run.', cause),
|
|
354
|
+
),
|
|
355
|
+
)
|
|
356
356
|
return rows[0] ?? null
|
|
357
357
|
})
|
|
358
358
|
}
|
|
@@ -378,8 +378,8 @@ function scheduleRowEffect(
|
|
|
378
378
|
})
|
|
379
379
|
: createRunRowEffect(deps, { autonomousJobId: row.id, threadId: row.threadId })
|
|
380
380
|
|
|
381
|
-
const enqueueResult = yield*
|
|
382
|
-
() =>
|
|
381
|
+
const enqueueResult = yield* Effect.tryPromise({
|
|
382
|
+
try: () =>
|
|
383
383
|
deps.autonomousJobQueue.enqueueAutonomousJobRun({
|
|
384
384
|
payload: {
|
|
385
385
|
autonomousJobId: jobId,
|
|
@@ -389,47 +389,53 @@ function scheduleRowEffect(
|
|
|
389
389
|
delayMs: Math.max(0, (nextRunAt ?? referenceTime).getTime() - referenceTime.getTime()),
|
|
390
390
|
jobId: buildAutonomousAtJobId(jobId),
|
|
391
391
|
}),
|
|
392
|
-
'Failed to enqueue autonomous job run.',
|
|
393
|
-
)
|
|
392
|
+
catch: (cause) => toAutonomousJobServiceError('Failed to enqueue autonomous job run.', cause),
|
|
393
|
+
})
|
|
394
394
|
|
|
395
395
|
const queueJobId = enqueueResult.queueJobId
|
|
396
396
|
if (queueJobId) {
|
|
397
|
-
yield*
|
|
398
|
-
(
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
397
|
+
yield* deps.db
|
|
398
|
+
.update(
|
|
399
|
+
TABLES.AUTONOMOUS_JOB_RUN,
|
|
400
|
+
queuedRun.id,
|
|
401
|
+
{ queueJobId: ensureRecordId(queueJobId, TABLES.QUEUE_JOB) },
|
|
402
|
+
AutonomousJobRunRowSchema,
|
|
403
|
+
)
|
|
404
|
+
.pipe(
|
|
405
|
+
Effect.mapError((cause) =>
|
|
406
|
+
toAutonomousJobServiceError('Failed to persist autonomous job run queue job id.', cause),
|
|
404
407
|
),
|
|
405
|
-
|
|
406
|
-
)
|
|
408
|
+
)
|
|
407
409
|
}
|
|
408
410
|
|
|
409
|
-
yield*
|
|
410
|
-
()
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
411
|
+
yield* deps.db
|
|
412
|
+
.update(TABLES.AUTONOMOUS_JOB, row.id, { nextRunAt: nextRunAt ?? undefined }, AutonomousJobRowSchema)
|
|
413
|
+
.pipe(
|
|
414
|
+
Effect.mapError((cause) =>
|
|
415
|
+
toAutonomousJobServiceError('Failed to update autonomous job next run time.', cause),
|
|
416
|
+
),
|
|
417
|
+
)
|
|
414
418
|
})
|
|
415
419
|
} else {
|
|
416
420
|
const recurringSchedule = row.schedule
|
|
417
421
|
return Effect.gen(function* () {
|
|
418
|
-
yield*
|
|
419
|
-
() =>
|
|
422
|
+
yield* Effect.tryPromise({
|
|
423
|
+
try: () =>
|
|
420
424
|
deps.autonomousJobQueue.upsertAutonomousJobScheduler({ autonomousJobId: jobId, schedule: recurringSchedule }),
|
|
421
|
-
'Failed to upsert autonomous job scheduler.',
|
|
422
|
-
)
|
|
425
|
+
catch: (cause) => toAutonomousJobServiceError('Failed to upsert autonomous job scheduler.', cause),
|
|
426
|
+
})
|
|
423
427
|
|
|
424
428
|
if (options.runImmediate ?? recurringSchedule.immediately) {
|
|
425
429
|
yield* runNowEffect(deps, jobId).pipe(Effect.asVoid)
|
|
426
430
|
}
|
|
427
431
|
|
|
428
|
-
yield*
|
|
429
|
-
()
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
432
|
+
yield* deps.db
|
|
433
|
+
.update(TABLES.AUTONOMOUS_JOB, row.id, { nextRunAt: nextRunAt ?? undefined }, AutonomousJobRowSchema)
|
|
434
|
+
.pipe(
|
|
435
|
+
Effect.mapError((cause) =>
|
|
436
|
+
toAutonomousJobServiceError('Failed to update autonomous job next run time.', cause),
|
|
437
|
+
),
|
|
438
|
+
)
|
|
433
439
|
})
|
|
434
440
|
}
|
|
435
441
|
}
|
|
@@ -439,8 +445,19 @@ function createEffect(
|
|
|
439
445
|
input: CreateAutonomousJobInput,
|
|
440
446
|
): Effect.Effect<AutonomousJob, AutonomousJobServiceError> {
|
|
441
447
|
return Effect.gen(function* () {
|
|
442
|
-
yield*
|
|
443
|
-
|
|
448
|
+
yield* deps.db
|
|
449
|
+
.connect()
|
|
450
|
+
.pipe(
|
|
451
|
+
Effect.mapError((cause) => toAutonomousJobServiceError('Failed to connect to autonomous job database.', cause)),
|
|
452
|
+
)
|
|
453
|
+
const parsed = yield* Effect.try({
|
|
454
|
+
try: () => CreateAutonomousJobInputSchema.parse(input),
|
|
455
|
+
catch: (cause) =>
|
|
456
|
+
new AutonomousJobServiceError({
|
|
457
|
+
message: 'Failed to validate CreateAutonomousJobInput.',
|
|
458
|
+
cause: toValidationError(cause),
|
|
459
|
+
}),
|
|
460
|
+
})
|
|
444
461
|
const organizationId = ensureRecordId(parsed.organizationId, TABLES.ORGANIZATION)
|
|
445
462
|
const ownerUserId = ensureRecordId(parsed.ownerUserId, TABLES.USER)
|
|
446
463
|
const thread = yield* deps.thread
|
|
@@ -453,29 +470,27 @@ function createEffect(
|
|
|
453
470
|
|
|
454
471
|
const jobId = new RecordId(TABLES.AUTONOMOUS_JOB, Bun.randomUUIDv7())
|
|
455
472
|
const nextRunAt = computeNextRunAt(parsed.schedule)
|
|
456
|
-
const created = yield*
|
|
457
|
-
(
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
'Failed to create autonomous job.',
|
|
478
|
-
)
|
|
473
|
+
const created = yield* deps.db
|
|
474
|
+
.createWithId(
|
|
475
|
+
TABLES.AUTONOMOUS_JOB,
|
|
476
|
+
jobId,
|
|
477
|
+
compactRecord({
|
|
478
|
+
organizationId,
|
|
479
|
+
ownerUserId,
|
|
480
|
+
ownerUserName: parsed.ownerUserName,
|
|
481
|
+
threadId: ensureRecordId(thread.id, TABLES.THREAD),
|
|
482
|
+
agentId: parsed.agentId,
|
|
483
|
+
title: parsed.title,
|
|
484
|
+
prompt: parsed.prompt,
|
|
485
|
+
schedule: parsed.schedule,
|
|
486
|
+
status: 'active',
|
|
487
|
+
autoPauseThreshold: parsed.autoPauseThreshold,
|
|
488
|
+
consecutiveErrorCount: 0,
|
|
489
|
+
nextRunAt: nextRunAt ?? undefined,
|
|
490
|
+
}),
|
|
491
|
+
AutonomousJobRowSchema,
|
|
492
|
+
)
|
|
493
|
+
.pipe(Effect.mapError((cause) => toAutonomousJobServiceError('Failed to create autonomous job.', cause)))
|
|
479
494
|
|
|
480
495
|
yield* scheduleRowEffect(deps, created)
|
|
481
496
|
const row = yield* getRowEffect(deps, created.id)
|
|
@@ -488,17 +503,19 @@ function recoverActiveJobsEffect(
|
|
|
488
503
|
now = nowDate(),
|
|
489
504
|
): Effect.Effect<void, AutonomousJobServiceError> {
|
|
490
505
|
return Effect.gen(function* () {
|
|
491
|
-
yield*
|
|
492
|
-
|
|
493
|
-
(
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
506
|
+
yield* deps.db
|
|
507
|
+
.connect()
|
|
508
|
+
.pipe(
|
|
509
|
+
Effect.mapError((cause) => toAutonomousJobServiceError('Failed to connect to autonomous job database.', cause)),
|
|
510
|
+
)
|
|
511
|
+
const activeRows = yield* deps.db
|
|
512
|
+
.queryMany(
|
|
513
|
+
new BoundQuery(`SELECT * FROM ${TABLES.AUTONOMOUS_JOB} WHERE status = $status ORDER BY createdAt ASC`, {
|
|
514
|
+
status: 'active',
|
|
515
|
+
}),
|
|
516
|
+
AutonomousJobRowSchema,
|
|
517
|
+
)
|
|
518
|
+
.pipe(Effect.mapError((cause) => toAutonomousJobServiceError('Failed to load active autonomous jobs.', cause)))
|
|
502
519
|
yield* Effect.forEach(activeRows, (row) =>
|
|
503
520
|
scheduleRowEffect(deps, row, { runImmediate: false, referenceTime: now, reusePendingAtRun: true }),
|
|
504
521
|
)
|
|
@@ -520,21 +537,23 @@ function listEffect(
|
|
|
520
537
|
params: { organizationId: RecordIdInput; ownerUserId?: RecordIdInput; status?: AutonomousJobStatus },
|
|
521
538
|
): Effect.Effect<AutonomousJob[], AutonomousJobServiceError> {
|
|
522
539
|
return Effect.gen(function* () {
|
|
523
|
-
yield*
|
|
524
|
-
|
|
525
|
-
(
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
),
|
|
536
|
-
|
|
537
|
-
|
|
540
|
+
yield* deps.db
|
|
541
|
+
.connect()
|
|
542
|
+
.pipe(
|
|
543
|
+
Effect.mapError((cause) => toAutonomousJobServiceError('Failed to connect to autonomous job database.', cause)),
|
|
544
|
+
)
|
|
545
|
+
const rows = yield* deps.db
|
|
546
|
+
.findMany(
|
|
547
|
+
TABLES.AUTONOMOUS_JOB,
|
|
548
|
+
compactRecord({
|
|
549
|
+
organizationId: ensureRecordId(params.organizationId, TABLES.ORGANIZATION),
|
|
550
|
+
ownerUserId: params.ownerUserId ? ensureRecordId(params.ownerUserId, TABLES.USER) : undefined,
|
|
551
|
+
status: params.status,
|
|
552
|
+
}),
|
|
553
|
+
AutonomousJobRowSchema,
|
|
554
|
+
{ orderBy: 'createdAt', orderDir: 'DESC' },
|
|
555
|
+
)
|
|
556
|
+
.pipe(Effect.mapError((cause) => toAutonomousJobServiceError('Failed to list autonomous jobs.', cause)))
|
|
538
557
|
return rows.map((row) => toPublicJob(row))
|
|
539
558
|
})
|
|
540
559
|
}
|
|
@@ -545,7 +564,14 @@ function updateEffect(
|
|
|
545
564
|
input: UpdateAutonomousJobInput,
|
|
546
565
|
): Effect.Effect<AutonomousJob, AutonomousJobServiceError> {
|
|
547
566
|
return Effect.gen(function* () {
|
|
548
|
-
const parsed =
|
|
567
|
+
const parsed = yield* Effect.try({
|
|
568
|
+
try: () => UpdateAutonomousJobInputSchema.parse(input),
|
|
569
|
+
catch: (cause) =>
|
|
570
|
+
new AutonomousJobServiceError({
|
|
571
|
+
message: 'Failed to validate UpdateAutonomousJobInput.',
|
|
572
|
+
cause: toValidationError(cause),
|
|
573
|
+
}),
|
|
574
|
+
})
|
|
549
575
|
const existing = yield* getRowEffect(deps, jobId)
|
|
550
576
|
yield* unscheduleRowEffect(deps, existing)
|
|
551
577
|
const nextTitle = parsed.title
|
|
@@ -562,22 +588,20 @@ function updateEffect(
|
|
|
562
588
|
}
|
|
563
589
|
|
|
564
590
|
const nextRunAt = computeNextRunAt(parsed.schedule ?? existing.schedule)
|
|
565
|
-
const updated = yield*
|
|
566
|
-
(
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
'Failed to update autonomous job.',
|
|
580
|
-
)
|
|
591
|
+
const updated = yield* deps.db
|
|
592
|
+
.update(
|
|
593
|
+
TABLES.AUTONOMOUS_JOB,
|
|
594
|
+
existing.id,
|
|
595
|
+
compactRecord({
|
|
596
|
+
title: parsed.title,
|
|
597
|
+
prompt: parsed.prompt,
|
|
598
|
+
schedule: parsed.schedule,
|
|
599
|
+
autoPauseThreshold: parsed.autoPauseThreshold,
|
|
600
|
+
nextRunAt: existing.status === 'active' ? (nextRunAt ?? undefined) : undefined,
|
|
601
|
+
}),
|
|
602
|
+
AutonomousJobRowSchema,
|
|
603
|
+
)
|
|
604
|
+
.pipe(Effect.mapError((cause) => toAutonomousJobServiceError('Failed to update autonomous job.', cause)))
|
|
581
605
|
if (!updated) {
|
|
582
606
|
return yield* new AutonomousJobServiceError({
|
|
583
607
|
message: `Failed to update autonomous job: ${recordIdToString(existing.id, TABLES.AUTONOMOUS_JOB)}`,
|
|
@@ -600,16 +624,9 @@ function pauseEffect(
|
|
|
600
624
|
return Effect.gen(function* () {
|
|
601
625
|
const row = yield* getRowEffect(deps, jobId)
|
|
602
626
|
yield* unscheduleRowEffect(deps, row)
|
|
603
|
-
yield*
|
|
604
|
-
()
|
|
605
|
-
|
|
606
|
-
TABLES.AUTONOMOUS_JOB,
|
|
607
|
-
row.id,
|
|
608
|
-
{ status: 'paused', nextRunAt: undefined },
|
|
609
|
-
AutonomousJobRowSchema,
|
|
610
|
-
),
|
|
611
|
-
'Failed to pause autonomous job.',
|
|
612
|
-
)
|
|
627
|
+
yield* deps.db
|
|
628
|
+
.update(TABLES.AUTONOMOUS_JOB, row.id, { status: 'paused', nextRunAt: undefined }, AutonomousJobRowSchema)
|
|
629
|
+
.pipe(Effect.mapError((cause) => toAutonomousJobServiceError('Failed to pause autonomous job.', cause)))
|
|
613
630
|
const updatedRow = yield* getRowEffect(deps, row.id)
|
|
614
631
|
return toPublicJob(updatedRow)
|
|
615
632
|
})
|
|
@@ -622,16 +639,14 @@ function resumeEffect(
|
|
|
622
639
|
return Effect.gen(function* () {
|
|
623
640
|
const row = yield* getRowEffect(deps, jobId)
|
|
624
641
|
const nextRunAt = computeNextRunAt(row.schedule)
|
|
625
|
-
const resumed = yield*
|
|
626
|
-
(
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
'Failed to resume autonomous job.',
|
|
634
|
-
)
|
|
642
|
+
const resumed = yield* deps.db
|
|
643
|
+
.update(
|
|
644
|
+
TABLES.AUTONOMOUS_JOB,
|
|
645
|
+
row.id,
|
|
646
|
+
{ status: 'active', nextRunAt: nextRunAt ?? undefined },
|
|
647
|
+
AutonomousJobRowSchema,
|
|
648
|
+
)
|
|
649
|
+
.pipe(Effect.mapError((cause) => toAutonomousJobServiceError('Failed to resume autonomous job.', cause)))
|
|
635
650
|
if (!resumed) {
|
|
636
651
|
return yield* new AutonomousJobServiceError({
|
|
637
652
|
message: `Failed to resume autonomous job: ${recordIdToString(row.id, TABLES.AUTONOMOUS_JOB)}`,
|
|
@@ -651,8 +666,8 @@ function runNowEffect(
|
|
|
651
666
|
const row = yield* getRowEffect(deps, jobId)
|
|
652
667
|
const queuedRun = yield* createRunRowEffect(deps, { autonomousJobId: row.id, threadId: row.threadId })
|
|
653
668
|
|
|
654
|
-
const enqueueResult = yield*
|
|
655
|
-
() =>
|
|
669
|
+
const enqueueResult = yield* Effect.tryPromise({
|
|
670
|
+
try: () =>
|
|
656
671
|
deps.autonomousJobQueue.enqueueAutonomousJobRun({
|
|
657
672
|
payload: {
|
|
658
673
|
autonomousJobId: recordIdToString(row.id, TABLES.AUTONOMOUS_JOB),
|
|
@@ -661,21 +676,23 @@ function runNowEffect(
|
|
|
661
676
|
},
|
|
662
677
|
jobId: buildAutonomousManualJobId(recordIdToString(row.id, TABLES.AUTONOMOUS_JOB)),
|
|
663
678
|
}),
|
|
664
|
-
'Failed to enqueue autonomous job run.',
|
|
665
|
-
)
|
|
679
|
+
catch: (cause) => toAutonomousJobServiceError('Failed to enqueue autonomous job run.', cause),
|
|
680
|
+
})
|
|
666
681
|
|
|
667
682
|
const queueJobId = enqueueResult.queueJobId
|
|
668
683
|
if (queueJobId) {
|
|
669
|
-
yield*
|
|
670
|
-
(
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
684
|
+
yield* deps.db
|
|
685
|
+
.update(
|
|
686
|
+
TABLES.AUTONOMOUS_JOB_RUN,
|
|
687
|
+
queuedRun.id,
|
|
688
|
+
{ queueJobId: ensureRecordId(queueJobId, TABLES.QUEUE_JOB) },
|
|
689
|
+
AutonomousJobRunRowSchema,
|
|
690
|
+
)
|
|
691
|
+
.pipe(
|
|
692
|
+
Effect.mapError((cause) =>
|
|
693
|
+
toAutonomousJobServiceError('Failed to persist autonomous job run queue job id.', cause),
|
|
676
694
|
),
|
|
677
|
-
|
|
678
|
-
)
|
|
695
|
+
)
|
|
679
696
|
}
|
|
680
697
|
|
|
681
698
|
const runRow = yield* getRunRowEffect(deps, queuedRun.id)
|
|
@@ -690,16 +707,9 @@ function cancelEffect(
|
|
|
690
707
|
return Effect.gen(function* () {
|
|
691
708
|
const row = yield* getRowEffect(deps, jobId)
|
|
692
709
|
yield* unscheduleRowEffect(deps, row)
|
|
693
|
-
yield*
|
|
694
|
-
()
|
|
695
|
-
|
|
696
|
-
TABLES.AUTONOMOUS_JOB,
|
|
697
|
-
row.id,
|
|
698
|
-
{ status: 'cancelled', nextRunAt: undefined },
|
|
699
|
-
AutonomousJobRowSchema,
|
|
700
|
-
),
|
|
701
|
-
'Failed to cancel autonomous job.',
|
|
702
|
-
)
|
|
710
|
+
yield* deps.db
|
|
711
|
+
.update(TABLES.AUTONOMOUS_JOB, row.id, { status: 'cancelled', nextRunAt: undefined }, AutonomousJobRowSchema)
|
|
712
|
+
.pipe(Effect.mapError((cause) => toAutonomousJobServiceError('Failed to cancel autonomous job.', cause)))
|
|
703
713
|
const updatedRow = yield* getRowEffect(deps, row.id)
|
|
704
714
|
return toPublicJob(updatedRow)
|
|
705
715
|
})
|
|
@@ -728,17 +738,19 @@ function listRunsEffect(
|
|
|
728
738
|
jobId: RecordIdInput,
|
|
729
739
|
): Effect.Effect<AutonomousJobRun[], AutonomousJobServiceError> {
|
|
730
740
|
return Effect.gen(function* () {
|
|
731
|
-
yield*
|
|
732
|
-
|
|
733
|
-
(
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
),
|
|
740
|
-
|
|
741
|
-
|
|
741
|
+
yield* deps.db
|
|
742
|
+
.connect()
|
|
743
|
+
.pipe(
|
|
744
|
+
Effect.mapError((cause) => toAutonomousJobServiceError('Failed to connect to autonomous job database.', cause)),
|
|
745
|
+
)
|
|
746
|
+
const rows = yield* deps.db
|
|
747
|
+
.findMany(
|
|
748
|
+
TABLES.AUTONOMOUS_JOB_RUN,
|
|
749
|
+
{ autonomousJobId: ensureRecordId(jobId, TABLES.AUTONOMOUS_JOB) },
|
|
750
|
+
AutonomousJobRunRowSchema,
|
|
751
|
+
{ orderBy: 'createdAt', orderDir: 'DESC' },
|
|
752
|
+
)
|
|
753
|
+
.pipe(Effect.mapError((cause) => toAutonomousJobServiceError('Failed to list autonomous job runs.', cause)))
|
|
742
754
|
return rows.map((row) => toPublicRun(row))
|
|
743
755
|
})
|
|
744
756
|
}
|
|
@@ -772,16 +784,16 @@ function markQueuedRunRunningEffect(
|
|
|
772
784
|
const startedAt = nowDate()
|
|
773
785
|
|
|
774
786
|
return Effect.gen(function* () {
|
|
775
|
-
const updatedRunRow = yield*
|
|
776
|
-
(
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
787
|
+
const updatedRunRow = yield* deps.db
|
|
788
|
+
.update(
|
|
789
|
+
TABLES.AUTONOMOUS_JOB_RUN,
|
|
790
|
+
runRow.id,
|
|
791
|
+
{ queueJobId: ensureRecordId(queueJobId, TABLES.QUEUE_JOB), status: 'running', startedAt },
|
|
792
|
+
AutonomousJobRunRowSchema,
|
|
793
|
+
)
|
|
794
|
+
.pipe(
|
|
795
|
+
Effect.mapError((cause) => toAutonomousJobServiceError('Failed to mark autonomous job run as running.', cause)),
|
|
796
|
+
)
|
|
785
797
|
|
|
786
798
|
return updatedRunRow ?? runRow
|
|
787
799
|
})
|
|
@@ -810,9 +822,9 @@ function completeQueuedRunEffect(
|
|
|
810
822
|
)
|
|
811
823
|
const completedAt = nowDate()
|
|
812
824
|
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
825
|
+
yield* Effect.asVoid(
|
|
826
|
+
deps.db
|
|
827
|
+
.update(
|
|
816
828
|
TABLES.AUTONOMOUS_JOB_RUN,
|
|
817
829
|
currentRunRow.id,
|
|
818
830
|
compactRecord({
|
|
@@ -825,9 +837,13 @@ function completeQueuedRunEffect(
|
|
|
825
837
|
completedAt,
|
|
826
838
|
}),
|
|
827
839
|
AutonomousJobRunRowSchema,
|
|
840
|
+
)
|
|
841
|
+
.pipe(
|
|
842
|
+
Effect.mapError((cause) =>
|
|
843
|
+
toAutonomousJobServiceError('Failed to update autonomous job run completion.', cause),
|
|
844
|
+
),
|
|
828
845
|
),
|
|
829
|
-
|
|
830
|
-
))
|
|
846
|
+
)
|
|
831
847
|
|
|
832
848
|
const nextRunAt =
|
|
833
849
|
job.data.trigger === 'scheduled' && currentJobRow.schedule.kind !== 'at'
|
|
@@ -836,25 +852,27 @@ function completeQueuedRunEffect(
|
|
|
836
852
|
const nextStatus =
|
|
837
853
|
currentJobRow.schedule.kind === 'at' && job.data.trigger === 'scheduled' ? 'completed' : currentJobRow.status
|
|
838
854
|
|
|
839
|
-
yield*
|
|
840
|
-
(
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
+
yield* deps.db
|
|
856
|
+
.update(
|
|
857
|
+
TABLES.AUTONOMOUS_JOB,
|
|
858
|
+
currentJobRow.id,
|
|
859
|
+
compactRecord({
|
|
860
|
+
status: nextStatus,
|
|
861
|
+
consecutiveErrorCount: 0,
|
|
862
|
+
lastRunStatus: runStatus,
|
|
863
|
+
lastRunAt: completedAt,
|
|
864
|
+
nextRunAt: nextStatus === 'active' ? (nextRunAt ?? undefined) : undefined,
|
|
865
|
+
linkedPlanSpecId: activePlan?.specId ? ensureRecordId(activePlan.specId, TABLES.PLAN_SPEC) : undefined,
|
|
866
|
+
linkedPlanRunId: activePlan?.runId ? ensureRecordId(activePlan.runId, TABLES.PLAN_RUN) : undefined,
|
|
867
|
+
lastError: undefined,
|
|
868
|
+
}),
|
|
869
|
+
AutonomousJobRowSchema,
|
|
870
|
+
)
|
|
871
|
+
.pipe(
|
|
872
|
+
Effect.mapError((cause) =>
|
|
873
|
+
toAutonomousJobServiceError('Failed to update autonomous job after successful run.', cause),
|
|
855
874
|
),
|
|
856
|
-
|
|
857
|
-
)
|
|
875
|
+
)
|
|
858
876
|
|
|
859
877
|
yield* maybeNotifyEffect(deps, {
|
|
860
878
|
organizationId: recordIdToString(currentJobRow.organizationId, TABLES.ORGANIZATION),
|
|
@@ -890,41 +908,43 @@ function failQueuedRunEffect(
|
|
|
890
908
|
const terminalOneShot = currentJobRow.schedule.kind === 'at' && job.data.trigger === 'scheduled'
|
|
891
909
|
const nextStatus: AutonomousJobStatus = terminalOneShot ? 'failed' : autoPause ? 'paused' : currentJobRow.status
|
|
892
910
|
|
|
893
|
-
yield*
|
|
894
|
-
(
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
911
|
+
yield* deps.db
|
|
912
|
+
.update(
|
|
913
|
+
TABLES.AUTONOMOUS_JOB_RUN,
|
|
914
|
+
currentRunRow.id,
|
|
915
|
+
{ status: 'failed', error: normalizedError, completedAt },
|
|
916
|
+
AutonomousJobRunRowSchema,
|
|
917
|
+
)
|
|
918
|
+
.pipe(
|
|
919
|
+
Effect.mapError((cause) => toAutonomousJobServiceError('Failed to persist failed autonomous job run.', cause)),
|
|
920
|
+
)
|
|
903
921
|
|
|
904
922
|
if (autoPause || terminalOneShot) {
|
|
905
923
|
yield* unscheduleRowEffect(deps, currentJobRow)
|
|
906
924
|
}
|
|
907
925
|
|
|
908
|
-
yield*
|
|
909
|
-
(
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
926
|
+
yield* deps.db
|
|
927
|
+
.update(
|
|
928
|
+
TABLES.AUTONOMOUS_JOB,
|
|
929
|
+
currentJobRow.id,
|
|
930
|
+
compactRecord({
|
|
931
|
+
status: nextStatus,
|
|
932
|
+
consecutiveErrorCount: nextConsecutiveErrorCount,
|
|
933
|
+
lastRunStatus: 'failed',
|
|
934
|
+
lastRunAt: completedAt,
|
|
935
|
+
nextRunAt:
|
|
936
|
+
nextStatus === 'active' && currentJobRow.schedule.kind !== 'at'
|
|
937
|
+
? (computeNextRunAt(currentJobRow.schedule, completedAt) ?? undefined)
|
|
938
|
+
: undefined,
|
|
939
|
+
lastError: normalizedError,
|
|
940
|
+
}),
|
|
941
|
+
AutonomousJobRowSchema,
|
|
942
|
+
)
|
|
943
|
+
.pipe(
|
|
944
|
+
Effect.mapError((cause) =>
|
|
945
|
+
toAutonomousJobServiceError('Failed to update autonomous job failure state.', cause),
|
|
925
946
|
),
|
|
926
|
-
|
|
927
|
-
)
|
|
947
|
+
)
|
|
928
948
|
|
|
929
949
|
yield* maybeNotifyEffect(deps, {
|
|
930
950
|
organizationId: recordIdToString(currentJobRow.organizationId, TABLES.ORGANIZATION),
|
|
@@ -946,13 +966,19 @@ function failQueuedRunEffect(
|
|
|
946
966
|
function executeQueuedRunEffect(
|
|
947
967
|
deps: AutonomousJobDeps,
|
|
948
968
|
job: Job<AutonomousJobQueuePayload>,
|
|
949
|
-
): Effect.Effect<{ status: string; summary?: string }, AutonomousJobServiceError,
|
|
969
|
+
): Effect.Effect<{ status: string; summary?: string }, AutonomousJobServiceError, never> {
|
|
950
970
|
return Effect.gen(function* () {
|
|
951
971
|
const autonomousJobRowRef = yield* Ref.make<AutonomousJobRow | null>(null)
|
|
952
972
|
const runRowRef = yield* Ref.make<AutonomousJobRunRow | null>(null)
|
|
953
973
|
|
|
954
974
|
return yield* Effect.gen(function* () {
|
|
955
|
-
yield*
|
|
975
|
+
yield* deps.db
|
|
976
|
+
.connect()
|
|
977
|
+
.pipe(
|
|
978
|
+
Effect.mapError((cause) =>
|
|
979
|
+
toAutonomousJobServiceError('Failed to connect to autonomous job database.', cause),
|
|
980
|
+
),
|
|
981
|
+
)
|
|
956
982
|
const currentJobRow = yield* getRowEffect(deps, job.data.autonomousJobId)
|
|
957
983
|
yield* Ref.set(autonomousJobRowRef, currentJobRow)
|
|
958
984
|
const { queueJobId, runRow: initialRunRow } = yield* getOrCreateQueuedRunRowEffect(deps, job, currentJobRow)
|
|
@@ -990,6 +1016,7 @@ function executeQueuedRunEffect(
|
|
|
990
1016
|
inputMessage,
|
|
991
1017
|
})
|
|
992
1018
|
.pipe(
|
|
1019
|
+
deps.provideCurrentContext,
|
|
993
1020
|
Effect.mapError(
|
|
994
1021
|
(cause) => new AutonomousJobServiceError({ message: 'Failed to run autonomous job thread turn.', cause }),
|
|
995
1022
|
),
|
|
@@ -1022,19 +1049,48 @@ function executeQueuedRunEffect(
|
|
|
1022
1049
|
function createAutonomousJobService(deps: AutonomousJobDeps) {
|
|
1023
1050
|
return {
|
|
1024
1051
|
computeNextRunAt,
|
|
1025
|
-
create: (input: CreateAutonomousJobInput)
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1052
|
+
create: Effect.fn('AutonomousJobService.create')(function* (input: CreateAutonomousJobInput) {
|
|
1053
|
+
return yield* createEffect(deps, input)
|
|
1054
|
+
}),
|
|
1055
|
+
recoverActiveJobs: Effect.fn('AutonomousJobService.recoverActiveJobs')(function* (now: Date = nowDate()) {
|
|
1056
|
+
return yield* recoverActiveJobsEffect(deps, now)
|
|
1057
|
+
}),
|
|
1058
|
+
get: Effect.fn('AutonomousJobService.get')(function* (jobId: RecordIdInput) {
|
|
1059
|
+
return yield* getEffect(deps, jobId)
|
|
1060
|
+
}),
|
|
1061
|
+
list: Effect.fn('AutonomousJobService.list')(function* (params: {
|
|
1062
|
+
organizationId: RecordIdInput
|
|
1063
|
+
ownerUserId?: RecordIdInput
|
|
1064
|
+
status?: AutonomousJobStatus
|
|
1065
|
+
}) {
|
|
1066
|
+
return yield* listEffect(deps, params)
|
|
1067
|
+
}),
|
|
1068
|
+
update: Effect.fn('AutonomousJobService.update')(function* (jobId: RecordIdInput, input: UpdateAutonomousJobInput) {
|
|
1069
|
+
return yield* updateEffect(deps, jobId, input)
|
|
1070
|
+
}),
|
|
1071
|
+
pause: Effect.fn('AutonomousJobService.pause')(function* (jobId: RecordIdInput) {
|
|
1072
|
+
return yield* pauseEffect(deps, jobId)
|
|
1073
|
+
}),
|
|
1074
|
+
resume: Effect.fn('AutonomousJobService.resume')(function* (jobId: RecordIdInput) {
|
|
1075
|
+
return yield* resumeEffect(deps, jobId)
|
|
1076
|
+
}),
|
|
1077
|
+
runNow: Effect.fn('AutonomousJobService.runNow')(function* (jobId: RecordIdInput) {
|
|
1078
|
+
return yield* runNowEffect(deps, jobId)
|
|
1079
|
+
}),
|
|
1080
|
+
cancel: Effect.fn('AutonomousJobService.cancel')(function* (jobId: RecordIdInput) {
|
|
1081
|
+
return yield* cancelEffect(deps, jobId)
|
|
1082
|
+
}),
|
|
1083
|
+
delete: Effect.fn('AutonomousJobService.delete')(function* (jobId: RecordIdInput) {
|
|
1084
|
+
return yield* deleteJobEffect(deps, jobId)
|
|
1085
|
+
}),
|
|
1086
|
+
listRuns: Effect.fn('AutonomousJobService.listRuns')(function* (jobId: RecordIdInput) {
|
|
1087
|
+
return yield* listRunsEffect(deps, jobId)
|
|
1088
|
+
}),
|
|
1089
|
+
executeQueuedRun: Effect.fn('AutonomousJobService.executeQueuedRun')(function* (
|
|
1090
|
+
job: Job<AutonomousJobQueuePayload>,
|
|
1091
|
+
) {
|
|
1092
|
+
return yield* executeQueuedRunEffect(deps, job)
|
|
1093
|
+
}),
|
|
1038
1094
|
} as const
|
|
1039
1095
|
}
|
|
1040
1096
|
|
|
@@ -1043,6 +1099,7 @@ interface AutonomousJobDeps {
|
|
|
1043
1099
|
config: ResolvedLotaRuntimeConfig
|
|
1044
1100
|
executionPlan: ReturnType<typeof makeExecutionPlanService>
|
|
1045
1101
|
queueJob: ReturnType<typeof makeQueueJobService>
|
|
1102
|
+
provideCurrentContext: <A, E, R>(effect: Effect.Effect<A, E, R>) => Effect.Effect<A, E, never>
|
|
1046
1103
|
thread: ReturnType<typeof makeThreadService>
|
|
1047
1104
|
autonomousJobQueue: AutonomousJobQueueRuntime
|
|
1048
1105
|
}
|
|
@@ -1059,6 +1116,9 @@ export class AutonomousJobServiceTag extends Context.Service<
|
|
|
1059
1116
|
export const AutonomousJobServiceLive = Layer.effect(
|
|
1060
1117
|
AutonomousJobServiceTag,
|
|
1061
1118
|
Effect.gen(function* () {
|
|
1119
|
+
const currentContext = yield* Effect.context()
|
|
1120
|
+
const provideCurrentContext = <A, E, R>(effect: Effect.Effect<A, E, R>): Effect.Effect<A, E, never> =>
|
|
1121
|
+
effect.pipe(Effect.provide(currentContext)) as Effect.Effect<A, E, never>
|
|
1062
1122
|
const db = yield* DatabaseServiceTag
|
|
1063
1123
|
const config = yield* RuntimeConfigServiceTag
|
|
1064
1124
|
const executionPlan = yield* ExecutionPlanServiceTag
|
|
@@ -1070,6 +1130,7 @@ export const AutonomousJobServiceLive = Layer.effect(
|
|
|
1070
1130
|
config,
|
|
1071
1131
|
executionPlan,
|
|
1072
1132
|
queueJob,
|
|
1133
|
+
provideCurrentContext,
|
|
1073
1134
|
thread,
|
|
1074
1135
|
autonomousJobQueue: queues.autonomousJob,
|
|
1075
1136
|
})
|