@lota-sdk/core 0.4.9 → 0.4.11

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 (182) hide show
  1. package/package.json +2 -2
  2. package/src/ai/embedding-cache.ts +3 -1
  3. package/src/ai-gateway/ai-gateway.ts +164 -82
  4. package/src/ai-gateway/index.ts +16 -1
  5. package/src/config/agent-defaults.ts +4 -107
  6. package/src/config/agent-types.ts +1 -1
  7. package/src/config/background-processing.ts +1 -1
  8. package/src/config/index.ts +0 -1
  9. package/src/config/logger.ts +22 -25
  10. package/src/config/thread-defaults.ts +1 -10
  11. package/src/create-runtime.ts +145 -670
  12. package/src/db/base.service.ts +30 -38
  13. package/src/db/memory-query-builder.ts +2 -1
  14. package/src/db/memory-store.ts +29 -20
  15. package/src/db/memory.ts +188 -195
  16. package/src/db/service-normalization.ts +97 -64
  17. package/src/db/service.ts +496 -384
  18. package/src/db/startup.ts +30 -19
  19. package/src/effect/helpers.ts +30 -5
  20. package/src/effect/index.ts +7 -7
  21. package/src/effect/layers.ts +75 -72
  22. package/src/effect/services.ts +15 -11
  23. package/src/embeddings/provider.ts +65 -71
  24. package/src/index.ts +13 -12
  25. package/src/queues/autonomous-job.queue.ts +177 -143
  26. package/src/queues/context-compaction.queue.ts +41 -39
  27. package/src/queues/delayed-node-promotion.queue.ts +61 -42
  28. package/src/queues/document-processor.queue.ts +5 -3
  29. package/src/queues/index.ts +1 -0
  30. package/src/queues/memory-consolidation.queue.ts +79 -53
  31. package/src/queues/organization-learning.queue.ts +70 -33
  32. package/src/queues/plan-agent-heartbeat.queue.ts +111 -83
  33. package/src/queues/plan-scheduler.queue.ts +101 -97
  34. package/src/queues/post-chat-memory.queue.ts +56 -46
  35. package/src/queues/queue-factory.ts +146 -69
  36. package/src/queues/queues.service.ts +61 -0
  37. package/src/queues/title-generation.queue.ts +44 -44
  38. package/src/redis/connection.ts +181 -164
  39. package/src/redis/org-memory-lock.ts +24 -9
  40. package/src/redis/redis-lease-lock.ts +8 -1
  41. package/src/redis/stream-context.ts +17 -9
  42. package/src/runtime/agent-identity-overrides.ts +7 -3
  43. package/src/runtime/agent-runtime-policy.ts +10 -5
  44. package/src/runtime/agent-stream-helpers.ts +24 -15
  45. package/src/runtime/chat-run-orchestration.ts +1 -1
  46. package/src/runtime/context-compaction/context-compaction-runtime.ts +28 -32
  47. package/src/runtime/context-compaction/context-compaction.ts +131 -85
  48. package/src/runtime/domain-layer.ts +203 -0
  49. package/src/runtime/execution-plan-visibility.ts +5 -2
  50. package/src/runtime/graph-designer.ts +0 -14
  51. package/src/runtime/helper-model.ts +8 -4
  52. package/src/runtime/index.ts +1 -1
  53. package/src/runtime/indexed-repositories-policy.ts +2 -6
  54. package/src/runtime/memory/memory-block.ts +19 -9
  55. package/src/runtime/memory/memory-pipeline.ts +53 -66
  56. package/src/runtime/memory/memory-scope.ts +33 -29
  57. package/src/runtime/plugin-resolution.ts +58 -62
  58. package/src/runtime/post-turn-side-effects.ts +139 -161
  59. package/src/runtime/retrieval-adapters.ts +4 -4
  60. package/src/runtime/runtime-config.ts +3 -9
  61. package/src/runtime/runtime-extensions.ts +0 -43
  62. package/src/runtime/runtime-lifecycle.ts +124 -0
  63. package/src/runtime/runtime-services.ts +455 -0
  64. package/src/runtime/runtime-worker-registry.ts +113 -30
  65. package/src/runtime/social-chat/social-chat-agent-runner.ts +13 -8
  66. package/src/runtime/social-chat/social-chat-history.ts +24 -13
  67. package/src/runtime/social-chat/social-chat.ts +420 -369
  68. package/src/runtime/team-consultation/team-consultation-orchestrator.ts +64 -57
  69. package/src/runtime/team-consultation/team-consultation-prompts.ts +11 -6
  70. package/src/runtime/thread-chat-helpers.ts +18 -9
  71. package/src/runtime/thread-turn-context.ts +28 -74
  72. package/src/runtime/turn-lifecycle.ts +6 -14
  73. package/src/services/agent-activity.service.ts +169 -176
  74. package/src/services/agent-executor.service.ts +207 -196
  75. package/src/services/artifact.service.ts +10 -5
  76. package/src/services/attachment.service.ts +16 -48
  77. package/src/services/autonomous-job.service.ts +81 -87
  78. package/src/services/background-work.service.ts +54 -0
  79. package/src/services/chat-run-registry.service.ts +3 -1
  80. package/src/services/context-compaction.service.ts +8 -10
  81. package/src/services/document-chunk.service.ts +8 -17
  82. package/src/services/execution-plan/execution-plan-graph.ts +122 -109
  83. package/src/services/execution-plan/execution-plan-schedule.ts +1 -15
  84. package/src/services/execution-plan/execution-plan.service.ts +68 -51
  85. package/src/services/feedback-loop.service.ts +1 -1
  86. package/src/services/global-orchestrator.service.ts +49 -15
  87. package/src/services/graph-full-routing.ts +49 -37
  88. package/src/services/index.ts +1 -0
  89. package/src/services/institutional-memory.service.ts +8 -17
  90. package/src/services/learned-skill.service.ts +38 -35
  91. package/src/services/memory/memory-conversation.ts +10 -5
  92. package/src/services/memory/memory-errors.ts +27 -0
  93. package/src/services/memory/memory-org-memory.ts +14 -3
  94. package/src/services/memory/memory-preseeded.ts +10 -4
  95. package/src/services/memory/memory-utils.ts +2 -1
  96. package/src/services/memory/memory.service.ts +37 -52
  97. package/src/services/memory/rerank.service.ts +3 -11
  98. package/src/services/monitoring-window.service.ts +1 -1
  99. package/src/services/mutating-approval.service.ts +1 -1
  100. package/src/services/node-workspace.service.ts +2 -2
  101. package/src/services/notification.service.ts +16 -4
  102. package/src/services/organization-member.service.ts +1 -1
  103. package/src/services/organization.service.ts +34 -51
  104. package/src/services/ownership-dispatcher.service.ts +148 -95
  105. package/src/services/plan/plan-agent-heartbeat.service.ts +30 -16
  106. package/src/services/plan/plan-agent-query.service.ts +13 -9
  107. package/src/services/plan/plan-approval.service.ts +52 -48
  108. package/src/services/plan/plan-artifact.service.ts +2 -2
  109. package/src/services/plan/plan-builder.service.ts +2 -2
  110. package/src/services/plan/plan-checkpoint.service.ts +1 -1
  111. package/src/services/plan/plan-compiler.service.ts +1 -1
  112. package/src/services/plan/plan-completion-side-effects.ts +99 -113
  113. package/src/services/plan/plan-coordination.service.ts +1 -1
  114. package/src/services/plan/plan-cycle.service.ts +171 -202
  115. package/src/services/plan/plan-deadline.service.ts +304 -307
  116. package/src/services/plan/plan-event-delivery.service.ts +84 -72
  117. package/src/services/plan/plan-executor-context.ts +2 -0
  118. package/src/services/plan/plan-executor-graph.ts +375 -353
  119. package/src/services/plan/plan-executor-helpers.ts +60 -75
  120. package/src/services/plan/plan-executor.service.ts +494 -489
  121. package/src/services/plan/plan-run.service.ts +12 -19
  122. package/src/services/plan/plan-scheduler.service.ts +89 -82
  123. package/src/services/plan/plan-template.service.ts +1 -1
  124. package/src/services/plan/plan-transaction-events.ts +8 -5
  125. package/src/services/plan/plan-validator.service.ts +1 -1
  126. package/src/services/plan/plan-workspace.service.ts +17 -11
  127. package/src/services/plugin-executor.service.ts +26 -21
  128. package/src/services/quality-metrics.service.ts +1 -1
  129. package/src/services/queue-job.service.ts +8 -17
  130. package/src/services/recent-activity-title.service.ts +22 -10
  131. package/src/services/recent-activity.service.ts +1 -1
  132. package/src/services/skill-resolver.service.ts +1 -1
  133. package/src/services/social-chat-history.service.ts +37 -20
  134. package/src/services/system-executor.service.ts +25 -20
  135. package/src/services/thread/thread-bootstrap.ts +37 -19
  136. package/src/services/thread/thread-listing.ts +2 -1
  137. package/src/services/thread/thread-memory-block.ts +18 -5
  138. package/src/services/thread/thread-message.service.ts +30 -13
  139. package/src/services/thread/thread-title.service.ts +1 -1
  140. package/src/services/thread/thread-turn-execution.ts +87 -83
  141. package/src/services/thread/thread-turn-preparation.service.ts +65 -40
  142. package/src/services/thread/thread-turn-streaming.ts +32 -36
  143. package/src/services/thread/thread-turn.ts +43 -29
  144. package/src/services/thread/thread.service.ts +32 -8
  145. package/src/services/user.service.ts +1 -1
  146. package/src/services/write-intent-validator.service.ts +1 -1
  147. package/src/storage/attachment-storage.service.ts +7 -4
  148. package/src/storage/generated-document-storage.service.ts +1 -1
  149. package/src/system-agents/context-compaction.agent.ts +1 -1
  150. package/src/system-agents/helper-agent-options.ts +1 -1
  151. package/src/system-agents/memory-reranker.agent.ts +1 -1
  152. package/src/system-agents/memory.agent.ts +1 -1
  153. package/src/system-agents/recent-activity-title-refiner.agent.ts +9 -6
  154. package/src/system-agents/regular-chat-memory-digest.agent.ts +1 -1
  155. package/src/system-agents/skill-extractor.agent.ts +1 -1
  156. package/src/system-agents/skill-manager.agent.ts +1 -1
  157. package/src/system-agents/thread-router.agent.ts +23 -20
  158. package/src/system-agents/title-generator.agent.ts +1 -1
  159. package/src/tools/execution-plan.tool.ts +36 -20
  160. package/src/tools/fetch-webpage.tool.ts +30 -22
  161. package/src/tools/firecrawl-client.ts +1 -6
  162. package/src/tools/plan-approval.tool.ts +9 -1
  163. package/src/tools/remember-memory.tool.ts +3 -6
  164. package/src/tools/research-topic.tool.ts +12 -3
  165. package/src/tools/search-web.tool.ts +26 -18
  166. package/src/tools/search.tool.ts +4 -5
  167. package/src/tools/team-think.tool.ts +139 -121
  168. package/src/utils/async.ts +15 -6
  169. package/src/utils/errors.ts +27 -15
  170. package/src/workers/bootstrap.ts +34 -58
  171. package/src/workers/memory-consolidation.worker.ts +4 -1
  172. package/src/workers/organization-learning.worker.ts +16 -3
  173. package/src/workers/regular-chat-memory-digest.helpers.ts +3 -4
  174. package/src/workers/regular-chat-memory-digest.runner.ts +46 -29
  175. package/src/workers/skill-extraction.runner.ts +13 -15
  176. package/src/workers/worker-utils.ts +14 -8
  177. package/src/config/search.ts +0 -3
  178. package/src/effect/awaitable-effect.ts +0 -87
  179. package/src/effect/runtime-ref.ts +0 -25
  180. package/src/effect/runtime.ts +0 -31
  181. package/src/redis/runtime-connection.ts +0 -10
  182. package/src/runtime/agent-types.ts +0 -1
@@ -10,20 +10,19 @@ import type { ChatMessage, ConsultSpecialistArgs } from '@lota-sdk/shared'
10
10
  import { tool as createTool } from 'ai'
11
11
  import { Chat, ConsoleLogger } from 'chat'
12
12
  import type { Message, Thread, WebhookOptions } from 'chat'
13
- import type { Context } from 'effect'
14
- import { Cause, Clock, Effect } from 'effect'
13
+ import type { Context, Cause } from 'effect'
14
+ import { Clock, Effect, Schema } from 'effect'
15
15
 
16
- import { getAgentDisplayNames, getTeamConsultParticipants } from '../../config/agent-defaults'
16
+ import type { ResolvedAgentConfig, ResolvedAgentFactoryConfig } from '../../config/agent-defaults'
17
17
  import { aiLogger } from '../../config/logger'
18
18
  import { recordIdToString } from '../../db/record-id'
19
19
  import { TABLES } from '../../db/tables'
20
20
  import { ForbiddenError } from '../../effect/errors'
21
21
  import { effectTryMaybeAsync, effectTryPromise } from '../../effect/helpers'
22
- import { enqueueRegularChatMemoryDigest, enqueueSkillExtraction } from '../../queues/organization-learning.queue'
23
- import { enqueuePostChatMemory } from '../../queues/post-chat-memory.queue'
22
+ import type { LotaQueuesRuntime } from '../../queues/queues.service'
24
23
  import type { LearnedSkillServiceTag } from '../../services/learned-skill.service'
25
24
  import type { MemoryServiceTag } from '../../services/memory/memory.service'
26
- import type { makeSocialChatHistoryService } from '../../services/social-chat-history.service'
25
+ import type { SocialChatHistoryError, makeSocialChatHistoryService } from '../../services/social-chat-history.service'
27
26
  import { safeEnqueue } from '../../utils/async'
28
27
  import { buildAgentPromptContext } from '../agent-prompt-context'
29
28
  import { createServerRunAbortController } from '../agent-stream-helpers'
@@ -32,7 +31,7 @@ import type {
32
31
  LotaRuntimeSocialChatConfig,
33
32
  LotaSocialChatResolvedContext,
34
33
  } from '../runtime-config'
35
- import { getRuntimeAdapters } from '../runtime-extensions'
34
+ import type { LotaRuntimeAdapters } from '../runtime-extensions'
36
35
  import { runSpecialistSession } from '../specialist-runner'
37
36
  import {
38
37
  buildAgentHistoryMessages,
@@ -58,8 +57,15 @@ export interface SocialChatRuntimeServices {
58
57
  learnedSkillService: Context.Service.Shape<typeof LearnedSkillServiceTag>
59
58
  memoryService: Context.Service.Shape<typeof MemoryServiceTag>
60
59
  socialChatHistoryService: ReturnType<typeof makeSocialChatHistoryService>
60
+ runtimeAdapters: LotaRuntimeAdapters
61
+ queues: LotaQueuesRuntime
61
62
  }
62
63
 
64
+ class SocialChatServiceError extends Schema.TaggedErrorClass<SocialChatServiceError>()('SocialChatServiceError', {
65
+ message: Schema.String,
66
+ cause: Schema.optional(Schema.Defect),
67
+ }) {}
68
+
63
69
  const DEFAULT_SOCIAL_CHAT_AGENT_ID = 'socialChat'
64
70
  const DEFAULT_SOCIAL_CHAT_AGENT_DISPLAY_NAME = 'Lota'
65
71
  const DEFAULT_SOCIAL_CHAT_STATE_PREFIX = 'lota:social:chat-sdk'
@@ -107,8 +113,8 @@ function createAssistantMessage(params: {
107
113
  }
108
114
  }
109
115
 
110
- function getAgentDisplayName(agentId: string): string {
111
- return getAgentDisplayNames()[agentId] ?? agentId
116
+ function getAgentDisplayName(agentConfig: ResolvedAgentConfig, agentId: string): string {
117
+ return agentConfig.displayNames[agentId] ?? agentId
112
118
  }
113
119
 
114
120
  function toSafeJobIdSegment(value: string): string {
@@ -152,6 +158,8 @@ function buildBuildToolsParams(params: {
152
158
  }
153
159
 
154
160
  export function createSocialChatRuntime(params: {
161
+ agentConfig: ResolvedAgentConfig
162
+ agentFactoryConfig: ResolvedAgentFactoryConfig
155
163
  redisClient: SocialChatStateRedisClient
156
164
  socialChat?: LotaRuntimeSocialChatConfig
157
165
  services: SocialChatRuntimeServices
@@ -192,384 +200,427 @@ export function createSocialChatRuntime(params: {
192
200
  const handleMessage = (
193
201
  thread: Thread,
194
202
  incomingMessage: Message,
195
- ): Effect.Effect<void, Cause.UnknownError | ForbiddenError, never> =>
196
- Effect.gen(function* () {
197
- const { memoryService, learnedSkillService, socialChatHistoryService } = params.services
198
- const currentContext = yield* Effect.context()
199
- const runPromiseWithCurrentContext = Effect.runPromiseWith(currentContext)
200
- const rawSlackMessage = incomingMessage.raw as { channel?: unknown } | undefined
201
- const channelId = toOptionalTrimmedString(rawSlackMessage?.channel) ?? thread.channelId
202
- const messageContext: SlackSocialMessageContext = {
203
- channelId,
204
- threadId: thread.id,
205
- messageId: incomingMessage.id,
206
- text: incomingMessage.text.trim(),
207
- authorId: toOptionalTrimmedString(incomingMessage.author.userId) ?? undefined,
208
- authorName: readSlackAuthorName(incomingMessage),
209
- }
210
- aiLogger.info`Slack social-chat message received: channelId=${messageContext.channelId}, threadId=${messageContext.threadId}, messageId=${messageContext.messageId}, author=${messageContext.authorName ?? 'unknown'}, textLength=${messageContext.text.length}`
211
-
212
- const resolvedContext = yield* effectTryMaybeAsync(() =>
213
- socialChatConfig.resolveContext({
214
- platform: 'slack',
215
- channelId: messageContext.channelId,
216
- threadId: messageContext.threadId,
217
- messageId: messageContext.messageId,
218
- text: messageContext.text,
219
- authorId: messageContext.authorId,
220
- authorName: messageContext.authorName,
221
- }),
222
- )
223
- const workspaceIdString = recordIdToString(resolvedContext.workspaceId, TABLES.ORGANIZATION)
224
- const userIdString = recordIdToString(resolvedContext.userId, TABLES.USER)
225
- aiLogger.info`Slack social-chat context resolved: workspaceId=${workspaceIdString}, userId=${userIdString}`
226
-
227
- const threadMessages = yield* effectTryPromise(() => collectThreadMessages(thread, incomingMessage))
228
- const normalizedMessages = threadMessages
229
- .map((message) =>
230
- normalizeSocialHistoryMessage({
231
- workspaceId: workspaceIdString,
203
+ ): Effect.Effect<
204
+ void,
205
+ Cause.UnknownError | ForbiddenError | SocialChatHistoryError | SocialChatServiceError,
206
+ never
207
+ > =>
208
+ Effect.scoped(
209
+ Effect.gen(function* () {
210
+ const { memoryService, learnedSkillService, socialChatHistoryService } = params.services
211
+ const agentConfig = params.agentConfig
212
+ const agentFactoryConfig = params.agentFactoryConfig
213
+ const currentContext = yield* Effect.context()
214
+ const runPromiseWithCurrentContext = Effect.runPromiseWith(currentContext)
215
+ const rawSlackMessage = incomingMessage.raw as { channel?: unknown } | undefined
216
+ const channelId = toOptionalTrimmedString(rawSlackMessage?.channel) ?? thread.channelId
217
+ const messageContext: SlackSocialMessageContext = {
218
+ channelId,
219
+ threadId: thread.id,
220
+ messageId: incomingMessage.id,
221
+ text: incomingMessage.text.trim(),
222
+ authorId: toOptionalTrimmedString(incomingMessage.author.userId) ?? undefined,
223
+ authorName: readSlackAuthorName(incomingMessage),
224
+ }
225
+ 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
+
227
+ const resolvedContext = yield* effectTryMaybeAsync(() =>
228
+ socialChatConfig.resolveContext({
229
+ platform: 'slack',
232
230
  channelId: messageContext.channelId,
233
- agentId: socialAgentId,
234
- agentDisplayName: socialAgentDisplayName,
235
- message,
231
+ threadId: messageContext.threadId,
232
+ messageId: messageContext.messageId,
233
+ text: messageContext.text,
234
+ authorId: messageContext.authorId,
235
+ authorName: messageContext.authorName,
236
236
  }),
237
237
  )
238
- .filter((message): message is NonNullable<typeof message> => message !== null)
239
- yield* socialChatHistoryService.upsertMessages(normalizedMessages)
240
-
241
- const historyBeforeReply = yield* socialChatHistoryService.listThreadMessages({
242
- workspaceId: workspaceIdString,
243
- threadId: messageContext.threadId,
244
- })
245
- const currentUserMessage =
246
- historyBeforeReply.find((message) => message.messageId === incomingMessage.id) ?? historyBeforeReply.at(-1)
247
- const priorHistory = currentUserMessage
248
- ? historyBeforeReply.filter((message) => message.cursor.id !== currentUserMessage.cursor.id)
249
- : historyBeforeReply
250
-
251
- const workspaceProvider = getRuntimeAdapters().workspaceProvider
252
- const getWorkspace = workspaceProvider?.getWorkspace
253
- ? workspaceProvider.getWorkspace.bind(workspaceProvider)
254
- : undefined
255
- const getLifecycleState = workspaceProvider?.getLifecycleState
256
- ? workspaceProvider.getLifecycleState.bind(workspaceProvider)
257
- : undefined
258
- const readProfileProjectionState = workspaceProvider?.readProfileProjectionState
259
- ? workspaceProvider.readProfileProjectionState.bind(workspaceProvider)
260
- : undefined
261
- const listRecentDomainEvents = workspaceProvider?.listRecentDomainEvents
262
- ? workspaceProvider.listRecentDomainEvents.bind(workspaceProvider)
263
- : undefined
264
- const buildPromptSummary = workspaceProvider?.buildPromptSummary
265
- ? workspaceProvider.buildPromptSummary.bind(workspaceProvider)
266
- : undefined
267
- const buildRetrievedKnowledgeSection = workspaceProvider?.buildRetrievedKnowledgeSection
268
- ? workspaceProvider.buildRetrievedKnowledgeSection.bind(workspaceProvider)
269
- : undefined
270
- const getConsultParticipants = socialChatConfig.getConsultParticipants
271
-
272
- const workspace = getWorkspace ? yield* effectTryMaybeAsync(() => getWorkspace(resolvedContext.workspaceId)) : {}
273
- const lifecycleState = getLifecycleState
274
- ? yield* effectTryMaybeAsync(() => getLifecycleState(workspace))
275
- : undefined
276
- const workspaceProfileState = readProfileProjectionState
277
- ? yield* effectTryMaybeAsync(() => readProfileProjectionState(workspace))
278
- : undefined
279
- const recentDomainEvents = listRecentDomainEvents
280
- ? yield* effectTryMaybeAsync(() => listRecentDomainEvents(resolvedContext.workspaceId, 5))
281
- : ([] as Array<Record<string, unknown>>)
282
- const promptSummary = buildPromptSummary
283
- ? yield* effectTryMaybeAsync(() => buildPromptSummary(resolvedContext.workspaceId)).pipe(
284
- Effect.orElseSucceed(() => undefined),
238
+ const workspaceIdString = recordIdToString(resolvedContext.workspaceId, TABLES.ORGANIZATION)
239
+ const userIdString = recordIdToString(resolvedContext.userId, TABLES.USER)
240
+ aiLogger.debug`Slack social-chat context resolved: workspaceId=${workspaceIdString}, userId=${userIdString}`
241
+
242
+ const threadMessages = yield* effectTryPromise(() => collectThreadMessages(thread, incomingMessage))
243
+ const normalizedMessages = threadMessages
244
+ .map((message) =>
245
+ normalizeSocialHistoryMessage({
246
+ workspaceId: workspaceIdString,
247
+ channelId: messageContext.channelId,
248
+ agentId: socialAgentId,
249
+ agentDisplayName: socialAgentDisplayName,
250
+ message,
251
+ }),
285
252
  )
286
- : undefined
287
-
288
- const promptContext = buildAgentPromptContext({
289
- workspaceName:
290
- workspaceProfileState?.workspaceName ??
291
- toOptionalTrimmedString((workspace as { name?: unknown }).name) ??
292
- undefined,
293
- summaryBlock: workspaceProfileState?.summaryBlock,
294
- promptSummary,
295
- userName: messageContext.authorName,
296
- recentDomainEvents,
297
- })
298
- const retrievedKnowledgeSection =
299
- lifecycleState?.bootstrapActive || messageContext.text.length === 0
300
- ? undefined
301
- : buildRetrievedKnowledgeSection
302
- ? yield* effectTryMaybeAsync(() =>
303
- buildRetrievedKnowledgeSection({
304
- workspaceId: workspaceIdString,
305
- userId: userIdString,
306
- query: messageContext.text,
253
+ .filter((message): message is NonNullable<typeof message> => message !== null)
254
+ yield* socialChatHistoryService.upsertMessages(normalizedMessages)
255
+
256
+ const historyBeforeReply = yield* socialChatHistoryService.listThreadMessages({
257
+ workspaceId: workspaceIdString,
258
+ threadId: messageContext.threadId,
259
+ })
260
+ const currentUserMessage =
261
+ historyBeforeReply.find((message) => message.messageId === incomingMessage.id) ?? historyBeforeReply.at(-1)
262
+ const priorHistory = currentUserMessage
263
+ ? historyBeforeReply.filter((message) => message.cursor.id !== currentUserMessage.cursor.id)
264
+ : historyBeforeReply
265
+
266
+ const workspaceProvider = params.services.runtimeAdapters.workspaceProvider
267
+ const getWorkspace = workspaceProvider?.getWorkspace
268
+ ? workspaceProvider.getWorkspace.bind(workspaceProvider)
269
+ : undefined
270
+ const getLifecycleState = workspaceProvider?.getLifecycleState
271
+ ? workspaceProvider.getLifecycleState.bind(workspaceProvider)
272
+ : undefined
273
+ const readProfileProjectionState = workspaceProvider?.readProfileProjectionState
274
+ ? workspaceProvider.readProfileProjectionState.bind(workspaceProvider)
275
+ : undefined
276
+ const listRecentDomainEvents = workspaceProvider?.listRecentDomainEvents
277
+ ? workspaceProvider.listRecentDomainEvents.bind(workspaceProvider)
278
+ : undefined
279
+ const buildPromptSummary = workspaceProvider?.buildPromptSummary
280
+ ? workspaceProvider.buildPromptSummary.bind(workspaceProvider)
281
+ : undefined
282
+ const buildRetrievedKnowledgeSection = workspaceProvider?.buildRetrievedKnowledgeSection
283
+ ? workspaceProvider.buildRetrievedKnowledgeSection.bind(workspaceProvider)
284
+ : undefined
285
+ const getConsultParticipants = socialChatConfig.getConsultParticipants
286
+
287
+ const workspace = getWorkspace
288
+ ? yield* effectTryMaybeAsync(() => getWorkspace(resolvedContext.workspaceId))
289
+ : {}
290
+ const lifecycleState = getLifecycleState
291
+ ? yield* effectTryMaybeAsync(() => getLifecycleState(workspace))
292
+ : undefined
293
+ const workspaceProfileState = readProfileProjectionState
294
+ ? yield* effectTryMaybeAsync(() => readProfileProjectionState(workspace))
295
+ : undefined
296
+ const recentDomainEvents = listRecentDomainEvents
297
+ ? yield* effectTryMaybeAsync(() => listRecentDomainEvents(resolvedContext.workspaceId, 5))
298
+ : ([] as Array<Record<string, unknown>>)
299
+ const promptSummary = buildPromptSummary
300
+ ? yield* effectTryMaybeAsync(() => buildPromptSummary(resolvedContext.workspaceId)).pipe(
301
+ Effect.orElseSucceed(() => undefined),
302
+ )
303
+ : undefined
304
+
305
+ const promptContext = buildAgentPromptContext({
306
+ workspaceName:
307
+ workspaceProfileState?.workspaceName ??
308
+ toOptionalTrimmedString((workspace as { name?: unknown }).name) ??
309
+ undefined,
310
+ summaryBlock: workspaceProfileState?.summaryBlock,
311
+ promptSummary,
312
+ userName: messageContext.authorName,
313
+ recentDomainEvents,
314
+ })
315
+ const retrievedKnowledgeSection =
316
+ lifecycleState?.bootstrapActive || messageContext.text.length === 0
317
+ ? undefined
318
+ : buildRetrievedKnowledgeSection
319
+ ? yield* effectTryMaybeAsync(() =>
320
+ buildRetrievedKnowledgeSection({
321
+ workspaceId: workspaceIdString,
322
+ userId: userIdString,
323
+ query: messageContext.text,
324
+ }),
325
+ )
326
+ : undefined
327
+
328
+ const preSeededMemoriesSection = yield* memoryService
329
+ .getTopMemories({ orgId: workspaceIdString, agentName: socialAgentId, limit: PRESEEDED_MEMORY_LOOKUP_LIMIT })
330
+ .pipe(
331
+ Effect.mapError(
332
+ (cause) =>
333
+ new SocialChatServiceError({
334
+ message: `Failed to load pre-seeded memories for ${socialAgentId}.`,
335
+ cause,
307
336
  }),
308
- )
309
- : undefined
310
-
311
- const preSeededMemoriesSection = yield* memoryService
312
- .getTopMemories({ orgId: workspaceIdString, agentName: socialAgentId, limit: PRESEEDED_MEMORY_LOOKUP_LIMIT })
313
- .pipe(Effect.mapError((cause) => new Cause.UnknownError(cause)))
314
- const learnedSkillsSection = lifecycleState?.bootstrapActive
315
- ? undefined
316
- : yield* learnedSkillService
317
- .retrieveForTurn({
318
- orgId: workspaceIdString,
319
- agentId: socialAgentId,
320
- query: messageContext.text,
321
- limit: 3,
322
- minConfidence: 0.6,
323
- })
324
- .pipe(Effect.orElseSucceed(() => undefined))
325
-
326
- let memoryBlock = ''
327
- const consultedAgents = getConsultParticipants
328
- ? yield* effectTryMaybeAsync(() =>
329
- getConsultParticipants({ workspaceId: resolvedContext.workspaceId, workspaceIdString, platform: 'slack' }),
337
+ ),
330
338
  )
331
- : [...getTeamConsultParticipants()]
332
- const consultParticipants = [...new Set(consultedAgents)].filter((agentId) => agentId !== socialAgentId)
333
- const executedToolNames: string[] = []
334
-
335
- const baseTools = withLoggedSocialToolSet(
336
- yield* effectTryMaybeAsync(() =>
337
- socialChatConfig.buildAgentTools(
338
- buildBuildToolsParams({
339
- agentId: socialAgentId,
340
- context: resolvedContext,
341
- workspaceIdString,
342
- userIdString,
343
- messageContext,
344
- memoryBlock,
345
- onAppendMemoryBlock: (value: string) => {
346
- memoryBlock = value
347
- },
348
- }),
339
+ const learnedSkillsSection = lifecycleState?.bootstrapActive
340
+ ? undefined
341
+ : yield* learnedSkillService
342
+ .retrieveForTurn({
343
+ orgId: workspaceIdString,
344
+ agentId: socialAgentId,
345
+ query: messageContext.text,
346
+ limit: 3,
347
+ minConfidence: 0.6,
348
+ })
349
+ .pipe(Effect.orElseSucceed(() => undefined))
350
+
351
+ let memoryBlock = ''
352
+ const consultedAgents = getConsultParticipants
353
+ ? yield* effectTryMaybeAsync(() =>
354
+ getConsultParticipants({
355
+ workspaceId: resolvedContext.workspaceId,
356
+ workspaceIdString,
357
+ platform: 'slack',
358
+ }),
359
+ )
360
+ : [...agentConfig.teamConsultParticipants]
361
+ const consultParticipants = [...new Set(consultedAgents)].filter((agentId) => agentId !== socialAgentId)
362
+ const executedToolNames: string[] = []
363
+
364
+ 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
+ ),
349
379
  ),
350
- ),
351
- {
352
- agentId: socialAgentId,
353
- channelId: messageContext.channelId,
354
- threadId: messageContext.threadId,
355
- executedToolNames,
356
- },
357
- )
358
-
359
- const transcript = buildSocialChatThreadTranscript(historyBeforeReply)
360
- const runAbort = createServerRunAbortController()
361
-
362
- const consultSpecialistTool = createTool({
363
- description: 'Consult one specialist teammate for targeted guidance before replying to the user.',
364
- inputSchema: ConsultSpecialistArgsSchema,
365
- execute: ({ agentId, task }: ConsultSpecialistArgs) =>
366
- runPromiseWithCurrentContext(
367
- Effect.gen(function* () {
368
- if (!consultParticipants.includes(agentId)) {
369
- return yield* new ForbiddenError({
370
- message: `Agent "${agentId}" is not an allowed social-chat specialist.`,
371
- })
372
- }
373
-
374
- const { result: specialistRun } = yield* effectTryMaybeAsync(() =>
375
- runSpecialistSession({
376
- initialMemoryBlock: '',
377
- buildTools: ({ memoryBlock: currentMemoryBlock, onAppendMemoryBlock }) =>
378
- runPromiseWithCurrentContext(
379
- Effect.gen(function* () {
380
- const tools = yield* effectTryMaybeAsync(() =>
381
- socialChatConfig.buildAgentTools(
382
- buildBuildToolsParams({
383
- agentId,
384
- context: resolvedContext,
385
- workspaceIdString,
386
- userIdString,
387
- messageContext,
388
- memoryBlock: currentMemoryBlock,
389
- onAppendMemoryBlock,
390
- }),
391
- ),
392
- )
393
-
394
- return withLoggedSocialToolSet(tools, {
395
- agentId,
396
- channelId: messageContext.channelId,
397
- threadId: messageContext.threadId,
398
- executedToolNames,
399
- })
400
- }),
401
- ),
402
- run: ({ tools }) =>
403
- runPromiseWithCurrentContext(
404
- Effect.gen(function* () {
405
- const specialistPreSeededMemories = yield* memoryService
406
- .getTopMemories({
407
- orgId: workspaceIdString,
408
- agentName: agentId,
409
- limit: PRESEEDED_MEMORY_LOOKUP_LIMIT,
410
- })
411
- .pipe(Effect.mapError((cause) => new Cause.UnknownError(cause)))
412
- const specialistLearnedSkills = lifecycleState?.bootstrapActive
413
- ? undefined
414
- : yield* learnedSkillService
415
- .retrieveForTurn({
416
- orgId: workspaceIdString,
380
+ {
381
+ agentId: socialAgentId,
382
+ channelId: messageContext.channelId,
383
+ threadId: messageContext.threadId,
384
+ executedToolNames,
385
+ },
386
+ )
387
+
388
+ const transcript = buildSocialChatThreadTranscript(agentConfig, historyBeforeReply)
389
+ const runAbort = yield* Effect.acquireRelease(
390
+ Effect.sync(() => createServerRunAbortController()),
391
+ (controller) => Effect.sync(() => controller.dispose()),
392
+ )
393
+
394
+ const consultSpecialistTool = createTool({
395
+ description: 'Consult one specialist teammate for targeted guidance before replying to the user.',
396
+ inputSchema: ConsultSpecialistArgsSchema,
397
+ execute: ({ agentId, task }: ConsultSpecialistArgs) =>
398
+ runPromiseWithCurrentContext(
399
+ Effect.gen(function* () {
400
+ if (!consultParticipants.includes(agentId)) {
401
+ return yield* new ForbiddenError({
402
+ message: `Agent "${agentId}" is not an allowed social-chat specialist.`,
403
+ })
404
+ }
405
+
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({
417
415
  agentId,
418
- query: task,
419
- limit: 3,
420
- minConfidence: 0.6,
421
- })
422
- .pipe(Effect.orElseSucceed(() => undefined))
423
-
424
- return yield* effectTryPromise(() =>
425
- runSocialAgentTurn({
416
+ context: resolvedContext,
417
+ workspaceIdString,
418
+ userIdString,
419
+ messageContext,
420
+ memoryBlock: currentMemoryBlock,
421
+ onAppendMemoryBlock,
422
+ }),
423
+ ),
424
+ )
425
+
426
+ return withLoggedSocialToolSet(tools, {
426
427
  agentId,
427
- mode: 'fixedThreadMode',
428
- threadType: 'group',
429
- onboardingActive: lifecycleState?.bootstrapActive ?? false,
430
- linearInstalled: false,
431
- systemWorkspaceDetails: promptContext.systemWorkspaceDetails,
432
- preSeededMemoriesSection: specialistPreSeededMemories,
433
- retrievedKnowledgeSection,
434
- learnedSkillsSection: specialistLearnedSkills,
435
- userMessageText: task,
436
- additionalInstructionSections: [
437
- `You are supporting ${socialAgentDisplayName} in a Slack social-chat thread. Stay within your role.`,
438
- ],
439
- tools,
440
- prompt: buildSpecialistSocialChatPrompt({
441
- requesterName: socialAgentDisplayName,
442
- agentName: getAgentDisplayName(agentId),
443
- task,
444
- transcript,
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,
449
+ }),
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,
467
+ 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,
445
488
  }),
446
- abortSignal: runAbort.signal,
447
- }),
448
- )
449
- }),
450
- ),
451
- }),
452
- )
453
- const text = specialistRun.text
454
- const createdAt = yield* Clock.currentTimeMillis
455
-
456
- return createAssistantMessage({ agentId, agentName: getAgentDisplayName(agentId), text, createdAt })
489
+ )
490
+ }),
491
+ ),
492
+ }),
493
+ )
494
+ const text = specialistRun.text
495
+ const createdAt = yield* Clock.currentTimeMillis
496
+
497
+ return createAssistantMessage({
498
+ agentId,
499
+ agentName: getAgentDisplayName(agentConfig, agentId),
500
+ text,
501
+ createdAt,
502
+ })
503
+ }),
504
+ ),
505
+ toModelOutput: ({ output }) => {
506
+ const message = output
507
+ const agentName =
508
+ typeof message.metadata?.agentName === 'string' && message.metadata.agentName.trim().length > 0
509
+ ? message.metadata.agentName.trim()
510
+ : 'Specialist'
511
+ const summary = extractMessageText(message).trim()
512
+ return {
513
+ type: 'text',
514
+ value: summary ? `${agentName}: ${summary}` : `${agentName} completed the requested task.`,
515
+ }
516
+ },
517
+ })
518
+
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,
457
543
  }),
458
- ),
459
- toModelOutput: ({ output }) => {
460
- const message = output
461
- const agentName =
462
- typeof message.metadata?.agentName === 'string' && message.metadata.agentName.trim().length > 0
463
- ? message.metadata.agentName.trim()
464
- : 'Specialist'
465
- const summary = extractMessageText(message).trim()
466
- return {
467
- type: 'text',
468
- value: summary ? `${agentName}: ${summary}` : `${agentName} completed the requested task.`,
469
- }
470
- },
471
- })
472
-
473
- yield* effectTryPromise(() => thread.startTyping('Thinking...')).pipe(Effect.orElseSucceed(() => undefined))
474
- aiLogger.info`Slack social-chat generating reply: channelId=${messageContext.channelId}, threadId=${messageContext.threadId}`
475
- const leadRun = yield* effectTryPromise(() =>
476
- runSocialAgentTurn({
477
- agentId: socialAgentId,
478
- mode: 'threadMode',
479
- threadType: 'group',
480
- onboardingActive: lifecycleState?.bootstrapActive ?? false,
481
- linearInstalled: false,
482
- systemWorkspaceDetails: promptContext.systemWorkspaceDetails,
483
- preSeededMemoriesSection,
484
- retrievedKnowledgeSection,
485
- learnedSkillsSection,
486
- userMessageText: messageContext.text,
487
- additionalInstructionSections: [buildSocialChatIdentitySection(socialAgentDisplayName)],
488
- tools: { ...baseTools, [CONSULT_SPECIALIST_TOOL_NAME]: consultSpecialistTool },
489
- prompt: buildLeadSocialChatPrompt({
490
- agentDisplayName: socialAgentDisplayName,
491
- channelId: messageContext.channelId,
492
- threadId: messageContext.threadId,
493
- transcript,
494
- latestUserMessage: messageContext.text,
495
- latestAuthorName: messageContext.authorName,
544
+ abortSignal: runAbort.signal,
496
545
  }),
497
- abortSignal: runAbort.signal,
498
- }),
499
- )
500
- const responseText = leadRun.text
501
-
502
- const replyMarkdown = buildSlackSocialReplyMarkdown({ replyMarkdown: responseText, executedToolNames })
503
- const sentMessage = yield* effectTryPromise(() => thread.post({ markdown: replyMarkdown }))
504
- aiLogger.info`Slack social-chat reply posted: channelId=${messageContext.channelId}, threadId=${messageContext.threadId}, replyMessageId=${sentMessage.id}`
505
- const normalizedResponse = normalizeSocialHistoryMessage({
506
- workspaceId: workspaceIdString,
507
- channelId: messageContext.channelId,
508
- agentId: socialAgentId,
509
- agentDisplayName: socialAgentDisplayName,
510
- message: sentMessage,
511
- textOverride: responseText,
512
- })
513
- if (normalizedResponse) {
514
- yield* socialChatHistoryService.upsertMessages([normalizedResponse])
515
- }
516
-
517
- const priorHistoryMessages = toHistoryMessages(priorHistory)
518
- const agentMessages = normalizedResponse ? buildAgentHistoryMessages([normalizedResponse]) : []
519
- if (messageContext.text && agentMessages.length > 0) {
546
+ )
547
+ const responseText = leadRun.text
548
+
549
+ const replyMarkdown = buildSlackSocialReplyMarkdown({ replyMarkdown: responseText, executedToolNames })
550
+ const sentMessage = yield* effectTryPromise(() => thread.post({ markdown: replyMarkdown }))
551
+ aiLogger.debug`Slack social-chat reply posted: channelId=${messageContext.channelId}, threadId=${messageContext.threadId}, replyMessageId=${sentMessage.id}`
552
+ const normalizedResponse = normalizeSocialHistoryMessage({
553
+ workspaceId: workspaceIdString,
554
+ channelId: messageContext.channelId,
555
+ agentId: socialAgentId,
556
+ agentDisplayName: socialAgentDisplayName,
557
+ message: sentMessage,
558
+ textOverride: responseText,
559
+ })
560
+ if (normalizedResponse) {
561
+ yield* socialChatHistoryService.upsertMessages([normalizedResponse])
562
+ }
563
+
564
+ const priorHistoryMessages = toHistoryMessages(agentConfig, priorHistory)
565
+ const agentMessages = normalizedResponse ? buildAgentHistoryMessages(agentConfig, [normalizedResponse]) : []
566
+ 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,
591
+ },
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
+ )
604
+ }
605
+
520
606
  yield* effectTryPromise(() =>
521
607
  safeEnqueue(
522
608
  () =>
523
- enqueuePostChatMemory(
524
- {
525
- orgId: workspaceIdString,
526
- threadId: `social:slack:${messageContext.threadId}`,
527
- sourceId: createSocialChatCursorId({
528
- workspaceId: workspaceIdString,
529
- threadId: messageContext.threadId,
530
- messageId: messageContext.messageId,
531
- }),
532
- userMessage: messageContext.text,
533
- historyMessages: priorHistoryMessages,
534
- agentMessages,
535
- memoryBlock: memoryBlock.trim() ? memoryBlock : undefined,
536
- source: 'social_chat',
537
- sourceMetadata: {
538
- platform: 'slack',
539
- channelId: messageContext.channelId,
540
- threadId: messageContext.threadId,
541
- messageId: messageContext.messageId,
542
- authorId: messageContext.authorId,
543
- authorName: messageContext.authorName,
544
- },
545
- },
546
- {
547
- dedupeKey: createSocialMemoryDedupeKey({
548
- workspaceId: workspaceIdString,
549
- threadId: messageContext.threadId,
550
- messageId: messageContext.messageId,
551
- }),
552
- },
553
- ),
554
- { operationName: 'social post-chat memory extraction enqueue' },
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' },
555
617
  ),
556
618
  )
557
- }
558
-
559
- yield* effectTryPromise(() =>
560
- safeEnqueue(() => enqueueRegularChatMemoryDigest({ orgId: workspaceIdString }), {
561
- operationName: 'social regular chat memory digest enqueue',
562
- }),
563
- )
564
- yield* effectTryPromise(() =>
565
- safeEnqueue(() => enqueueSkillExtraction({ orgId: workspaceIdString }), {
566
- operationName: 'social skill extraction enqueue',
567
- }),
568
- )
569
- })
619
+ }),
620
+ )
570
621
 
571
622
  chat.onNewMention((thread, message) => {
572
- aiLogger.info`Slack social-chat new mention received: threadId=${thread.id}, messageId=${message.id}`
623
+ aiLogger.debug`Slack social-chat new mention received: threadId=${thread.id}, messageId=${message.id}`
573
624
  return Effect.runPromise(
574
625
  Effect.gen(function* () {
575
626
  yield* effectTryPromise(() => thread.subscribe())
@@ -578,7 +629,7 @@ export function createSocialChatRuntime(params: {
578
629
  )
579
630
  })
580
631
  chat.onSubscribedMessage((thread, message) => {
581
- aiLogger.info`Slack social-chat subscribed thread message received: threadId=${thread.id}, messageId=${message.id}`
632
+ aiLogger.debug`Slack social-chat subscribed thread message received: threadId=${thread.id}, messageId=${message.id}`
582
633
  return Effect.runPromise(handleMessage(thread, message))
583
634
  })
584
635