@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.
Files changed (74) hide show
  1. package/package.json +2 -2
  2. package/src/ai/definitions.ts +5 -59
  3. package/src/ai-gateway/ai-gateway.ts +36 -28
  4. package/src/ai-gateway/cache-headers.ts +9 -0
  5. package/src/config/model-constants.ts +6 -2
  6. package/src/create-runtime.ts +1 -17
  7. package/src/db/memory-types.ts +13 -8
  8. package/src/db/memory.ts +74 -53
  9. package/src/queues/autonomous-job.queue.ts +1 -8
  10. package/src/queues/context-compaction.queue.ts +2 -2
  11. package/src/queues/index.ts +2 -6
  12. package/src/queues/organization-learning.queue.ts +78 -0
  13. package/src/queues/plan-agent-heartbeat.queue.ts +10 -16
  14. package/src/queues/title-generation.queue.ts +62 -0
  15. package/src/runtime/agent-prompt-context.ts +0 -18
  16. package/src/runtime/agent-runtime-policy.ts +9 -2
  17. package/src/runtime/context-compaction-constants.ts +4 -2
  18. package/src/runtime/context-compaction.ts +135 -118
  19. package/src/runtime/memory-pipeline.ts +70 -1
  20. package/src/runtime/memory-prompts-fact.ts +16 -0
  21. package/src/runtime/plugin-resolution.ts +3 -2
  22. package/src/runtime/plugin-types.ts +1 -42
  23. package/src/runtime/post-turn-side-effects.ts +212 -0
  24. package/src/runtime/runtime-config.ts +0 -13
  25. package/src/runtime/runtime-extensions.ts +10 -16
  26. package/src/runtime/runtime-worker-registry.ts +8 -19
  27. package/src/runtime/social-chat-agent-runner.ts +119 -0
  28. package/src/runtime/social-chat-history.ts +110 -0
  29. package/src/runtime/social-chat-prompts.ts +58 -0
  30. package/src/runtime/social-chat.ts +104 -340
  31. package/src/runtime/specialist-runner.ts +18 -0
  32. package/src/runtime/workstream-chat-helpers.ts +19 -0
  33. package/src/runtime/workstream-plan-turn.ts +195 -0
  34. package/src/runtime/workstream-state.ts +11 -8
  35. package/src/runtime/workstream-turn-context.ts +183 -0
  36. package/src/services/autonomous-job.service.ts +1 -8
  37. package/src/services/execution-plan.service.ts +205 -334
  38. package/src/services/index.ts +1 -4
  39. package/src/services/memory.service.ts +54 -44
  40. package/src/services/ownership-dispatcher.service.ts +2 -19
  41. package/src/services/plan-completion-side-effects.ts +80 -0
  42. package/src/services/plan-event-delivery.service.ts +1 -1
  43. package/src/services/plan-executor.service.ts +42 -190
  44. package/src/services/plan-node-spec.ts +60 -0
  45. package/src/services/plan-run-data.ts +88 -0
  46. package/src/services/plan-validator.service.ts +10 -8
  47. package/src/services/workstream-constants.ts +2 -0
  48. package/src/services/workstream-title.service.ts +1 -1
  49. package/src/services/workstream-turn-preparation.service.ts +208 -715
  50. package/src/services/workstream.service.ts +162 -192
  51. package/src/services/workstream.types.ts +12 -44
  52. package/src/system-agents/regular-chat-memory-digest.agent.ts +3 -0
  53. package/src/tools/execution-plan.tool.ts +7 -6
  54. package/src/tools/remember-memory.tool.ts +7 -10
  55. package/src/tools/research-topic.tool.ts +1 -1
  56. package/src/tools/team-think.tool.ts +1 -1
  57. package/src/tools/user-questions.tool.ts +1 -1
  58. package/src/utils/autonomous-job-ids.ts +7 -0
  59. package/src/workers/organization-learning.worker.ts +31 -0
  60. package/src/workers/regular-chat-memory-digest.runner.ts +9 -3
  61. package/src/workers/skill-extraction.runner.ts +2 -2
  62. package/src/queues/recent-activity-title-refinement.queue.ts +0 -30
  63. package/src/queues/regular-chat-memory-digest.config.ts +0 -12
  64. package/src/queues/regular-chat-memory-digest.queue.ts +0 -34
  65. package/src/queues/skill-extraction.config.ts +0 -9
  66. package/src/queues/skill-extraction.queue.ts +0 -27
  67. package/src/queues/workstream-title-generation.queue.ts +0 -33
  68. package/src/services/context-enrichment.service.ts +0 -33
  69. package/src/services/coordination-registry.service.ts +0 -117
  70. package/src/services/domain-agent-executor.service.ts +0 -71
  71. package/src/services/memory-assessment.service.ts +0 -44
  72. package/src/services/playbook-registry.service.ts +0 -67
  73. package/src/workers/regular-chat-memory-digest.worker.ts +0 -22
  74. 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
+ }