@lota-sdk/core 0.1.24 → 0.1.25
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.
- package/package.json +2 -2
- package/src/ai/definitions.ts +5 -59
- package/src/ai-gateway/ai-gateway.ts +36 -28
- package/src/ai-gateway/cache-headers.ts +9 -0
- package/src/config/model-constants.ts +6 -2
- package/src/create-runtime.ts +1 -17
- package/src/db/memory-types.ts +13 -8
- package/src/db/memory.ts +74 -53
- package/src/queues/autonomous-job.queue.ts +1 -8
- package/src/queues/context-compaction.queue.ts +2 -2
- package/src/queues/index.ts +2 -6
- package/src/queues/organization-learning.queue.ts +78 -0
- package/src/queues/plan-agent-heartbeat.queue.ts +10 -16
- package/src/queues/title-generation.queue.ts +62 -0
- package/src/runtime/agent-prompt-context.ts +0 -18
- package/src/runtime/agent-runtime-policy.ts +9 -2
- package/src/runtime/context-compaction-constants.ts +4 -2
- package/src/runtime/context-compaction.ts +135 -118
- package/src/runtime/memory-pipeline.ts +70 -1
- package/src/runtime/memory-prompts-fact.ts +16 -0
- package/src/runtime/plugin-resolution.ts +3 -2
- package/src/runtime/plugin-types.ts +1 -42
- package/src/runtime/post-turn-side-effects.ts +212 -0
- package/src/runtime/runtime-config.ts +0 -13
- package/src/runtime/runtime-extensions.ts +10 -16
- package/src/runtime/runtime-worker-registry.ts +8 -19
- package/src/runtime/social-chat-agent-runner.ts +119 -0
- package/src/runtime/social-chat-history.ts +110 -0
- package/src/runtime/social-chat-prompts.ts +58 -0
- package/src/runtime/social-chat.ts +104 -340
- package/src/runtime/specialist-runner.ts +18 -0
- package/src/runtime/workstream-chat-helpers.ts +19 -0
- package/src/runtime/workstream-plan-turn.ts +195 -0
- package/src/runtime/workstream-state.ts +11 -8
- package/src/runtime/workstream-turn-context.ts +183 -0
- package/src/services/autonomous-job.service.ts +1 -8
- package/src/services/execution-plan.service.ts +205 -334
- package/src/services/index.ts +1 -4
- package/src/services/memory.service.ts +54 -44
- package/src/services/ownership-dispatcher.service.ts +2 -19
- package/src/services/plan-completion-side-effects.ts +80 -0
- package/src/services/plan-event-delivery.service.ts +1 -1
- package/src/services/plan-executor.service.ts +42 -190
- package/src/services/plan-node-spec.ts +60 -0
- package/src/services/plan-run-data.ts +88 -0
- package/src/services/plan-validator.service.ts +10 -8
- package/src/services/workstream-constants.ts +2 -0
- package/src/services/workstream-title.service.ts +1 -1
- package/src/services/workstream-turn-preparation.service.ts +208 -715
- package/src/services/workstream.service.ts +162 -192
- package/src/services/workstream.types.ts +12 -44
- package/src/system-agents/regular-chat-memory-digest.agent.ts +3 -0
- package/src/tools/execution-plan.tool.ts +7 -6
- package/src/tools/remember-memory.tool.ts +7 -10
- package/src/tools/research-topic.tool.ts +1 -1
- package/src/tools/team-think.tool.ts +1 -1
- package/src/tools/user-questions.tool.ts +1 -1
- package/src/utils/autonomous-job-ids.ts +7 -0
- package/src/workers/organization-learning.worker.ts +31 -0
- package/src/workers/regular-chat-memory-digest.runner.ts +9 -3
- package/src/workers/skill-extraction.runner.ts +2 -2
- package/src/queues/recent-activity-title-refinement.queue.ts +0 -30
- package/src/queues/regular-chat-memory-digest.config.ts +0 -12
- package/src/queues/regular-chat-memory-digest.queue.ts +0 -34
- package/src/queues/skill-extraction.config.ts +0 -9
- package/src/queues/skill-extraction.queue.ts +0 -27
- package/src/queues/workstream-title-generation.queue.ts +0 -33
- package/src/services/context-enrichment.service.ts +0 -33
- package/src/services/coordination-registry.service.ts +0 -117
- package/src/services/domain-agent-executor.service.ts +0 -71
- package/src/services/memory-assessment.service.ts +0 -44
- package/src/services/playbook-registry.service.ts +0 -67
- package/src/workers/regular-chat-memory-digest.worker.ts +0 -22
- package/src/workers/skill-extraction.worker.ts +0 -22
|
@@ -4,27 +4,19 @@ import {
|
|
|
4
4
|
buildSlackSocialReplyMarkdown,
|
|
5
5
|
CONSULT_SPECIALIST_TOOL_NAME,
|
|
6
6
|
ConsultSpecialistArgsSchema,
|
|
7
|
-
stripSlackToolExecutionNoticeMarkdown,
|
|
8
7
|
} from '@lota-sdk/shared'
|
|
9
8
|
import type { ChatMessage, ConsultSpecialistArgs } from '@lota-sdk/shared'
|
|
10
|
-
import {
|
|
11
|
-
import type { ToolLoopAgent, ToolSet } from 'ai'
|
|
9
|
+
import { tool as createTool } from 'ai'
|
|
12
10
|
import { Chat, ConsoleLogger } from 'chat'
|
|
13
11
|
import type { Message, Thread, WebhookOptions } from 'chat'
|
|
14
12
|
import type IORedis from 'ioredis'
|
|
15
13
|
|
|
16
|
-
import {
|
|
17
|
-
agentDisplayNames,
|
|
18
|
-
createAgent,
|
|
19
|
-
getAgentRuntimeConfig,
|
|
20
|
-
teamConsultParticipants,
|
|
21
|
-
} from '../config/agent-defaults'
|
|
14
|
+
import { agentDisplayNames, teamConsultParticipants } from '../config/agent-defaults'
|
|
22
15
|
import { aiLogger } from '../config/logger'
|
|
23
16
|
import { recordIdToString } from '../db/record-id'
|
|
24
17
|
import { TABLES } from '../db/tables'
|
|
18
|
+
import { enqueueRegularChatMemoryDigest, enqueueSkillExtraction } from '../queues/organization-learning.queue'
|
|
25
19
|
import { enqueuePostChatMemory } from '../queues/post-chat-memory.queue'
|
|
26
|
-
import { enqueueRegularChatMemoryDigest } from '../queues/regular-chat-memory-digest.queue'
|
|
27
|
-
import { enqueueSkillExtraction } from '../queues/skill-extraction.queue'
|
|
28
20
|
import type {
|
|
29
21
|
BuildSocialChatAgentToolsParams,
|
|
30
22
|
LotaRuntimeSocialChatConfig,
|
|
@@ -37,7 +29,26 @@ import { socialChatHistoryService } from '../services/social-chat-history.servic
|
|
|
37
29
|
import { safeEnqueue } from '../utils/async'
|
|
38
30
|
import { buildAgentPromptContext } from './agent-prompt-context'
|
|
39
31
|
import { createServerRunAbortController } from './agent-stream-helpers'
|
|
40
|
-
import {
|
|
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 './workstream-chat-helpers'
|
|
41
52
|
|
|
42
53
|
const DEFAULT_SOCIAL_CHAT_AGENT_ID = 'socialChat'
|
|
43
54
|
const DEFAULT_SOCIAL_CHAT_AGENT_DISPLAY_NAME = 'Lota'
|
|
@@ -45,8 +56,6 @@ const DEFAULT_SOCIAL_CHAT_STATE_PREFIX = 'lota:social:chat-sdk'
|
|
|
45
56
|
const DEFAULT_SOCIAL_CHAT_DEDUPE_TTL_MS = 15 * 60 * 1000
|
|
46
57
|
const PRESEEDED_MEMORY_LOOKUP_LIMIT = 3
|
|
47
58
|
|
|
48
|
-
type SocialChatAgent = ToolLoopAgent<never, ToolSet>
|
|
49
|
-
|
|
50
59
|
export interface LotaRuntimeSocialChat {
|
|
51
60
|
enabled: boolean
|
|
52
61
|
initialize(): Promise<void>
|
|
@@ -76,96 +85,6 @@ function createDisabledSocialChatRuntime(): LotaRuntimeSocialChat {
|
|
|
76
85
|
}
|
|
77
86
|
}
|
|
78
87
|
|
|
79
|
-
function toOptionalTrimmedString(value: unknown): string | undefined {
|
|
80
|
-
if (typeof value !== 'string') return undefined
|
|
81
|
-
const normalized = value.trim()
|
|
82
|
-
return normalized.length > 0 ? normalized : undefined
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
function readSlackAuthorName(message: Message): string | undefined {
|
|
86
|
-
return (
|
|
87
|
-
toOptionalTrimmedString(message.author.fullName) ??
|
|
88
|
-
toOptionalTrimmedString(message.author.userName) ??
|
|
89
|
-
toOptionalTrimmedString(message.author.userId)
|
|
90
|
-
)
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
function buildSocialChatIdentitySection(agentDisplayName: string): string {
|
|
94
|
-
return [
|
|
95
|
-
'<social-chat-agent>',
|
|
96
|
-
`You are ${agentDisplayName}, the social chat agent for this workspace.`,
|
|
97
|
-
'- You respond inside Slack threads.',
|
|
98
|
-
'- Use the available tools, memories, learned skills, and workspace knowledge before guessing.',
|
|
99
|
-
'- If the answer may depend on prior workspace decisions, preferences, promises, history, or relationship context, check memory with the available memory tools before answering.',
|
|
100
|
-
'- If the user shares a URL or asks about a website/page, read it with fetchWebpage before answering. Do not summarize website contents from prior knowledge.',
|
|
101
|
-
'- When another roster agent is better suited for a question, consult that specialist and then synthesize the final answer yourself.',
|
|
102
|
-
'- Keep the final reply crisp and readable for Slack unless the user explicitly asks for depth.',
|
|
103
|
-
'</social-chat-agent>',
|
|
104
|
-
].join('\n')
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
function buildThreadTranscript(
|
|
108
|
-
messages: Array<{
|
|
109
|
-
role: 'user' | 'assistant'
|
|
110
|
-
parts: Array<Record<string, unknown>>
|
|
111
|
-
metadata?: Record<string, unknown>
|
|
112
|
-
}>,
|
|
113
|
-
): string {
|
|
114
|
-
const historyMessages = toHistoryMessages(messages)
|
|
115
|
-
if (historyMessages.length === 0) return 'No prior thread history.'
|
|
116
|
-
|
|
117
|
-
return historyMessages
|
|
118
|
-
.map((message) =>
|
|
119
|
-
message.role === 'user' ? `User: ${message.content}` : `${message.agentName ?? 'Assistant'}: ${message.content}`,
|
|
120
|
-
)
|
|
121
|
-
.join('\n\n')
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
function buildLeadAgentPrompt(params: {
|
|
125
|
-
agentDisplayName: string
|
|
126
|
-
channelId: string
|
|
127
|
-
threadId: string
|
|
128
|
-
transcript: string
|
|
129
|
-
latestUserMessage: string
|
|
130
|
-
latestAuthorName?: string
|
|
131
|
-
}): string {
|
|
132
|
-
return [
|
|
133
|
-
`Platform: Slack`,
|
|
134
|
-
`Channel ID: ${params.channelId}`,
|
|
135
|
-
`Thread ID: ${params.threadId}`,
|
|
136
|
-
`Current Slack author: ${params.latestAuthorName ?? 'Unknown'}`,
|
|
137
|
-
'',
|
|
138
|
-
`You are replying as ${params.agentDisplayName}.`,
|
|
139
|
-
'',
|
|
140
|
-
'<thread-transcript>',
|
|
141
|
-
params.transcript,
|
|
142
|
-
'</thread-transcript>',
|
|
143
|
-
'',
|
|
144
|
-
'Latest user message:',
|
|
145
|
-
params.latestUserMessage,
|
|
146
|
-
].join('\n')
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
function buildSpecialistPrompt(params: {
|
|
150
|
-
requesterName: string
|
|
151
|
-
agentName: string
|
|
152
|
-
task: string
|
|
153
|
-
transcript: string
|
|
154
|
-
}): string {
|
|
155
|
-
return [
|
|
156
|
-
`${params.requesterName} needs specialist help from ${params.agentName}.`,
|
|
157
|
-
'',
|
|
158
|
-
'<thread-transcript>',
|
|
159
|
-
params.transcript,
|
|
160
|
-
'</thread-transcript>',
|
|
161
|
-
'',
|
|
162
|
-
'Specialist task:',
|
|
163
|
-
params.task,
|
|
164
|
-
'',
|
|
165
|
-
'Answer only with the specialist guidance that should be used in the final Slack reply.',
|
|
166
|
-
].join('\n')
|
|
167
|
-
}
|
|
168
|
-
|
|
169
88
|
function createAssistantMessage(params: { agentId: string; agentName: string; text: string }): ChatMessage {
|
|
170
89
|
return {
|
|
171
90
|
id: Bun.randomUUIDv7(),
|
|
@@ -175,10 +94,6 @@ function createAssistantMessage(params: { agentId: string; agentName: string; te
|
|
|
175
94
|
}
|
|
176
95
|
}
|
|
177
96
|
|
|
178
|
-
function createCursorId(message: { workspaceId: string; threadId: string; messageId: string }): string {
|
|
179
|
-
return `social:slack:${message.workspaceId}:${message.threadId}:${message.messageId}`
|
|
180
|
-
}
|
|
181
|
-
|
|
182
97
|
function toSafeJobIdSegment(value: string): string {
|
|
183
98
|
return value.replace(/[^a-zA-Z0-9_-]/g, '_')
|
|
184
99
|
}
|
|
@@ -192,132 +107,6 @@ function createSocialMemoryDedupeKey(params: { workspaceId: string; threadId: st
|
|
|
192
107
|
].join('-')
|
|
193
108
|
}
|
|
194
109
|
|
|
195
|
-
interface ExecutableToolDefinition {
|
|
196
|
-
execute: (...args: unknown[]) => Promise<unknown>
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
function withLoggedToolSet(
|
|
200
|
-
tools: ToolSet,
|
|
201
|
-
params: { agentId: string; channelId: string; threadId: string; executedToolNames: string[] },
|
|
202
|
-
): ToolSet {
|
|
203
|
-
return Object.fromEntries(
|
|
204
|
-
Object.entries(tools).map(([toolName, toolDefinition]) => {
|
|
205
|
-
if (
|
|
206
|
-
typeof toolDefinition !== 'object' ||
|
|
207
|
-
!('execute' in toolDefinition) ||
|
|
208
|
-
typeof toolDefinition.execute !== 'function'
|
|
209
|
-
) {
|
|
210
|
-
return [toolName, toolDefinition]
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
const executableTool = toolDefinition as typeof toolDefinition & ExecutableToolDefinition
|
|
214
|
-
|
|
215
|
-
return [
|
|
216
|
-
toolName,
|
|
217
|
-
{
|
|
218
|
-
...toolDefinition,
|
|
219
|
-
execute: async (...args: unknown[]): Promise<unknown> => {
|
|
220
|
-
aiLogger.info`Slack social-chat tool start: agentId=${params.agentId}, tool=${toolName}, channelId=${params.channelId}, threadId=${params.threadId}`
|
|
221
|
-
try {
|
|
222
|
-
const result: unknown = await executableTool.execute(...args)
|
|
223
|
-
params.executedToolNames.push(toolName)
|
|
224
|
-
aiLogger.info`Slack social-chat tool finish: agentId=${params.agentId}, tool=${toolName}, channelId=${params.channelId}, threadId=${params.threadId}`
|
|
225
|
-
return result
|
|
226
|
-
} catch (error) {
|
|
227
|
-
aiLogger.warn`Slack social-chat tool failed: agentId=${params.agentId}, tool=${toolName}, channelId=${params.channelId}, threadId=${params.threadId}, error=${error}`
|
|
228
|
-
throw error
|
|
229
|
-
}
|
|
230
|
-
},
|
|
231
|
-
},
|
|
232
|
-
]
|
|
233
|
-
}),
|
|
234
|
-
)
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
function normalizeSocialHistoryMessage(params: {
|
|
238
|
-
workspaceId: string
|
|
239
|
-
channelId: string
|
|
240
|
-
agentId: string
|
|
241
|
-
agentDisplayName: string
|
|
242
|
-
message: Message
|
|
243
|
-
textOverride?: string
|
|
244
|
-
}): {
|
|
245
|
-
source: 'social'
|
|
246
|
-
sourceId: string
|
|
247
|
-
platform: 'slack'
|
|
248
|
-
workspaceId: string
|
|
249
|
-
channelId: string
|
|
250
|
-
threadId: string
|
|
251
|
-
messageId: string
|
|
252
|
-
role: 'user' | 'assistant'
|
|
253
|
-
parts: Array<Record<string, unknown>>
|
|
254
|
-
metadata?: Record<string, unknown>
|
|
255
|
-
cursor: { createdAt: Date; id: string }
|
|
256
|
-
} | null {
|
|
257
|
-
const role: 'user' | 'assistant' = params.message.author.isMe ? 'assistant' : 'user'
|
|
258
|
-
const text = toOptionalTrimmedString(
|
|
259
|
-
role === 'assistant'
|
|
260
|
-
? stripSlackToolExecutionNoticeMarkdown(params.textOverride ?? params.message.text)
|
|
261
|
-
: (params.textOverride ?? params.message.text),
|
|
262
|
-
)
|
|
263
|
-
const fileParts = params.message.attachments.map((attachment) => ({
|
|
264
|
-
type: 'file',
|
|
265
|
-
filename: attachment.name ?? 'attachment',
|
|
266
|
-
mediaType: attachment.mimeType ?? 'application/octet-stream',
|
|
267
|
-
sizeBytes: typeof attachment.size === 'number' ? attachment.size : null,
|
|
268
|
-
storageKey: attachment.url ?? 'external',
|
|
269
|
-
}))
|
|
270
|
-
const parts = [...(text ? [{ type: 'text', text }] : []), ...fileParts]
|
|
271
|
-
if (parts.length === 0) return null
|
|
272
|
-
|
|
273
|
-
const createdAt = params.message.metadata.dateSent
|
|
274
|
-
const metadata: Record<string, unknown> = {
|
|
275
|
-
platform: 'slack',
|
|
276
|
-
channelId: params.channelId,
|
|
277
|
-
threadId: params.message.threadId,
|
|
278
|
-
messageId: params.message.id,
|
|
279
|
-
authorId: params.message.author.userId,
|
|
280
|
-
authorName: readSlackAuthorName(params.message),
|
|
281
|
-
}
|
|
282
|
-
if (role === 'assistant') {
|
|
283
|
-
metadata.agentId = params.agentId
|
|
284
|
-
metadata.agentName = params.agentDisplayName
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
return {
|
|
288
|
-
source: 'social',
|
|
289
|
-
sourceId: `slack:${params.channelId}:${params.message.threadId}`,
|
|
290
|
-
platform: 'slack',
|
|
291
|
-
workspaceId: params.workspaceId,
|
|
292
|
-
channelId: params.channelId,
|
|
293
|
-
threadId: params.message.threadId,
|
|
294
|
-
messageId: params.message.id,
|
|
295
|
-
role,
|
|
296
|
-
parts,
|
|
297
|
-
metadata,
|
|
298
|
-
cursor: {
|
|
299
|
-
createdAt,
|
|
300
|
-
id: createCursorId({
|
|
301
|
-
workspaceId: params.workspaceId,
|
|
302
|
-
threadId: params.message.threadId,
|
|
303
|
-
messageId: params.message.id,
|
|
304
|
-
}),
|
|
305
|
-
},
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
async function collectThreadMessages(thread: Thread, incomingMessage: Message): Promise<Message[]> {
|
|
310
|
-
try {
|
|
311
|
-
const messages: Message[] = []
|
|
312
|
-
for await (const message of thread.allMessages) {
|
|
313
|
-
messages.push(message)
|
|
314
|
-
}
|
|
315
|
-
return messages.length > 0 ? messages : [incomingMessage]
|
|
316
|
-
} catch {
|
|
317
|
-
return thread.recentMessages.length > 0 ? [...thread.recentMessages] : [incomingMessage]
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
|
|
321
110
|
function buildBuildToolsParams(params: {
|
|
322
111
|
agentId: string
|
|
323
112
|
context: LotaSocialChatResolvedContext
|
|
@@ -395,7 +184,7 @@ export function createSocialChatRuntime(params: {
|
|
|
395
184
|
threadId: thread.id,
|
|
396
185
|
messageId: incomingMessage.id,
|
|
397
186
|
text: incomingMessage.text.trim(),
|
|
398
|
-
authorId: toOptionalTrimmedString(incomingMessage.author.userId),
|
|
187
|
+
authorId: toOptionalTrimmedString(incomingMessage.author.userId) ?? undefined,
|
|
399
188
|
authorName: readSlackAuthorName(incomingMessage),
|
|
400
189
|
}
|
|
401
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}`
|
|
@@ -437,7 +226,7 @@ export function createSocialChatRuntime(params: {
|
|
|
437
226
|
? historyBeforeReply.filter((message) => message.cursor.id !== currentUserMessage.cursor.id)
|
|
438
227
|
: historyBeforeReply
|
|
439
228
|
|
|
440
|
-
const workspaceProvider = getRuntimeAdapters().
|
|
229
|
+
const workspaceProvider = getRuntimeAdapters().workspaceProvider
|
|
441
230
|
const workspace = workspaceProvider ? await workspaceProvider.getWorkspace(resolvedContext.workspaceId) : {}
|
|
442
231
|
const lifecycleState = workspaceProvider ? await workspaceProvider.getLifecycleState?.(workspace) : undefined
|
|
443
232
|
const workspaceProfileState = workspaceProvider
|
|
@@ -453,9 +242,10 @@ export function createSocialChatRuntime(params: {
|
|
|
453
242
|
|
|
454
243
|
const promptContext = buildAgentPromptContext({
|
|
455
244
|
workspaceName:
|
|
456
|
-
workspaceProfileState?.workspaceName ??
|
|
245
|
+
workspaceProfileState?.workspaceName ??
|
|
246
|
+
toOptionalTrimmedString((workspace as { name?: unknown }).name) ??
|
|
247
|
+
undefined,
|
|
457
248
|
summaryBlock: workspaceProfileState?.summaryBlock,
|
|
458
|
-
structuredProfile: workspaceProfileState?.structuredProfile,
|
|
459
249
|
promptSummary,
|
|
460
250
|
userName: messageContext.authorName,
|
|
461
251
|
recentDomainEvents,
|
|
@@ -492,23 +282,6 @@ export function createSocialChatRuntime(params: {
|
|
|
492
282
|
])
|
|
493
283
|
|
|
494
284
|
let memoryBlock = ''
|
|
495
|
-
const runtimeConfig = getAgentRuntimeConfig({
|
|
496
|
-
agentId: socialAgentId,
|
|
497
|
-
workstreamMode: 'group',
|
|
498
|
-
mode: 'workstreamMode',
|
|
499
|
-
onboardingActive: lifecycleState?.bootstrapActive ?? false,
|
|
500
|
-
linearInstalled: false,
|
|
501
|
-
systemWorkspaceDetails: promptContext.systemWorkspaceDetails,
|
|
502
|
-
preSeededMemoriesSection,
|
|
503
|
-
retrievedKnowledgeSection,
|
|
504
|
-
learnedSkillsSection,
|
|
505
|
-
additionalInstructionSections: [buildSocialChatIdentitySection(socialAgentDisplayName)],
|
|
506
|
-
}) as Record<string, unknown>
|
|
507
|
-
const agentFactory = createAgent[runtimeConfig.id as string]
|
|
508
|
-
if (typeof agentFactory !== 'function') {
|
|
509
|
-
throw new Error(`Social chat agent factory is not configured for ${String(runtimeConfig.id)}`)
|
|
510
|
-
}
|
|
511
|
-
|
|
512
285
|
const consultParticipants = [
|
|
513
286
|
...new Set(
|
|
514
287
|
(await socialChatConfig.getConsultParticipants?.({
|
|
@@ -520,7 +293,7 @@ export function createSocialChatRuntime(params: {
|
|
|
520
293
|
].filter((agentId) => agentId !== socialAgentId)
|
|
521
294
|
const executedToolNames: string[] = []
|
|
522
295
|
|
|
523
|
-
const baseTools =
|
|
296
|
+
const baseTools = withLoggedSocialToolSet(
|
|
524
297
|
await socialChatConfig.buildAgentTools(
|
|
525
298
|
buildBuildToolsParams({
|
|
526
299
|
agentId: socialAgentId,
|
|
@@ -542,7 +315,7 @@ export function createSocialChatRuntime(params: {
|
|
|
542
315
|
},
|
|
543
316
|
)
|
|
544
317
|
|
|
545
|
-
const transcript =
|
|
318
|
+
const transcript = buildSocialChatThreadTranscript(historyBeforeReply)
|
|
546
319
|
const runAbort = createServerRunAbortController()
|
|
547
320
|
|
|
548
321
|
const consultSpecialistTool = createTool({
|
|
@@ -553,77 +326,66 @@ export function createSocialChatRuntime(params: {
|
|
|
553
326
|
throw new Error(`Agent "${agentId}" is not an allowed social-chat specialist.`)
|
|
554
327
|
}
|
|
555
328
|
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
}),
|
|
570
|
-
),
|
|
571
|
-
{ agentId, channelId: messageContext.channelId, threadId: messageContext.threadId, executedToolNames },
|
|
572
|
-
)
|
|
573
|
-
const [specialistPreSeededMemories, specialistLearnedSkills] = await Promise.all([
|
|
574
|
-
memoryService.getTopMemories({
|
|
575
|
-
orgId: workspaceIdString,
|
|
576
|
-
agentName: agentId,
|
|
577
|
-
limit: PRESEEDED_MEMORY_LOOKUP_LIMIT,
|
|
578
|
-
}),
|
|
579
|
-
lifecycleState?.bootstrapActive
|
|
580
|
-
? Promise.resolve(undefined)
|
|
581
|
-
: learnedSkillService
|
|
582
|
-
.retrieveForTurn({ orgId: workspaceIdString, agentId, query: task, limit: 3, minConfidence: 0.6 })
|
|
583
|
-
.catch((error) => {
|
|
584
|
-
aiLogger.warn`Failed to retrieve learned skills for ${agentId}: ${error}`
|
|
585
|
-
return undefined
|
|
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,
|
|
586
342
|
}),
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
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: 'fixedWorkstreamMode',
|
|
366
|
+
workstreamMode: '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
|
+
},
|
|
622
387
|
})
|
|
623
|
-
const text =
|
|
624
|
-
if (!text) {
|
|
625
|
-
throw new Error(`Specialist ${agentId} returned an empty response.`)
|
|
626
|
-
}
|
|
388
|
+
const text = specialistRun.text
|
|
627
389
|
|
|
628
390
|
return createAssistantMessage({ agentId, agentName: agentDisplayNames[agentId] ?? agentId, text })
|
|
629
391
|
},
|
|
@@ -641,17 +403,22 @@ export function createSocialChatRuntime(params: {
|
|
|
641
403
|
},
|
|
642
404
|
})
|
|
643
405
|
|
|
644
|
-
const agent = agentFactory({
|
|
645
|
-
mode: 'workstreamMode',
|
|
646
|
-
tools: { ...baseTools, [CONSULT_SPECIALIST_TOOL_NAME]: consultSpecialistTool },
|
|
647
|
-
extraInstructions: runtimeConfig.extraInstructions,
|
|
648
|
-
stopWhen: [stepCountIs(runtimeConfig.maxSteps as number)],
|
|
649
|
-
}) as SocialChatAgent
|
|
650
|
-
|
|
651
406
|
await thread.startTyping('Thinking...').catch(() => undefined)
|
|
652
407
|
aiLogger.info`Slack social-chat generating reply: channelId=${messageContext.channelId}, threadId=${messageContext.threadId}`
|
|
653
|
-
const
|
|
654
|
-
|
|
408
|
+
const leadRun = await runSocialAgentTurn({
|
|
409
|
+
agentId: socialAgentId,
|
|
410
|
+
mode: 'workstreamMode',
|
|
411
|
+
workstreamMode: '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({
|
|
655
422
|
agentDisplayName: socialAgentDisplayName,
|
|
656
423
|
channelId: messageContext.channelId,
|
|
657
424
|
threadId: messageContext.threadId,
|
|
@@ -661,10 +428,7 @@ export function createSocialChatRuntime(params: {
|
|
|
661
428
|
}),
|
|
662
429
|
abortSignal: runAbort.signal,
|
|
663
430
|
})
|
|
664
|
-
const responseText =
|
|
665
|
-
if (!responseText) {
|
|
666
|
-
throw new Error('Social chat agent returned an empty response.')
|
|
667
|
-
}
|
|
431
|
+
const responseText = leadRun.text
|
|
668
432
|
|
|
669
433
|
const replyMarkdown = buildSlackSocialReplyMarkdown({ replyMarkdown: responseText, executedToolNames })
|
|
670
434
|
const sentMessage = await thread.post({ markdown: replyMarkdown })
|
|
@@ -690,7 +454,7 @@ export function createSocialChatRuntime(params: {
|
|
|
690
454
|
{
|
|
691
455
|
orgId: workspaceIdString,
|
|
692
456
|
workstreamId: `social:slack:${messageContext.threadId}`,
|
|
693
|
-
sourceId:
|
|
457
|
+
sourceId: createSocialChatCursorId({
|
|
694
458
|
workspaceId: workspaceIdString,
|
|
695
459
|
threadId: messageContext.threadId,
|
|
696
460
|
messageId: messageContext.messageId,
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { ToolSet } from 'ai'
|
|
2
|
+
|
|
3
|
+
export async function runSpecialistSession<TResult>(params: {
|
|
4
|
+
initialMemoryBlock: string
|
|
5
|
+
buildTools: (options: { memoryBlock: string; onAppendMemoryBlock: (value: string) => void }) => Promise<ToolSet>
|
|
6
|
+
run: (options: { tools: ToolSet; memoryBlock: string }) => Promise<TResult>
|
|
7
|
+
}): Promise<{ result: TResult; memoryBlock: string }> {
|
|
8
|
+
let specialistMemoryBlock = params.initialMemoryBlock
|
|
9
|
+
const tools = await params.buildTools({
|
|
10
|
+
memoryBlock: specialistMemoryBlock,
|
|
11
|
+
onAppendMemoryBlock: (value) => {
|
|
12
|
+
specialistMemoryBlock = value
|
|
13
|
+
},
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
const result = await params.run({ tools, memoryBlock: specialistMemoryBlock })
|
|
17
|
+
return { result, memoryBlock: specialistMemoryBlock }
|
|
18
|
+
}
|
|
@@ -7,6 +7,25 @@ export interface WorkstreamHistoryMessage {
|
|
|
7
7
|
agentName?: string
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
+
export function asRecord(value: unknown): Record<string, unknown> | null {
|
|
11
|
+
return value && typeof value === 'object' ? (value as Record<string, unknown>) : null
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function readOptionalString(value: unknown): string | undefined {
|
|
15
|
+
return typeof value === 'string' && value.trim().length > 0 ? value : undefined
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function readInstructionSections(value: unknown): string[] {
|
|
19
|
+
if (!Array.isArray(value)) {
|
|
20
|
+
return []
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return value
|
|
24
|
+
.filter((section): section is string => typeof section === 'string')
|
|
25
|
+
.map((section) => section.trim())
|
|
26
|
+
.filter((section) => section.length > 0)
|
|
27
|
+
}
|
|
28
|
+
|
|
10
29
|
function getAgentName(message: ChatMessageLike): string | undefined {
|
|
11
30
|
const metadata = message.metadata
|
|
12
31
|
if (!metadata || typeof metadata !== 'object') return undefined
|