@lota-sdk/core 0.1.16 → 0.1.18

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 (51) hide show
  1. package/package.json +6 -3
  2. package/src/ai/definitions.ts +1 -1
  3. package/src/ai/embedding-cache.ts +2 -4
  4. package/src/{bifrost/bifrost.ts → ai-gateway/ai-gateway.ts} +115 -79
  5. package/src/ai-gateway/cache-headers.ts +8 -0
  6. package/src/ai-gateway/index.ts +2 -0
  7. package/src/config/model-constants.ts +1 -1
  8. package/src/create-runtime.ts +26 -1
  9. package/src/db/memory-store.helpers.ts +1 -3
  10. package/src/db/schema-fingerprint.ts +1 -3
  11. package/src/embeddings/provider.ts +2 -2
  12. package/src/index.ts +1 -1
  13. package/src/queues/document-processor.queue.ts +2 -4
  14. package/src/queues/post-chat-memory.queue.ts +8 -2
  15. package/src/queues/recent-activity-title-refinement.queue.ts +1 -1
  16. package/src/queues/skill-extraction.queue.ts +1 -1
  17. package/src/queues/workstream-title-generation.queue.ts +1 -1
  18. package/src/redis/redis-lease-lock.ts +1 -2
  19. package/src/runtime/agent-runtime-policy.ts +3 -14
  20. package/src/runtime/context-compaction.ts +2 -4
  21. package/src/runtime/index.ts +1 -1
  22. package/src/runtime/runtime-config.ts +87 -7
  23. package/src/runtime/runtime-extensions.ts +0 -1
  24. package/src/runtime/social-chat.ts +752 -0
  25. package/src/runtime/team-consultation-orchestrator.ts +0 -4
  26. package/src/services/agent-executor.service.ts +0 -1
  27. package/src/services/document-chunk.service.ts +1 -3
  28. package/src/services/index.ts +1 -0
  29. package/src/services/memory.service.ts +7 -2
  30. package/src/services/recent-activity.service.ts +1 -3
  31. package/src/services/social-chat-history.service.ts +197 -0
  32. package/src/services/workstream-message.service.ts +1 -3
  33. package/src/services/workstream-turn-preparation.service.ts +0 -23
  34. package/src/system-agents/context-compaction.agent.ts +4 -2
  35. package/src/system-agents/delegated-agent-factory.ts +3 -0
  36. package/src/system-agents/memory-reranker.agent.ts +5 -3
  37. package/src/system-agents/memory.agent.ts +4 -2
  38. package/src/system-agents/recent-activity-title-refiner.agent.ts +4 -2
  39. package/src/system-agents/regular-chat-memory-digest.agent.ts +4 -2
  40. package/src/system-agents/skill-extractor.agent.ts +4 -2
  41. package/src/system-agents/skill-manager.agent.ts +4 -2
  42. package/src/system-agents/title-generator.agent.ts +4 -2
  43. package/src/tools/research-topic.tool.ts +4 -2
  44. package/src/tools/team-think.tool.ts +0 -3
  45. package/src/workers/regular-chat-memory-digest.helpers.ts +1 -1
  46. package/src/workers/regular-chat-memory-digest.runner.ts +43 -10
  47. package/src/workers/skill-extraction.runner.ts +25 -5
  48. package/src/workers/utils/repo-structure-extractor.ts +2 -2
  49. package/src/workers/utils/workstream-message-query.ts +3 -5
  50. package/src/bifrost/index.ts +0 -1
  51. package/src/runtime/workstream-routing-policy.ts +0 -267
@@ -15,6 +15,8 @@ interface PostChatMemoryExtractionJob {
15
15
  orgId: string
16
16
  workstreamId: string
17
17
  sourceId: string
18
+ source?: string
19
+ sourceMetadata?: Record<string, unknown>
18
20
  onboardStatus?: OrganizationOnboardStatus
19
21
  userMessage: string
20
22
  historyMessages: PostChatMemoryMessage[]
@@ -54,6 +56,8 @@ async function processPostChatMemoryJob(job: Job<PostChatMemoryExtractionJob>):
54
56
  input: userMessage,
55
57
  output: joinedOutput,
56
58
  sourceId: data.sourceId,
59
+ source: data.source,
60
+ sourceMetadata: data.sourceMetadata,
57
61
  onboardStatus: data.onboardStatus,
58
62
  ...(uniqueAgentNames.length > 0 ? { agentName: uniqueAgentNames[0] } : {}),
59
63
  historyMessages: data.historyMessages,
@@ -67,7 +71,7 @@ const postChatMemory = createQueueFactory<PostChatMemoryExtractionJob>({
67
71
  name: 'post-chat-memory',
68
72
  displayName: 'Post-chat memory',
69
73
  jobName: 'extract-memory',
70
- concurrency: 3,
74
+ concurrency: 10,
71
75
  lockDuration: 900_000,
72
76
  maxStalledCount: 10,
73
77
  stalledInterval: 120_000,
@@ -75,7 +79,9 @@ const postChatMemory = createQueueFactory<PostChatMemoryExtractionJob>({
75
79
  processor: processPostChatMemoryJob,
76
80
  })
77
81
 
78
- export const enqueuePostChatMemory = postChatMemory.enqueue
82
+ export function enqueuePostChatMemory(job: PostChatMemoryExtractionJob, options?: { dedupeKey?: string }) {
83
+ return postChatMemory.enqueue(job, options?.dedupeKey ? { jobId: options.dedupeKey } : undefined)
84
+ }
79
85
  export const startPostChatMemoryWorker = postChatMemory.startWorker
80
86
 
81
87
  if (import.meta.main) {
@@ -17,7 +17,7 @@ const recentActivityTitleRefinement = createQueueFactory<RecentActivityTitleRefi
17
17
  name: 'recent-activity-title-refinement',
18
18
  displayName: 'Recent activity title refinement',
19
19
  jobName: 'refine-recent-activity-title',
20
- concurrency: 2,
20
+ concurrency: 10,
21
21
  lockDuration: 300_000,
22
22
  defaultJobOptions: { attempts: 3, backoff: { type: 'exponential', delay: 2_000 } },
23
23
  processor: processRecentActivityTitleRefinementJob,
@@ -10,7 +10,7 @@ const skillExtraction = createQueueFactory<SkillExtractionJob>({
10
10
  name: 'skill-extraction',
11
11
  displayName: 'Skill extraction',
12
12
  jobName: 'run-extraction',
13
- concurrency: 1,
13
+ concurrency: 10,
14
14
  lockDuration: 600_000,
15
15
  defaultJobOptions: { attempts: 2, backoff: { type: 'exponential', delay: 5000 } },
16
16
  processorPath: getWorkerPath('skill-extraction.worker.ts'),
@@ -20,7 +20,7 @@ const workstreamTitleGeneration = createQueueFactory<WorkstreamTitleGenerationJo
20
20
  name: 'workstream-title-generation',
21
21
  displayName: 'Workstream title generation',
22
22
  jobName: 'generate-workstream-title',
23
- concurrency: 2,
23
+ concurrency: 10,
24
24
  lockDuration: 60_000,
25
25
  defaultJobOptions: { attempts: 2, backoff: { type: 'exponential', delay: 2_000 } },
26
26
  processor: processWorkstreamTitleGenerationJob,
@@ -1,4 +1,3 @@
1
- import { randomUUID } from 'node:crypto'
2
1
  import { setTimeout as delay } from 'node:timers/promises'
3
2
 
4
3
  import type IORedis from 'ioredis'
@@ -147,7 +146,7 @@ export async function withRedisLeaseLock<T>(
147
146
  const heldInfoThresholdMs = options.heldInfoThresholdMs ?? 5_000
148
147
  const label = options.label ?? 'redis lease lock'
149
148
 
150
- const lockValue = randomUUID()
149
+ const lockValue = crypto.randomUUID()
151
150
  const waitStart = Date.now()
152
151
  await acquireLeaseLock({ ...options, lockKey, lockValue, label, retryDelayMs, waitLogIntervalMs, maxWaitMs })
153
152
  const waitedMs = Date.now() - waitStart
@@ -3,18 +3,12 @@ import type { ExecutionMode, PlanArtifactSubmission, PlanNodeSpec } from '@lota-
3
3
  import { getLeadAgentId } from '../config/agent-defaults'
4
4
  import { resolveOnboardingOwnerAgentId } from '../config/workstream-defaults'
5
5
  import type { ChatMode } from './agent-types'
6
- import { resolveReasoningProfile } from './workstream-routing-policy'
7
- import type { ReasoningProfileName } from './workstream-routing-policy'
8
-
9
6
  export interface AgentRuntimeConfig<TAgent extends string> {
10
7
  id: TAgent
11
8
  displayName: string
12
9
  mode: ChatMode
13
10
  extraInstructions?: string
14
11
  maxSteps: number
15
- reasoningProfile: ReasoningProfileName
16
- toolCallBudget: number
17
- maxInputTokensHint: number
18
12
  }
19
13
 
20
14
  export interface AgentToolPolicy<TSkill extends PropertyKey> {
@@ -100,7 +94,6 @@ export function buildAgentRuntimeConfig<TAgent extends string, TSkill extends Pr
100
94
  skills?: TSkill[]
101
95
  onboardingActive: boolean
102
96
  linearInstalled: boolean
103
- reasoningProfile?: ReasoningProfileName
104
97
  systemWorkspaceDetails?: string
105
98
  preSeededMemoriesSection?: string
106
99
  retrievedKnowledgeSection?: string
@@ -115,7 +108,6 @@ export function buildAgentRuntimeConfig<TAgent extends string, TSkill extends Pr
115
108
  buildOnboardingPromptSection: () => string
116
109
  }): AgentRuntimeConfig<TAgent> {
117
110
  const mode = params.mode ?? toChatMode(params.workstreamMode)
118
- const profile = resolveReasoningProfile({ message: '', explicitProfile: params.reasoningProfile ?? 'standard' })
119
111
  const rulesSection = params.buildGlobalRuleInstructionSection()
120
112
  const skillsSection =
121
113
  params.skills && params.skills.length > 0 ? params.buildSkillInstructionSection(params.skills) : ''
@@ -141,10 +133,7 @@ export function buildAgentRuntimeConfig<TAgent extends string, TSkill extends Pr
141
133
  displayName: params.displayNameByAgent[params.agentId] ?? params.agentId,
142
134
  mode,
143
135
  extraInstructions,
144
- maxSteps: profile.maxSteps,
145
- reasoningProfile: profile.name,
146
- toolCallBudget: profile.toolCallBudget,
147
- maxInputTokensHint: profile.maxInputTokensHint,
136
+ maxSteps: 15,
148
137
  }
149
138
  }
150
139
 
@@ -180,8 +169,8 @@ export function buildWorkstreamAgentToolPolicy<TAgent extends string, TSkill ext
180
169
  includeReadFileParts: true,
181
170
  includeInspectWebsite: params.onboardingActive && params.agentId === onboardingOwnerAgentId,
182
171
  includeProceedInOnboarding: params.onboardingActive && params.agentId === onboardingOwnerAgentId,
183
- includeGithubIntegration: params.onboardingActive && params.agentId === onboardingOwnerAgentId,
184
- includeIndexRepositoryByURL: params.onboardingActive && params.agentId === onboardingOwnerAgentId,
172
+ includeGithubIntegration: params.agentId === onboardingOwnerAgentId,
173
+ includeIndexRepositoryByURL: params.agentId === onboardingOwnerAgentId,
185
174
  includeIndexedRepository: params.githubInstalled && params.provideRepoTool,
186
175
  }
187
176
  }
@@ -1,5 +1,3 @@
1
- import { createHash, randomUUID } from 'node:crypto'
2
-
3
1
  import type { ChatMessage } from '@lota-sdk/shared'
4
2
 
5
3
  import { CHARS_PER_TOKEN_ESTIMATE, compactWhitespace, readRecord, readString, stringifyUnknown } from '../utils/string'
@@ -108,7 +106,7 @@ function createStableId(prefix: string, ...parts: Array<string | number | undefi
108
106
  .map((part) => (part === undefined ? '' : String(part)))
109
107
  .map((part) => compactWhitespace(part))
110
108
  .join('|')
111
- const hash = createHash('sha1').update(`${prefix}|${payload}`).digest('hex').slice(0, 20)
109
+ const hash = new Bun.CryptoHasher('sha1').update(`${prefix}|${payload}`).digest('hex').slice(0, 20)
112
110
  return `${prefix}_${hash}`
113
111
  }
114
112
 
@@ -532,7 +530,7 @@ export function createContextCompactionRuntime(
532
530
  options: CreateContextCompactionRuntimeOptions,
533
531
  ): ContextCompactionRuntime {
534
532
  const now = options.now ?? (() => Date.now())
535
- const randomId = options.randomId ?? (() => randomUUID())
533
+ const randomId = options.randomId ?? (() => crypto.randomUUID())
536
534
  const thresholdRatio = options.thresholdRatio ?? CONTEXT_COMPACTION_THRESHOLD_RATIO
537
535
  const outputReserveTokens = options.outputReserveTokens ?? CONTEXT_OUTPUT_RESERVE_TOKENS
538
536
  const safetyMarginTokens = options.safetyMarginTokens ?? CONTEXT_SAFETY_MARGIN_TOKENS
@@ -20,11 +20,11 @@ export * from './runtime-config'
20
20
  export * from './runtime-extensions'
21
21
  export * from './runtime-worker-registry'
22
22
  export * from './skill-extraction-policy'
23
+ export * from './social-chat'
23
24
  export * from './team-consultation-orchestrator'
24
25
  export * from './team-consultation-prompts'
25
26
  export * from './turn-lifecycle'
26
27
  export * from './workstream-chat-helpers'
27
- export * from './workstream-routing-policy'
28
28
  export {
29
29
  WorkstreamStateSchema,
30
30
  type WorkstreamState,
@@ -1,8 +1,10 @@
1
+ import type { ToolSet } from 'ai'
1
2
  import { z } from 'zod'
2
3
 
3
4
  import type { CoreWorkstreamProfile } from '../config/agent-defaults'
4
5
  import type { AgentFactory, AgentRuntimeConfigProvider, AgentToolBuilder } from '../config/agent-types'
5
6
  import type { LotaWorkstreamConfig, WorkstreamBootstrapWelcomeConfig } from '../config/workstream-defaults'
7
+ import type { RecordIdRef } from '../db/record-id'
6
8
  import type { NotificationService } from '../services/notification.service'
7
9
  import { isRecord } from '../utils/string'
8
10
  import type { GraphDesigner } from './graph-designer'
@@ -61,6 +63,87 @@ function isWorkerExtensionRecord(value: unknown): value is LotaRuntimeWorkerExte
61
63
  return true
62
64
  }
63
65
 
66
+ export interface LotaSocialChatSlackConfig {
67
+ botToken?: string
68
+ signingSecret?: string
69
+ userName?: string
70
+ dedupeTtlMs?: number
71
+ }
72
+
73
+ export interface LotaSocialChatResolveContextParams {
74
+ platform: 'slack'
75
+ channelId: string
76
+ threadId: string
77
+ messageId: string
78
+ text: string
79
+ authorId?: string
80
+ authorName?: string
81
+ }
82
+
83
+ export interface LotaSocialChatResolvedContext {
84
+ workspaceId: RecordIdRef
85
+ userId: RecordIdRef
86
+ userName?: string | null
87
+ }
88
+
89
+ export interface BuildSocialChatAgentToolsParams {
90
+ agentId: string
91
+ workspaceId: RecordIdRef
92
+ workspaceIdString: string
93
+ userId: RecordIdRef
94
+ userIdString: string
95
+ userName?: string | null
96
+ platform: 'slack'
97
+ channelId: string
98
+ threadId: string
99
+ incomingMessageId: string
100
+ incomingText: string
101
+ memoryBlock: string
102
+ onAppendMemoryBlock: (value: string) => void
103
+ context?: Record<string, unknown> | null
104
+ }
105
+
106
+ export interface LotaRuntimeSocialChatConfig {
107
+ agentId?: string
108
+ agentDisplayName?: string
109
+ slack?: LotaSocialChatSlackConfig
110
+ historyRedisKeyPrefix?: string
111
+ stateRedisKeyPrefix?: string
112
+ resolveContext: (
113
+ params: LotaSocialChatResolveContextParams,
114
+ ) => LotaSocialChatResolvedContext | Promise<LotaSocialChatResolvedContext>
115
+ buildAgentTools: (params: BuildSocialChatAgentToolsParams) => ToolSet | Promise<ToolSet>
116
+ getConsultParticipants?:
117
+ | ((params: {
118
+ workspaceId: RecordIdRef
119
+ workspaceIdString: string
120
+ platform: 'slack'
121
+ }) => string[] | Promise<string[]>)
122
+ | undefined
123
+ }
124
+
125
+ function isSlackSocialChatConfig(value: unknown): value is LotaSocialChatSlackConfig {
126
+ if (!isRecord(value)) return false
127
+ if (value.botToken !== undefined && typeof value.botToken !== 'string') return false
128
+ if (value.signingSecret !== undefined && typeof value.signingSecret !== 'string') return false
129
+ if (value.userName !== undefined && typeof value.userName !== 'string') return false
130
+ if (value.dedupeTtlMs !== undefined && typeof value.dedupeTtlMs !== 'number') return false
131
+ return true
132
+ }
133
+
134
+ function isSocialChatConfig(value: unknown): value is LotaRuntimeSocialChatConfig {
135
+ if (!isRecord(value)) return false
136
+ if (value.agentId !== undefined && typeof value.agentId !== 'string') return false
137
+ if (value.agentDisplayName !== undefined && typeof value.agentDisplayName !== 'string') return false
138
+ if (value.historyRedisKeyPrefix !== undefined && typeof value.historyRedisKeyPrefix !== 'string') return false
139
+ if (value.stateRedisKeyPrefix !== undefined && typeof value.stateRedisKeyPrefix !== 'string') return false
140
+ if (value.slack !== undefined && !isSlackSocialChatConfig(value.slack)) return false
141
+ if (!isFunction(value.resolveContext)) return false
142
+ if (!isFunction(value.buildAgentTools)) return false
143
+ if (value.getConsultParticipants !== undefined && !isFunction(value.getConsultParticipants)) return false
144
+ return true
145
+ }
146
+
64
147
  const workstreamBootstrapWelcomeConfigSchema = z.object({
65
148
  directAgentId: z.string().trim().min(1),
66
149
  buildMessageText: z.custom<WorkstreamBootstrapWelcomeConfig['buildMessageText']>(isFunction, {
@@ -137,8 +220,6 @@ export const LotaRuntimeConfigSchema = z.object({
137
220
  aiGateway: z.object({
138
221
  url: z.string().trim().min(1),
139
222
  key: z.string().trim().min(1),
140
- admin: z.string().trim().min(1).optional(),
141
- pass: z.string().trim().min(1).optional(),
142
223
  embeddingModel: z.string().trim().min(1).default('openai/text-embedding-3-small'),
143
224
  }),
144
225
  s3: z.object({
@@ -161,9 +242,9 @@ export const LotaRuntimeConfigSchema = z.object({
161
242
  memory: z
162
243
  .object({
163
244
  searchK: z.coerce.number().int().positive().default(6),
164
- embeddingCacheTtlSeconds: z.coerce.number().int().positive().default(3600),
245
+ embeddingCacheTtlSeconds: z.coerce.number().int().positive().default(7200),
165
246
  })
166
- .default({ searchK: 6, embeddingCacheTtlSeconds: 3600 }),
247
+ .default({ searchK: 6, embeddingCacheTtlSeconds: 7200 }),
167
248
  workstreams: workstreamConfigSchema.default({}),
168
249
  backgroundProcessing: z
169
250
  .object({
@@ -188,6 +269,7 @@ export const LotaRuntimeConfigSchema = z.object({
188
269
  runtimeAdapters: z.custom<LotaRuntimeAdapters>(isRecord).optional(),
189
270
  turnHooks: z.custom<LotaRuntimeTurnHooks>(isRecord).optional(),
190
271
  graphDesigner: z.custom<GraphDesigner>(isGraphDesigner).optional(),
272
+ socialChat: z.custom<LotaRuntimeSocialChatConfig>(isSocialChatConfig).optional(),
191
273
  })
192
274
 
193
275
  export type LotaRuntimeConfig = z.input<typeof LotaRuntimeConfigSchema>
@@ -210,9 +292,7 @@ export const LOTA_RUNTIME_ENV_KEYS = Object.freeze([
210
292
  'SURREALDB_PASSWORD',
211
293
  'REDIS_URL',
212
294
  'AI_GATEWAY_URL',
213
- 'AI_GATEWAY_KEY',
214
- 'AI_GATEWAY_ADMIN',
215
- 'AI_GATEWAY_PASS',
295
+ 'LOTA_KEY',
216
296
  'AI_EMBEDDING_MODEL',
217
297
  'S3_ENDPOINT',
218
298
  'S3_BUCKET',
@@ -123,7 +123,6 @@ export interface ResolveAgentParams {
123
123
  onboardingActive: boolean
124
124
  linearInstalled: boolean
125
125
  githubInstalled: boolean
126
- reasoningProfile: string
127
126
  skills?: string[]
128
127
  additionalInstructionSections?: string[]
129
128
  context: Record<string, unknown> | null