@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
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { stepCountIs } from 'ai'
|
|
2
|
+
import type { ToolLoopAgent, ToolSet } from 'ai'
|
|
3
|
+
|
|
4
|
+
import { createAgent, getAgentRuntimeConfig } from '../config/agent-defaults'
|
|
5
|
+
import { aiLogger } from '../config/logger'
|
|
6
|
+
|
|
7
|
+
type SocialChatAgent = ToolLoopAgent<never, ToolSet>
|
|
8
|
+
|
|
9
|
+
interface ExecutableToolDefinition {
|
|
10
|
+
execute: (...args: unknown[]) => Promise<unknown>
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const SOCIAL_CHAT_RETRIEVAL_TOOL_NAMES = [
|
|
14
|
+
'memorySearch',
|
|
15
|
+
'conversationSearch',
|
|
16
|
+
'queryKnowledge',
|
|
17
|
+
'researchTopic',
|
|
18
|
+
'searchWeb',
|
|
19
|
+
'fetchWebpage',
|
|
20
|
+
'inspectWebsite',
|
|
21
|
+
] as const
|
|
22
|
+
|
|
23
|
+
export function withLoggedSocialToolSet(
|
|
24
|
+
tools: ToolSet,
|
|
25
|
+
params: { agentId: string; channelId: string; threadId: string; executedToolNames: string[] },
|
|
26
|
+
): ToolSet {
|
|
27
|
+
return Object.fromEntries(
|
|
28
|
+
Object.entries(tools).map(([toolName, toolDefinition]) => {
|
|
29
|
+
if (
|
|
30
|
+
typeof toolDefinition !== 'object' ||
|
|
31
|
+
!('execute' in toolDefinition) ||
|
|
32
|
+
typeof toolDefinition.execute !== 'function'
|
|
33
|
+
) {
|
|
34
|
+
return [toolName, toolDefinition]
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const executableTool = toolDefinition as typeof toolDefinition & ExecutableToolDefinition
|
|
38
|
+
return [
|
|
39
|
+
toolName,
|
|
40
|
+
{
|
|
41
|
+
...toolDefinition,
|
|
42
|
+
execute: async (...args: unknown[]): Promise<unknown> => {
|
|
43
|
+
aiLogger.info`Slack social-chat tool start: agentId=${params.agentId}, tool=${toolName}, channelId=${params.channelId}, threadId=${params.threadId}`
|
|
44
|
+
try {
|
|
45
|
+
const result: unknown = await executableTool.execute(...args)
|
|
46
|
+
params.executedToolNames.push(toolName)
|
|
47
|
+
aiLogger.info`Slack social-chat tool finish: agentId=${params.agentId}, tool=${toolName}, channelId=${params.channelId}, threadId=${params.threadId}`
|
|
48
|
+
return result
|
|
49
|
+
} catch (error) {
|
|
50
|
+
aiLogger.warn`Slack social-chat tool failed: agentId=${params.agentId}, tool=${toolName}, channelId=${params.channelId}, threadId=${params.threadId}, error=${error}`
|
|
51
|
+
throw error
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
]
|
|
56
|
+
}),
|
|
57
|
+
)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export async function runSocialAgentTurn(params: {
|
|
61
|
+
agentId: string
|
|
62
|
+
mode: 'fixedWorkstreamMode' | 'workstreamMode'
|
|
63
|
+
workstreamMode: 'group'
|
|
64
|
+
onboardingActive: boolean
|
|
65
|
+
linearInstalled: boolean
|
|
66
|
+
systemWorkspaceDetails?: string
|
|
67
|
+
preSeededMemoriesSection?: string
|
|
68
|
+
retrievedKnowledgeSection?: string
|
|
69
|
+
learnedSkillsSection?: string
|
|
70
|
+
userMessageText?: string
|
|
71
|
+
additionalInstructionSections?: string[]
|
|
72
|
+
prompt: string
|
|
73
|
+
tools: ToolSet
|
|
74
|
+
abortSignal: AbortSignal
|
|
75
|
+
}): Promise<{ text: string; displayName: string }> {
|
|
76
|
+
const runtimeConfig = getAgentRuntimeConfig({
|
|
77
|
+
agentId: params.agentId,
|
|
78
|
+
workstreamMode: params.workstreamMode,
|
|
79
|
+
mode: params.mode,
|
|
80
|
+
onboardingActive: params.onboardingActive,
|
|
81
|
+
linearInstalled: params.linearInstalled,
|
|
82
|
+
systemWorkspaceDetails: params.systemWorkspaceDetails,
|
|
83
|
+
preSeededMemoriesSection: params.preSeededMemoriesSection,
|
|
84
|
+
retrievedKnowledgeSection: params.retrievedKnowledgeSection,
|
|
85
|
+
learnedSkillsSection: params.learnedSkillsSection,
|
|
86
|
+
userMessageText: params.userMessageText,
|
|
87
|
+
ruleOptions: {
|
|
88
|
+
includeExecutionPlanRule: false,
|
|
89
|
+
includeMemr3Rule: SOCIAL_CHAT_RETRIEVAL_TOOL_NAMES.some((toolName) => toolName in params.tools),
|
|
90
|
+
includeDomainReasoningFallbackRule: params.agentId === 'cpo' || params.agentId === 'mentor',
|
|
91
|
+
},
|
|
92
|
+
additionalInstructionSections: params.additionalInstructionSections,
|
|
93
|
+
}) as Record<string, unknown>
|
|
94
|
+
|
|
95
|
+
const agentFactory = createAgent[runtimeConfig.id as string]
|
|
96
|
+
if (typeof agentFactory !== 'function') {
|
|
97
|
+
throw new Error(`Social chat agent factory is not configured for ${String(runtimeConfig.id)}`)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const agent = agentFactory({
|
|
101
|
+
mode: params.mode,
|
|
102
|
+
tools: params.tools,
|
|
103
|
+
extraInstructions: runtimeConfig.extraInstructions,
|
|
104
|
+
stopWhen: [stepCountIs(runtimeConfig.maxSteps as number)],
|
|
105
|
+
}) as SocialChatAgent
|
|
106
|
+
const response = await agent.generate({ prompt: params.prompt, abortSignal: params.abortSignal })
|
|
107
|
+
const text = response.text.trim()
|
|
108
|
+
if (!text) {
|
|
109
|
+
throw new Error(`Social chat agent ${params.agentId} returned an empty response.`)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return {
|
|
113
|
+
text,
|
|
114
|
+
displayName:
|
|
115
|
+
typeof runtimeConfig.displayName === 'string' && runtimeConfig.displayName.trim()
|
|
116
|
+
? runtimeConfig.displayName.trim()
|
|
117
|
+
: params.agentId,
|
|
118
|
+
}
|
|
119
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { stripSlackToolExecutionNoticeMarkdown } from '@lota-sdk/shared'
|
|
2
|
+
import type { Message, Thread } from 'chat'
|
|
3
|
+
|
|
4
|
+
import type { SocialChatHistoryMessage } from '../services/social-chat-history.service'
|
|
5
|
+
import { toHistoryMessages, toOptionalTrimmedString } from './workstream-chat-helpers'
|
|
6
|
+
|
|
7
|
+
export function readSlackAuthorName(message: Message): string | undefined {
|
|
8
|
+
return (
|
|
9
|
+
toOptionalTrimmedString(message.author.fullName) ??
|
|
10
|
+
toOptionalTrimmedString(message.author.userName) ??
|
|
11
|
+
toOptionalTrimmedString(message.author.userId) ??
|
|
12
|
+
undefined
|
|
13
|
+
)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function buildSocialChatThreadTranscript(
|
|
17
|
+
messages: Array<{
|
|
18
|
+
role: 'user' | 'assistant'
|
|
19
|
+
parts: Array<Record<string, unknown>>
|
|
20
|
+
metadata?: Record<string, unknown>
|
|
21
|
+
}>,
|
|
22
|
+
): string {
|
|
23
|
+
const historyMessages = toHistoryMessages(messages)
|
|
24
|
+
if (historyMessages.length === 0) return 'No prior thread history.'
|
|
25
|
+
|
|
26
|
+
return historyMessages
|
|
27
|
+
.map((message) =>
|
|
28
|
+
message.role === 'user' ? `User: ${message.content}` : `${message.agentName ?? 'Assistant'}: ${message.content}`,
|
|
29
|
+
)
|
|
30
|
+
.join('\n\n')
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function createSocialChatCursorId(message: {
|
|
34
|
+
workspaceId: string
|
|
35
|
+
threadId: string
|
|
36
|
+
messageId: string
|
|
37
|
+
}): string {
|
|
38
|
+
return `social:slack:${message.workspaceId}:${message.threadId}:${message.messageId}`
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function normalizeSocialHistoryMessage(params: {
|
|
42
|
+
workspaceId: string
|
|
43
|
+
channelId: string
|
|
44
|
+
agentId: string
|
|
45
|
+
agentDisplayName: string
|
|
46
|
+
message: Message
|
|
47
|
+
textOverride?: string
|
|
48
|
+
}): SocialChatHistoryMessage | null {
|
|
49
|
+
const role: 'user' | 'assistant' = params.message.author.isMe ? 'assistant' : 'user'
|
|
50
|
+
const text = toOptionalTrimmedString(
|
|
51
|
+
role === 'assistant'
|
|
52
|
+
? stripSlackToolExecutionNoticeMarkdown(params.textOverride ?? params.message.text)
|
|
53
|
+
: (params.textOverride ?? params.message.text),
|
|
54
|
+
)
|
|
55
|
+
const fileParts = params.message.attachments.map((attachment) => ({
|
|
56
|
+
type: 'file',
|
|
57
|
+
filename: attachment.name ?? 'attachment',
|
|
58
|
+
mediaType: attachment.mimeType ?? 'application/octet-stream',
|
|
59
|
+
sizeBytes: typeof attachment.size === 'number' ? attachment.size : null,
|
|
60
|
+
storageKey: attachment.url ?? 'external',
|
|
61
|
+
}))
|
|
62
|
+
const parts = [...(text ? [{ type: 'text', text }] : []), ...fileParts]
|
|
63
|
+
if (parts.length === 0) return null
|
|
64
|
+
|
|
65
|
+
const metadata: Record<string, unknown> = {
|
|
66
|
+
platform: 'slack',
|
|
67
|
+
channelId: params.channelId,
|
|
68
|
+
threadId: params.message.threadId,
|
|
69
|
+
messageId: params.message.id,
|
|
70
|
+
authorId: params.message.author.userId,
|
|
71
|
+
authorName: readSlackAuthorName(params.message),
|
|
72
|
+
}
|
|
73
|
+
if (role === 'assistant') {
|
|
74
|
+
metadata.agentId = params.agentId
|
|
75
|
+
metadata.agentName = params.agentDisplayName
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return {
|
|
79
|
+
source: 'social',
|
|
80
|
+
sourceId: `slack:${params.channelId}:${params.message.threadId}`,
|
|
81
|
+
platform: 'slack',
|
|
82
|
+
workspaceId: params.workspaceId,
|
|
83
|
+
channelId: params.channelId,
|
|
84
|
+
threadId: params.message.threadId,
|
|
85
|
+
messageId: params.message.id,
|
|
86
|
+
role,
|
|
87
|
+
parts,
|
|
88
|
+
metadata,
|
|
89
|
+
cursor: {
|
|
90
|
+
createdAt: params.message.metadata.dateSent,
|
|
91
|
+
id: createSocialChatCursorId({
|
|
92
|
+
workspaceId: params.workspaceId,
|
|
93
|
+
threadId: params.message.threadId,
|
|
94
|
+
messageId: params.message.id,
|
|
95
|
+
}),
|
|
96
|
+
},
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export async function collectThreadMessages(thread: Thread, incomingMessage: Message): Promise<Message[]> {
|
|
101
|
+
try {
|
|
102
|
+
const messages: Message[] = []
|
|
103
|
+
for await (const message of thread.allMessages) {
|
|
104
|
+
messages.push(message)
|
|
105
|
+
}
|
|
106
|
+
return messages.length > 0 ? messages : [incomingMessage]
|
|
107
|
+
} catch {
|
|
108
|
+
return thread.recentMessages.length > 0 ? [...thread.recentMessages] : [incomingMessage]
|
|
109
|
+
}
|
|
110
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
export function buildSocialChatIdentitySection(agentDisplayName: string): string {
|
|
2
|
+
return [
|
|
3
|
+
'<social-chat-agent>',
|
|
4
|
+
`You are ${agentDisplayName}, the social chat agent for this workspace.`,
|
|
5
|
+
'- You respond inside Slack threads.',
|
|
6
|
+
'- Use the available tools, memories, learned skills, and workspace knowledge before guessing.',
|
|
7
|
+
'- If the answer may depend on prior workspace decisions, preferences, promises, history, or relationship context, check memory with the available memory tools before answering.',
|
|
8
|
+
'- 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.',
|
|
9
|
+
'- When another roster agent is better suited for a question, consult that specialist and then synthesize the final answer yourself.',
|
|
10
|
+
'- Keep the final reply crisp and readable for Slack unless the user explicitly asks for depth.',
|
|
11
|
+
'</social-chat-agent>',
|
|
12
|
+
].join('\n')
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function buildLeadSocialChatPrompt(params: {
|
|
16
|
+
agentDisplayName: string
|
|
17
|
+
channelId: string
|
|
18
|
+
threadId: string
|
|
19
|
+
transcript: string
|
|
20
|
+
latestUserMessage: string
|
|
21
|
+
latestAuthorName?: string
|
|
22
|
+
}): string {
|
|
23
|
+
return [
|
|
24
|
+
`Platform: Slack`,
|
|
25
|
+
`Channel ID: ${params.channelId}`,
|
|
26
|
+
`Thread ID: ${params.threadId}`,
|
|
27
|
+
`Current Slack author: ${params.latestAuthorName ?? 'Unknown'}`,
|
|
28
|
+
'',
|
|
29
|
+
`You are replying as ${params.agentDisplayName}.`,
|
|
30
|
+
'',
|
|
31
|
+
'<thread-transcript>',
|
|
32
|
+
params.transcript,
|
|
33
|
+
'</thread-transcript>',
|
|
34
|
+
'',
|
|
35
|
+
'Latest user message:',
|
|
36
|
+
params.latestUserMessage,
|
|
37
|
+
].join('\n')
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function buildSpecialistSocialChatPrompt(params: {
|
|
41
|
+
requesterName: string
|
|
42
|
+
agentName: string
|
|
43
|
+
task: string
|
|
44
|
+
transcript: string
|
|
45
|
+
}): string {
|
|
46
|
+
return [
|
|
47
|
+
`${params.requesterName} needs specialist help from ${params.agentName}.`,
|
|
48
|
+
'',
|
|
49
|
+
'<thread-transcript>',
|
|
50
|
+
params.transcript,
|
|
51
|
+
'</thread-transcript>',
|
|
52
|
+
'',
|
|
53
|
+
'Specialist task:',
|
|
54
|
+
params.task,
|
|
55
|
+
'',
|
|
56
|
+
'Answer only with the specialist guidance that should be used in the final Slack reply.',
|
|
57
|
+
].join('\n')
|
|
58
|
+
}
|