@lota-sdk/core 0.2.3 → 0.3.1
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_identity.surql +2 -2
- package/infrastructure/schema/00_thread.surql +73 -0
- package/infrastructure/schema/02_execution_plan.surql +10 -11
- package/infrastructure/schema/04_runtime_bootstrap.surql +1 -0
- package/infrastructure/schema/10_autonomous_job.surql +3 -3
- package/package.json +2 -2
- package/src/ai/definitions.ts +1 -1
- package/src/config/agent-defaults.ts +5 -5
- package/src/config/index.ts +1 -1
- package/src/config/thread-defaults.ts +72 -0
- package/src/create-runtime.ts +90 -94
- package/src/db/record-id.ts +21 -21
- package/src/db/service.ts +44 -40
- package/src/db/tables.ts +3 -3
- package/src/db/{workstream-message-row.ts → thread-message-row.ts} +3 -3
- package/src/queues/context-compaction.queue.ts +6 -6
- package/src/queues/plan-agent-heartbeat.queue.ts +3 -3
- package/src/queues/post-chat-memory.queue.ts +1 -1
- package/src/queues/title-generation.queue.ts +10 -13
- package/src/redis/index.ts +1 -1
- package/src/redis/stream-context.ts +1 -1
- package/src/runtime/agent-identity-overrides.ts +1 -1
- package/src/runtime/agent-runtime-policy.ts +19 -21
- package/src/runtime/chat-request-routing.ts +1 -1
- package/src/runtime/context-compaction-constants.ts +1 -1
- package/src/runtime/context-compaction.ts +1 -1
- package/src/runtime/execution-plan.ts +1 -1
- package/src/runtime/index.ts +1 -1
- package/src/runtime/memory-digest-policy.ts +1 -1
- package/src/runtime/plugin-types.ts +1 -1
- package/src/runtime/post-turn-side-effects.ts +35 -35
- package/src/runtime/runtime-config.ts +24 -21
- package/src/runtime/runtime-extensions.ts +11 -11
- package/src/runtime/social-chat-agent-runner.ts +3 -3
- package/src/runtime/social-chat-history.ts +1 -1
- package/src/runtime/social-chat.ts +6 -6
- package/src/runtime/team-consultation-orchestrator.ts +1 -1
- package/src/runtime/{workstream-chat-helpers.ts → thread-chat-helpers.ts} +7 -7
- package/src/runtime/{workstream-plan-turn.ts → thread-plan-turn.ts} +11 -17
- package/src/runtime/{workstream-turn-context.ts → thread-turn-context.ts} +10 -10
- package/src/services/agent-activity.service.ts +39 -44
- package/src/services/agent-executor.service.ts +17 -19
- package/src/services/attachment.service.ts +4 -8
- package/src/services/autonomous-job.service.ts +29 -28
- package/src/services/context-compaction.service.ts +19 -29
- package/src/services/execution-plan.service.ts +58 -70
- package/src/services/global-orchestrator.service.ts +5 -5
- package/src/services/index.ts +6 -6
- package/src/services/memory.service.ts +1 -1
- package/src/services/monitoring-window.service.ts +2 -2
- package/src/services/mutating-approval.service.ts +7 -10
- package/src/services/node-workspace.service.ts +8 -7
- package/src/services/notification.service.ts +1 -1
- package/src/services/organization.service.ts +9 -9
- package/src/services/ownership-dispatcher.service.ts +13 -19
- package/src/services/plan-agent-heartbeat.service.ts +13 -13
- package/src/services/plan-agent-query.service.ts +7 -7
- package/src/services/plan-artifact.service.ts +1 -2
- package/src/services/plan-coordination.service.ts +4 -4
- package/src/services/plan-cycle.service.ts +7 -7
- package/src/services/plan-deadline.service.ts +4 -4
- package/src/services/plan-event-delivery.service.ts +8 -12
- package/src/services/plan-executor.service.ts +25 -39
- package/src/services/plan-run-data.ts +27 -8
- package/src/services/plan-run.service.ts +7 -9
- package/src/services/plan-scheduler.service.ts +4 -4
- package/src/services/plan-template.service.ts +2 -2
- package/src/services/plan-validator.service.ts +0 -11
- package/src/services/plugin-executor.service.ts +1 -1
- package/src/services/queue-job.service.ts +1 -1
- package/src/services/recent-activity-title.service.ts +1 -1
- package/src/services/recent-activity.service.ts +4 -4
- package/src/services/system-executor.service.ts +2 -2
- package/src/services/{workstream-message.service.ts → thread-message.service.ts} +72 -76
- package/src/services/thread-plan-registry.service.ts +22 -0
- package/src/services/thread-title.service.ts +39 -0
- package/src/services/{workstream-turn-preparation.service.ts → thread-turn-preparation.service.ts} +148 -171
- package/src/services/{workstream-turn.ts → thread-turn.ts} +27 -31
- package/src/services/thread.service.ts +853 -0
- package/src/services/thread.types.ts +17 -0
- package/src/storage/attachment-storage.service.ts +4 -4
- package/src/system-agents/index.ts +1 -1
- package/src/system-agents/memory.agent.ts +1 -1
- package/src/system-agents/recent-activity-title-refiner.agent.ts +2 -2
- package/src/system-agents/regular-chat-memory-digest.agent.ts +1 -1
- package/src/system-agents/researcher.agent.ts +3 -3
- package/src/system-agents/{workstream-router.agent.ts → thread-router.agent.ts} +68 -135
- package/src/system-agents/title-generator.agent.ts +8 -8
- package/src/tools/execution-plan.tool.ts +39 -40
- package/src/tools/memory-block.tool.ts +4 -4
- package/src/tools/research-topic.tool.ts +1 -0
- package/src/tools/search-web.tool.ts +1 -1
- package/src/tools/search.tool.ts +4 -4
- package/src/tools/team-think.tool.ts +9 -9
- package/src/utils/async.ts +6 -7
- package/src/workers/regular-chat-memory-digest.helpers.ts +1 -1
- package/src/workers/regular-chat-memory-digest.runner.ts +43 -43
- package/src/workers/skill-extraction.runner.ts +9 -13
- package/src/workers/utils/{workstream-message-query.ts → thread-message-query.ts} +21 -21
- package/infrastructure/schema/00_workstream.surql +0 -64
- package/src/config/workstream-defaults.ts +0 -72
- package/src/services/workstream-plan-registry.service.ts +0 -22
- package/src/services/workstream-title.service.ts +0 -42
- package/src/services/workstream.service.ts +0 -803
- package/src/services/workstream.types.ts +0 -17
- /package/src/services/{workstream-constants.ts → thread-constants.ts} +0 -0
package/src/create-runtime.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { configureEmbeddingCache } from './ai/embedding-cache'
|
|
|
4
4
|
import { configureAgentFactory, configureAgents } from './config/agent-defaults'
|
|
5
5
|
import { configureBackgroundProcessing } from './config/background-processing'
|
|
6
6
|
import { configureLotaLogger } from './config/logger'
|
|
7
|
-
import {
|
|
7
|
+
import { configureThreads } from './config/thread-defaults'
|
|
8
8
|
import { ensureRecordId } from './db/record-id'
|
|
9
9
|
import { computeSchemaFingerprint } from './db/schema-fingerprint'
|
|
10
10
|
import { LOTA_SDK_DATABASE_NAME } from './db/sdk-database'
|
|
@@ -17,7 +17,7 @@ import { createRedisConnectionManager } from './redis/connection'
|
|
|
17
17
|
import { setRedisConnectionManager } from './redis/index'
|
|
18
18
|
import { closeSharedSubscriber } from './redis/stream-context'
|
|
19
19
|
import type { isApprovalContinuationRequest } from './runtime/approval-continuation'
|
|
20
|
-
import {
|
|
20
|
+
import { routeThreadChatMessages } from './runtime/chat-request-routing'
|
|
21
21
|
import { configureGraphDesigner } from './runtime/graph-designer'
|
|
22
22
|
import type { LotaPlugin, SystemNodeExecutor } from './runtime/plugin-types'
|
|
23
23
|
import { configureRuntimeConfig, LOTA_RUNTIME_ENV_KEYS, parseLotaRuntimeConfig } from './runtime/runtime-config'
|
|
@@ -57,41 +57,41 @@ import {
|
|
|
57
57
|
socialChatHistoryService as socialChatHistoryServiceSingleton,
|
|
58
58
|
} from './services/social-chat-history.service'
|
|
59
59
|
import { getBuiltInSystemExecutors } from './services/system-executor.service'
|
|
60
|
-
import type {
|
|
61
|
-
import {
|
|
62
|
-
import type {
|
|
63
|
-
import {
|
|
64
|
-
import type { workstreamTitleService } from './services/workstream-title.service'
|
|
65
|
-
import { workstreamTitleService as workstreamTitleServiceSingleton } from './services/workstream-title.service'
|
|
60
|
+
import type { threadMessageService } from './services/thread-message.service'
|
|
61
|
+
import { threadMessageService as threadMessageServiceSingleton } from './services/thread-message.service'
|
|
62
|
+
import type { threadTitleService } from './services/thread-title.service'
|
|
63
|
+
import { threadTitleService as threadTitleServiceSingleton } from './services/thread-title.service'
|
|
66
64
|
import type {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
65
|
+
createThreadApprovalContinuationStream,
|
|
66
|
+
createThreadNativeToolApprovalStream,
|
|
67
|
+
createThreadTurnStream,
|
|
68
|
+
runThreadTurnInBackground,
|
|
71
69
|
triggerPlanNodeTurn,
|
|
72
|
-
} from './services/
|
|
70
|
+
} from './services/thread-turn'
|
|
73
71
|
import {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
72
|
+
createThreadApprovalContinuationStream as createThreadApprovalContinuationStreamSingleton,
|
|
73
|
+
createThreadNativeToolApprovalStream as createThreadNativeToolApprovalStreamSingleton,
|
|
74
|
+
createThreadTurnStream as createThreadTurnStreamSingleton,
|
|
77
75
|
isApprovalContinuationRequest as isApprovalContinuationRequestSingleton,
|
|
78
|
-
|
|
76
|
+
runThreadTurnInBackground as runThreadTurnInBackgroundSingleton,
|
|
79
77
|
triggerPlanNodeTurn as triggerPlanNodeTurnSingleton,
|
|
80
|
-
} from './services/
|
|
81
|
-
import type {
|
|
82
|
-
import {
|
|
78
|
+
} from './services/thread-turn'
|
|
79
|
+
import type { threadService } from './services/thread.service'
|
|
80
|
+
import { threadService as threadServiceSingleton } from './services/thread.service'
|
|
81
|
+
import type { userService } from './services/user.service'
|
|
82
|
+
import { userService as userServiceSingleton } from './services/user.service'
|
|
83
83
|
import type { generatedDocumentStorageService } from './storage/generated-document-storage.service'
|
|
84
84
|
import { generatedDocumentStorageService as generatedDocumentStorageServiceSingleton } from './storage/generated-document-storage.service'
|
|
85
85
|
|
|
86
|
-
type
|
|
87
|
-
|
|
86
|
+
type ArchiveSdkThread = (
|
|
87
|
+
threadId: Parameters<typeof threadServiceSingleton.updateStatus>[0],
|
|
88
88
|
status?: 'archived',
|
|
89
|
-
) => ReturnType<typeof
|
|
89
|
+
) => ReturnType<typeof threadServiceSingleton.updateStatus>
|
|
90
90
|
|
|
91
|
-
type
|
|
92
|
-
|
|
93
|
-
status?: '
|
|
94
|
-
) => ReturnType<typeof
|
|
91
|
+
type UnarchiveSdkThread = (
|
|
92
|
+
threadId: Parameters<typeof threadServiceSingleton.updateStatus>[0],
|
|
93
|
+
status?: 'active',
|
|
94
|
+
) => ReturnType<typeof threadServiceSingleton.updateStatus>
|
|
95
95
|
|
|
96
96
|
let activeRuntimeToken: symbol | null = null
|
|
97
97
|
|
|
@@ -131,14 +131,14 @@ export interface LotaRuntime {
|
|
|
131
131
|
socialChatHistoryService: typeof socialChatHistoryServiceSingleton
|
|
132
132
|
executionPlanService: typeof executionPlanService
|
|
133
133
|
planAgentQueryService: typeof planAgentQueryService
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
134
|
+
threadMessageService: typeof threadMessageService
|
|
135
|
+
threadService: typeof threadService
|
|
136
|
+
threadTitleService: typeof threadTitleService
|
|
137
|
+
createThreadApprovalContinuationStream: typeof createThreadApprovalContinuationStream
|
|
138
|
+
createThreadNativeToolApprovalStream: typeof createThreadNativeToolApprovalStream
|
|
139
|
+
createThreadTurnStream: typeof createThreadTurnStream
|
|
140
140
|
isApprovalContinuationRequest: typeof isApprovalContinuationRequest
|
|
141
|
-
|
|
141
|
+
runThreadTurnInBackground: typeof runThreadTurnInBackground
|
|
142
142
|
triggerPlanNodeTurn: typeof triggerPlanNodeTurn
|
|
143
143
|
}
|
|
144
144
|
lota: {
|
|
@@ -164,32 +164,32 @@ export interface LotaRuntime {
|
|
|
164
164
|
remove: typeof organizationMemberServiceSingleton.removeMembership
|
|
165
165
|
isMember: typeof organizationMemberServiceSingleton.isMember
|
|
166
166
|
}
|
|
167
|
-
|
|
168
|
-
create: typeof
|
|
169
|
-
list: typeof
|
|
170
|
-
get: typeof
|
|
171
|
-
update: typeof
|
|
172
|
-
archive:
|
|
173
|
-
unarchive:
|
|
174
|
-
delete: typeof
|
|
175
|
-
stop: typeof
|
|
176
|
-
listMessages: typeof
|
|
177
|
-
getMessage: (params: {
|
|
167
|
+
threads: {
|
|
168
|
+
create: typeof threadServiceSingleton.createThread
|
|
169
|
+
list: typeof threadServiceSingleton.listThreads
|
|
170
|
+
get: typeof threadServiceSingleton.getThread
|
|
171
|
+
update: typeof threadServiceSingleton.updateTitle
|
|
172
|
+
archive: ArchiveSdkThread
|
|
173
|
+
unarchive: UnarchiveSdkThread
|
|
174
|
+
delete: typeof threadServiceSingleton.deleteThread
|
|
175
|
+
stop: typeof threadServiceSingleton.stopActiveRun
|
|
176
|
+
listMessages: typeof threadMessageServiceSingleton.listMessageHistoryPage
|
|
177
|
+
getMessage: (params: { threadId: string; messageId: string }) => Promise<ChatMessage>
|
|
178
178
|
sendMessage: (params: {
|
|
179
|
-
|
|
179
|
+
threadId: string
|
|
180
180
|
organizationId: string
|
|
181
181
|
userId: string
|
|
182
182
|
userName: string
|
|
183
|
-
messages: Parameters<typeof
|
|
184
|
-
}) => Promise<Awaited<ReturnType<typeof
|
|
183
|
+
messages: Parameters<typeof routeThreadChatMessages>[0]
|
|
184
|
+
}) => Promise<Awaited<ReturnType<typeof createThreadTurnStream>>>
|
|
185
185
|
continueApproval: (params: {
|
|
186
|
-
|
|
186
|
+
threadId: string
|
|
187
187
|
organizationId: string
|
|
188
188
|
userId: string
|
|
189
189
|
userName: string
|
|
190
|
-
messages: Parameters<typeof
|
|
191
|
-
}) => Promise<Awaited<ReturnType<typeof
|
|
192
|
-
uploadAttachment: typeof attachmentServiceSingleton.
|
|
190
|
+
messages: Parameters<typeof routeThreadChatMessages>[0]
|
|
191
|
+
}) => Promise<Awaited<ReturnType<typeof createThreadApprovalContinuationStream>>>
|
|
192
|
+
uploadAttachment: typeof attachmentServiceSingleton.uploadThreadAttachment
|
|
193
193
|
}
|
|
194
194
|
}
|
|
195
195
|
redis: {
|
|
@@ -253,7 +253,7 @@ export async function createLotaRuntime(config: LotaRuntimeConfig): Promise<Lota
|
|
|
253
253
|
descriptions: runtimeConfig.agents.descriptions,
|
|
254
254
|
routerModelId: runtimeConfig.agents.routerModelId,
|
|
255
255
|
teamConsultParticipants: runtimeConfig.agents.teamConsultParticipants,
|
|
256
|
-
|
|
256
|
+
getCoreThreadProfile: runtimeConfig.agents.getCoreThreadProfile,
|
|
257
257
|
})
|
|
258
258
|
configureAgentFactory({
|
|
259
259
|
createAgent: runtimeConfig.agents.createAgent,
|
|
@@ -261,7 +261,7 @@ export async function createLotaRuntime(config: LotaRuntimeConfig): Promise<Lota
|
|
|
261
261
|
getAgentRuntimeConfig: runtimeConfig.agents.getAgentRuntimeConfig,
|
|
262
262
|
pluginRuntime: runtimeConfig.pluginRuntime,
|
|
263
263
|
})
|
|
264
|
-
|
|
264
|
+
configureThreads({ agentRoster: runtimeConfig.agents.roster, config: runtimeConfig.threads })
|
|
265
265
|
configureNotificationService(runtimeConfig.notificationService ?? null)
|
|
266
266
|
configureRuntimeExtensions({
|
|
267
267
|
adapters: runtimeConfig.runtimeAdapters,
|
|
@@ -311,65 +311,61 @@ export async function createLotaRuntime(config: LotaRuntimeConfig): Promise<Lota
|
|
|
311
311
|
remove: organizationMemberServiceSingleton.removeMembership.bind(organizationMemberServiceSingleton),
|
|
312
312
|
isMember: organizationMemberServiceSingleton.isMember.bind(organizationMemberServiceSingleton),
|
|
313
313
|
},
|
|
314
|
-
|
|
315
|
-
create:
|
|
316
|
-
list:
|
|
317
|
-
get:
|
|
318
|
-
update:
|
|
319
|
-
archive: async (
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
getMessage: async ({ workstreamId, messageId }) => {
|
|
327
|
-
const messages = await workstreamMessageServiceSingleton.listMessages(
|
|
328
|
-
ensureRecordId(workstreamId, TABLES.WORKSTREAM),
|
|
329
|
-
)
|
|
314
|
+
threads: {
|
|
315
|
+
create: threadServiceSingleton.createThread.bind(threadServiceSingleton),
|
|
316
|
+
list: threadServiceSingleton.listThreads.bind(threadServiceSingleton),
|
|
317
|
+
get: threadServiceSingleton.getThread.bind(threadServiceSingleton),
|
|
318
|
+
update: threadServiceSingleton.updateTitle.bind(threadServiceSingleton),
|
|
319
|
+
archive: async (threadId, status = 'archived') => await threadServiceSingleton.updateStatus(threadId, status),
|
|
320
|
+
unarchive: async (threadId, status = 'active') => await threadServiceSingleton.updateStatus(threadId, status),
|
|
321
|
+
delete: threadServiceSingleton.deleteThread.bind(threadServiceSingleton),
|
|
322
|
+
stop: threadServiceSingleton.stopActiveRun.bind(threadServiceSingleton),
|
|
323
|
+
listMessages: threadMessageServiceSingleton.listMessageHistoryPage.bind(threadMessageServiceSingleton),
|
|
324
|
+
getMessage: async ({ threadId, messageId }) => {
|
|
325
|
+
const messages = await threadMessageServiceSingleton.listMessages(ensureRecordId(threadId, TABLES.THREAD))
|
|
330
326
|
const message = messages.find((candidate) => candidate.id === messageId)
|
|
331
327
|
if (!message) {
|
|
332
|
-
throw new Error(`
|
|
328
|
+
throw new Error(`Thread message not found: ${messageId}`)
|
|
333
329
|
}
|
|
334
330
|
return message
|
|
335
331
|
},
|
|
336
|
-
sendMessage: async ({
|
|
337
|
-
const
|
|
338
|
-
const
|
|
339
|
-
const routed =
|
|
332
|
+
sendMessage: async ({ threadId, organizationId, userId, userName, messages }) => {
|
|
333
|
+
const threadRef = ensureRecordId(threadId, TABLES.THREAD)
|
|
334
|
+
const thread = await threadServiceSingleton.getThread(threadRef)
|
|
335
|
+
const routed = routeThreadChatMessages(messages)
|
|
340
336
|
if (routed.kind !== 'turn') {
|
|
341
337
|
throw new Error(routed.kind === 'invalid' ? routed.message : 'Expected a user turn payload.')
|
|
342
338
|
}
|
|
343
339
|
|
|
344
|
-
return
|
|
345
|
-
|
|
346
|
-
|
|
340
|
+
return createThreadTurnStreamSingleton({
|
|
341
|
+
thread,
|
|
342
|
+
threadRef,
|
|
347
343
|
orgRef: ensureRecordId(organizationId, TABLES.ORGANIZATION),
|
|
348
344
|
userRef: ensureRecordId(userId, TABLES.USER),
|
|
349
345
|
userName,
|
|
350
346
|
inputMessage: routed.inputMessage,
|
|
351
347
|
})
|
|
352
348
|
},
|
|
353
|
-
continueApproval: async ({
|
|
354
|
-
const
|
|
355
|
-
const
|
|
356
|
-
const routed =
|
|
349
|
+
continueApproval: async ({ threadId, organizationId, userId, userName, messages }) => {
|
|
350
|
+
const threadRef = ensureRecordId(threadId, TABLES.THREAD)
|
|
351
|
+
const thread = await threadServiceSingleton.getThread(threadRef)
|
|
352
|
+
const routed = routeThreadChatMessages(messages)
|
|
357
353
|
if (routed.kind !== 'approval-continuation') {
|
|
358
354
|
throw new Error(
|
|
359
355
|
routed.kind === 'invalid' ? routed.message : 'Expected approval continuation messages payload.',
|
|
360
356
|
)
|
|
361
357
|
}
|
|
362
358
|
|
|
363
|
-
return
|
|
364
|
-
|
|
365
|
-
|
|
359
|
+
return createThreadApprovalContinuationStreamSingleton({
|
|
360
|
+
thread,
|
|
361
|
+
threadRef,
|
|
366
362
|
orgRef: ensureRecordId(organizationId, TABLES.ORGANIZATION),
|
|
367
363
|
userRef: ensureRecordId(userId, TABLES.USER),
|
|
368
364
|
userName,
|
|
369
365
|
approvalMessages: routed.approvalMessages,
|
|
370
366
|
})
|
|
371
367
|
},
|
|
372
|
-
uploadAttachment: attachmentServiceSingleton.
|
|
368
|
+
uploadAttachment: attachmentServiceSingleton.uploadThreadAttachment.bind(attachmentServiceSingleton),
|
|
373
369
|
},
|
|
374
370
|
} satisfies LotaRuntime['lota']
|
|
375
371
|
|
|
@@ -395,14 +391,14 @@ export async function createLotaRuntime(config: LotaRuntimeConfig): Promise<Lota
|
|
|
395
391
|
socialChatHistoryService: socialChatHistoryServiceSingleton,
|
|
396
392
|
executionPlanService: executionPlanServiceSingleton,
|
|
397
393
|
planAgentQueryService: planAgentQueryServiceSingleton,
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
394
|
+
threadMessageService: threadMessageServiceSingleton,
|
|
395
|
+
threadService: threadServiceSingleton,
|
|
396
|
+
threadTitleService: threadTitleServiceSingleton,
|
|
397
|
+
createThreadApprovalContinuationStream: createThreadApprovalContinuationStreamSingleton,
|
|
398
|
+
createThreadNativeToolApprovalStream: createThreadNativeToolApprovalStreamSingleton,
|
|
399
|
+
createThreadTurnStream: createThreadTurnStreamSingleton,
|
|
404
400
|
isApprovalContinuationRequest: isApprovalContinuationRequestSingleton,
|
|
405
|
-
|
|
401
|
+
runThreadTurnInBackground: runThreadTurnInBackgroundSingleton,
|
|
406
402
|
triggerPlanNodeTurn: triggerPlanNodeTurnSingleton,
|
|
407
403
|
},
|
|
408
404
|
lota,
|
|
@@ -456,7 +452,7 @@ export async function createLotaRuntime(config: LotaRuntimeConfig): Promise<Lota
|
|
|
456
452
|
function getBuiltInSchemaFiles(): URL[] {
|
|
457
453
|
return [
|
|
458
454
|
new URL('../infrastructure/schema/00_identity.surql', import.meta.url),
|
|
459
|
-
new URL('../infrastructure/schema/
|
|
455
|
+
new URL('../infrastructure/schema/00_thread.surql', import.meta.url),
|
|
460
456
|
new URL('../infrastructure/schema/01_memory.surql', import.meta.url),
|
|
461
457
|
new URL('../infrastructure/schema/02_execution_plan.surql', import.meta.url),
|
|
462
458
|
new URL('../infrastructure/schema/03_learned_skill.surql', import.meta.url),
|
package/src/db/record-id.ts
CHANGED
|
@@ -8,13 +8,18 @@ export interface RecordIdShape {
|
|
|
8
8
|
id: RecordIdValue
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
export
|
|
11
|
+
export type RecordIdInput = string | RecordId | StringRecordId | RecordIdShape
|
|
12
|
+
export type RecordIdRef = RecordIdInput
|
|
13
|
+
|
|
14
|
+
const SURREAL_RECORD_ID_CLASS_NAMES = new Set(['RecordId', 'StringRecordId'])
|
|
15
|
+
|
|
16
|
+
interface SurrealRecordIdLike {
|
|
12
17
|
toString(): string
|
|
13
18
|
}
|
|
14
19
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
20
|
+
interface NamedConstructor {
|
|
21
|
+
name?: unknown
|
|
22
|
+
}
|
|
18
23
|
|
|
19
24
|
class InvalidRecordIdError extends BadRequestError {
|
|
20
25
|
constructor(message: string) {
|
|
@@ -23,18 +28,18 @@ class InvalidRecordIdError extends BadRequestError {
|
|
|
23
28
|
}
|
|
24
29
|
}
|
|
25
30
|
|
|
26
|
-
export function
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
return null
|
|
31
|
+
export function isSurrealRecordIdValue(value: unknown): boolean {
|
|
32
|
+
if (!value || typeof value !== 'object') {
|
|
33
|
+
return false
|
|
30
34
|
}
|
|
31
35
|
|
|
32
|
-
const
|
|
33
|
-
if (typeof
|
|
34
|
-
return
|
|
36
|
+
const constructor = (value as { constructor?: unknown }).constructor
|
|
37
|
+
if (typeof constructor !== 'function') {
|
|
38
|
+
return false
|
|
35
39
|
}
|
|
36
40
|
|
|
37
|
-
|
|
41
|
+
const constructorName = (constructor as NamedConstructor).name
|
|
42
|
+
return typeof constructorName === 'string' && SURREAL_RECORD_ID_CLASS_NAMES.has(constructorName)
|
|
38
43
|
}
|
|
39
44
|
|
|
40
45
|
export function ensureRecordId(value: RecordIdInput, fallbackTable?: string): RecordId {
|
|
@@ -42,8 +47,8 @@ export function ensureRecordId(value: RecordIdInput, fallbackTable?: string): Re
|
|
|
42
47
|
return value
|
|
43
48
|
}
|
|
44
49
|
|
|
45
|
-
if (value instanceof StringRecordId) {
|
|
46
|
-
return ensureRecordId(value.toString(), fallbackTable)
|
|
50
|
+
if (value instanceof StringRecordId || isSurrealRecordIdValue(value)) {
|
|
51
|
+
return ensureRecordId((value as SurrealRecordIdLike).toString(), fallbackTable)
|
|
47
52
|
}
|
|
48
53
|
|
|
49
54
|
if (typeof value === 'string') {
|
|
@@ -58,16 +63,11 @@ export function ensureRecordId(value: RecordIdInput, fallbackTable?: string): Re
|
|
|
58
63
|
return new RecordId(fallbackTable, value)
|
|
59
64
|
}
|
|
60
65
|
|
|
61
|
-
if (typeof value === 'object') {
|
|
66
|
+
if (typeof value === 'object' && Object.keys(value).length === 2) {
|
|
62
67
|
const record = value as { tb?: string; id?: RecordIdValue }
|
|
63
|
-
if (record.tb && record.id !== undefined) {
|
|
68
|
+
if (typeof record.tb === 'string' && record.id !== undefined) {
|
|
64
69
|
return new RecordId(record.tb, record.id)
|
|
65
70
|
}
|
|
66
|
-
|
|
67
|
-
const stringValue = readCustomStringValue(value)
|
|
68
|
-
if (stringValue && stringValue !== '[object Object]') {
|
|
69
|
-
return ensureRecordId(stringValue, fallbackTable)
|
|
70
|
-
}
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
throw new InvalidRecordIdError('Invalid record id value')
|
package/src/db/service.ts
CHANGED
|
@@ -16,7 +16,7 @@ import { serverLogger } from '../config/logger'
|
|
|
16
16
|
import { withTimeout } from '../utils/async'
|
|
17
17
|
import { isRecord } from '../utils/string'
|
|
18
18
|
import type { RecordIdInput } from './record-id'
|
|
19
|
-
import { ensureRecordId,
|
|
19
|
+
import { ensureRecordId, isSurrealRecordIdValue } from './record-id'
|
|
20
20
|
import type { DatabaseTable } from './tables'
|
|
21
21
|
|
|
22
22
|
export class SurrealDBError extends Error {
|
|
@@ -103,19 +103,6 @@ function isBoundQueryLike(value: unknown): value is BoundQueryLike {
|
|
|
103
103
|
return value.bindings === undefined || isRecord(value.bindings)
|
|
104
104
|
}
|
|
105
105
|
|
|
106
|
-
function toStringLikeValue(value: unknown): string | null {
|
|
107
|
-
if (!value || typeof value !== 'object') {
|
|
108
|
-
return null
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
const stringValue = readCustomStringValue(value)
|
|
112
|
-
if (typeof stringValue !== 'string' || stringValue.length === 0 || stringValue === '[object Object]') {
|
|
113
|
-
return null
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
return stringValue
|
|
117
|
-
}
|
|
118
|
-
|
|
119
106
|
const CONNECT_MAX_ATTEMPTS = 5
|
|
120
107
|
const CONNECT_RETRY_BASE_DELAY_MS = 100
|
|
121
108
|
const CONNECT_RETRY_JITTER_MS = 50
|
|
@@ -353,6 +340,10 @@ export class SurrealDBService {
|
|
|
353
340
|
return value.map((entry) => this.normalizeParseValue(entry))
|
|
354
341
|
}
|
|
355
342
|
|
|
343
|
+
if (isSurrealRecordIdValue(value)) {
|
|
344
|
+
return ensureRecordId(value as RecordIdInput)
|
|
345
|
+
}
|
|
346
|
+
|
|
356
347
|
if (!isRecord(value)) {
|
|
357
348
|
return value
|
|
358
349
|
}
|
|
@@ -413,10 +404,6 @@ export class SurrealDBService {
|
|
|
413
404
|
return new BoundQuery(query.query, this.normalizeBindings(query.bindings))
|
|
414
405
|
}
|
|
415
406
|
|
|
416
|
-
private isSerializedRecordId(value: string): boolean {
|
|
417
|
-
return /^[a-zA-Z][a-zA-Z0-9_]*:(?:⟨.+⟩|.+)$/.test(value)
|
|
418
|
-
}
|
|
419
|
-
|
|
420
407
|
private normalizeRuntimeValue(value: unknown): unknown {
|
|
421
408
|
if (value === null || value === undefined) {
|
|
422
409
|
return value
|
|
@@ -435,17 +422,16 @@ export class SurrealDBService {
|
|
|
435
422
|
return value.map((entry) => this.normalizeRuntimeValue(entry))
|
|
436
423
|
}
|
|
437
424
|
|
|
425
|
+
if (isSurrealRecordIdValue(value)) {
|
|
426
|
+
return ensureRecordId(value as RecordIdInput)
|
|
427
|
+
}
|
|
428
|
+
|
|
438
429
|
if (!isRecord(value)) {
|
|
439
430
|
return value
|
|
440
431
|
}
|
|
441
432
|
|
|
442
433
|
if ('tb' in value && 'id' in value && Object.keys(value).length === 2) {
|
|
443
|
-
return ensureRecordId(value as RecordIdInput)
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
const stringValue = toStringLikeValue(value)
|
|
447
|
-
if (stringValue && this.isSerializedRecordId(stringValue)) {
|
|
448
|
-
return ensureRecordId(stringValue)
|
|
434
|
+
return ensureRecordId(value as unknown as RecordIdInput)
|
|
449
435
|
}
|
|
450
436
|
|
|
451
437
|
const entries = Object.entries(value)
|
|
@@ -467,17 +453,23 @@ export class SurrealDBService {
|
|
|
467
453
|
}
|
|
468
454
|
|
|
469
455
|
private normalizeMutationFieldValue(value: unknown): unknown {
|
|
470
|
-
if (value ===
|
|
456
|
+
if (value === undefined) {
|
|
471
457
|
return undefined
|
|
472
458
|
}
|
|
473
459
|
|
|
460
|
+
if (value === null) {
|
|
461
|
+
return null
|
|
462
|
+
}
|
|
463
|
+
|
|
474
464
|
return this.normalizeRuntimeValue(value)
|
|
475
465
|
}
|
|
476
466
|
|
|
477
467
|
// Cast is safe: normalizeRuntimeValue preserves Record shape when input is a Record
|
|
478
468
|
private normalizeMutationData(data: Record<string, unknown>): Record<string, unknown> {
|
|
479
469
|
return Object.fromEntries(
|
|
480
|
-
Object.entries(data)
|
|
470
|
+
Object.entries(data)
|
|
471
|
+
.map(([key, value]) => [key, this.normalizeMutationFieldValue(value)] as const)
|
|
472
|
+
.filter((entry): entry is readonly [string, unknown] => entry[1] !== undefined),
|
|
481
473
|
) as Record<string, unknown>
|
|
482
474
|
}
|
|
483
475
|
|
|
@@ -490,11 +482,6 @@ export class SurrealDBService {
|
|
|
490
482
|
return new Table(value)
|
|
491
483
|
}
|
|
492
484
|
|
|
493
|
-
const stringValue = toStringLikeValue(value)
|
|
494
|
-
if (stringValue) {
|
|
495
|
-
return new Table(stringValue)
|
|
496
|
-
}
|
|
497
|
-
|
|
498
485
|
throw new SurrealDBError('Invalid table value')
|
|
499
486
|
}
|
|
500
487
|
|
|
@@ -503,20 +490,17 @@ export class SurrealDBService {
|
|
|
503
490
|
return true
|
|
504
491
|
}
|
|
505
492
|
|
|
493
|
+
if (isSurrealRecordIdValue(value)) {
|
|
494
|
+
return true
|
|
495
|
+
}
|
|
496
|
+
|
|
506
497
|
if (typeof value === 'string') {
|
|
507
498
|
return /^[a-zA-Z][a-zA-Z0-9_]*:/.test(value)
|
|
508
499
|
}
|
|
509
500
|
|
|
510
501
|
if (value && typeof value === 'object') {
|
|
511
502
|
const record = value as { tb?: unknown; id?: unknown }
|
|
512
|
-
|
|
513
|
-
return true
|
|
514
|
-
}
|
|
515
|
-
|
|
516
|
-
const stringValue = toStringLikeValue(value)
|
|
517
|
-
if (stringValue) {
|
|
518
|
-
return /^[a-zA-Z][a-zA-Z0-9_]*:/.test(stringValue)
|
|
519
|
-
}
|
|
503
|
+
return typeof record.tb === 'string' && record.id !== undefined && Object.keys(value).length === 2
|
|
520
504
|
}
|
|
521
505
|
|
|
522
506
|
return false
|
|
@@ -689,7 +673,11 @@ export class SurrealDBService {
|
|
|
689
673
|
for (const key of filterKeys) {
|
|
690
674
|
this.assertValidIdentifier(key, 'filter field')
|
|
691
675
|
}
|
|
692
|
-
const
|
|
676
|
+
const rawOrderDir: unknown = options?.orderDir
|
|
677
|
+
if (rawOrderDir !== undefined && rawOrderDir !== 'ASC' && rawOrderDir !== 'DESC') {
|
|
678
|
+
throw new SurrealDBError(`Invalid orderDir value: ${this.describeInvalidValue(rawOrderDir)}`)
|
|
679
|
+
}
|
|
680
|
+
const orderDir = rawOrderDir ?? 'ASC'
|
|
693
681
|
const limit = options?.limit
|
|
694
682
|
const offset = options?.offset
|
|
695
683
|
const vars: Record<string, unknown> = this.normalizeMutationData(filter)
|
|
@@ -730,6 +718,22 @@ export class SurrealDBService {
|
|
|
730
718
|
}
|
|
731
719
|
}
|
|
732
720
|
|
|
721
|
+
private describeInvalidValue(value: unknown): string {
|
|
722
|
+
if (typeof value === 'string') {
|
|
723
|
+
return value
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
try {
|
|
727
|
+
const serialized = JSON.stringify(value)
|
|
728
|
+
if (typeof serialized === 'string') {
|
|
729
|
+
return serialized
|
|
730
|
+
}
|
|
731
|
+
return Object.prototype.toString.call(value)
|
|
732
|
+
} catch {
|
|
733
|
+
return Object.prototype.toString.call(value)
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
|
|
733
737
|
async create<T extends z.ZodTypeAny>(
|
|
734
738
|
table: DatabaseTable,
|
|
735
739
|
data: Record<string, unknown>,
|
package/src/db/tables.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
export const TABLES = {
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
THREAD: 'thread',
|
|
3
|
+
THREAD_MESSAGE: 'threadMessage',
|
|
4
|
+
THREAD_ATTACHMENT: 'threadAttachment',
|
|
4
5
|
RUNTIME_BOOTSTRAP: 'runtimeBootstrap',
|
|
5
|
-
WORKSTREAM_ATTACHMENT: 'workstreamAttachment',
|
|
6
6
|
MEMORY: 'memory',
|
|
7
7
|
MEMORY_RELATION: 'memoryRelation',
|
|
8
8
|
MEMORY_HISTORY: 'memoryHistory',
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { recordIdSchema } from '@lota-sdk/shared'
|
|
2
2
|
import { z } from 'zod'
|
|
3
3
|
|
|
4
|
-
export const
|
|
4
|
+
export const ThreadMessageRowSchema = z.object({
|
|
5
5
|
id: recordIdSchema,
|
|
6
|
-
|
|
6
|
+
threadId: recordIdSchema,
|
|
7
7
|
messageId: z.string(),
|
|
8
8
|
role: z.enum(['system', 'user', 'assistant']),
|
|
9
9
|
parts: z.array(z.record(z.string(), z.unknown())).optional(),
|
|
@@ -12,4 +12,4 @@ export const WorkstreamMessageRowSchema = z.object({
|
|
|
12
12
|
updatedAt: z.coerce.date().optional(),
|
|
13
13
|
})
|
|
14
14
|
|
|
15
|
-
export type
|
|
15
|
+
export type ThreadMessageRow = z.infer<typeof ThreadMessageRowSchema>
|
|
@@ -4,11 +4,11 @@ import { ensureRecordId } from '../db/record-id'
|
|
|
4
4
|
import { databaseService } from '../db/service'
|
|
5
5
|
import { TABLES } from '../db/tables'
|
|
6
6
|
import { contextCompactionService } from '../services/context-compaction.service'
|
|
7
|
-
import {
|
|
7
|
+
import { threadService } from '../services/thread.service'
|
|
8
8
|
import { createQueueFactory } from './queue-factory'
|
|
9
9
|
|
|
10
10
|
interface ContextCompactionJob {
|
|
11
|
-
domain: '
|
|
11
|
+
domain: 'thread'
|
|
12
12
|
entityId: string
|
|
13
13
|
contextSize?: number
|
|
14
14
|
}
|
|
@@ -17,12 +17,12 @@ async function processContextCompactionJob(job: Job<ContextCompactionJob>): Prom
|
|
|
17
17
|
await databaseService.connect()
|
|
18
18
|
|
|
19
19
|
const { entityId, contextSize } = job.data
|
|
20
|
-
const
|
|
21
|
-
await
|
|
20
|
+
const threadRef = ensureRecordId(entityId, TABLES.THREAD)
|
|
21
|
+
await threadService.setCompacting(threadRef, true)
|
|
22
22
|
try {
|
|
23
|
-
await contextCompactionService.
|
|
23
|
+
await contextCompactionService.compactThreadHistory({ threadId: threadRef, contextSize })
|
|
24
24
|
} finally {
|
|
25
|
-
await
|
|
25
|
+
await threadService.setCompacting(threadRef, false)
|
|
26
26
|
}
|
|
27
27
|
}
|
|
28
28
|
|
|
@@ -10,7 +10,7 @@ import { createQueueFactory } from './queue-factory'
|
|
|
10
10
|
export interface PlanAgentHeartbeatWakeJob {
|
|
11
11
|
type: 'wake-node'
|
|
12
12
|
organizationId: string
|
|
13
|
-
|
|
13
|
+
threadId: string
|
|
14
14
|
runId: string
|
|
15
15
|
nodeId: string
|
|
16
16
|
agentId: string
|
|
@@ -58,7 +58,7 @@ const planAgentHeartbeatQueue = createQueueFactory<PlanAgentHeartbeatJob>({
|
|
|
58
58
|
|
|
59
59
|
function buildWakeJobId(params: {
|
|
60
60
|
organizationId: string
|
|
61
|
-
|
|
61
|
+
threadId: string
|
|
62
62
|
runId: string
|
|
63
63
|
nodeId: string
|
|
64
64
|
agentId: string
|
|
@@ -70,7 +70,7 @@ function buildWakeJobId(params: {
|
|
|
70
70
|
|
|
71
71
|
export async function enqueuePlanAgentHeartbeatWake(params: {
|
|
72
72
|
organizationId: string
|
|
73
|
-
|
|
73
|
+
threadId: string
|
|
74
74
|
runId: string
|
|
75
75
|
nodeId: string
|
|
76
76
|
agentId: string
|