@lota-sdk/core 0.1.9 → 0.1.12
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/infrastructure/schema/00_workstream.surql +1 -0
- package/infrastructure/schema/02_execution_plan.surql +202 -52
- package/package.json +4 -87
- package/src/ai/index.ts +3 -0
- package/src/bifrost/bifrost.ts +94 -25
- package/src/bifrost/index.ts +1 -0
- package/src/config/agent-defaults.ts +30 -7
- package/src/config/constants.ts +0 -9
- package/src/config/debug-logger.ts +43 -0
- package/src/config/index.ts +5 -0
- package/src/config/model-constants.ts +8 -9
- package/src/config/workstream-defaults.ts +4 -0
- package/src/db/cursor-pagination.ts +2 -2
- package/src/db/index.ts +10 -0
- package/src/db/memory-store.ts +3 -71
- package/src/db/memory.ts +9 -15
- package/src/db/service.ts +42 -2
- package/src/db/tables.ts +9 -2
- package/src/document/index.ts +2 -0
- package/src/document/parsing.ts +0 -25
- package/src/embeddings/provider.ts +102 -22
- package/src/index.ts +15 -499
- package/src/queues/index.ts +10 -0
- package/src/redis/connection-accessor.ts +26 -0
- package/src/redis/connection.ts +1 -1
- package/src/redis/index.ts +9 -25
- package/src/redis/org-memory-lock.ts +1 -1
- package/src/redis/redis-lease-lock.ts +1 -1
- package/src/redis/stream-context.ts +54 -0
- package/src/runtime/agent-runtime-policy.ts +9 -5
- package/src/runtime/agent-stream-helpers.ts +6 -3
- package/src/runtime/agent-types.ts +1 -5
- package/src/runtime/approval-continuation.ts +68 -1
- package/src/runtime/chat-attachments.ts +1 -1
- package/src/runtime/chat-request-routing.ts +6 -2
- package/src/runtime/context-compaction-runtime.ts +2 -2
- package/src/runtime/context-compaction.ts +1 -1
- package/src/runtime/execution-plan.ts +22 -15
- package/src/runtime/index.ts +26 -0
- package/src/runtime/indexed-repositories-policy.ts +10 -10
- package/src/runtime/memory-pipeline.ts +0 -2
- package/src/runtime/runtime-config.ts +238 -0
- package/src/runtime/runtime-extensions.ts +3 -2
- package/src/runtime/runtime-worker-registry.ts +47 -0
- package/src/runtime/team-consultation-orchestrator.ts +9 -6
- package/src/runtime/team-consultation-prompts.ts +3 -2
- package/src/runtime/turn-lifecycle.ts +13 -5
- package/src/runtime/workstream-chat-helpers.ts +0 -54
- package/src/runtime/workstream-routing-policy.ts +3 -7
- package/src/runtime.ts +387 -0
- package/src/services/chat-attachments.service.ts +1 -1
- package/src/services/context-compaction.service.ts +1 -1
- package/src/services/document-chunk.service.ts +2 -2
- package/src/services/execution-plan.service.ts +584 -793
- package/src/services/index.ts +14 -0
- package/src/services/learned-skill.service.ts +82 -39
- package/src/services/memory.service.ts +5 -4
- package/src/services/mutating-approval.service.ts +1 -1
- package/src/services/organization-member.service.ts +1 -1
- package/src/services/organization.service.ts +1 -1
- package/src/services/plan-approval.service.ts +83 -0
- package/src/services/plan-artifact.service.ts +44 -0
- package/src/services/plan-builder.service.ts +61 -0
- package/src/services/plan-checkpoint.service.ts +53 -0
- package/src/services/plan-compiler.service.ts +81 -0
- package/src/services/plan-executor.service.ts +1624 -0
- package/src/services/plan-run.service.ts +422 -0
- package/src/services/plan-validator.service.ts +760 -0
- package/src/services/recent-activity-title.service.ts +1 -1
- package/src/services/recent-activity.service.ts +14 -16
- package/src/services/user.service.ts +2 -2
- package/src/services/workstream-message.service.ts +2 -3
- package/src/services/workstream-title.service.ts +1 -1
- package/src/services/workstream-turn-preparation.ts +156 -59
- package/src/services/workstream-turn.ts +26 -1
- package/src/services/workstream.service.ts +35 -9
- package/src/services/workstream.types.ts +1 -0
- package/src/storage/attachment-parser.ts +1 -1
- package/src/storage/attachment-storage.service.ts +11 -10
- package/src/storage/generated-document-storage.service.ts +7 -6
- package/src/storage/index.ts +10 -0
- package/src/system-agents/delegated-agent-factory.ts +78 -29
- package/src/system-agents/index.ts +4 -0
- package/src/system-agents/recent-activity-title-refiner.agent.ts +38 -3
- package/src/system-agents/regular-chat-memory-digest.agent.ts +1 -1
- package/src/system-agents/skill-extractor.agent.ts +1 -1
- package/src/system-agents/skill-manager.agent.ts +2 -4
- package/src/system-agents/title-generator.agent.ts +2 -2
- package/src/tools/execution-plan.tool.ts +22 -48
- package/src/tools/firecrawl-client.ts +2 -2
- package/src/tools/index.ts +12 -0
- package/src/tools/log-hello-world.tool.ts +17 -0
- 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 +2 -2
- package/src/utils/index.ts +6 -0
- package/src/workers/bootstrap.ts +8 -16
- package/src/workers/index.ts +7 -0
- package/src/workers/regular-chat-memory-digest.runner.ts +1 -1
- package/src/workers/skill-extraction.runner.ts +3 -3
- package/src/workers/utils/{repo-indexer-chunker.ts → file-section-chunker.ts} +23 -52
- package/src/workers/utils/repo-structure-extractor.ts +2 -5
- package/src/workers/utils/repomix-file-sections.ts +42 -0
- package/src/config/env-shapes.ts +0 -121
- package/src/runtime/agent-contract.ts +0 -1
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
import { withMessageCreatedAt } from '@lota-sdk/shared
|
|
2
|
-
import type { ChatMessage } from '@lota-sdk/shared
|
|
3
|
-
import { ConsultTeamArgsSchema } from '@lota-sdk/shared/schemas/tools'
|
|
4
|
-
import type { ConsultTeamResultData } from '@lota-sdk/shared/schemas/tools'
|
|
1
|
+
import { ConsultTeamArgsSchema, withMessageCreatedAt } from '@lota-sdk/shared'
|
|
2
|
+
import type { ChatMessage, ConsultTeamResultData } from '@lota-sdk/shared'
|
|
5
3
|
import { convertToModelMessages, readUIMessageStream, tool as createTool } from 'ai'
|
|
6
4
|
|
|
7
5
|
import { agentDisplayNames, teamConsultParticipants } from '../config/agent-defaults'
|
|
@@ -79,9 +77,14 @@ export interface CreateConsultTeamToolParams {
|
|
|
79
77
|
}
|
|
80
78
|
|
|
81
79
|
export function createConsultTeamTool(params: CreateConsultTeamToolParams) {
|
|
80
|
+
const participantNames = teamConsultParticipants
|
|
81
|
+
.map((agentId) => agentDisplayNames[agentId] ?? agentId)
|
|
82
|
+
.filter((value) => value.trim().length > 0)
|
|
83
|
+
const participantSummary =
|
|
84
|
+
participantNames.length > 0 ? participantNames.join(', ') : 'the configured specialist participants'
|
|
85
|
+
|
|
82
86
|
return createTool({
|
|
83
|
-
description:
|
|
84
|
-
'Consult the specialist team in parallel before replying. Use this when the answer benefits from structured executive input across product, engineering, finance, marketing, strategy, and mentorship.',
|
|
87
|
+
description: `Consult the specialist team in parallel before replying. Use this when the answer benefits from structured input across ${participantSummary}.`,
|
|
85
88
|
inputSchema: ConsultTeamArgsSchema,
|
|
86
89
|
execute: async function* ({ task }) {
|
|
87
90
|
const uploadMetadataText = buildReadableUploadMetadataText(params.availableUploads)
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { agentDisplayNames } from '../config/agent-defaults'
|
|
1
|
+
import { agentDisplayNames, getLeadAgentDisplayName } from '../config/agent-defaults'
|
|
2
2
|
|
|
3
3
|
export function buildTeamConsultationResponseGuard(params: { agentId: string; task: string }) {
|
|
4
4
|
const agentName = agentDisplayNames[params.agentId] ?? params.agentId
|
|
5
|
+
const leadAgentDisplayName = getLeadAgentDisplayName()
|
|
5
6
|
return [
|
|
6
7
|
'<team-consultation-agent-protocol>',
|
|
7
|
-
|
|
8
|
+
`- You are participating in a structured internal team consultation led by ${leadAgentDisplayName}.`,
|
|
8
9
|
`- Your role for this response is ${agentName}.`,
|
|
9
10
|
'- Use markdown when it helps clarity.',
|
|
10
11
|
'- Make the recommendation, explain key tradeoffs, and note the next decision.',
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ChatMessage } from '@lota-sdk/shared
|
|
1
|
+
import type { ChatMessage } from '@lota-sdk/shared'
|
|
2
2
|
|
|
3
3
|
export async function finalizeTurnRun(params: {
|
|
4
4
|
serverRunId: string
|
|
@@ -9,7 +9,17 @@ export async function finalizeTurnRun(params: {
|
|
|
9
9
|
unregisterRun: (runId: string) => void
|
|
10
10
|
clearActiveRunId: (runId: string) => Promise<void>
|
|
11
11
|
disposeAbort: () => void
|
|
12
|
+
activeStreamId?: string
|
|
13
|
+
clearActiveStreamId?: (streamId: string) => Promise<void>
|
|
12
14
|
}): Promise<void> {
|
|
15
|
+
// Clear the active run immediately so new messages are not blocked
|
|
16
|
+
params.unregisterRun(params.serverRunId)
|
|
17
|
+
await params.clearActiveRunId(params.serverRunId)
|
|
18
|
+
if (params.activeStreamId && params.clearActiveStreamId) {
|
|
19
|
+
await params.clearActiveStreamId(params.activeStreamId)
|
|
20
|
+
}
|
|
21
|
+
params.disposeAbort()
|
|
22
|
+
|
|
13
23
|
try {
|
|
14
24
|
const entity = await params.getEntity()
|
|
15
25
|
const cursor = typeof entity.lastCompactedMessageId === 'string' ? entity.lastCompactedMessageId : undefined
|
|
@@ -20,9 +30,7 @@ export async function finalizeTurnRun(params: {
|
|
|
20
30
|
if (shouldCompact) {
|
|
21
31
|
await params.enqueueCompaction()
|
|
22
32
|
}
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
await params.clearActiveRunId(params.serverRunId)
|
|
26
|
-
params.disposeAbort()
|
|
33
|
+
} catch {
|
|
34
|
+
// compaction assessment errors should not surface to callers
|
|
27
35
|
}
|
|
28
36
|
}
|
|
@@ -16,49 +16,6 @@ function getAgentName(message: ChatMessageLike): string | undefined {
|
|
|
16
16
|
return resolvedAgentName ? (agentDisplayNames[resolvedAgentName] ?? value.trim()) : value.trim()
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
function truncateTrackerText(value: string, maxChars: number): string {
|
|
20
|
-
return value.length <= maxChars ? value : `${value.slice(0, maxChars).trimEnd()}...`
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
function toTrackerToolLine(part: ChatMessageLike['parts'][number]): string | null {
|
|
24
|
-
if (typeof part !== 'object') return null
|
|
25
|
-
const record = part as Record<string, unknown>
|
|
26
|
-
const type = typeof record.type === 'string' ? record.type : null
|
|
27
|
-
if (!type?.startsWith('tool-')) return null
|
|
28
|
-
|
|
29
|
-
const toolName = type.slice('tool-'.length) || 'unknown'
|
|
30
|
-
const state = typeof record.state === 'string' ? record.state : null
|
|
31
|
-
const input = record.input
|
|
32
|
-
const output = record.output
|
|
33
|
-
|
|
34
|
-
const inputTask =
|
|
35
|
-
input && typeof input === 'object' && typeof (input as Record<string, unknown>).task === 'string'
|
|
36
|
-
? truncateTrackerText(((input as Record<string, unknown>).task as string).trim(), 220)
|
|
37
|
-
: null
|
|
38
|
-
const outputSummary =
|
|
39
|
-
output && typeof output === 'object' && typeof (output as Record<string, unknown>).summary === 'string'
|
|
40
|
-
? truncateTrackerText(((output as Record<string, unknown>).summary as string).trim(), 220)
|
|
41
|
-
: null
|
|
42
|
-
const outputResult =
|
|
43
|
-
output && typeof output === 'object' && typeof (output as Record<string, unknown>).result === 'string'
|
|
44
|
-
? truncateTrackerText(((output as Record<string, unknown>).result as string).trim(), 220)
|
|
45
|
-
: null
|
|
46
|
-
const errorText = typeof record.errorText === 'string' ? truncateTrackerText(record.errorText.trim(), 220) : null
|
|
47
|
-
|
|
48
|
-
if (state === 'output-error') {
|
|
49
|
-
return errorText ? `Tool ${toolName} failed: ${errorText}` : `Tool ${toolName} failed.`
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
if (state !== 'output-available') {
|
|
53
|
-
return inputTask ? `Tool ${toolName} ran for: ${inputTask}` : `Tool ${toolName} ran.`
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
if (outputSummary) return `Tool ${toolName} completed: ${outputSummary}`
|
|
57
|
-
if (inputTask) return `Tool ${toolName} completed for: ${inputTask}`
|
|
58
|
-
if (outputResult) return `Tool ${toolName} completed: ${outputResult}`
|
|
59
|
-
return `Tool ${toolName} completed.`
|
|
60
|
-
}
|
|
61
|
-
|
|
62
19
|
export function extractMessageText(message: ChatMessageLike): string {
|
|
63
20
|
return message.parts
|
|
64
21
|
.flatMap((part) => (part.type === 'text' && typeof part.text === 'string' ? [part.text] : []))
|
|
@@ -66,17 +23,6 @@ export function extractMessageText(message: ChatMessageLike): string {
|
|
|
66
23
|
.trim()
|
|
67
24
|
}
|
|
68
25
|
|
|
69
|
-
export function extractTrackerMessageText(message: ChatMessageLike): string {
|
|
70
|
-
const textParts = message.parts.flatMap((part) =>
|
|
71
|
-
part.type === 'text' && typeof part.text === 'string' ? [part.text] : [],
|
|
72
|
-
)
|
|
73
|
-
const toolParts = message.parts
|
|
74
|
-
.map((part) => toTrackerToolLine(part))
|
|
75
|
-
.filter((value): value is string => Boolean(value))
|
|
76
|
-
|
|
77
|
-
return [...textParts, ...toolParts].join('\n').trim()
|
|
78
|
-
}
|
|
79
|
-
|
|
80
26
|
export function toHistoryMessages(messages: ChatMessageLike[], maxItems = 24): WorkstreamHistoryMessage[] {
|
|
81
27
|
return messages
|
|
82
28
|
.map((message): WorkstreamHistoryMessage | null => {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { extractAgentMentions } from '../config/agent-defaults'
|
|
1
|
+
import { extractAgentMentions, getLeadAgentId } from '../config/agent-defaults'
|
|
2
2
|
|
|
3
3
|
export type MessageRoute =
|
|
4
4
|
| { type: 'direct'; agents: [string] }
|
|
@@ -285,7 +285,7 @@ export function resolveMessageRoute(params: {
|
|
|
285
285
|
message: string
|
|
286
286
|
}): MessageRoute {
|
|
287
287
|
if (params.workstreamMode === 'direct') {
|
|
288
|
-
return { type: 'direct', agents: [params.workstreamAgentId ??
|
|
288
|
+
return { type: 'direct', agents: [params.workstreamAgentId ?? getLeadAgentId()] }
|
|
289
289
|
}
|
|
290
290
|
|
|
291
291
|
const mentions = uniqueMentionOrder(params.message)
|
|
@@ -293,9 +293,5 @@ export function resolveMessageRoute(params: {
|
|
|
293
293
|
return { type: 'mentions', agents: mentions }
|
|
294
294
|
}
|
|
295
295
|
|
|
296
|
-
|
|
297
|
-
return { type: 'group-default', agents: ['cmo'] }
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
return { type: 'group-default', agents: ['chief'] }
|
|
296
|
+
return { type: 'group-default', agents: [getLeadAgentId()] }
|
|
301
297
|
}
|
package/src/runtime.ts
ADDED
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
import { configureEmbeddingCache } from './ai/embedding-cache'
|
|
2
|
+
import { configureAgentFactory, configureAgents } from './config/agent-defaults'
|
|
3
|
+
import { configureBackgroundProcessing } from './config/background-processing'
|
|
4
|
+
import { configureLogger } from './config/logger'
|
|
5
|
+
import { configureWorkstreams } from './config/workstream-defaults'
|
|
6
|
+
import { ensureRecordId } from './db/record-id'
|
|
7
|
+
import { computeSchemaFingerprint } from './db/schema-fingerprint'
|
|
8
|
+
import { LOTA_SDK_DATABASE_NAME } from './db/sdk-database'
|
|
9
|
+
import type { SurrealDBService } from './db/service'
|
|
10
|
+
import { SurrealDBService as SurrealDBServiceClass, setDatabaseService } from './db/service'
|
|
11
|
+
import { publishDatabaseBootstrap } from './db/startup'
|
|
12
|
+
import { TABLES } from './db/tables'
|
|
13
|
+
import type { RedisConnectionManager } from './redis/connection'
|
|
14
|
+
import { createRedisConnectionManager } from './redis/connection'
|
|
15
|
+
import { setRedisConnectionManager } from './redis/index'
|
|
16
|
+
import type { isApprovalContinuationRequest } from './runtime/approval-continuation'
|
|
17
|
+
import { routeWorkstreamChatMessages } from './runtime/chat-request-routing'
|
|
18
|
+
import type { LotaPlugin } from './runtime/plugin-types'
|
|
19
|
+
import { configureRuntimeConfig, LOTA_RUNTIME_ENV_KEYS, parseLotaRuntimeConfig } from './runtime/runtime-config'
|
|
20
|
+
import type { LotaRuntimeConfig, ResolvedLotaRuntimeConfig } from './runtime/runtime-config'
|
|
21
|
+
import { configureRuntimeExtensions } from './runtime/runtime-extensions'
|
|
22
|
+
import type { LotaRuntimeWorkers } from './runtime/runtime-worker-registry'
|
|
23
|
+
import { buildRuntimeWorkerRegistry } from './runtime/runtime-worker-registry'
|
|
24
|
+
import type { attachmentService } from './services/attachment.service'
|
|
25
|
+
import { attachmentService as attachmentServiceSingleton } from './services/attachment.service'
|
|
26
|
+
import type { documentChunkService } from './services/document-chunk.service'
|
|
27
|
+
import { documentChunkService as documentChunkServiceSingleton } from './services/document-chunk.service'
|
|
28
|
+
import type { executionPlanService } from './services/execution-plan.service'
|
|
29
|
+
import { executionPlanService as executionPlanServiceSingleton } from './services/execution-plan.service'
|
|
30
|
+
import type { memoryService } from './services/memory.service'
|
|
31
|
+
import { memoryService as memoryServiceSingleton } from './services/memory.service'
|
|
32
|
+
import type { verifyMutatingApproval } from './services/mutating-approval.service'
|
|
33
|
+
import { verifyMutatingApproval as verifyMutatingApprovalSingleton } from './services/mutating-approval.service'
|
|
34
|
+
import type { organizationMemberService } from './services/organization-member.service'
|
|
35
|
+
import { organizationMemberService as organizationMemberServiceSingleton } from './services/organization-member.service'
|
|
36
|
+
import type { organizationService } from './services/organization.service'
|
|
37
|
+
import { organizationService as organizationServiceSingleton } from './services/organization.service'
|
|
38
|
+
import type { recentActivityTitleService } from './services/recent-activity-title.service'
|
|
39
|
+
import { recentActivityTitleService as recentActivityTitleServiceSingleton } from './services/recent-activity-title.service'
|
|
40
|
+
import type { recentActivityService } from './services/recent-activity.service'
|
|
41
|
+
import { recentActivityService as recentActivityServiceSingleton } from './services/recent-activity.service'
|
|
42
|
+
import type { userService } from './services/user.service'
|
|
43
|
+
import { userService as userServiceSingleton } from './services/user.service'
|
|
44
|
+
import type { workstreamMessageService } from './services/workstream-message.service'
|
|
45
|
+
import { workstreamMessageService as workstreamMessageServiceSingleton } from './services/workstream-message.service'
|
|
46
|
+
import type { workstreamTitleService } from './services/workstream-title.service'
|
|
47
|
+
import { workstreamTitleService as workstreamTitleServiceSingleton } from './services/workstream-title.service'
|
|
48
|
+
import type {
|
|
49
|
+
createWorkstreamApprovalContinuationStream,
|
|
50
|
+
createWorkstreamNativeToolApprovalStream,
|
|
51
|
+
createWorkstreamTurnStream,
|
|
52
|
+
runWorkstreamTurnInBackground,
|
|
53
|
+
} from './services/workstream-turn'
|
|
54
|
+
import {
|
|
55
|
+
createWorkstreamApprovalContinuationStream as createWorkstreamApprovalContinuationStreamSingleton,
|
|
56
|
+
createWorkstreamNativeToolApprovalStream as createWorkstreamNativeToolApprovalStreamSingleton,
|
|
57
|
+
createWorkstreamTurnStream as createWorkstreamTurnStreamSingleton,
|
|
58
|
+
isApprovalContinuationRequest as isApprovalContinuationRequestSingleton,
|
|
59
|
+
runWorkstreamTurnInBackground as runWorkstreamTurnInBackgroundSingleton,
|
|
60
|
+
} from './services/workstream-turn'
|
|
61
|
+
import type { workstreamService } from './services/workstream.service'
|
|
62
|
+
import { workstreamService as workstreamServiceSingleton } from './services/workstream.service'
|
|
63
|
+
import type { generatedDocumentStorageService } from './storage/generated-document-storage.service'
|
|
64
|
+
import { generatedDocumentStorageService as generatedDocumentStorageServiceSingleton } from './storage/generated-document-storage.service'
|
|
65
|
+
|
|
66
|
+
type ArchiveSdkWorkstream = (
|
|
67
|
+
workstreamId: Parameters<typeof workstreamServiceSingleton.updateStatus>[0],
|
|
68
|
+
status?: 'archived',
|
|
69
|
+
) => ReturnType<typeof workstreamServiceSingleton.updateStatus>
|
|
70
|
+
|
|
71
|
+
type UnarchiveSdkWorkstream = (
|
|
72
|
+
workstreamId: Parameters<typeof workstreamServiceSingleton.updateStatus>[0],
|
|
73
|
+
status?: 'regular',
|
|
74
|
+
) => ReturnType<typeof workstreamServiceSingleton.updateStatus>
|
|
75
|
+
|
|
76
|
+
export interface LotaRuntime {
|
|
77
|
+
services: {
|
|
78
|
+
database: SurrealDBService
|
|
79
|
+
databaseService: SurrealDBService
|
|
80
|
+
redis: RedisConnectionManager
|
|
81
|
+
closeRedisConnection: () => Promise<void>
|
|
82
|
+
attachmentService: typeof attachmentService
|
|
83
|
+
documentChunkService: typeof documentChunkService
|
|
84
|
+
generatedDocumentStorageService: typeof generatedDocumentStorageService
|
|
85
|
+
memoryService: typeof memoryService
|
|
86
|
+
verifyMutatingApproval: typeof verifyMutatingApproval
|
|
87
|
+
organizationService: typeof organizationService
|
|
88
|
+
organizationMemberService: typeof organizationMemberService
|
|
89
|
+
userService: typeof userService
|
|
90
|
+
recentActivityService: typeof recentActivityService
|
|
91
|
+
recentActivityTitleService: typeof recentActivityTitleService
|
|
92
|
+
executionPlanService: typeof executionPlanService
|
|
93
|
+
workstreamMessageService: typeof workstreamMessageService
|
|
94
|
+
workstreamService: typeof workstreamService
|
|
95
|
+
workstreamTitleService: typeof workstreamTitleService
|
|
96
|
+
createWorkstreamApprovalContinuationStream: typeof createWorkstreamApprovalContinuationStream
|
|
97
|
+
createWorkstreamNativeToolApprovalStream: typeof createWorkstreamNativeToolApprovalStream
|
|
98
|
+
createWorkstreamTurnStream: typeof createWorkstreamTurnStream
|
|
99
|
+
isApprovalContinuationRequest: typeof isApprovalContinuationRequest
|
|
100
|
+
runWorkstreamTurnInBackground: typeof runWorkstreamTurnInBackground
|
|
101
|
+
}
|
|
102
|
+
lota: {
|
|
103
|
+
organizations: {
|
|
104
|
+
create: typeof organizationServiceSingleton.createOrganization
|
|
105
|
+
upsert: typeof organizationServiceSingleton.upsertOrganization
|
|
106
|
+
get: typeof organizationServiceSingleton.getOrganization
|
|
107
|
+
list: typeof organizationServiceSingleton.listOrganizations
|
|
108
|
+
update: typeof organizationServiceSingleton.updateOrganization
|
|
109
|
+
delete: typeof organizationServiceSingleton.deleteOrganization
|
|
110
|
+
}
|
|
111
|
+
users: {
|
|
112
|
+
upsert: typeof userServiceSingleton.upsertUser
|
|
113
|
+
get: typeof userServiceSingleton.getUser
|
|
114
|
+
list: typeof userServiceSingleton.listUsers
|
|
115
|
+
update: typeof userServiceSingleton.updateUser
|
|
116
|
+
delete: typeof userServiceSingleton.deleteUser
|
|
117
|
+
}
|
|
118
|
+
memberships: {
|
|
119
|
+
add: typeof organizationMemberServiceSingleton.addMembership
|
|
120
|
+
listForOrganization: typeof organizationMemberServiceSingleton.listMembershipsForOrganization
|
|
121
|
+
listForUser: typeof organizationMemberServiceSingleton.listMembershipsForUser
|
|
122
|
+
remove: typeof organizationMemberServiceSingleton.removeMembership
|
|
123
|
+
isMember: typeof organizationMemberServiceSingleton.isMember
|
|
124
|
+
}
|
|
125
|
+
workstreams: {
|
|
126
|
+
create: typeof workstreamServiceSingleton.createWorkstream
|
|
127
|
+
list: typeof workstreamServiceSingleton.listWorkstreams
|
|
128
|
+
get: typeof workstreamServiceSingleton.getWorkstream
|
|
129
|
+
update: typeof workstreamServiceSingleton.updateTitle
|
|
130
|
+
archive: ArchiveSdkWorkstream
|
|
131
|
+
unarchive: UnarchiveSdkWorkstream
|
|
132
|
+
delete: typeof workstreamServiceSingleton.deleteWorkstream
|
|
133
|
+
stop: typeof workstreamServiceSingleton.stopActiveRun
|
|
134
|
+
listMessages: typeof workstreamMessageServiceSingleton.listMessageHistoryPage
|
|
135
|
+
getMessage: (params: { workstreamId: string; messageId: string }) => Promise<unknown>
|
|
136
|
+
sendMessage: (params: {
|
|
137
|
+
workstreamId: string
|
|
138
|
+
organizationId: string
|
|
139
|
+
userId: string
|
|
140
|
+
userName: string
|
|
141
|
+
messages: Parameters<typeof routeWorkstreamChatMessages>[0]
|
|
142
|
+
}) => Promise<Awaited<ReturnType<typeof createWorkstreamTurnStream>>>
|
|
143
|
+
continueApproval: (params: {
|
|
144
|
+
workstreamId: string
|
|
145
|
+
organizationId: string
|
|
146
|
+
userId: string
|
|
147
|
+
userName: string
|
|
148
|
+
messages: Parameters<typeof routeWorkstreamChatMessages>[0]
|
|
149
|
+
}) => Promise<Awaited<ReturnType<typeof createWorkstreamApprovalContinuationStream>>>
|
|
150
|
+
uploadAttachment: typeof attachmentServiceSingleton.uploadWorkstreamAttachment
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
redis: {
|
|
154
|
+
manager: RedisConnectionManager
|
|
155
|
+
getConnection: () => ReturnType<RedisConnectionManager['getConnection']>
|
|
156
|
+
getConnectionForBullMQ: () => ReturnType<RedisConnectionManager['getConnectionForBullMQ']>
|
|
157
|
+
closeConnection: () => Promise<void>
|
|
158
|
+
}
|
|
159
|
+
workers: LotaRuntimeWorkers
|
|
160
|
+
schemaFiles: Array<string | URL>
|
|
161
|
+
contributions: { envKeys: readonly string[]; schemaFiles: Array<string | URL> }
|
|
162
|
+
config: ResolvedLotaRuntimeConfig
|
|
163
|
+
plugins: Record<string, LotaPlugin>
|
|
164
|
+
connectPluginDatabases(): Promise<void>
|
|
165
|
+
connect(): Promise<void>
|
|
166
|
+
disconnect(): Promise<void>
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export async function createLotaRuntime(config: LotaRuntimeConfig): Promise<LotaRuntime> {
|
|
170
|
+
const resolvedConfig = parseLotaRuntimeConfig(config)
|
|
171
|
+
configureRuntimeConfig(resolvedConfig)
|
|
172
|
+
|
|
173
|
+
await configureLogger(resolvedConfig.logging.level)
|
|
174
|
+
|
|
175
|
+
const db = new SurrealDBServiceClass({
|
|
176
|
+
url: resolvedConfig.database.url,
|
|
177
|
+
namespace: resolvedConfig.database.namespace,
|
|
178
|
+
database: LOTA_SDK_DATABASE_NAME,
|
|
179
|
+
username: resolvedConfig.database.username,
|
|
180
|
+
password: resolvedConfig.database.password,
|
|
181
|
+
})
|
|
182
|
+
setDatabaseService(db)
|
|
183
|
+
|
|
184
|
+
const redisManager = createRedisConnectionManager({ url: resolvedConfig.redis.url })
|
|
185
|
+
setRedisConnectionManager(redisManager)
|
|
186
|
+
configureEmbeddingCache(redisManager.getConnection(), resolvedConfig.memory.embeddingCacheTtlSeconds)
|
|
187
|
+
configureBackgroundProcessing(resolvedConfig.backgroundProcessing)
|
|
188
|
+
|
|
189
|
+
configureAgents({
|
|
190
|
+
roster: resolvedConfig.agents.roster,
|
|
191
|
+
leadAgentId: resolvedConfig.agents.leadAgentId,
|
|
192
|
+
displayNames: resolvedConfig.agents.displayNames,
|
|
193
|
+
shortDisplayNames: resolvedConfig.agents.shortDisplayNames,
|
|
194
|
+
teamConsultParticipants: resolvedConfig.agents.teamConsultParticipants,
|
|
195
|
+
getCoreWorkstreamProfile: resolvedConfig.agents.getCoreWorkstreamProfile,
|
|
196
|
+
})
|
|
197
|
+
configureAgentFactory({
|
|
198
|
+
createAgent: resolvedConfig.agents.createAgent,
|
|
199
|
+
buildAgentTools: resolvedConfig.agents.buildAgentTools,
|
|
200
|
+
getAgentRuntimeConfig: resolvedConfig.agents.getAgentRuntimeConfig,
|
|
201
|
+
pluginRuntime: resolvedConfig.pluginRuntime,
|
|
202
|
+
})
|
|
203
|
+
configureWorkstreams({ agentRoster: resolvedConfig.agents.roster, config: resolvedConfig.workstreams })
|
|
204
|
+
configureRuntimeExtensions({
|
|
205
|
+
adapters: resolvedConfig.runtimeAdapters,
|
|
206
|
+
turnHooks: resolvedConfig.turnHooks,
|
|
207
|
+
toolProviders: (resolvedConfig.toolProviders ?? {}) as never,
|
|
208
|
+
extraWorkers: resolvedConfig.extraWorkers,
|
|
209
|
+
})
|
|
210
|
+
|
|
211
|
+
const pluginRuntime = resolvedConfig.pluginRuntime ?? {}
|
|
212
|
+
const pluginContributions = Object.values(pluginRuntime).map((plugin) => plugin.contributions)
|
|
213
|
+
const schemaFiles = [...getBuiltInSchemaFiles(), ...(resolvedConfig.extraSchemaFiles ?? [])]
|
|
214
|
+
const hostContributionSchemaFiles = pluginContributions.flatMap((plugin) => plugin.schemaFiles)
|
|
215
|
+
const contributionEnvKeys = [...LOTA_RUNTIME_ENV_KEYS, ...pluginContributions.flatMap((plugin) => plugin.envKeys)]
|
|
216
|
+
const connectPluginDatabases = createPluginDatabaseConnector(pluginRuntime)
|
|
217
|
+
const workers = buildRuntimeWorkerRegistry(resolvedConfig.extraWorkers)
|
|
218
|
+
|
|
219
|
+
const lota = {
|
|
220
|
+
organizations: {
|
|
221
|
+
create: organizationServiceSingleton.createOrganization.bind(organizationServiceSingleton),
|
|
222
|
+
upsert: organizationServiceSingleton.upsertOrganization.bind(organizationServiceSingleton),
|
|
223
|
+
get: organizationServiceSingleton.getOrganization.bind(organizationServiceSingleton),
|
|
224
|
+
list: organizationServiceSingleton.listOrganizations.bind(organizationServiceSingleton),
|
|
225
|
+
update: organizationServiceSingleton.updateOrganization.bind(organizationServiceSingleton),
|
|
226
|
+
delete: organizationServiceSingleton.deleteOrganization.bind(organizationServiceSingleton),
|
|
227
|
+
},
|
|
228
|
+
users: {
|
|
229
|
+
upsert: userServiceSingleton.upsertUser.bind(userServiceSingleton),
|
|
230
|
+
get: userServiceSingleton.getUser.bind(userServiceSingleton),
|
|
231
|
+
list: userServiceSingleton.listUsers.bind(userServiceSingleton),
|
|
232
|
+
update: userServiceSingleton.updateUser.bind(userServiceSingleton),
|
|
233
|
+
delete: userServiceSingleton.deleteUser.bind(userServiceSingleton),
|
|
234
|
+
},
|
|
235
|
+
memberships: {
|
|
236
|
+
add: organizationMemberServiceSingleton.addMembership.bind(organizationMemberServiceSingleton),
|
|
237
|
+
listForOrganization: organizationMemberServiceSingleton.listMembershipsForOrganization.bind(
|
|
238
|
+
organizationMemberServiceSingleton,
|
|
239
|
+
),
|
|
240
|
+
listForUser: organizationMemberServiceSingleton.listMembershipsForUser.bind(organizationMemberServiceSingleton),
|
|
241
|
+
remove: organizationMemberServiceSingleton.removeMembership.bind(organizationMemberServiceSingleton),
|
|
242
|
+
isMember: organizationMemberServiceSingleton.isMember.bind(organizationMemberServiceSingleton),
|
|
243
|
+
},
|
|
244
|
+
workstreams: {
|
|
245
|
+
create: workstreamServiceSingleton.createWorkstream.bind(workstreamServiceSingleton),
|
|
246
|
+
list: workstreamServiceSingleton.listWorkstreams.bind(workstreamServiceSingleton),
|
|
247
|
+
get: workstreamServiceSingleton.getWorkstream.bind(workstreamServiceSingleton),
|
|
248
|
+
update: workstreamServiceSingleton.updateTitle.bind(workstreamServiceSingleton),
|
|
249
|
+
archive: async (workstreamId, status = 'archived') =>
|
|
250
|
+
await workstreamServiceSingleton.updateStatus(workstreamId, status),
|
|
251
|
+
unarchive: async (workstreamId, status = 'regular') =>
|
|
252
|
+
await workstreamServiceSingleton.updateStatus(workstreamId, status),
|
|
253
|
+
delete: workstreamServiceSingleton.deleteWorkstream.bind(workstreamServiceSingleton),
|
|
254
|
+
stop: workstreamServiceSingleton.stopActiveRun.bind(workstreamServiceSingleton),
|
|
255
|
+
listMessages: workstreamMessageServiceSingleton.listMessageHistoryPage.bind(workstreamMessageServiceSingleton),
|
|
256
|
+
getMessage: async ({ workstreamId, messageId }) => {
|
|
257
|
+
const messages = await workstreamMessageServiceSingleton.listMessages(
|
|
258
|
+
ensureRecordId(workstreamId, TABLES.WORKSTREAM),
|
|
259
|
+
)
|
|
260
|
+
const message = messages.find((candidate) => candidate.id === messageId)
|
|
261
|
+
if (!message) {
|
|
262
|
+
throw new Error(`Workstream message not found: ${messageId}`)
|
|
263
|
+
}
|
|
264
|
+
return message
|
|
265
|
+
},
|
|
266
|
+
sendMessage: async ({ workstreamId, organizationId, userId, userName, messages }) => {
|
|
267
|
+
const workstreamRef = ensureRecordId(workstreamId, TABLES.WORKSTREAM)
|
|
268
|
+
const workstream = await workstreamServiceSingleton.getWorkstream(workstreamRef)
|
|
269
|
+
const routed = routeWorkstreamChatMessages(messages)
|
|
270
|
+
if (routed.kind !== 'turn') {
|
|
271
|
+
throw new Error(routed.kind === 'invalid' ? routed.message : 'Expected a user turn payload.')
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
return await createWorkstreamTurnStreamSingleton({
|
|
275
|
+
workstream,
|
|
276
|
+
workstreamRef,
|
|
277
|
+
orgRef: ensureRecordId(organizationId, TABLES.ORGANIZATION),
|
|
278
|
+
userRef: ensureRecordId(userId, TABLES.USER),
|
|
279
|
+
userName,
|
|
280
|
+
inputMessage: routed.inputMessage,
|
|
281
|
+
})
|
|
282
|
+
},
|
|
283
|
+
continueApproval: async ({ workstreamId, organizationId, userId, userName, messages }) => {
|
|
284
|
+
const workstreamRef = ensureRecordId(workstreamId, TABLES.WORKSTREAM)
|
|
285
|
+
const workstream = await workstreamServiceSingleton.getWorkstream(workstreamRef)
|
|
286
|
+
const routed = routeWorkstreamChatMessages(messages)
|
|
287
|
+
if (routed.kind !== 'approval-continuation') {
|
|
288
|
+
throw new Error(
|
|
289
|
+
routed.kind === 'invalid' ? routed.message : 'Expected approval continuation messages payload.',
|
|
290
|
+
)
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
return await createWorkstreamApprovalContinuationStreamSingleton({
|
|
294
|
+
workstream,
|
|
295
|
+
workstreamRef,
|
|
296
|
+
orgRef: ensureRecordId(organizationId, TABLES.ORGANIZATION),
|
|
297
|
+
userRef: ensureRecordId(userId, TABLES.USER),
|
|
298
|
+
userName,
|
|
299
|
+
approvalMessages: routed.approvalMessages,
|
|
300
|
+
})
|
|
301
|
+
},
|
|
302
|
+
uploadAttachment: attachmentServiceSingleton.uploadWorkstreamAttachment.bind(attachmentServiceSingleton),
|
|
303
|
+
},
|
|
304
|
+
} satisfies LotaRuntime['lota']
|
|
305
|
+
|
|
306
|
+
return {
|
|
307
|
+
services: {
|
|
308
|
+
database: db,
|
|
309
|
+
databaseService: db,
|
|
310
|
+
redis: redisManager,
|
|
311
|
+
closeRedisConnection: async () => await redisManager.closeConnection(),
|
|
312
|
+
attachmentService: attachmentServiceSingleton,
|
|
313
|
+
documentChunkService: documentChunkServiceSingleton,
|
|
314
|
+
generatedDocumentStorageService: generatedDocumentStorageServiceSingleton,
|
|
315
|
+
memoryService: memoryServiceSingleton,
|
|
316
|
+
verifyMutatingApproval: verifyMutatingApprovalSingleton,
|
|
317
|
+
organizationService: organizationServiceSingleton,
|
|
318
|
+
organizationMemberService: organizationMemberServiceSingleton,
|
|
319
|
+
userService: userServiceSingleton,
|
|
320
|
+
recentActivityService: recentActivityServiceSingleton,
|
|
321
|
+
recentActivityTitleService: recentActivityTitleServiceSingleton,
|
|
322
|
+
executionPlanService: executionPlanServiceSingleton,
|
|
323
|
+
workstreamMessageService: workstreamMessageServiceSingleton,
|
|
324
|
+
workstreamService: workstreamServiceSingleton,
|
|
325
|
+
workstreamTitleService: workstreamTitleServiceSingleton,
|
|
326
|
+
createWorkstreamApprovalContinuationStream: createWorkstreamApprovalContinuationStreamSingleton,
|
|
327
|
+
createWorkstreamNativeToolApprovalStream: createWorkstreamNativeToolApprovalStreamSingleton,
|
|
328
|
+
createWorkstreamTurnStream: createWorkstreamTurnStreamSingleton,
|
|
329
|
+
isApprovalContinuationRequest: isApprovalContinuationRequestSingleton,
|
|
330
|
+
runWorkstreamTurnInBackground: runWorkstreamTurnInBackgroundSingleton,
|
|
331
|
+
},
|
|
332
|
+
lota,
|
|
333
|
+
redis: {
|
|
334
|
+
manager: redisManager,
|
|
335
|
+
getConnection: () => redisManager.getConnection(),
|
|
336
|
+
getConnectionForBullMQ: () => redisManager.getConnectionForBullMQ(),
|
|
337
|
+
closeConnection: async () => await redisManager.closeConnection(),
|
|
338
|
+
},
|
|
339
|
+
workers,
|
|
340
|
+
schemaFiles,
|
|
341
|
+
contributions: { envKeys: [...new Set(contributionEnvKeys)], schemaFiles: hostContributionSchemaFiles },
|
|
342
|
+
config: resolvedConfig,
|
|
343
|
+
plugins: pluginRuntime,
|
|
344
|
+
async connectPluginDatabases() {
|
|
345
|
+
await connectPluginDatabases()
|
|
346
|
+
},
|
|
347
|
+
async connect() {
|
|
348
|
+
await db.connect()
|
|
349
|
+
const bunFiles = schemaFiles.map((schemaFile) =>
|
|
350
|
+
schemaFile instanceof URL ? Bun.file(schemaFile.pathname) : Bun.file(schemaFile),
|
|
351
|
+
)
|
|
352
|
+
await db.applySchemaAndMigrations(bunFiles)
|
|
353
|
+
const schemaFingerprint = await computeSchemaFingerprint(schemaFiles)
|
|
354
|
+
await publishDatabaseBootstrap({ databaseService: db, schemaFingerprint })
|
|
355
|
+
},
|
|
356
|
+
async disconnect() {
|
|
357
|
+
await db.disconnect()
|
|
358
|
+
await redisManager.closeConnection()
|
|
359
|
+
},
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
function getBuiltInSchemaFiles(): URL[] {
|
|
364
|
+
return [
|
|
365
|
+
new URL('../infrastructure/schema/00_identity.surql', import.meta.url),
|
|
366
|
+
new URL('../infrastructure/schema/00_workstream.surql', import.meta.url),
|
|
367
|
+
new URL('../infrastructure/schema/01_memory.surql', import.meta.url),
|
|
368
|
+
new URL('../infrastructure/schema/02_execution_plan.surql', import.meta.url),
|
|
369
|
+
new URL('../infrastructure/schema/03_learned_skill.surql', import.meta.url),
|
|
370
|
+
new URL('../infrastructure/schema/05_recent_activity.surql', import.meta.url),
|
|
371
|
+
new URL('../infrastructure/schema/04_runtime_bootstrap.surql', import.meta.url),
|
|
372
|
+
]
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
function createPluginDatabaseConnector(pluginRuntime: Record<string, LotaPlugin>): () => Promise<void> {
|
|
376
|
+
return async () => {
|
|
377
|
+
for (const plugin of Object.values(pluginRuntime)) {
|
|
378
|
+
const services = plugin.services as Record<string, unknown>
|
|
379
|
+
const connectDatabase = services.connectDatabase
|
|
380
|
+
if (typeof connectDatabase !== 'function') {
|
|
381
|
+
continue
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
await Reflect.apply(connectDatabase, services, [])
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
}
|
|
@@ -2,7 +2,7 @@ import { createHash } from 'node:crypto'
|
|
|
2
2
|
|
|
3
3
|
import { chunkMarkdownDocument, chunkPagedDocument, chunkPlainTextDocument } from '../document/org-document-chunking'
|
|
4
4
|
import type { ParsedDocumentChunk } from '../document/org-document-chunking'
|
|
5
|
-
import {
|
|
5
|
+
import { getDefaultEmbeddings } from '../embeddings/provider'
|
|
6
6
|
|
|
7
7
|
type DocumentChunkEmbeddings = {
|
|
8
8
|
embedDocuments(documents: string[]): Promise<number[][]>
|
|
@@ -10,7 +10,7 @@ type DocumentChunkEmbeddings = {
|
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
function createDocumentChunkEmbeddings(): DocumentChunkEmbeddings {
|
|
13
|
-
const embeddings =
|
|
13
|
+
const embeddings = getDefaultEmbeddings()
|
|
14
14
|
|
|
15
15
|
return {
|
|
16
16
|
embedDocuments: async (documents) => await embeddings.embedDocuments(documents),
|