@lota-sdk/core 0.4.8 → 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/thread-defaults.ts +33 -21
  9. package/src/create-runtime.ts +725 -387
  10. package/src/db/base.service.ts +52 -28
  11. package/src/db/cursor-pagination.ts +71 -30
  12. package/src/db/memory-store.helpers.ts +4 -7
  13. package/src/db/memory-store.ts +856 -598
  14. package/src/db/memory.ts +398 -275
  15. package/src/db/record-id.ts +32 -10
  16. package/src/db/schema-fingerprint.ts +30 -12
  17. package/src/db/service-normalization.ts +255 -0
  18. package/src/db/service.ts +726 -761
  19. package/src/db/startup.ts +140 -66
  20. package/src/db/transaction-conflict.ts +15 -0
  21. package/src/effect/awaitable-effect.ts +87 -0
  22. package/src/effect/errors.ts +121 -0
  23. package/src/effect/helpers.ts +98 -0
  24. package/src/effect/index.ts +22 -0
  25. package/src/effect/layers.ts +228 -0
  26. package/src/effect/runtime-ref.ts +25 -0
  27. package/src/effect/runtime.ts +31 -0
  28. package/src/effect/services.ts +57 -0
  29. package/src/effect/zod.ts +43 -0
  30. package/src/embeddings/provider.ts +122 -76
  31. package/src/index.ts +46 -1
  32. package/src/openrouter/direct-provider.ts +11 -35
  33. package/src/queues/autonomous-job.queue.ts +130 -74
  34. package/src/queues/context-compaction.queue.ts +60 -15
  35. package/src/queues/delayed-node-promotion.queue.ts +52 -15
  36. package/src/queues/document-processor.queue.ts +52 -77
  37. package/src/queues/memory-consolidation.queue.ts +47 -32
  38. package/src/queues/organization-learning.queue.ts +13 -4
  39. package/src/queues/plan-agent-heartbeat.queue.ts +65 -21
  40. package/src/queues/plan-scheduler.queue.ts +107 -31
  41. package/src/queues/post-chat-memory.queue.ts +66 -24
  42. package/src/queues/queue-factory.ts +142 -52
  43. package/src/queues/standalone-worker.ts +39 -0
  44. package/src/queues/title-generation.queue.ts +54 -9
  45. package/src/redis/connection.ts +84 -32
  46. package/src/redis/index.ts +6 -8
  47. package/src/redis/org-memory-lock.ts +60 -27
  48. package/src/redis/redis-lease-lock.ts +200 -121
  49. package/src/redis/runtime-connection.ts +10 -0
  50. package/src/redis/stream-context.ts +84 -46
  51. package/src/runtime/agent-identity-overrides.ts +2 -2
  52. package/src/runtime/agent-runtime-policy.ts +4 -1
  53. package/src/runtime/agent-stream-helpers.ts +20 -9
  54. package/src/runtime/chat-run-orchestration.ts +102 -19
  55. package/src/runtime/chat-run-registry.ts +36 -2
  56. package/src/runtime/context-compaction/context-compaction-runtime.ts +107 -0
  57. package/src/runtime/{context-compaction.ts → context-compaction/context-compaction.ts} +114 -91
  58. package/src/runtime/execution-plan-visibility.ts +2 -2
  59. package/src/runtime/execution-plan.ts +42 -15
  60. package/src/runtime/graph-designer.ts +11 -7
  61. package/src/runtime/helper-model.ts +135 -48
  62. package/src/runtime/index.ts +7 -7
  63. package/src/runtime/indexed-repositories-policy.ts +3 -3
  64. package/src/runtime/{memory-block.ts → memory/memory-block.ts} +40 -36
  65. package/src/runtime/{memory-digest-policy.ts → memory/memory-digest-policy.ts} +1 -1
  66. package/src/runtime/{memory-pipeline.ts → memory/memory-pipeline.ts} +1 -1
  67. package/src/runtime/{memory-prompts-fact.ts → memory/memory-prompts-fact.ts} +2 -2
  68. package/src/runtime/{memory-scope.ts → memory/memory-scope.ts} +12 -6
  69. package/src/runtime/plugin-resolution.ts +144 -24
  70. package/src/runtime/plugin-types.ts +9 -1
  71. package/src/runtime/post-turn-side-effects.ts +197 -130
  72. package/src/runtime/retrieval-adapters.ts +38 -4
  73. package/src/runtime/runtime-config.ts +150 -61
  74. package/src/runtime/runtime-extensions.ts +21 -34
  75. package/src/runtime/social-chat/social-chat-agent-runner.ts +157 -0
  76. package/src/runtime/{social-chat-history.ts → social-chat/social-chat-history.ts} +42 -20
  77. package/src/runtime/social-chat/social-chat.ts +594 -0
  78. package/src/runtime/specialist-runner.ts +36 -10
  79. package/src/runtime/team-consultation/team-consultation-orchestrator.ts +427 -0
  80. package/src/runtime/{team-consultation-prompts.ts → team-consultation/team-consultation-prompts.ts} +6 -2
  81. package/src/runtime/thread-chat-helpers.ts +2 -2
  82. package/src/runtime/thread-plan-turn.ts +2 -1
  83. package/src/runtime/thread-turn-context.ts +172 -94
  84. package/src/runtime/turn-lifecycle.ts +93 -27
  85. package/src/services/agent-activity.service.ts +287 -203
  86. package/src/services/agent-executor.service.ts +329 -217
  87. package/src/services/artifact.service.ts +225 -148
  88. package/src/services/attachment.service.ts +137 -115
  89. package/src/services/autonomous-job.service.ts +888 -491
  90. package/src/services/chat-run-registry.service.ts +11 -1
  91. package/src/services/context-compaction.service.ts +136 -86
  92. package/src/services/document-chunk.service.ts +162 -90
  93. package/src/services/execution-plan/execution-plan-approval.ts +26 -0
  94. package/src/services/execution-plan/execution-plan-context.ts +29 -0
  95. package/src/services/execution-plan/execution-plan-graph.ts +256 -0
  96. package/src/services/execution-plan/execution-plan-schedule.ts +84 -0
  97. package/src/services/execution-plan/execution-plan-spec.ts +75 -0
  98. package/src/services/execution-plan/execution-plan.service.ts +1041 -0
  99. package/src/services/feedback-loop.service.ts +132 -76
  100. package/src/services/global-orchestrator.service.ts +80 -170
  101. package/src/services/graph-full-routing.ts +182 -0
  102. package/src/services/index.ts +18 -21
  103. package/src/services/institutional-memory.service.ts +220 -123
  104. package/src/services/learned-skill.service.ts +364 -259
  105. package/src/services/memory/memory-conversation.ts +95 -0
  106. package/src/services/memory/memory-org-memory.ts +39 -0
  107. package/src/services/memory/memory-preseeded.ts +80 -0
  108. package/src/services/memory/memory-rerank.ts +297 -0
  109. package/src/services/{memory-utils.ts → memory/memory-utils.ts} +5 -5
  110. package/src/services/memory/memory.service.ts +692 -0
  111. package/src/services/memory/rerank.service.ts +209 -0
  112. package/src/services/monitoring-window.service.ts +92 -70
  113. package/src/services/mutating-approval.service.ts +62 -53
  114. package/src/services/node-workspace.service.ts +141 -98
  115. package/src/services/notification.service.ts +17 -16
  116. package/src/services/organization-member.service.ts +120 -66
  117. package/src/services/organization.service.ts +144 -51
  118. package/src/services/ownership-dispatcher.service.ts +415 -264
  119. package/src/services/plan/plan-agent-heartbeat.service.ts +234 -0
  120. package/src/services/plan/plan-agent-query.service.ts +322 -0
  121. package/src/services/plan/plan-approval.service.ts +102 -0
  122. package/src/services/plan/plan-artifact.service.ts +60 -0
  123. package/src/services/plan/plan-builder.service.ts +76 -0
  124. package/src/services/plan/plan-checkpoint.service.ts +103 -0
  125. package/src/services/{plan-compiler.service.ts → plan/plan-compiler.service.ts} +26 -9
  126. package/src/services/plan/plan-completion-side-effects.ts +175 -0
  127. package/src/services/plan/plan-coordination.service.ts +181 -0
  128. package/src/services/plan/plan-cycle.service.ts +398 -0
  129. package/src/services/plan/plan-deadline.service.ts +547 -0
  130. package/src/services/plan/plan-event-delivery.service.ts +261 -0
  131. package/src/services/plan/plan-executor-context.ts +35 -0
  132. package/src/services/plan/plan-executor-graph.ts +475 -0
  133. package/src/services/plan/plan-executor-helpers.ts +322 -0
  134. package/src/services/plan/plan-executor-persistence.ts +209 -0
  135. package/src/services/plan/plan-executor.service.ts +1654 -0
  136. package/src/services/{plan-helpers.ts → plan/plan-helpers.ts} +1 -1
  137. package/src/services/{plan-run-data.ts → plan/plan-run-data.ts} +4 -4
  138. package/src/services/plan/plan-run-serialization.ts +15 -0
  139. package/src/services/plan/plan-run.service.ts +644 -0
  140. package/src/services/plan/plan-scheduler.service.ts +385 -0
  141. package/src/services/plan/plan-template.service.ts +224 -0
  142. package/src/services/plan/plan-transaction-events.ts +33 -0
  143. package/src/services/plan/plan-validator.service.ts +907 -0
  144. package/src/services/plan/plan-workspace.service.ts +125 -0
  145. package/src/services/plugin-executor.service.ts +97 -68
  146. package/src/services/quality-metrics.service.ts +112 -94
  147. package/src/services/queue-job.service.ts +296 -230
  148. package/src/services/recent-activity-title.service.ts +65 -36
  149. package/src/services/recent-activity.service.ts +274 -259
  150. package/src/services/skill-resolver.service.ts +38 -12
  151. package/src/services/social-chat-history.service.ts +176 -125
  152. package/src/services/system-executor.service.ts +91 -61
  153. package/src/services/thread/thread-active-run.ts +203 -0
  154. package/src/services/thread/thread-bootstrap.ts +369 -0
  155. package/src/services/thread/thread-listing.ts +198 -0
  156. package/src/services/thread/thread-memory-block.ts +117 -0
  157. package/src/services/thread/thread-message.service.ts +363 -0
  158. package/src/services/thread/thread-record-store.ts +155 -0
  159. package/src/services/thread/thread-title.service.ts +74 -0
  160. package/src/services/thread/thread-turn-execution.ts +280 -0
  161. package/src/services/thread/thread-turn-message-context.ts +73 -0
  162. package/src/services/thread/thread-turn-preparation.service.ts +1146 -0
  163. package/src/services/thread/thread-turn-streaming.ts +402 -0
  164. package/src/services/thread/thread-turn-tracing.ts +35 -0
  165. package/src/services/thread/thread-turn.ts +343 -0
  166. package/src/services/thread/thread.service.ts +335 -0
  167. package/src/services/user.service.ts +82 -32
  168. package/src/services/write-intent-validator.service.ts +63 -51
  169. package/src/storage/attachment-parser.ts +69 -27
  170. package/src/storage/attachment-storage.service.ts +331 -275
  171. package/src/storage/generated-document-storage.service.ts +66 -34
  172. package/src/system-agents/agent-result.ts +3 -1
  173. package/src/system-agents/context-compaction.agent.ts +2 -2
  174. package/src/system-agents/delegated-agent-factory.ts +159 -90
  175. package/src/system-agents/memory-reranker.agent.ts +2 -2
  176. package/src/system-agents/memory.agent.ts +2 -2
  177. package/src/system-agents/recent-activity-title-refiner.agent.ts +2 -2
  178. package/src/system-agents/regular-chat-memory-digest.agent.ts +2 -2
  179. package/src/system-agents/skill-extractor.agent.ts +2 -2
  180. package/src/system-agents/skill-manager.agent.ts +2 -2
  181. package/src/system-agents/thread-router.agent.ts +157 -113
  182. package/src/system-agents/title-generator.agent.ts +2 -2
  183. package/src/tools/execution-plan.tool.ts +220 -161
  184. package/src/tools/fetch-webpage.tool.ts +21 -17
  185. package/src/tools/firecrawl-client.ts +16 -6
  186. package/src/tools/index.ts +1 -0
  187. package/src/tools/memory-block.tool.ts +14 -6
  188. package/src/tools/plan-approval.tool.ts +49 -47
  189. package/src/tools/read-file-parts.tool.ts +44 -33
  190. package/src/tools/remember-memory.tool.ts +65 -45
  191. package/src/tools/search-web.tool.ts +26 -22
  192. package/src/tools/search.tool.ts +41 -29
  193. package/src/tools/team-think.tool.ts +124 -83
  194. package/src/tools/user-questions.tool.ts +4 -3
  195. package/src/tools/web-tool-shared.ts +6 -0
  196. package/src/utils/async.ts +17 -23
  197. package/src/utils/crypto.ts +21 -0
  198. package/src/utils/date-time.ts +40 -1
  199. package/src/utils/errors.ts +95 -16
  200. package/src/utils/hono-error-handler.ts +24 -39
  201. package/src/utils/index.ts +2 -1
  202. package/src/utils/null-proto-record.ts +41 -0
  203. package/src/utils/sse-keepalive.ts +124 -21
  204. package/src/workers/bootstrap.ts +186 -51
  205. package/src/workers/memory-consolidation.worker.ts +325 -237
  206. package/src/workers/organization-learning.worker.ts +50 -16
  207. package/src/workers/regular-chat-memory-digest.helpers.ts +28 -27
  208. package/src/workers/regular-chat-memory-digest.runner.ts +175 -114
  209. package/src/workers/skill-extraction.runner.ts +176 -93
  210. package/src/workers/utils/file-section-chunker.ts +8 -10
  211. package/src/workers/utils/repo-structure-extractor.ts +349 -260
  212. package/src/workers/utils/repomix-file-sections.ts +2 -2
  213. package/src/workers/utils/thread-message-query.ts +97 -38
  214. package/src/workers/worker-utils.ts +56 -31
  215. package/src/config/debug-logger.ts +0 -47
  216. package/src/redis/connection-accessor.ts +0 -26
  217. package/src/runtime/context-compaction-runtime.ts +0 -87
  218. package/src/runtime/social-chat-agent-runner.ts +0 -118
  219. package/src/runtime/social-chat.ts +0 -516
  220. package/src/runtime/team-consultation-orchestrator.ts +0 -272
  221. package/src/services/adaptive-playbook.service.ts +0 -152
  222. package/src/services/artifact-provenance.service.ts +0 -172
  223. package/src/services/chat-attachments.service.ts +0 -17
  224. package/src/services/context-compaction-runtime.singleton.ts +0 -13
  225. package/src/services/execution-plan.service.ts +0 -1118
  226. package/src/services/memory.service.ts +0 -914
  227. package/src/services/plan-agent-heartbeat.service.ts +0 -136
  228. package/src/services/plan-agent-query.service.ts +0 -267
  229. package/src/services/plan-approval.service.ts +0 -83
  230. package/src/services/plan-artifact.service.ts +0 -50
  231. package/src/services/plan-builder.service.ts +0 -67
  232. package/src/services/plan-checkpoint.service.ts +0 -81
  233. package/src/services/plan-completion-side-effects.ts +0 -80
  234. package/src/services/plan-coordination.service.ts +0 -157
  235. package/src/services/plan-cycle.service.ts +0 -284
  236. package/src/services/plan-deadline.service.ts +0 -430
  237. package/src/services/plan-event-delivery.service.ts +0 -166
  238. package/src/services/plan-executor.service.ts +0 -1950
  239. package/src/services/plan-run.service.ts +0 -515
  240. package/src/services/plan-scheduler.service.ts +0 -240
  241. package/src/services/plan-template.service.ts +0 -177
  242. package/src/services/plan-validator.service.ts +0 -818
  243. package/src/services/plan-workspace.service.ts +0 -83
  244. package/src/services/rerank.service.ts +0 -156
  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
- }