@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
@@ -17,13 +17,13 @@ import type { ResolvedAgentConfig, ResolvedAgentFactoryConfig } from '../../conf
17
17
  import { aiLogger } from '../../config/logger'
18
18
  import { recordIdToString } from '../../db/record-id'
19
19
  import { TABLES } from '../../db/tables'
20
- import { ForbiddenError } from '../../effect/errors'
21
- import { effectTryMaybeAsync, effectTryPromise } from '../../effect/helpers'
20
+ import { ERROR_TAGS, ForbiddenError } from '../../effect/errors'
22
21
  import type { LotaQueuesRuntime } from '../../queues/queues.service'
23
22
  import type { LearnedSkillServiceTag } from '../../services/learned-skill.service'
24
23
  import type { MemoryServiceTag } from '../../services/memory/memory.service'
25
24
  import type { SocialChatHistoryError, makeSocialChatHistoryService } from '../../services/social-chat-history.service'
26
25
  import { safeEnqueue } from '../../utils/async'
26
+ import { getErrorMessage } from '../../utils/errors'
27
27
  import { buildAgentPromptContext } from '../agent-prompt-context'
28
28
  import { createServerRunAbortController } from '../agent-stream-helpers'
29
29
  import type {
@@ -61,10 +61,31 @@ export interface SocialChatRuntimeServices {
61
61
  queues: LotaQueuesRuntime
62
62
  }
63
63
 
64
- class SocialChatServiceError extends Schema.TaggedErrorClass<SocialChatServiceError>()('SocialChatServiceError', {
65
- message: Schema.String,
66
- cause: Schema.optional(Schema.Defect),
67
- }) {}
64
+ class SocialChatServiceError extends Schema.TaggedErrorClass<SocialChatServiceError>()(
65
+ ERROR_TAGS.SocialChatServiceError,
66
+ { message: Schema.String, cause: Schema.optional(Schema.Defect) },
67
+ ) {}
68
+
69
+ function toSocialChatServiceError(message: string, cause: unknown): SocialChatServiceError {
70
+ return new SocialChatServiceError({ message, cause })
71
+ }
72
+
73
+ function runSlackMessageHandler(params: {
74
+ kind: 'mention' | 'subscribed-message'
75
+ thread: Thread
76
+ message: Message
77
+ effect: Effect.Effect<
78
+ void,
79
+ Cause.UnknownError | ForbiddenError | SocialChatHistoryError | SocialChatServiceError,
80
+ never
81
+ >
82
+ }): Promise<void> {
83
+ return Effect.runPromise(params.effect).catch((cause) => {
84
+ const error = toSocialChatServiceError(`Failed to handle Slack social-chat ${params.kind}.`, cause)
85
+ aiLogger.error`Slack social-chat ${params.kind} failed: threadId=${params.thread.id}, messageId=${params.message.id}, error=${getErrorMessage(error)}`
86
+ throw error
87
+ })
88
+ }
68
89
 
69
90
  const DEFAULT_SOCIAL_CHAT_AGENT_ID = 'socialChat'
70
91
  const DEFAULT_SOCIAL_CHAT_AGENT_DISPLAY_NAME = 'Lota'
@@ -194,8 +215,20 @@ export function createSocialChatRuntime(params: {
194
215
  dedupeTtlMs: slackConfig?.dedupeTtlMs ?? DEFAULT_SOCIAL_CHAT_DEDUPE_TTL_MS,
195
216
  })
196
217
 
197
- const initialize = () => Effect.runPromise(effectTryPromise(() => chat.initialize()))
198
- const shutdown = () => Effect.runPromise(effectTryPromise(() => chat.shutdown()))
218
+ const initialize = () =>
219
+ Effect.runPromise(
220
+ Effect.tryPromise({
221
+ try: () => chat.initialize(),
222
+ catch: (cause) => toSocialChatServiceError('Failed to initialize social chat.', cause),
223
+ }),
224
+ )
225
+ const shutdown = () =>
226
+ Effect.runPromise(
227
+ Effect.tryPromise({
228
+ try: () => chat.shutdown(),
229
+ catch: (cause) => toSocialChatServiceError('Failed to shut down social chat.', cause),
230
+ }),
231
+ )
199
232
 
200
233
  const handleMessage = (
201
234
  thread: Thread,
@@ -224,22 +257,27 @@ export function createSocialChatRuntime(params: {
224
257
  }
225
258
  aiLogger.debug`Slack social-chat message received: channelId=${messageContext.channelId}, threadId=${messageContext.threadId}, messageId=${messageContext.messageId}, author=${messageContext.authorName ?? 'unknown'}, textLength=${messageContext.text.length}`
226
259
 
227
- const resolvedContext = yield* effectTryMaybeAsync(() =>
228
- socialChatConfig.resolveContext({
229
- platform: 'slack',
230
- channelId: messageContext.channelId,
231
- threadId: messageContext.threadId,
232
- messageId: messageContext.messageId,
233
- text: messageContext.text,
234
- authorId: messageContext.authorId,
235
- authorName: messageContext.authorName,
236
- }),
237
- )
260
+ const resolvedContext = yield* Effect.tryPromise({
261
+ try: () =>
262
+ socialChatConfig.resolveContext({
263
+ platform: 'slack',
264
+ channelId: messageContext.channelId,
265
+ threadId: messageContext.threadId,
266
+ messageId: messageContext.messageId,
267
+ text: messageContext.text,
268
+ authorId: messageContext.authorId,
269
+ authorName: messageContext.authorName,
270
+ }),
271
+ catch: (cause) => toSocialChatServiceError('Failed to resolve social chat context.', cause),
272
+ })
238
273
  const workspaceIdString = recordIdToString(resolvedContext.workspaceId, TABLES.ORGANIZATION)
239
274
  const userIdString = recordIdToString(resolvedContext.userId, TABLES.USER)
240
275
  aiLogger.debug`Slack social-chat context resolved: workspaceId=${workspaceIdString}, userId=${userIdString}`
241
276
 
242
- const threadMessages = yield* effectTryPromise(() => collectThreadMessages(thread, incomingMessage))
277
+ const threadMessages = yield* Effect.tryPromise({
278
+ try: () => collectThreadMessages(thread, incomingMessage),
279
+ catch: (cause) => toSocialChatServiceError('Failed to collect thread messages.', cause),
280
+ })
243
281
  const normalizedMessages = threadMessages
244
282
  .map((message) =>
245
283
  normalizeSocialHistoryMessage({
@@ -285,19 +323,39 @@ export function createSocialChatRuntime(params: {
285
323
  const getConsultParticipants = socialChatConfig.getConsultParticipants
286
324
 
287
325
  const workspace = getWorkspace
288
- ? yield* effectTryMaybeAsync(() => getWorkspace(resolvedContext.workspaceId))
326
+ ? yield* Effect.tryPromise({
327
+ try: () => getWorkspace(resolvedContext.workspaceId),
328
+ catch: (cause) => toSocialChatServiceError('Failed to load social chat workspace.', cause),
329
+ })
289
330
  : {}
290
331
  const lifecycleState = getLifecycleState
291
- ? yield* effectTryMaybeAsync(() => getLifecycleState(workspace))
332
+ ? yield* Effect.tryPromise({
333
+ try: () => getLifecycleState(workspace),
334
+ catch: (cause) => toSocialChatServiceError('Failed to load workspace lifecycle state.', cause),
335
+ })
292
336
  : undefined
293
337
  const workspaceProfileState = readProfileProjectionState
294
- ? yield* effectTryMaybeAsync(() => readProfileProjectionState(workspace))
338
+ ? yield* Effect.tryPromise({
339
+ try: () => readProfileProjectionState(workspace),
340
+ catch: (cause) => toSocialChatServiceError('Failed to read workspace profile projection state.', cause),
341
+ })
295
342
  : undefined
296
343
  const recentDomainEvents = listRecentDomainEvents
297
- ? yield* effectTryMaybeAsync(() => listRecentDomainEvents(resolvedContext.workspaceId, 5))
344
+ ? yield* Effect.tryPromise({
345
+ try: () => listRecentDomainEvents(resolvedContext.workspaceId, 5),
346
+ catch: (cause) => toSocialChatServiceError('Failed to load recent workspace domain events.', cause),
347
+ })
298
348
  : ([] as Array<Record<string, unknown>>)
299
349
  const promptSummary = buildPromptSummary
300
- ? yield* effectTryMaybeAsync(() => buildPromptSummary(resolvedContext.workspaceId)).pipe(
350
+ ? yield* Effect.tryPromise({
351
+ try: () => buildPromptSummary(resolvedContext.workspaceId),
352
+ catch: (cause) => toSocialChatServiceError('Failed to build workspace prompt summary.', cause),
353
+ }).pipe(
354
+ Effect.tapError((error) =>
355
+ Effect.sync(() => {
356
+ aiLogger.warn`Slack social-chat buildPromptSummary failed: ${error}`
357
+ }),
358
+ ),
301
359
  Effect.orElseSucceed(() => undefined),
302
360
  )
303
361
  : undefined
@@ -316,13 +374,15 @@ export function createSocialChatRuntime(params: {
316
374
  lifecycleState?.bootstrapActive || messageContext.text.length === 0
317
375
  ? undefined
318
376
  : buildRetrievedKnowledgeSection
319
- ? yield* effectTryMaybeAsync(() =>
320
- buildRetrievedKnowledgeSection({
321
- workspaceId: workspaceIdString,
322
- userId: userIdString,
323
- query: messageContext.text,
324
- }),
325
- )
377
+ ? yield* Effect.tryPromise({
378
+ try: () =>
379
+ buildRetrievedKnowledgeSection({
380
+ workspaceId: workspaceIdString,
381
+ userId: userIdString,
382
+ query: messageContext.text,
383
+ }),
384
+ catch: (cause) => toSocialChatServiceError('Failed to build retrieved knowledge section.', cause),
385
+ })
326
386
  : undefined
327
387
 
328
388
  const preSeededMemoriesSection = yield* memoryService
@@ -346,37 +406,49 @@ export function createSocialChatRuntime(params: {
346
406
  limit: 3,
347
407
  minConfidence: 0.6,
348
408
  })
349
- .pipe(Effect.orElseSucceed(() => undefined))
409
+ .pipe(
410
+ Effect.tapError((error) =>
411
+ Effect.sync(() => {
412
+ aiLogger.warn`Slack social-chat memory retrieval failed: ${error}`
413
+ }),
414
+ ),
415
+ Effect.orElseSucceed(() => undefined),
416
+ )
350
417
 
351
418
  let memoryBlock = ''
352
419
  const consultedAgents = getConsultParticipants
353
- ? yield* effectTryMaybeAsync(() =>
354
- getConsultParticipants({
355
- workspaceId: resolvedContext.workspaceId,
356
- workspaceIdString,
357
- platform: 'slack',
358
- }),
359
- )
420
+ ? yield* Effect.tryPromise({
421
+ try: () =>
422
+ getConsultParticipants({
423
+ workspaceId: resolvedContext.workspaceId,
424
+ workspaceIdString,
425
+ platform: 'slack',
426
+ }),
427
+ catch: (cause) => toSocialChatServiceError('Failed to resolve social chat consult participants.', cause),
428
+ })
360
429
  : [...agentConfig.teamConsultParticipants]
361
430
  const consultParticipants = [...new Set(consultedAgents)].filter((agentId) => agentId !== socialAgentId)
362
431
  const executedToolNames: string[] = []
363
432
 
364
433
  const baseTools = withLoggedSocialToolSet(
365
- yield* effectTryMaybeAsync(() =>
366
- socialChatConfig.buildAgentTools(
367
- buildBuildToolsParams({
368
- agentId: socialAgentId,
369
- context: resolvedContext,
370
- workspaceIdString,
371
- userIdString,
372
- messageContext,
373
- memoryBlock,
374
- onAppendMemoryBlock: (value: string) => {
375
- memoryBlock = value
376
- },
377
- }),
378
- ),
379
- ),
434
+ yield* Effect.tryPromise({
435
+ try: () =>
436
+ socialChatConfig.buildAgentTools(
437
+ buildBuildToolsParams({
438
+ agentId: socialAgentId,
439
+ context: resolvedContext,
440
+ workspaceIdString,
441
+ userIdString,
442
+ messageContext,
443
+ memoryBlock,
444
+ onAppendMemoryBlock: (value: string) => {
445
+ memoryBlock = value
446
+ },
447
+ }),
448
+ ),
449
+ catch: (cause) =>
450
+ toSocialChatServiceError(`Failed to build social chat tools for ${socialAgentId}.`, cause),
451
+ }),
380
452
  {
381
453
  agentId: socialAgentId,
382
454
  channelId: messageContext.channelId,
@@ -403,94 +475,116 @@ export function createSocialChatRuntime(params: {
403
475
  })
404
476
  }
405
477
 
406
- const { result: specialistRun } = yield* effectTryMaybeAsync(() =>
407
- runSpecialistSession({
408
- initialMemoryBlock: '',
409
- buildTools: ({ memoryBlock: currentMemoryBlock, onAppendMemoryBlock }) =>
410
- runPromiseWithCurrentContext(
411
- Effect.gen(function* () {
412
- const tools = yield* effectTryMaybeAsync(() =>
413
- socialChatConfig.buildAgentTools(
414
- buildBuildToolsParams({
415
- agentId,
416
- context: resolvedContext,
417
- workspaceIdString,
418
- userIdString,
419
- messageContext,
420
- memoryBlock: currentMemoryBlock,
421
- onAppendMemoryBlock,
422
- }),
423
- ),
424
- )
425
-
426
- return withLoggedSocialToolSet(tools, {
427
- agentId,
428
- channelId: messageContext.channelId,
429
- threadId: messageContext.threadId,
430
- executedToolNames,
431
- })
432
- }),
433
- ),
434
- run: ({ tools }) =>
435
- runPromiseWithCurrentContext(
436
- Effect.gen(function* () {
437
- const specialistPreSeededMemories = yield* memoryService
438
- .getTopMemories({
439
- orgId: workspaceIdString,
440
- agentName: agentId,
441
- limit: PRESEEDED_MEMORY_LOOKUP_LIMIT,
442
- })
443
- .pipe(
444
- Effect.mapError(
445
- (cause) =>
446
- new SocialChatServiceError({
447
- message: `Failed to load pre-seeded memories for specialist ${agentId}.`,
448
- cause,
478
+ const { result: specialistRun } = yield* Effect.tryPromise({
479
+ try: () =>
480
+ runSpecialistSession({
481
+ initialMemoryBlock: '',
482
+ buildTools: ({ memoryBlock: currentMemoryBlock, onAppendMemoryBlock }) =>
483
+ runPromiseWithCurrentContext(
484
+ Effect.gen(function* () {
485
+ const tools = yield* Effect.tryPromise({
486
+ try: () =>
487
+ socialChatConfig.buildAgentTools(
488
+ buildBuildToolsParams({
489
+ agentId,
490
+ context: resolvedContext,
491
+ workspaceIdString,
492
+ userIdString,
493
+ messageContext,
494
+ memoryBlock: currentMemoryBlock,
495
+ onAppendMemoryBlock,
449
496
  }),
450
- ),
451
- )
452
- const specialistLearnedSkills = lifecycleState?.bootstrapActive
453
- ? undefined
454
- : yield* learnedSkillService
455
- .retrieveForTurn({
456
- orgId: workspaceIdString,
457
- agentId,
458
- query: task,
459
- limit: 3,
460
- minConfidence: 0.6,
461
- })
462
- .pipe(Effect.orElseSucceed(() => undefined))
463
-
464
- return yield* effectTryPromise(() =>
465
- runSocialAgentTurn({
466
- agentFactoryConfig,
497
+ ),
498
+ catch: (cause) =>
499
+ toSocialChatServiceError(
500
+ `Failed to build social chat tools for specialist ${agentId}.`,
501
+ cause,
502
+ ),
503
+ })
504
+
505
+ return withLoggedSocialToolSet(tools, {
467
506
  agentId,
468
- mode: 'fixedThreadMode',
469
- threadType: 'group',
470
- onboardingActive: lifecycleState?.bootstrapActive ?? false,
471
- linearInstalled: false,
472
- systemWorkspaceDetails: promptContext.systemWorkspaceDetails,
473
- preSeededMemoriesSection: specialistPreSeededMemories,
474
- retrievedKnowledgeSection,
475
- learnedSkillsSection: specialistLearnedSkills,
476
- userMessageText: task,
477
- additionalInstructionSections: [
478
- `You are supporting ${socialAgentDisplayName} in a Slack social-chat thread. Stay within your role.`,
479
- ],
480
- tools,
481
- prompt: buildSpecialistSocialChatPrompt({
482
- requesterName: socialAgentDisplayName,
483
- agentName: getAgentDisplayName(agentConfig, agentId),
484
- task,
485
- transcript,
486
- }),
487
- abortSignal: runAbort.signal,
488
- }),
489
- )
490
- }),
491
- ),
492
- }),
493
- )
507
+ channelId: messageContext.channelId,
508
+ threadId: messageContext.threadId,
509
+ executedToolNames,
510
+ })
511
+ }),
512
+ ),
513
+ run: ({ tools }) =>
514
+ runPromiseWithCurrentContext(
515
+ Effect.gen(function* () {
516
+ const specialistPreSeededMemories = yield* memoryService
517
+ .getTopMemories({
518
+ orgId: workspaceIdString,
519
+ agentName: agentId,
520
+ limit: PRESEEDED_MEMORY_LOOKUP_LIMIT,
521
+ })
522
+ .pipe(
523
+ Effect.mapError(
524
+ (cause) =>
525
+ new SocialChatServiceError({
526
+ message: `Failed to load pre-seeded memories for specialist ${agentId}.`,
527
+ cause,
528
+ }),
529
+ ),
530
+ )
531
+ const specialistLearnedSkills = lifecycleState?.bootstrapActive
532
+ ? undefined
533
+ : yield* learnedSkillService
534
+ .retrieveForTurn({
535
+ orgId: workspaceIdString,
536
+ agentId,
537
+ query: task,
538
+ limit: 3,
539
+ minConfidence: 0.6,
540
+ })
541
+ .pipe(
542
+ Effect.tapError((error) =>
543
+ Effect.sync(() => {
544
+ aiLogger.warn`Slack social-chat participant memory retrieval failed: ${error}`
545
+ }),
546
+ ),
547
+ Effect.orElseSucceed(() => undefined),
548
+ )
549
+
550
+ return yield* Effect.tryPromise({
551
+ try: () =>
552
+ runSocialAgentTurn({
553
+ agentFactoryConfig,
554
+ agentId,
555
+ mode: 'fixedThreadMode',
556
+ threadType: 'group',
557
+ onboardingActive: lifecycleState?.bootstrapActive ?? false,
558
+ linearInstalled: false,
559
+ systemWorkspaceDetails: promptContext.systemWorkspaceDetails,
560
+ preSeededMemoriesSection: specialistPreSeededMemories,
561
+ retrievedKnowledgeSection,
562
+ learnedSkillsSection: specialistLearnedSkills,
563
+ userMessageText: task,
564
+ additionalInstructionSections: [
565
+ `You are supporting ${socialAgentDisplayName} in a Slack social-chat thread. Stay within your role.`,
566
+ ],
567
+ tools,
568
+ prompt: buildSpecialistSocialChatPrompt({
569
+ requesterName: socialAgentDisplayName,
570
+ agentName: getAgentDisplayName(agentConfig, agentId),
571
+ task,
572
+ transcript,
573
+ }),
574
+ abortSignal: runAbort.signal,
575
+ }),
576
+ catch: (cause) =>
577
+ toSocialChatServiceError(
578
+ `Failed to run specialist social agent turn for ${agentId}.`,
579
+ cause,
580
+ ),
581
+ })
582
+ }),
583
+ ),
584
+ }),
585
+ catch: (cause) =>
586
+ toSocialChatServiceError(`Failed to run social specialist session for ${agentId}.`, cause),
587
+ })
494
588
  const text = specialistRun.text
495
589
  const createdAt = yield* Clock.currentTimeMillis
496
590
 
@@ -516,38 +610,53 @@ export function createSocialChatRuntime(params: {
516
610
  },
517
611
  })
518
612
 
519
- yield* effectTryPromise(() => thread.startTyping('Thinking...')).pipe(Effect.orElseSucceed(() => undefined))
520
- aiLogger.debug`Slack social-chat generating reply: channelId=${messageContext.channelId}, threadId=${messageContext.threadId}`
521
- const leadRun = yield* effectTryPromise(() =>
522
- runSocialAgentTurn({
523
- agentFactoryConfig,
524
- agentId: socialAgentId,
525
- mode: 'threadMode',
526
- threadType: 'group',
527
- onboardingActive: lifecycleState?.bootstrapActive ?? false,
528
- linearInstalled: false,
529
- systemWorkspaceDetails: promptContext.systemWorkspaceDetails,
530
- preSeededMemoriesSection,
531
- retrievedKnowledgeSection,
532
- learnedSkillsSection,
533
- userMessageText: messageContext.text,
534
- additionalInstructionSections: [buildSocialChatIdentitySection(socialAgentDisplayName)],
535
- tools: { ...baseTools, [CONSULT_SPECIALIST_TOOL_NAME]: consultSpecialistTool },
536
- prompt: buildLeadSocialChatPrompt({
537
- agentDisplayName: socialAgentDisplayName,
538
- channelId: messageContext.channelId,
539
- threadId: messageContext.threadId,
540
- transcript,
541
- latestUserMessage: messageContext.text,
542
- latestAuthorName: messageContext.authorName,
613
+ yield* Effect.tryPromise({
614
+ try: () => thread.startTyping('Thinking...'),
615
+ catch: (cause) => toSocialChatServiceError('Failed to start Slack typing indicator.', cause),
616
+ }).pipe(
617
+ Effect.tapError((error) =>
618
+ Effect.sync(() => {
619
+ aiLogger.warn`Slack social-chat startTyping failed: ${error}`
543
620
  }),
544
- abortSignal: runAbort.signal,
545
- }),
621
+ ),
622
+ Effect.orElseSucceed(() => undefined),
546
623
  )
624
+ aiLogger.debug`Slack social-chat generating reply: channelId=${messageContext.channelId}, threadId=${messageContext.threadId}`
625
+ const leadRun = yield* Effect.tryPromise({
626
+ try: () =>
627
+ runSocialAgentTurn({
628
+ agentFactoryConfig,
629
+ agentId: socialAgentId,
630
+ mode: 'threadMode',
631
+ threadType: 'group',
632
+ onboardingActive: lifecycleState?.bootstrapActive ?? false,
633
+ linearInstalled: false,
634
+ systemWorkspaceDetails: promptContext.systemWorkspaceDetails,
635
+ preSeededMemoriesSection,
636
+ retrievedKnowledgeSection,
637
+ learnedSkillsSection,
638
+ userMessageText: messageContext.text,
639
+ additionalInstructionSections: [buildSocialChatIdentitySection(socialAgentDisplayName)],
640
+ tools: { ...baseTools, [CONSULT_SPECIALIST_TOOL_NAME]: consultSpecialistTool },
641
+ prompt: buildLeadSocialChatPrompt({
642
+ agentDisplayName: socialAgentDisplayName,
643
+ channelId: messageContext.channelId,
644
+ threadId: messageContext.threadId,
645
+ transcript,
646
+ latestUserMessage: messageContext.text,
647
+ latestAuthorName: messageContext.authorName,
648
+ }),
649
+ abortSignal: runAbort.signal,
650
+ }),
651
+ catch: (cause) => toSocialChatServiceError(`Failed to run social agent turn for ${socialAgentId}.`, cause),
652
+ })
547
653
  const responseText = leadRun.text
548
654
 
549
655
  const replyMarkdown = buildSlackSocialReplyMarkdown({ replyMarkdown: responseText, executedToolNames })
550
- const sentMessage = yield* effectTryPromise(() => thread.post({ markdown: replyMarkdown }))
656
+ const sentMessage = yield* Effect.tryPromise({
657
+ try: () => thread.post({ markdown: replyMarkdown }),
658
+ catch: (cause) => toSocialChatServiceError('Failed to post Slack social chat reply.', cause),
659
+ })
551
660
  aiLogger.debug`Slack social-chat reply posted: channelId=${messageContext.channelId}, threadId=${messageContext.threadId}, replyMessageId=${sentMessage.id}`
552
661
  const normalizedResponse = normalizeSocialHistoryMessage({
553
662
  workspaceId: workspaceIdString,
@@ -564,73 +673,92 @@ export function createSocialChatRuntime(params: {
564
673
  const priorHistoryMessages = toHistoryMessages(agentConfig, priorHistory)
565
674
  const agentMessages = normalizedResponse ? buildAgentHistoryMessages(agentConfig, [normalizedResponse]) : []
566
675
  if (messageContext.text && agentMessages.length > 0) {
567
- yield* effectTryPromise(() =>
568
- safeEnqueue(
569
- () =>
570
- params.services.queues.postChatMemory.enqueuePostChatMemory(
571
- {
572
- orgId: workspaceIdString,
573
- threadId: `social:slack:${messageContext.threadId}`,
574
- sourceId: createSocialChatCursorId({
575
- workspaceId: workspaceIdString,
576
- threadId: messageContext.threadId,
577
- messageId: messageContext.messageId,
578
- }),
579
- userMessage: messageContext.text,
580
- historyMessages: priorHistoryMessages,
581
- agentMessages,
582
- memoryBlock: memoryBlock.trim() ? memoryBlock : undefined,
583
- source: 'social_chat',
584
- sourceMetadata: {
585
- platform: 'slack',
586
- channelId: messageContext.channelId,
587
- threadId: messageContext.threadId,
588
- messageId: messageContext.messageId,
589
- authorId: messageContext.authorId,
590
- authorName: messageContext.authorName,
676
+ yield* Effect.tryPromise({
677
+ try: () =>
678
+ safeEnqueue(
679
+ () =>
680
+ params.services.queues.postChatMemory.enqueuePostChatMemory(
681
+ {
682
+ orgId: workspaceIdString,
683
+ threadId: `social:slack:${messageContext.threadId}`,
684
+ sourceId: createSocialChatCursorId({
685
+ workspaceId: workspaceIdString,
686
+ threadId: messageContext.threadId,
687
+ messageId: messageContext.messageId,
688
+ }),
689
+ userMessage: messageContext.text,
690
+ historyMessages: priorHistoryMessages,
691
+ agentMessages,
692
+ memoryBlock: memoryBlock.trim() ? memoryBlock : undefined,
693
+ source: 'social_chat',
694
+ sourceMetadata: {
695
+ platform: 'slack',
696
+ channelId: messageContext.channelId,
697
+ threadId: messageContext.threadId,
698
+ messageId: messageContext.messageId,
699
+ authorId: messageContext.authorId,
700
+ authorName: messageContext.authorName,
701
+ },
591
702
  },
592
- },
593
- {
594
- dedupeKey: createSocialMemoryDedupeKey({
595
- workspaceId: workspaceIdString,
596
- threadId: messageContext.threadId,
597
- messageId: messageContext.messageId,
598
- }),
599
- },
600
- ),
601
- { operationName: 'social post-chat memory extraction enqueue' },
602
- ),
603
- )
703
+ {
704
+ dedupeKey: createSocialMemoryDedupeKey({
705
+ workspaceId: workspaceIdString,
706
+ threadId: messageContext.threadId,
707
+ messageId: messageContext.messageId,
708
+ }),
709
+ },
710
+ ),
711
+ { operationName: 'social post-chat memory extraction enqueue' },
712
+ ),
713
+ catch: (cause) => toSocialChatServiceError('Failed to enqueue social post-chat memory extraction.', cause),
714
+ })
604
715
  }
605
716
 
606
- yield* effectTryPromise(() =>
607
- safeEnqueue(
608
- () =>
609
- params.services.queues.organizationLearning.enqueueRegularChatMemoryDigest({ orgId: workspaceIdString }),
610
- { operationName: 'social regular chat memory digest enqueue' },
611
- ),
612
- )
613
- yield* effectTryPromise(() =>
614
- safeEnqueue(
615
- () => params.services.queues.organizationLearning.enqueueSkillExtraction({ orgId: workspaceIdString }),
616
- { operationName: 'social skill extraction enqueue' },
617
- ),
618
- )
717
+ yield* Effect.tryPromise({
718
+ try: () =>
719
+ safeEnqueue(
720
+ () =>
721
+ params.services.queues.organizationLearning.enqueueRegularChatMemoryDigest({
722
+ orgId: workspaceIdString,
723
+ }),
724
+ { operationName: 'social regular chat memory digest enqueue' },
725
+ ),
726
+ catch: (cause) => toSocialChatServiceError('Failed to enqueue social regular chat memory digest.', cause),
727
+ })
728
+ yield* Effect.tryPromise({
729
+ try: () =>
730
+ safeEnqueue(
731
+ () => params.services.queues.organizationLearning.enqueueSkillExtraction({ orgId: workspaceIdString }),
732
+ { operationName: 'social skill extraction enqueue' },
733
+ ),
734
+ catch: (cause) => toSocialChatServiceError('Failed to enqueue social skill extraction.', cause),
735
+ })
619
736
  }),
620
737
  )
621
738
 
622
739
  chat.onNewMention((thread, message) => {
623
740
  aiLogger.debug`Slack social-chat new mention received: threadId=${thread.id}, messageId=${message.id}`
624
- return Effect.runPromise(
625
- Effect.gen(function* () {
626
- yield* effectTryPromise(() => thread.subscribe())
741
+ return runSlackMessageHandler({
742
+ kind: 'mention',
743
+ thread,
744
+ message,
745
+ effect: Effect.gen(function* () {
746
+ yield* Effect.tryPromise({
747
+ try: () => thread.subscribe(),
748
+ catch: (cause) => toSocialChatServiceError('Failed to subscribe to Slack thread.', cause),
749
+ })
627
750
  return yield* handleMessage(thread, message)
628
751
  }),
629
- )
752
+ })
630
753
  })
631
754
  chat.onSubscribedMessage((thread, message) => {
632
755
  aiLogger.debug`Slack social-chat subscribed thread message received: threadId=${thread.id}, messageId=${message.id}`
633
- return Effect.runPromise(handleMessage(thread, message))
756
+ return runSlackMessageHandler({
757
+ kind: 'subscribed-message',
758
+ thread,
759
+ message,
760
+ effect: handleMessage(thread, message),
761
+ })
634
762
  })
635
763
 
636
764
  return {
@@ -639,7 +767,12 @@ export function createSocialChatRuntime(params: {
639
767
  shutdown,
640
768
  webhooks: {
641
769
  slack: (request: Request, options?: WebhookOptions) =>
642
- Effect.runPromise(effectTryPromise(() => chat.webhooks.slack(request, options))),
770
+ Effect.runPromise(
771
+ Effect.tryPromise({
772
+ try: () => chat.webhooks.slack(request, options),
773
+ catch: (cause) => toSocialChatServiceError('Failed to handle Slack social chat webhook.', cause),
774
+ }),
775
+ ),
643
776
  },
644
777
  }
645
778
  }