@lota-sdk/core 0.4.7 → 0.4.9

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 (259) hide show
  1. package/package.json +11 -12
  2. package/src/ai/embedding-cache.ts +94 -22
  3. package/src/ai-gateway/ai-gateway.ts +738 -223
  4. package/src/config/agent-defaults.ts +176 -75
  5. package/src/config/agent-types.ts +54 -4
  6. package/src/config/constants.ts +8 -2
  7. package/src/config/logger.ts +286 -19
  8. package/src/config/model-constants.ts +1 -0
  9. package/src/config/thread-defaults.ts +33 -21
  10. package/src/create-runtime.ts +725 -383
  11. package/src/db/base.service.ts +52 -28
  12. package/src/db/cursor-pagination.ts +71 -30
  13. package/src/db/memory-store.helpers.ts +4 -7
  14. package/src/db/memory-store.ts +856 -598
  15. package/src/db/memory.ts +398 -275
  16. package/src/db/record-id.ts +32 -10
  17. package/src/db/schema-fingerprint.ts +30 -12
  18. package/src/db/service-normalization.ts +255 -0
  19. package/src/db/service.ts +726 -761
  20. package/src/db/startup.ts +140 -66
  21. package/src/db/transaction-conflict.ts +15 -0
  22. package/src/effect/awaitable-effect.ts +87 -0
  23. package/src/effect/errors.ts +121 -0
  24. package/src/effect/helpers.ts +98 -0
  25. package/src/effect/index.ts +22 -0
  26. package/src/effect/layers.ts +228 -0
  27. package/src/effect/runtime-ref.ts +25 -0
  28. package/src/effect/runtime.ts +31 -0
  29. package/src/effect/services.ts +57 -0
  30. package/src/effect/zod.ts +43 -0
  31. package/src/embeddings/provider.ts +122 -71
  32. package/src/index.ts +46 -1
  33. package/src/openrouter/direct-provider.ts +29 -0
  34. package/src/queues/autonomous-job.queue.ts +130 -74
  35. package/src/queues/context-compaction.queue.ts +60 -15
  36. package/src/queues/delayed-node-promotion.queue.ts +52 -15
  37. package/src/queues/document-processor.queue.ts +52 -77
  38. package/src/queues/memory-consolidation.queue.ts +47 -32
  39. package/src/queues/organization-learning.queue.ts +13 -4
  40. package/src/queues/plan-agent-heartbeat.queue.ts +65 -21
  41. package/src/queues/plan-scheduler.queue.ts +107 -31
  42. package/src/queues/post-chat-memory.queue.ts +66 -24
  43. package/src/queues/queue-factory.ts +142 -52
  44. package/src/queues/standalone-worker.ts +39 -0
  45. package/src/queues/title-generation.queue.ts +54 -9
  46. package/src/redis/connection.ts +84 -32
  47. package/src/redis/index.ts +6 -8
  48. package/src/redis/org-memory-lock.ts +60 -27
  49. package/src/redis/redis-lease-lock.ts +200 -121
  50. package/src/redis/runtime-connection.ts +10 -0
  51. package/src/redis/stream-context.ts +84 -46
  52. package/src/runtime/agent-identity-overrides.ts +2 -2
  53. package/src/runtime/agent-runtime-policy.ts +4 -1
  54. package/src/runtime/agent-stream-helpers.ts +20 -9
  55. package/src/runtime/chat-run-orchestration.ts +102 -19
  56. package/src/runtime/chat-run-registry.ts +36 -2
  57. package/src/runtime/context-compaction/context-compaction-runtime.ts +107 -0
  58. package/src/runtime/{context-compaction.ts → context-compaction/context-compaction.ts} +114 -91
  59. package/src/runtime/execution-plan-visibility.ts +2 -2
  60. package/src/runtime/execution-plan.ts +42 -15
  61. package/src/runtime/graph-designer.ts +11 -7
  62. package/src/runtime/helper-model.ts +135 -48
  63. package/src/runtime/index.ts +7 -7
  64. package/src/runtime/indexed-repositories-policy.ts +3 -3
  65. package/src/runtime/{memory-block.ts → memory/memory-block.ts} +40 -36
  66. package/src/runtime/{memory-digest-policy.ts → memory/memory-digest-policy.ts} +1 -1
  67. package/src/runtime/{memory-pipeline.ts → memory/memory-pipeline.ts} +1 -1
  68. package/src/runtime/{memory-prompts-fact.ts → memory/memory-prompts-fact.ts} +2 -2
  69. package/src/runtime/{memory-scope.ts → memory/memory-scope.ts} +12 -6
  70. package/src/runtime/plugin-resolution.ts +144 -24
  71. package/src/runtime/plugin-types.ts +9 -1
  72. package/src/runtime/post-turn-side-effects.ts +197 -130
  73. package/src/runtime/retrieval-adapters.ts +38 -4
  74. package/src/runtime/runtime-config.ts +165 -61
  75. package/src/runtime/runtime-extensions.ts +21 -34
  76. package/src/runtime/social-chat/social-chat-agent-runner.ts +157 -0
  77. package/src/runtime/{social-chat-history.ts → social-chat/social-chat-history.ts} +42 -20
  78. package/src/runtime/social-chat/social-chat.ts +594 -0
  79. package/src/runtime/specialist-runner.ts +36 -10
  80. package/src/runtime/team-consultation/team-consultation-orchestrator.ts +427 -0
  81. package/src/runtime/{team-consultation-prompts.ts → team-consultation/team-consultation-prompts.ts} +6 -2
  82. package/src/runtime/thread-chat-helpers.ts +2 -2
  83. package/src/runtime/thread-plan-turn.ts +2 -1
  84. package/src/runtime/thread-turn-context.ts +172 -94
  85. package/src/runtime/turn-lifecycle.ts +93 -27
  86. package/src/services/agent-activity.service.ts +287 -203
  87. package/src/services/agent-executor.service.ts +329 -217
  88. package/src/services/artifact.service.ts +225 -148
  89. package/src/services/attachment.service.ts +137 -115
  90. package/src/services/autonomous-job.service.ts +888 -491
  91. package/src/services/chat-run-registry.service.ts +11 -1
  92. package/src/services/context-compaction.service.ts +136 -86
  93. package/src/services/document-chunk.service.ts +162 -90
  94. package/src/services/execution-plan/execution-plan-approval.ts +26 -0
  95. package/src/services/execution-plan/execution-plan-context.ts +29 -0
  96. package/src/services/execution-plan/execution-plan-graph.ts +256 -0
  97. package/src/services/execution-plan/execution-plan-schedule.ts +84 -0
  98. package/src/services/execution-plan/execution-plan-spec.ts +75 -0
  99. package/src/services/execution-plan/execution-plan.service.ts +1041 -0
  100. package/src/services/feedback-loop.service.ts +132 -76
  101. package/src/services/global-orchestrator.service.ts +80 -170
  102. package/src/services/graph-full-routing.ts +182 -0
  103. package/src/services/index.ts +18 -20
  104. package/src/services/institutional-memory.service.ts +220 -123
  105. package/src/services/learned-skill.service.ts +364 -259
  106. package/src/services/memory/memory-conversation.ts +95 -0
  107. package/src/services/memory/memory-org-memory.ts +39 -0
  108. package/src/services/memory/memory-preseeded.ts +80 -0
  109. package/src/services/memory/memory-rerank.ts +297 -0
  110. package/src/services/{memory-utils.ts → memory/memory-utils.ts} +5 -5
  111. package/src/services/memory/memory.service.ts +692 -0
  112. package/src/services/memory/rerank.service.ts +209 -0
  113. package/src/services/monitoring-window.service.ts +92 -70
  114. package/src/services/mutating-approval.service.ts +62 -53
  115. package/src/services/node-workspace.service.ts +141 -98
  116. package/src/services/notification.service.ts +17 -16
  117. package/src/services/organization-member.service.ts +120 -66
  118. package/src/services/organization.service.ts +144 -51
  119. package/src/services/ownership-dispatcher.service.ts +415 -264
  120. package/src/services/plan/plan-agent-heartbeat.service.ts +234 -0
  121. package/src/services/plan/plan-agent-query.service.ts +322 -0
  122. package/src/services/plan/plan-approval.service.ts +102 -0
  123. package/src/services/plan/plan-artifact.service.ts +60 -0
  124. package/src/services/plan/plan-builder.service.ts +76 -0
  125. package/src/services/plan/plan-checkpoint.service.ts +103 -0
  126. package/src/services/{plan-compiler.service.ts → plan/plan-compiler.service.ts} +26 -9
  127. package/src/services/plan/plan-completion-side-effects.ts +175 -0
  128. package/src/services/plan/plan-coordination.service.ts +181 -0
  129. package/src/services/plan/plan-cycle.service.ts +398 -0
  130. package/src/services/plan/plan-deadline.service.ts +547 -0
  131. package/src/services/plan/plan-event-delivery.service.ts +261 -0
  132. package/src/services/plan/plan-executor-context.ts +35 -0
  133. package/src/services/plan/plan-executor-graph.ts +475 -0
  134. package/src/services/plan/plan-executor-helpers.ts +322 -0
  135. package/src/services/plan/plan-executor-persistence.ts +209 -0
  136. package/src/services/plan/plan-executor.service.ts +1654 -0
  137. package/src/services/{plan-helpers.ts → plan/plan-helpers.ts} +1 -1
  138. package/src/services/{plan-run-data.ts → plan/plan-run-data.ts} +4 -4
  139. package/src/services/plan/plan-run-serialization.ts +15 -0
  140. package/src/services/plan/plan-run.service.ts +644 -0
  141. package/src/services/plan/plan-scheduler.service.ts +385 -0
  142. package/src/services/plan/plan-template.service.ts +224 -0
  143. package/src/services/plan/plan-transaction-events.ts +33 -0
  144. package/src/services/plan/plan-validator.service.ts +907 -0
  145. package/src/services/plan/plan-workspace.service.ts +125 -0
  146. package/src/services/plugin-executor.service.ts +97 -68
  147. package/src/services/quality-metrics.service.ts +112 -94
  148. package/src/services/queue-job.service.ts +296 -230
  149. package/src/services/recent-activity-title.service.ts +65 -36
  150. package/src/services/recent-activity.service.ts +274 -259
  151. package/src/services/skill-resolver.service.ts +38 -12
  152. package/src/services/social-chat-history.service.ts +176 -125
  153. package/src/services/system-executor.service.ts +91 -61
  154. package/src/services/thread/thread-active-run.ts +203 -0
  155. package/src/services/thread/thread-bootstrap.ts +369 -0
  156. package/src/services/thread/thread-listing.ts +198 -0
  157. package/src/services/thread/thread-memory-block.ts +117 -0
  158. package/src/services/thread/thread-message.service.ts +363 -0
  159. package/src/services/thread/thread-record-store.ts +155 -0
  160. package/src/services/thread/thread-title.service.ts +74 -0
  161. package/src/services/thread/thread-turn-execution.ts +280 -0
  162. package/src/services/thread/thread-turn-message-context.ts +73 -0
  163. package/src/services/thread/thread-turn-preparation.service.ts +1146 -0
  164. package/src/services/thread/thread-turn-streaming.ts +402 -0
  165. package/src/services/thread/thread-turn-tracing.ts +35 -0
  166. package/src/services/thread/thread-turn.ts +343 -0
  167. package/src/services/thread/thread.service.ts +335 -0
  168. package/src/services/user.service.ts +82 -32
  169. package/src/services/write-intent-validator.service.ts +63 -51
  170. package/src/storage/attachment-parser.ts +69 -27
  171. package/src/storage/attachment-storage.service.ts +331 -275
  172. package/src/storage/generated-document-storage.service.ts +66 -34
  173. package/src/system-agents/agent-result.ts +3 -1
  174. package/src/system-agents/context-compaction.agent.ts +2 -2
  175. package/src/system-agents/delegated-agent-factory.ts +159 -90
  176. package/src/system-agents/memory-reranker.agent.ts +2 -2
  177. package/src/system-agents/memory.agent.ts +2 -2
  178. package/src/system-agents/recent-activity-title-refiner.agent.ts +2 -2
  179. package/src/system-agents/regular-chat-memory-digest.agent.ts +2 -2
  180. package/src/system-agents/skill-extractor.agent.ts +2 -2
  181. package/src/system-agents/skill-manager.agent.ts +2 -2
  182. package/src/system-agents/thread-router.agent.ts +157 -113
  183. package/src/system-agents/title-generator.agent.ts +2 -2
  184. package/src/tools/execution-plan.tool.ts +220 -161
  185. package/src/tools/fetch-webpage.tool.ts +21 -17
  186. package/src/tools/firecrawl-client.ts +16 -6
  187. package/src/tools/index.ts +1 -0
  188. package/src/tools/memory-block.tool.ts +14 -6
  189. package/src/tools/plan-approval.tool.ts +49 -47
  190. package/src/tools/read-file-parts.tool.ts +44 -33
  191. package/src/tools/remember-memory.tool.ts +65 -45
  192. package/src/tools/search-web.tool.ts +26 -22
  193. package/src/tools/search.tool.ts +41 -29
  194. package/src/tools/team-think.tool.ts +124 -83
  195. package/src/tools/user-questions.tool.ts +4 -3
  196. package/src/tools/web-tool-shared.ts +6 -0
  197. package/src/utils/async.ts +17 -23
  198. package/src/utils/crypto.ts +21 -0
  199. package/src/utils/date-time.ts +40 -1
  200. package/src/utils/errors.ts +95 -16
  201. package/src/utils/hono-error-handler.ts +24 -39
  202. package/src/utils/index.ts +2 -1
  203. package/src/utils/null-proto-record.ts +41 -0
  204. package/src/utils/sse-keepalive.ts +124 -21
  205. package/src/workers/bootstrap.ts +186 -51
  206. package/src/workers/memory-consolidation.worker.ts +325 -237
  207. package/src/workers/organization-learning.worker.ts +50 -16
  208. package/src/workers/regular-chat-memory-digest.helpers.ts +28 -27
  209. package/src/workers/regular-chat-memory-digest.runner.ts +175 -114
  210. package/src/workers/skill-extraction.runner.ts +176 -93
  211. package/src/workers/utils/file-section-chunker.ts +8 -10
  212. package/src/workers/utils/repo-structure-extractor.ts +349 -260
  213. package/src/workers/utils/repomix-file-sections.ts +2 -2
  214. package/src/workers/utils/thread-message-query.ts +97 -38
  215. package/src/workers/worker-utils.ts +56 -31
  216. package/src/config/debug-logger.ts +0 -47
  217. package/src/redis/connection-accessor.ts +0 -26
  218. package/src/runtime/context-compaction-runtime.ts +0 -87
  219. package/src/runtime/social-chat-agent-runner.ts +0 -118
  220. package/src/runtime/social-chat.ts +0 -516
  221. package/src/runtime/team-consultation-orchestrator.ts +0 -272
  222. package/src/services/adaptive-playbook.service.ts +0 -152
  223. package/src/services/artifact-provenance.service.ts +0 -172
  224. package/src/services/chat-attachments.service.ts +0 -17
  225. package/src/services/context-compaction-runtime.singleton.ts +0 -13
  226. package/src/services/execution-plan.service.ts +0 -1118
  227. package/src/services/memory.service.ts +0 -844
  228. package/src/services/plan-agent-heartbeat.service.ts +0 -136
  229. package/src/services/plan-agent-query.service.ts +0 -267
  230. package/src/services/plan-approval.service.ts +0 -83
  231. package/src/services/plan-artifact.service.ts +0 -50
  232. package/src/services/plan-builder.service.ts +0 -67
  233. package/src/services/plan-checkpoint.service.ts +0 -81
  234. package/src/services/plan-completion-side-effects.ts +0 -80
  235. package/src/services/plan-coordination.service.ts +0 -157
  236. package/src/services/plan-cycle.service.ts +0 -284
  237. package/src/services/plan-deadline.service.ts +0 -430
  238. package/src/services/plan-event-delivery.service.ts +0 -166
  239. package/src/services/plan-executor.service.ts +0 -1950
  240. package/src/services/plan-run.service.ts +0 -515
  241. package/src/services/plan-scheduler.service.ts +0 -240
  242. package/src/services/plan-template.service.ts +0 -177
  243. package/src/services/plan-validator.service.ts +0 -818
  244. package/src/services/plan-workspace.service.ts +0 -83
  245. package/src/services/thread-message.service.ts +0 -275
  246. package/src/services/thread-plan-registry.service.ts +0 -22
  247. package/src/services/thread-title.service.ts +0 -39
  248. package/src/services/thread-turn-preparation.service.ts +0 -1147
  249. package/src/services/thread-turn.ts +0 -172
  250. package/src/services/thread.service.ts +0 -869
  251. package/src/utils/env.ts +0 -8
  252. /package/src/runtime/{context-compaction-constants.ts → context-compaction/context-compaction-constants.ts} +0 -0
  253. /package/src/runtime/{memory-format.ts → memory/memory-format.ts} +0 -0
  254. /package/src/runtime/{memory-prompts-parse.ts → memory/memory-prompts-parse.ts} +0 -0
  255. /package/src/runtime/{memory-prompts-update.ts → memory/memory-prompts-update.ts} +0 -0
  256. /package/src/runtime/{social-chat-prompts.ts → social-chat/social-chat-prompts.ts} +0 -0
  257. /package/src/services/{plan-node-spec.ts → plan/plan-node-spec.ts} +0 -0
  258. /package/src/services/{thread-constants.ts → thread/thread-constants.ts} +0 -0
  259. /package/src/services/{thread.types.ts → thread/thread.types.ts} +0 -0
@@ -1,516 +0,0 @@
1
- import { createSlackAdapter } from '@chat-adapter/slack'
2
- import { createIoRedisState } from '@chat-adapter/state-ioredis'
3
- import {
4
- buildSlackSocialReplyMarkdown,
5
- CONSULT_SPECIALIST_TOOL_NAME,
6
- ConsultSpecialistArgsSchema,
7
- } from '@lota-sdk/shared'
8
- import type { ChatMessage, ConsultSpecialistArgs } from '@lota-sdk/shared'
9
- import { tool as createTool } from 'ai'
10
- import { Chat, ConsoleLogger } from 'chat'
11
- import type { Message, Thread, WebhookOptions } from 'chat'
12
- import type IORedis from 'ioredis'
13
-
14
- import { agentDisplayNames, teamConsultParticipants } from '../config/agent-defaults'
15
- import { aiLogger } from '../config/logger'
16
- import { recordIdToString } from '../db/record-id'
17
- import { TABLES } from '../db/tables'
18
- import { enqueueRegularChatMemoryDigest, enqueueSkillExtraction } from '../queues/organization-learning.queue'
19
- import { enqueuePostChatMemory } from '../queues/post-chat-memory.queue'
20
- import type {
21
- BuildSocialChatAgentToolsParams,
22
- LotaRuntimeSocialChatConfig,
23
- LotaSocialChatResolvedContext,
24
- } from '../runtime/runtime-config'
25
- import { getRuntimeAdapters } from '../runtime/runtime-extensions'
26
- import { learnedSkillService } from '../services/learned-skill.service'
27
- import { memoryService } from '../services/memory.service'
28
- import { socialChatHistoryService } from '../services/social-chat-history.service'
29
- import { safeEnqueue } from '../utils/async'
30
- import { buildAgentPromptContext } from './agent-prompt-context'
31
- import { createServerRunAbortController } from './agent-stream-helpers'
32
- import { runSocialAgentTurn, withLoggedSocialToolSet } from './social-chat-agent-runner'
33
- import {
34
- collectThreadMessages,
35
- createSocialChatCursorId,
36
- normalizeSocialHistoryMessage,
37
- readSlackAuthorName,
38
- buildSocialChatThreadTranscript,
39
- } from './social-chat-history'
40
- import {
41
- buildLeadSocialChatPrompt,
42
- buildSocialChatIdentitySection,
43
- buildSpecialistSocialChatPrompt,
44
- } from './social-chat-prompts'
45
- import { runSpecialistSession } from './specialist-runner'
46
- import {
47
- buildAgentHistoryMessages,
48
- extractMessageText,
49
- toHistoryMessages,
50
- toOptionalTrimmedString,
51
- } from './thread-chat-helpers'
52
-
53
- const DEFAULT_SOCIAL_CHAT_AGENT_ID = 'socialChat'
54
- const DEFAULT_SOCIAL_CHAT_AGENT_DISPLAY_NAME = 'Lota'
55
- const DEFAULT_SOCIAL_CHAT_STATE_PREFIX = 'lota:social:chat-sdk'
56
- const DEFAULT_SOCIAL_CHAT_DEDUPE_TTL_MS = 15 * 60 * 1000
57
- const PRESEEDED_MEMORY_LOOKUP_LIMIT = 3
58
-
59
- export interface LotaRuntimeSocialChat {
60
- enabled: boolean
61
- initialize(): Promise<void>
62
- shutdown(): Promise<void>
63
- webhooks: { slack(request: Request, options?: WebhookOptions): Promise<Response> }
64
- }
65
-
66
- interface SlackSocialMessageContext {
67
- channelId: string
68
- threadId: string
69
- messageId: string
70
- text: string
71
- authorId?: string
72
- authorName?: string
73
- }
74
-
75
- function createDisabledSocialChatRuntime(): LotaRuntimeSocialChat {
76
- return {
77
- enabled: false,
78
- async initialize() {},
79
- async shutdown() {},
80
- webhooks: {
81
- async slack() {
82
- return new Response('Social chat is disabled.', { status: 404 })
83
- },
84
- },
85
- }
86
- }
87
-
88
- function createAssistantMessage(params: { agentId: string; agentName: string; text: string }): ChatMessage {
89
- return {
90
- id: Bun.randomUUIDv7(),
91
- role: 'assistant',
92
- parts: [{ type: 'text', text: params.text }],
93
- metadata: { agentId: params.agentId, agentName: params.agentName, createdAt: Date.now() },
94
- }
95
- }
96
-
97
- function toSafeJobIdSegment(value: string): string {
98
- return value.replace(/[^a-zA-Z0-9_-]/g, '_')
99
- }
100
-
101
- function createSocialMemoryDedupeKey(params: { workspaceId: string; threadId: string; messageId: string }): string {
102
- return [
103
- 'social-memory',
104
- toSafeJobIdSegment(params.workspaceId),
105
- toSafeJobIdSegment(params.threadId),
106
- toSafeJobIdSegment(params.messageId),
107
- ].join('-')
108
- }
109
-
110
- function buildBuildToolsParams(params: {
111
- agentId: string
112
- context: LotaSocialChatResolvedContext
113
- workspaceIdString: string
114
- userIdString: string
115
- messageContext: SlackSocialMessageContext
116
- memoryBlock: string
117
- onAppendMemoryBlock: (value: string) => void
118
- }): BuildSocialChatAgentToolsParams {
119
- return {
120
- agentId: params.agentId,
121
- workspaceId: params.context.workspaceId,
122
- workspaceIdString: params.workspaceIdString,
123
- userId: params.context.userId,
124
- userIdString: params.userIdString,
125
- userName: params.context.userName,
126
- platform: 'slack',
127
- channelId: params.messageContext.channelId,
128
- threadId: params.messageContext.threadId,
129
- incomingMessageId: params.messageContext.messageId,
130
- incomingText: params.messageContext.text,
131
- memoryBlock: params.memoryBlock,
132
- onAppendMemoryBlock: params.onAppendMemoryBlock,
133
- context: null,
134
- }
135
- }
136
-
137
- export function createSocialChatRuntime(params: {
138
- redisClient: IORedis
139
- socialChat?: LotaRuntimeSocialChatConfig
140
- }): LotaRuntimeSocialChat {
141
- const socialChatConfig = params.socialChat
142
- const slackConfig = socialChatConfig?.slack
143
- const slackEnabled = Boolean(slackConfig?.botToken?.trim() && slackConfig.signingSecret?.trim())
144
-
145
- if (!socialChatConfig || !slackEnabled) {
146
- return createDisabledSocialChatRuntime()
147
- }
148
-
149
- const socialAgentId = toOptionalTrimmedString(socialChatConfig.agentId) ?? DEFAULT_SOCIAL_CHAT_AGENT_ID
150
- const socialAgentDisplayName =
151
- toOptionalTrimmedString(socialChatConfig.agentDisplayName) ?? DEFAULT_SOCIAL_CHAT_AGENT_DISPLAY_NAME
152
- const stateKeyPrefix =
153
- toOptionalTrimmedString(socialChatConfig.stateRedisKeyPrefix) ?? DEFAULT_SOCIAL_CHAT_STATE_PREFIX
154
- const chat = new Chat({
155
- userName: toOptionalTrimmedString(slackConfig?.userName) ?? socialAgentDisplayName,
156
- adapters: {
157
- slack: createSlackAdapter({
158
- botToken: slackConfig?.botToken,
159
- signingSecret: slackConfig?.signingSecret,
160
- userName: toOptionalTrimmedString(slackConfig?.userName) ?? socialAgentDisplayName,
161
- }),
162
- },
163
- state: createIoRedisState({
164
- client: params.redisClient,
165
- keyPrefix: stateKeyPrefix,
166
- logger: new ConsoleLogger('warn', 'lota-social-chat'),
167
- }),
168
- dedupeTtlMs: slackConfig?.dedupeTtlMs ?? DEFAULT_SOCIAL_CHAT_DEDUPE_TTL_MS,
169
- })
170
-
171
- const initialize = async () => {
172
- await chat.initialize()
173
- }
174
-
175
- const shutdown = async () => {
176
- await chat.shutdown()
177
- }
178
-
179
- const handleMessage = async (thread: Thread, incomingMessage: Message) => {
180
- const rawSlackMessage = incomingMessage.raw as { channel?: unknown } | undefined
181
- const channelId = toOptionalTrimmedString(rawSlackMessage?.channel) ?? thread.channelId
182
- const messageContext: SlackSocialMessageContext = {
183
- channelId,
184
- threadId: thread.id,
185
- messageId: incomingMessage.id,
186
- text: incomingMessage.text.trim(),
187
- authorId: toOptionalTrimmedString(incomingMessage.author.userId) ?? undefined,
188
- authorName: readSlackAuthorName(incomingMessage),
189
- }
190
- aiLogger.info`Slack social-chat message received: channelId=${messageContext.channelId}, threadId=${messageContext.threadId}, messageId=${messageContext.messageId}, author=${messageContext.authorName ?? 'unknown'}, textLength=${messageContext.text.length}`
191
-
192
- const resolvedContext = await socialChatConfig.resolveContext({
193
- platform: 'slack',
194
- channelId: messageContext.channelId,
195
- threadId: messageContext.threadId,
196
- messageId: messageContext.messageId,
197
- text: messageContext.text,
198
- authorId: messageContext.authorId,
199
- authorName: messageContext.authorName,
200
- })
201
- const workspaceIdString = recordIdToString(resolvedContext.workspaceId, TABLES.ORGANIZATION)
202
- const userIdString = recordIdToString(resolvedContext.userId, TABLES.USER)
203
- aiLogger.info`Slack social-chat context resolved: workspaceId=${workspaceIdString}, userId=${userIdString}`
204
-
205
- const threadMessages = await collectThreadMessages(thread, incomingMessage)
206
- const normalizedMessages = threadMessages
207
- .map((message) =>
208
- normalizeSocialHistoryMessage({
209
- workspaceId: workspaceIdString,
210
- channelId: messageContext.channelId,
211
- agentId: socialAgentId,
212
- agentDisplayName: socialAgentDisplayName,
213
- message,
214
- }),
215
- )
216
- .filter((message): message is NonNullable<typeof message> => message !== null)
217
- await socialChatHistoryService.upsertMessages(normalizedMessages)
218
-
219
- const historyBeforeReply = await socialChatHistoryService.listThreadMessages({
220
- workspaceId: workspaceIdString,
221
- threadId: messageContext.threadId,
222
- })
223
- const currentUserMessage =
224
- historyBeforeReply.find((message) => message.messageId === incomingMessage.id) ?? historyBeforeReply.at(-1)
225
- const priorHistory = currentUserMessage
226
- ? historyBeforeReply.filter((message) => message.cursor.id !== currentUserMessage.cursor.id)
227
- : historyBeforeReply
228
-
229
- const workspaceProvider = getRuntimeAdapters().workspaceProvider
230
- const workspace = workspaceProvider ? await workspaceProvider.getWorkspace(resolvedContext.workspaceId) : {}
231
- const lifecycleState = workspaceProvider ? await workspaceProvider.getLifecycleState?.(workspace) : undefined
232
- const workspaceProfileState = workspaceProvider
233
- ? await workspaceProvider.readProfileProjectionState?.(workspace)
234
- : undefined
235
- const [recentDomainEvents, promptSummary] = await Promise.all([
236
- workspaceProvider?.listRecentDomainEvents?.(resolvedContext.workspaceId, 5) ??
237
- Promise.resolve([] as Array<Record<string, unknown>>),
238
- workspaceProvider?.buildPromptSummary
239
- ? workspaceProvider.buildPromptSummary(resolvedContext.workspaceId).catch(() => undefined)
240
- : Promise.resolve(undefined),
241
- ])
242
-
243
- const promptContext = buildAgentPromptContext({
244
- workspaceName:
245
- workspaceProfileState?.workspaceName ??
246
- toOptionalTrimmedString((workspace as { name?: unknown }).name) ??
247
- undefined,
248
- summaryBlock: workspaceProfileState?.summaryBlock,
249
- promptSummary,
250
- userName: messageContext.authorName,
251
- recentDomainEvents,
252
- })
253
- const retrievedKnowledgeSection =
254
- lifecycleState?.bootstrapActive || messageContext.text.length === 0
255
- ? undefined
256
- : await workspaceProvider?.buildRetrievedKnowledgeSection?.({
257
- workspaceId: workspaceIdString,
258
- userId: userIdString,
259
- query: messageContext.text,
260
- })
261
-
262
- const [preSeededMemoriesSection, learnedSkillsSection] = await Promise.all([
263
- memoryService.getTopMemories({
264
- orgId: workspaceIdString,
265
- agentName: socialAgentId,
266
- limit: PRESEEDED_MEMORY_LOOKUP_LIMIT,
267
- }),
268
- lifecycleState?.bootstrapActive
269
- ? Promise.resolve(undefined)
270
- : learnedSkillService
271
- .retrieveForTurn({
272
- orgId: workspaceIdString,
273
- agentId: socialAgentId,
274
- query: messageContext.text,
275
- limit: 3,
276
- minConfidence: 0.6,
277
- })
278
- .catch((error) => {
279
- aiLogger.warn`Failed to retrieve learned skills for ${socialAgentId}: ${error}`
280
- return undefined
281
- }),
282
- ])
283
-
284
- let memoryBlock = ''
285
- const consultParticipants = [
286
- ...new Set(
287
- (await socialChatConfig.getConsultParticipants?.({
288
- workspaceId: resolvedContext.workspaceId,
289
- workspaceIdString,
290
- platform: 'slack',
291
- })) ?? [...teamConsultParticipants],
292
- ),
293
- ].filter((agentId) => agentId !== socialAgentId)
294
- const executedToolNames: string[] = []
295
-
296
- const baseTools = withLoggedSocialToolSet(
297
- await socialChatConfig.buildAgentTools(
298
- buildBuildToolsParams({
299
- agentId: socialAgentId,
300
- context: resolvedContext,
301
- workspaceIdString,
302
- userIdString,
303
- messageContext,
304
- memoryBlock,
305
- onAppendMemoryBlock: (value: string) => {
306
- memoryBlock = value
307
- },
308
- }),
309
- ),
310
- {
311
- agentId: socialAgentId,
312
- channelId: messageContext.channelId,
313
- threadId: messageContext.threadId,
314
- executedToolNames,
315
- },
316
- )
317
-
318
- const transcript = buildSocialChatThreadTranscript(historyBeforeReply)
319
- const runAbort = createServerRunAbortController()
320
-
321
- const consultSpecialistTool = createTool({
322
- description: 'Consult one specialist teammate for targeted guidance before replying to the user.',
323
- inputSchema: ConsultSpecialistArgsSchema,
324
- execute: async ({ agentId, task }: ConsultSpecialistArgs) => {
325
- if (!consultParticipants.includes(agentId)) {
326
- throw new Error(`Agent "${agentId}" is not an allowed social-chat specialist.`)
327
- }
328
-
329
- const { result: specialistRun } = await runSpecialistSession({
330
- initialMemoryBlock: '',
331
- buildTools: async ({ memoryBlock: currentMemoryBlock, onAppendMemoryBlock }) =>
332
- withLoggedSocialToolSet(
333
- await socialChatConfig.buildAgentTools(
334
- buildBuildToolsParams({
335
- agentId,
336
- context: resolvedContext,
337
- workspaceIdString,
338
- userIdString,
339
- messageContext,
340
- memoryBlock: currentMemoryBlock,
341
- onAppendMemoryBlock,
342
- }),
343
- ),
344
- { agentId, channelId: messageContext.channelId, threadId: messageContext.threadId, executedToolNames },
345
- ),
346
- run: async ({ tools }) => {
347
- const [specialistPreSeededMemories, specialistLearnedSkills] = await Promise.all([
348
- memoryService.getTopMemories({
349
- orgId: workspaceIdString,
350
- agentName: agentId,
351
- limit: PRESEEDED_MEMORY_LOOKUP_LIMIT,
352
- }),
353
- lifecycleState?.bootstrapActive
354
- ? Promise.resolve(undefined)
355
- : learnedSkillService
356
- .retrieveForTurn({ orgId: workspaceIdString, agentId, query: task, limit: 3, minConfidence: 0.6 })
357
- .catch((error) => {
358
- aiLogger.warn`Failed to retrieve learned skills for ${agentId}: ${error}`
359
- return undefined
360
- }),
361
- ])
362
-
363
- return await runSocialAgentTurn({
364
- agentId,
365
- mode: 'fixedThreadMode',
366
- threadType: 'group',
367
- onboardingActive: lifecycleState?.bootstrapActive ?? false,
368
- linearInstalled: false,
369
- systemWorkspaceDetails: promptContext.systemWorkspaceDetails,
370
- preSeededMemoriesSection: specialistPreSeededMemories,
371
- retrievedKnowledgeSection,
372
- learnedSkillsSection: specialistLearnedSkills,
373
- userMessageText: task,
374
- additionalInstructionSections: [
375
- `You are supporting ${socialAgentDisplayName} in a Slack social-chat thread. Stay within your role.`,
376
- ],
377
- tools,
378
- prompt: buildSpecialistSocialChatPrompt({
379
- requesterName: socialAgentDisplayName,
380
- agentName: agentDisplayNames[agentId] ?? agentId,
381
- task,
382
- transcript,
383
- }),
384
- abortSignal: runAbort.signal,
385
- })
386
- },
387
- })
388
- const text = specialistRun.text
389
-
390
- return createAssistantMessage({ agentId, agentName: agentDisplayNames[agentId] ?? agentId, text })
391
- },
392
- toModelOutput: ({ output }) => {
393
- const message = output
394
- const agentName =
395
- typeof message.metadata?.agentName === 'string' && message.metadata.agentName.trim().length > 0
396
- ? message.metadata.agentName.trim()
397
- : 'Specialist'
398
- const summary = extractMessageText(message).trim()
399
- return {
400
- type: 'text',
401
- value: summary ? `${agentName}: ${summary}` : `${agentName} completed the requested task.`,
402
- }
403
- },
404
- })
405
-
406
- await thread.startTyping('Thinking...').catch(() => undefined)
407
- aiLogger.info`Slack social-chat generating reply: channelId=${messageContext.channelId}, threadId=${messageContext.threadId}`
408
- const leadRun = await runSocialAgentTurn({
409
- agentId: socialAgentId,
410
- mode: 'threadMode',
411
- threadType: 'group',
412
- onboardingActive: lifecycleState?.bootstrapActive ?? false,
413
- linearInstalled: false,
414
- systemWorkspaceDetails: promptContext.systemWorkspaceDetails,
415
- preSeededMemoriesSection,
416
- retrievedKnowledgeSection,
417
- learnedSkillsSection,
418
- userMessageText: messageContext.text,
419
- additionalInstructionSections: [buildSocialChatIdentitySection(socialAgentDisplayName)],
420
- tools: { ...baseTools, [CONSULT_SPECIALIST_TOOL_NAME]: consultSpecialistTool },
421
- prompt: buildLeadSocialChatPrompt({
422
- agentDisplayName: socialAgentDisplayName,
423
- channelId: messageContext.channelId,
424
- threadId: messageContext.threadId,
425
- transcript,
426
- latestUserMessage: messageContext.text,
427
- latestAuthorName: messageContext.authorName,
428
- }),
429
- abortSignal: runAbort.signal,
430
- })
431
- const responseText = leadRun.text
432
-
433
- const replyMarkdown = buildSlackSocialReplyMarkdown({ replyMarkdown: responseText, executedToolNames })
434
- const sentMessage = await thread.post({ markdown: replyMarkdown })
435
- aiLogger.info`Slack social-chat reply posted: channelId=${messageContext.channelId}, threadId=${messageContext.threadId}, replyMessageId=${sentMessage.id}`
436
- const normalizedResponse = normalizeSocialHistoryMessage({
437
- workspaceId: workspaceIdString,
438
- channelId: messageContext.channelId,
439
- agentId: socialAgentId,
440
- agentDisplayName: socialAgentDisplayName,
441
- message: sentMessage,
442
- textOverride: responseText,
443
- })
444
- if (normalizedResponse) {
445
- await socialChatHistoryService.upsertMessages([normalizedResponse])
446
- }
447
-
448
- const priorHistoryMessages = toHistoryMessages(priorHistory)
449
- const agentMessages = normalizedResponse ? buildAgentHistoryMessages([normalizedResponse]) : []
450
- if (messageContext.text && agentMessages.length > 0) {
451
- await safeEnqueue(
452
- () =>
453
- enqueuePostChatMemory(
454
- {
455
- orgId: workspaceIdString,
456
- threadId: `social:slack:${messageContext.threadId}`,
457
- sourceId: createSocialChatCursorId({
458
- workspaceId: workspaceIdString,
459
- threadId: messageContext.threadId,
460
- messageId: messageContext.messageId,
461
- }),
462
- userMessage: messageContext.text,
463
- historyMessages: priorHistoryMessages,
464
- agentMessages,
465
- memoryBlock: memoryBlock.trim() ? memoryBlock : undefined,
466
- source: 'social_chat',
467
- sourceMetadata: {
468
- platform: 'slack',
469
- channelId: messageContext.channelId,
470
- threadId: messageContext.threadId,
471
- messageId: messageContext.messageId,
472
- authorId: messageContext.authorId,
473
- authorName: messageContext.authorName,
474
- },
475
- },
476
- {
477
- dedupeKey: createSocialMemoryDedupeKey({
478
- workspaceId: workspaceIdString,
479
- threadId: messageContext.threadId,
480
- messageId: messageContext.messageId,
481
- }),
482
- },
483
- ),
484
- { operationName: 'social post-chat memory extraction enqueue' },
485
- )
486
- }
487
-
488
- await safeEnqueue(() => enqueueRegularChatMemoryDigest({ orgId: workspaceIdString }), {
489
- operationName: 'social regular chat memory digest enqueue',
490
- })
491
- await safeEnqueue(() => enqueueSkillExtraction({ orgId: workspaceIdString }), {
492
- operationName: 'social skill extraction enqueue',
493
- })
494
- }
495
-
496
- chat.onNewMention(async (thread, message) => {
497
- aiLogger.info`Slack social-chat new mention received: threadId=${thread.id}, messageId=${message.id}`
498
- await thread.subscribe()
499
- await handleMessage(thread, message)
500
- })
501
- chat.onSubscribedMessage(async (thread, message) => {
502
- aiLogger.info`Slack social-chat subscribed thread message received: threadId=${thread.id}, messageId=${message.id}`
503
- await handleMessage(thread, message)
504
- })
505
-
506
- return {
507
- enabled: true,
508
- initialize,
509
- shutdown,
510
- webhooks: {
511
- async slack(request: Request, options?: WebhookOptions) {
512
- return chat.webhooks.slack(request, options)
513
- },
514
- },
515
- }
516
- }