@lota-sdk/core 0.4.13 → 0.4.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (139) hide show
  1. package/package.json +4 -4
  2. package/src/ai/embedding-cache.ts +17 -11
  3. package/src/ai-gateway/ai-gateway.ts +164 -94
  4. package/src/ai-gateway/index.ts +4 -1
  5. package/src/config/agent-defaults.ts +2 -2
  6. package/src/config/agent-types.ts +1 -1
  7. package/src/config/constants.ts +1 -1
  8. package/src/create-runtime.ts +259 -200
  9. package/src/db/cursor-pagination.ts +2 -9
  10. package/src/db/memory-store.ts +194 -175
  11. package/src/db/memory.ts +125 -71
  12. package/src/db/schema-fingerprint.ts +5 -4
  13. package/src/db/service-normalization.ts +4 -3
  14. package/src/db/service.ts +3 -2
  15. package/src/db/startup.ts +15 -16
  16. package/src/effect/errors.ts +161 -21
  17. package/src/effect/index.ts +0 -1
  18. package/src/embeddings/provider.ts +15 -7
  19. package/src/queues/autonomous-job.queue.ts +10 -22
  20. package/src/queues/delayed-node-promotion.queue.ts +8 -14
  21. package/src/queues/document-processor.queue.ts +13 -4
  22. package/src/queues/memory-consolidation.queue.ts +26 -14
  23. package/src/queues/plan-agent-heartbeat.queue.ts +48 -31
  24. package/src/queues/plan-scheduler.queue.ts +37 -15
  25. package/src/queues/queue-factory.ts +59 -35
  26. package/src/queues/standalone-worker.ts +3 -2
  27. package/src/redis/connection.ts +10 -3
  28. package/src/redis/org-memory-lock.ts +1 -1
  29. package/src/redis/redis-lease-lock.ts +5 -5
  30. package/src/redis/stream-context.ts +1 -1
  31. package/src/runtime/chat-message.ts +64 -1
  32. package/src/runtime/chat-run-orchestration.ts +33 -20
  33. package/src/runtime/context-compaction/context-compaction-runtime.ts +14 -7
  34. package/src/runtime/context-compaction/context-compaction.ts +78 -66
  35. package/src/runtime/domain-layer.ts +19 -13
  36. package/src/runtime/execution-plan.ts +7 -3
  37. package/src/runtime/memory/memory-block.ts +3 -9
  38. package/src/runtime/memory/memory-scope.ts +3 -1
  39. package/src/runtime/plugin-resolution.ts +2 -1
  40. package/src/runtime/post-turn-side-effects.ts +6 -5
  41. package/src/runtime/retrieval-adapters.ts +8 -20
  42. package/src/runtime/runtime-config.ts +3 -9
  43. package/src/runtime/runtime-extensions.ts +2 -4
  44. package/src/runtime/runtime-lifecycle.ts +56 -16
  45. package/src/runtime/runtime-services.ts +180 -102
  46. package/src/runtime/runtime-worker-registry.ts +3 -1
  47. package/src/runtime/social-chat/social-chat-agent-runner.ts +1 -1
  48. package/src/runtime/social-chat/social-chat-history.ts +21 -18
  49. package/src/runtime/social-chat/social-chat.ts +356 -223
  50. package/src/runtime/specialist-runner.ts +3 -1
  51. package/src/runtime/team-consultation/team-consultation-orchestrator.ts +3 -2
  52. package/src/runtime/thread-turn-context.ts +142 -102
  53. package/src/runtime/turn-lifecycle.ts +15 -46
  54. package/src/services/agent-activity.service.ts +1 -1
  55. package/src/services/agent-executor.service.ts +107 -77
  56. package/src/services/autonomous-job.service.ts +354 -293
  57. package/src/services/background-work.service.ts +3 -3
  58. package/src/services/context-compaction.service.ts +7 -2
  59. package/src/services/document-chunk.service.ts +50 -32
  60. package/src/services/execution-plan/execution-plan-schedule.ts +5 -3
  61. package/src/services/execution-plan/execution-plan.service.ts +162 -179
  62. package/src/services/feedback-loop.service.ts +5 -4
  63. package/src/services/graph-full-routing.ts +37 -36
  64. package/src/services/institutional-memory.service.ts +28 -30
  65. package/src/services/learned-skill.service.ts +107 -72
  66. package/src/services/memory/memory-errors.ts +4 -23
  67. package/src/services/memory/memory-org-memory.ts +10 -5
  68. package/src/services/memory/memory-rerank.ts +18 -6
  69. package/src/services/memory/memory.service.ts +170 -111
  70. package/src/services/memory/rerank.service.ts +29 -20
  71. package/src/services/organization-member.service.ts +1 -1
  72. package/src/services/organization.service.ts +69 -75
  73. package/src/services/ownership-dispatcher.service.ts +40 -39
  74. package/src/services/plan/plan-agent-heartbeat.service.ts +22 -24
  75. package/src/services/plan/plan-agent-query.service.ts +39 -31
  76. package/src/services/plan/plan-completion-side-effects.ts +13 -17
  77. package/src/services/plan/plan-coordination.service.ts +2 -1
  78. package/src/services/plan/plan-cycle.service.ts +6 -5
  79. package/src/services/plan/plan-deadline.service.ts +57 -54
  80. package/src/services/plan/plan-event-delivery.service.ts +5 -4
  81. package/src/services/plan/plan-executor-graph.ts +18 -15
  82. package/src/services/plan/plan-executor.service.ts +235 -262
  83. package/src/services/plan/plan-run.service.ts +169 -93
  84. package/src/services/plan/plan-scheduler.service.ts +192 -202
  85. package/src/services/plan/plan-template.service.ts +1 -1
  86. package/src/services/plan/plan-transaction-events.ts +1 -1
  87. package/src/services/plan/plan-workspace.service.ts +23 -14
  88. package/src/services/plugin-executor.service.ts +5 -9
  89. package/src/services/queue-job.service.ts +117 -59
  90. package/src/services/recent-activity-title.service.ts +13 -12
  91. package/src/services/recent-activity.service.ts +6 -1
  92. package/src/services/social-chat-history.service.ts +29 -25
  93. package/src/services/system-executor.service.ts +5 -9
  94. package/src/services/thread/thread-active-run.ts +2 -2
  95. package/src/services/thread/thread-listing.ts +61 -57
  96. package/src/services/thread/thread-memory-block.ts +73 -48
  97. package/src/services/thread/thread-message.service.ts +76 -65
  98. package/src/services/thread/thread-record-store.ts +8 -8
  99. package/src/services/thread/thread-title.service.ts +10 -4
  100. package/src/services/thread/thread-turn-execution.ts +43 -45
  101. package/src/services/thread/thread-turn-preparation.service.ts +257 -135
  102. package/src/services/thread/thread-turn-streaming.ts +83 -92
  103. package/src/services/thread/thread-turn.ts +18 -16
  104. package/src/services/thread/thread.service.ts +135 -100
  105. package/src/services/user.service.ts +45 -48
  106. package/src/storage/attachment-parser.ts +6 -2
  107. package/src/storage/attachment-storage.service.ts +5 -6
  108. package/src/storage/generated-document-storage.service.ts +1 -1
  109. package/src/system-agents/context-compaction.agent.ts +10 -9
  110. package/src/system-agents/delegated-agent-factory.ts +30 -6
  111. package/src/system-agents/memory-reranker.agent.ts +10 -9
  112. package/src/system-agents/memory.agent.ts +10 -9
  113. package/src/system-agents/recent-activity-title-refiner.agent.ts +13 -15
  114. package/src/system-agents/regular-chat-memory-digest.agent.ts +13 -12
  115. package/src/system-agents/skill-extractor.agent.ts +13 -12
  116. package/src/system-agents/skill-manager.agent.ts +13 -12
  117. package/src/system-agents/thread-router.agent.ts +11 -7
  118. package/src/system-agents/title-generator.agent.ts +13 -12
  119. package/src/tools/fetch-webpage.tool.ts +13 -13
  120. package/src/tools/memory-block.tool.ts +3 -1
  121. package/src/tools/plan-approval.tool.ts +4 -2
  122. package/src/tools/read-file-parts.tool.ts +10 -4
  123. package/src/tools/remember-memory.tool.ts +3 -1
  124. package/src/tools/research-topic.tool.ts +9 -5
  125. package/src/tools/search-web.tool.ts +16 -16
  126. package/src/tools/search.tool.ts +20 -5
  127. package/src/tools/team-think.tool.ts +61 -38
  128. package/src/utils/async.ts +5 -5
  129. package/src/utils/errors.ts +19 -18
  130. package/src/utils/sse-keepalive.ts +28 -25
  131. package/src/workers/bootstrap.ts +75 -11
  132. package/src/workers/memory-consolidation.worker.ts +82 -91
  133. package/src/workers/organization-learning.worker.ts +14 -4
  134. package/src/workers/regular-chat-memory-digest.runner.ts +105 -67
  135. package/src/workers/skill-extraction.runner.ts +97 -61
  136. package/src/workers/utils/repo-structure-extractor.ts +13 -8
  137. package/src/workers/utils/thread-message-query.ts +24 -24
  138. package/src/workers/worker-utils.ts +23 -4
  139. package/src/effect/helpers.ts +0 -123
@@ -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
- const effectTryPromise = makeEffectTryPromiseWithMessage(
64
- (message, cause) => new AutonomousJobServiceError({ message, cause }),
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 effectTryPromise(() => notificationService.notify(params), 'Failed to send autonomous job notification.')
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 effectTryPromise(
247
- () =>
248
- deps.db.createWithId(
249
- TABLES.AUTONOMOUS_JOB_RUN,
250
- runId,
251
- compactRecord({
252
- autonomousJobId: ensureRecordId(params.autonomousJobId, TABLES.AUTONOMOUS_JOB),
253
- threadId: ensureRecordId(params.threadId, TABLES.THREAD),
254
- queueJobId: params.queueJobId ? ensureRecordId(params.queueJobId, TABLES.QUEUE_JOB) : undefined,
255
- status: params.status ?? 'queued',
256
- assistantMessageIds: [],
257
- }),
258
- AutonomousJobRunRowSchema,
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* effectTryPromise(() => deps.db.connect(), 'Failed to connect to autonomous job database.')
270
- const row = yield* effectTryPromise(
271
- () =>
272
- deps.db.findOne(
273
- TABLES.AUTONOMOUS_JOB,
274
- { id: ensureRecordId(jobId, TABLES.AUTONOMOUS_JOB) },
275
- AutonomousJobRowSchema,
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* effectTryPromise(() => deps.db.connect(), 'Failed to connect to autonomous job database.')
294
- const row = yield* effectTryPromise(
295
- () =>
296
- deps.db.findOne(
297
- TABLES.AUTONOMOUS_JOB_RUN,
298
- { id: ensureRecordId(runId, TABLES.AUTONOMOUS_JOB_RUN) },
299
- AutonomousJobRunRowSchema,
300
- ),
301
- 'Failed to load autonomous job run.',
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.gen(function* () {
318
- yield* effectTryPromise(
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.gen(function* () {
326
- yield* effectTryPromise(
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* effectTryPromise(
339
- () =>
340
- deps.db.queryMany(
341
- new BoundQuery(
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
- 'Failed to find recoverable autonomous job run.',
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* effectTryPromise(
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* effectTryPromise(
398
- () =>
399
- deps.db.update(
400
- TABLES.AUTONOMOUS_JOB_RUN,
401
- queuedRun.id,
402
- { queueJobId: ensureRecordId(queueJobId, TABLES.QUEUE_JOB) },
403
- AutonomousJobRunRowSchema,
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
- 'Failed to persist autonomous job run queue job id.',
406
- )
408
+ )
407
409
  }
408
410
 
409
- yield* effectTryPromise(
410
- () =>
411
- deps.db.update(TABLES.AUTONOMOUS_JOB, row.id, { nextRunAt: nextRunAt ?? undefined }, AutonomousJobRowSchema),
412
- 'Failed to update autonomous job next run time.',
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* effectTryPromise(
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* effectTryPromise(
429
- () =>
430
- deps.db.update(TABLES.AUTONOMOUS_JOB, row.id, { nextRunAt: nextRunAt ?? undefined }, AutonomousJobRowSchema),
431
- 'Failed to update autonomous job next run time.',
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* effectTryPromise(() => deps.db.connect(), 'Failed to connect to autonomous job database.')
443
- const parsed = CreateAutonomousJobInputSchema.parse(input)
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* effectTryPromise(
457
- () =>
458
- deps.db.createWithId(
459
- TABLES.AUTONOMOUS_JOB,
460
- jobId,
461
- compactRecord({
462
- organizationId,
463
- ownerUserId,
464
- ownerUserName: parsed.ownerUserName,
465
- threadId: ensureRecordId(thread.id, TABLES.THREAD),
466
- agentId: parsed.agentId,
467
- title: parsed.title,
468
- prompt: parsed.prompt,
469
- schedule: parsed.schedule,
470
- status: 'active',
471
- autoPauseThreshold: parsed.autoPauseThreshold,
472
- consecutiveErrorCount: 0,
473
- nextRunAt: nextRunAt ?? undefined,
474
- }),
475
- AutonomousJobRowSchema,
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* effectTryPromise(() => deps.db.connect(), 'Failed to connect to autonomous job database.')
492
- const activeRows = yield* effectTryPromise(
493
- () =>
494
- deps.db.queryMany(
495
- new BoundQuery(`SELECT * FROM ${TABLES.AUTONOMOUS_JOB} WHERE status = $status ORDER BY createdAt ASC`, {
496
- status: 'active',
497
- }),
498
- AutonomousJobRowSchema,
499
- ),
500
- 'Failed to load active autonomous jobs.',
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* effectTryPromise(() => deps.db.connect(), 'Failed to connect to autonomous job database.')
524
- const rows = yield* effectTryPromise(
525
- () =>
526
- deps.db.findMany(
527
- TABLES.AUTONOMOUS_JOB,
528
- compactRecord({
529
- organizationId: ensureRecordId(params.organizationId, TABLES.ORGANIZATION),
530
- ownerUserId: params.ownerUserId ? ensureRecordId(params.ownerUserId, TABLES.USER) : undefined,
531
- status: params.status,
532
- }),
533
- AutonomousJobRowSchema,
534
- { orderBy: 'createdAt', orderDir: 'DESC' },
535
- ),
536
- 'Failed to list autonomous jobs.',
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 = UpdateAutonomousJobInputSchema.parse(input)
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* effectTryPromise(
566
- () =>
567
- deps.db.update(
568
- TABLES.AUTONOMOUS_JOB,
569
- existing.id,
570
- compactRecord({
571
- title: parsed.title,
572
- prompt: parsed.prompt,
573
- schedule: parsed.schedule,
574
- autoPauseThreshold: parsed.autoPauseThreshold,
575
- nextRunAt: existing.status === 'active' ? (nextRunAt ?? undefined) : undefined,
576
- }),
577
- AutonomousJobRowSchema,
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* effectTryPromise(
604
- () =>
605
- deps.db.update(
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* effectTryPromise(
626
- () =>
627
- deps.db.update(
628
- TABLES.AUTONOMOUS_JOB,
629
- row.id,
630
- { status: 'active', nextRunAt: nextRunAt ?? undefined },
631
- AutonomousJobRowSchema,
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* effectTryPromise(
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* effectTryPromise(
670
- () =>
671
- deps.db.update(
672
- TABLES.AUTONOMOUS_JOB_RUN,
673
- queuedRun.id,
674
- { queueJobId: ensureRecordId(queueJobId, TABLES.QUEUE_JOB) },
675
- AutonomousJobRunRowSchema,
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
- 'Failed to persist autonomous job run queue job id.',
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* effectTryPromise(
694
- () =>
695
- deps.db.update(
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* effectTryPromise(() => deps.db.connect(), 'Failed to connect to autonomous job database.')
732
- const rows = yield* effectTryPromise(
733
- () =>
734
- deps.db.findMany(
735
- TABLES.AUTONOMOUS_JOB_RUN,
736
- { autonomousJobId: ensureRecordId(jobId, TABLES.AUTONOMOUS_JOB) },
737
- AutonomousJobRunRowSchema,
738
- { orderBy: 'createdAt', orderDir: 'DESC' },
739
- ),
740
- 'Failed to list autonomous job runs.',
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* effectTryPromise(
776
- () =>
777
- deps.db.update(
778
- TABLES.AUTONOMOUS_JOB_RUN,
779
- runRow.id,
780
- { queueJobId: ensureRecordId(queueJobId, TABLES.QUEUE_JOB), status: 'running', startedAt },
781
- AutonomousJobRunRowSchema,
782
- ),
783
- 'Failed to mark autonomous job run as running.',
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
- void (yield* effectTryPromise(
814
- () =>
815
- deps.db.update(
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
- 'Failed to update autonomous job run completion.',
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* effectTryPromise(
840
- () =>
841
- deps.db.update(
842
- TABLES.AUTONOMOUS_JOB,
843
- currentJobRow.id,
844
- compactRecord({
845
- status: nextStatus,
846
- consecutiveErrorCount: 0,
847
- lastRunStatus: runStatus,
848
- lastRunAt: completedAt,
849
- nextRunAt: nextStatus === 'active' ? (nextRunAt ?? undefined) : undefined,
850
- linkedPlanSpecId: activePlan?.specId ? ensureRecordId(activePlan.specId, TABLES.PLAN_SPEC) : undefined,
851
- linkedPlanRunId: activePlan?.runId ? ensureRecordId(activePlan.runId, TABLES.PLAN_RUN) : undefined,
852
- lastError: undefined,
853
- }),
854
- AutonomousJobRowSchema,
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
- 'Failed to update autonomous job after successful run.',
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* effectTryPromise(
894
- () =>
895
- deps.db.update(
896
- TABLES.AUTONOMOUS_JOB_RUN,
897
- currentRunRow.id,
898
- { status: 'failed', error: normalizedError, completedAt },
899
- AutonomousJobRunRowSchema,
900
- ),
901
- 'Failed to persist failed autonomous job run.',
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* effectTryPromise(
909
- () =>
910
- deps.db.update(
911
- TABLES.AUTONOMOUS_JOB,
912
- currentJobRow.id,
913
- compactRecord({
914
- status: nextStatus,
915
- consecutiveErrorCount: nextConsecutiveErrorCount,
916
- lastRunStatus: 'failed',
917
- lastRunAt: completedAt,
918
- nextRunAt:
919
- nextStatus === 'active' && currentJobRow.schedule.kind !== 'at'
920
- ? (computeNextRunAt(currentJobRow.schedule, completedAt) ?? undefined)
921
- : undefined,
922
- lastError: normalizedError,
923
- }),
924
- AutonomousJobRowSchema,
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
- 'Failed to update autonomous job failure state.',
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, unknown> {
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* effectTryPromise(() => deps.db.connect(), 'Failed to connect to autonomous job database.')
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) => createEffect(deps, input),
1026
- recoverActiveJobs: (now = nowDate()) => recoverActiveJobsEffect(deps, now),
1027
- get: (jobId: RecordIdInput) => getEffect(deps, jobId),
1028
- list: (params: { organizationId: RecordIdInput; ownerUserId?: RecordIdInput; status?: AutonomousJobStatus }) =>
1029
- listEffect(deps, params),
1030
- update: (jobId: RecordIdInput, input: UpdateAutonomousJobInput) => updateEffect(deps, jobId, input),
1031
- pause: (jobId: RecordIdInput) => pauseEffect(deps, jobId),
1032
- resume: (jobId: RecordIdInput) => resumeEffect(deps, jobId),
1033
- runNow: (jobId: RecordIdInput) => runNowEffect(deps, jobId),
1034
- cancel: (jobId: RecordIdInput) => cancelEffect(deps, jobId),
1035
- delete: (jobId: RecordIdInput) => deleteJobEffect(deps, jobId),
1036
- listRuns: (jobId: RecordIdInput) => listRunsEffect(deps, jobId),
1037
- executeQueuedRun: (job: Job<AutonomousJobQueuePayload>) => executeQueuedRunEffect(deps, job),
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
  })