@lota-sdk/core 0.4.7 → 0.4.9

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 (259) hide show
  1. package/package.json +11 -12
  2. package/src/ai/embedding-cache.ts +94 -22
  3. package/src/ai-gateway/ai-gateway.ts +738 -223
  4. package/src/config/agent-defaults.ts +176 -75
  5. package/src/config/agent-types.ts +54 -4
  6. package/src/config/constants.ts +8 -2
  7. package/src/config/logger.ts +286 -19
  8. package/src/config/model-constants.ts +1 -0
  9. package/src/config/thread-defaults.ts +33 -21
  10. package/src/create-runtime.ts +725 -383
  11. package/src/db/base.service.ts +52 -28
  12. package/src/db/cursor-pagination.ts +71 -30
  13. package/src/db/memory-store.helpers.ts +4 -7
  14. package/src/db/memory-store.ts +856 -598
  15. package/src/db/memory.ts +398 -275
  16. package/src/db/record-id.ts +32 -10
  17. package/src/db/schema-fingerprint.ts +30 -12
  18. package/src/db/service-normalization.ts +255 -0
  19. package/src/db/service.ts +726 -761
  20. package/src/db/startup.ts +140 -66
  21. package/src/db/transaction-conflict.ts +15 -0
  22. package/src/effect/awaitable-effect.ts +87 -0
  23. package/src/effect/errors.ts +121 -0
  24. package/src/effect/helpers.ts +98 -0
  25. package/src/effect/index.ts +22 -0
  26. package/src/effect/layers.ts +228 -0
  27. package/src/effect/runtime-ref.ts +25 -0
  28. package/src/effect/runtime.ts +31 -0
  29. package/src/effect/services.ts +57 -0
  30. package/src/effect/zod.ts +43 -0
  31. package/src/embeddings/provider.ts +122 -71
  32. package/src/index.ts +46 -1
  33. package/src/openrouter/direct-provider.ts +29 -0
  34. package/src/queues/autonomous-job.queue.ts +130 -74
  35. package/src/queues/context-compaction.queue.ts +60 -15
  36. package/src/queues/delayed-node-promotion.queue.ts +52 -15
  37. package/src/queues/document-processor.queue.ts +52 -77
  38. package/src/queues/memory-consolidation.queue.ts +47 -32
  39. package/src/queues/organization-learning.queue.ts +13 -4
  40. package/src/queues/plan-agent-heartbeat.queue.ts +65 -21
  41. package/src/queues/plan-scheduler.queue.ts +107 -31
  42. package/src/queues/post-chat-memory.queue.ts +66 -24
  43. package/src/queues/queue-factory.ts +142 -52
  44. package/src/queues/standalone-worker.ts +39 -0
  45. package/src/queues/title-generation.queue.ts +54 -9
  46. package/src/redis/connection.ts +84 -32
  47. package/src/redis/index.ts +6 -8
  48. package/src/redis/org-memory-lock.ts +60 -27
  49. package/src/redis/redis-lease-lock.ts +200 -121
  50. package/src/redis/runtime-connection.ts +10 -0
  51. package/src/redis/stream-context.ts +84 -46
  52. package/src/runtime/agent-identity-overrides.ts +2 -2
  53. package/src/runtime/agent-runtime-policy.ts +4 -1
  54. package/src/runtime/agent-stream-helpers.ts +20 -9
  55. package/src/runtime/chat-run-orchestration.ts +102 -19
  56. package/src/runtime/chat-run-registry.ts +36 -2
  57. package/src/runtime/context-compaction/context-compaction-runtime.ts +107 -0
  58. package/src/runtime/{context-compaction.ts → context-compaction/context-compaction.ts} +114 -91
  59. package/src/runtime/execution-plan-visibility.ts +2 -2
  60. package/src/runtime/execution-plan.ts +42 -15
  61. package/src/runtime/graph-designer.ts +11 -7
  62. package/src/runtime/helper-model.ts +135 -48
  63. package/src/runtime/index.ts +7 -7
  64. package/src/runtime/indexed-repositories-policy.ts +3 -3
  65. package/src/runtime/{memory-block.ts → memory/memory-block.ts} +40 -36
  66. package/src/runtime/{memory-digest-policy.ts → memory/memory-digest-policy.ts} +1 -1
  67. package/src/runtime/{memory-pipeline.ts → memory/memory-pipeline.ts} +1 -1
  68. package/src/runtime/{memory-prompts-fact.ts → memory/memory-prompts-fact.ts} +2 -2
  69. package/src/runtime/{memory-scope.ts → memory/memory-scope.ts} +12 -6
  70. package/src/runtime/plugin-resolution.ts +144 -24
  71. package/src/runtime/plugin-types.ts +9 -1
  72. package/src/runtime/post-turn-side-effects.ts +197 -130
  73. package/src/runtime/retrieval-adapters.ts +38 -4
  74. package/src/runtime/runtime-config.ts +165 -61
  75. package/src/runtime/runtime-extensions.ts +21 -34
  76. package/src/runtime/social-chat/social-chat-agent-runner.ts +157 -0
  77. package/src/runtime/{social-chat-history.ts → social-chat/social-chat-history.ts} +42 -20
  78. package/src/runtime/social-chat/social-chat.ts +594 -0
  79. package/src/runtime/specialist-runner.ts +36 -10
  80. package/src/runtime/team-consultation/team-consultation-orchestrator.ts +427 -0
  81. package/src/runtime/{team-consultation-prompts.ts → team-consultation/team-consultation-prompts.ts} +6 -2
  82. package/src/runtime/thread-chat-helpers.ts +2 -2
  83. package/src/runtime/thread-plan-turn.ts +2 -1
  84. package/src/runtime/thread-turn-context.ts +172 -94
  85. package/src/runtime/turn-lifecycle.ts +93 -27
  86. package/src/services/agent-activity.service.ts +287 -203
  87. package/src/services/agent-executor.service.ts +329 -217
  88. package/src/services/artifact.service.ts +225 -148
  89. package/src/services/attachment.service.ts +137 -115
  90. package/src/services/autonomous-job.service.ts +888 -491
  91. package/src/services/chat-run-registry.service.ts +11 -1
  92. package/src/services/context-compaction.service.ts +136 -86
  93. package/src/services/document-chunk.service.ts +162 -90
  94. package/src/services/execution-plan/execution-plan-approval.ts +26 -0
  95. package/src/services/execution-plan/execution-plan-context.ts +29 -0
  96. package/src/services/execution-plan/execution-plan-graph.ts +256 -0
  97. package/src/services/execution-plan/execution-plan-schedule.ts +84 -0
  98. package/src/services/execution-plan/execution-plan-spec.ts +75 -0
  99. package/src/services/execution-plan/execution-plan.service.ts +1041 -0
  100. package/src/services/feedback-loop.service.ts +132 -76
  101. package/src/services/global-orchestrator.service.ts +80 -170
  102. package/src/services/graph-full-routing.ts +182 -0
  103. package/src/services/index.ts +18 -20
  104. package/src/services/institutional-memory.service.ts +220 -123
  105. package/src/services/learned-skill.service.ts +364 -259
  106. package/src/services/memory/memory-conversation.ts +95 -0
  107. package/src/services/memory/memory-org-memory.ts +39 -0
  108. package/src/services/memory/memory-preseeded.ts +80 -0
  109. package/src/services/memory/memory-rerank.ts +297 -0
  110. package/src/services/{memory-utils.ts → memory/memory-utils.ts} +5 -5
  111. package/src/services/memory/memory.service.ts +692 -0
  112. package/src/services/memory/rerank.service.ts +209 -0
  113. package/src/services/monitoring-window.service.ts +92 -70
  114. package/src/services/mutating-approval.service.ts +62 -53
  115. package/src/services/node-workspace.service.ts +141 -98
  116. package/src/services/notification.service.ts +17 -16
  117. package/src/services/organization-member.service.ts +120 -66
  118. package/src/services/organization.service.ts +144 -51
  119. package/src/services/ownership-dispatcher.service.ts +415 -264
  120. package/src/services/plan/plan-agent-heartbeat.service.ts +234 -0
  121. package/src/services/plan/plan-agent-query.service.ts +322 -0
  122. package/src/services/plan/plan-approval.service.ts +102 -0
  123. package/src/services/plan/plan-artifact.service.ts +60 -0
  124. package/src/services/plan/plan-builder.service.ts +76 -0
  125. package/src/services/plan/plan-checkpoint.service.ts +103 -0
  126. package/src/services/{plan-compiler.service.ts → plan/plan-compiler.service.ts} +26 -9
  127. package/src/services/plan/plan-completion-side-effects.ts +175 -0
  128. package/src/services/plan/plan-coordination.service.ts +181 -0
  129. package/src/services/plan/plan-cycle.service.ts +398 -0
  130. package/src/services/plan/plan-deadline.service.ts +547 -0
  131. package/src/services/plan/plan-event-delivery.service.ts +261 -0
  132. package/src/services/plan/plan-executor-context.ts +35 -0
  133. package/src/services/plan/plan-executor-graph.ts +475 -0
  134. package/src/services/plan/plan-executor-helpers.ts +322 -0
  135. package/src/services/plan/plan-executor-persistence.ts +209 -0
  136. package/src/services/plan/plan-executor.service.ts +1654 -0
  137. package/src/services/{plan-helpers.ts → plan/plan-helpers.ts} +1 -1
  138. package/src/services/{plan-run-data.ts → plan/plan-run-data.ts} +4 -4
  139. package/src/services/plan/plan-run-serialization.ts +15 -0
  140. package/src/services/plan/plan-run.service.ts +644 -0
  141. package/src/services/plan/plan-scheduler.service.ts +385 -0
  142. package/src/services/plan/plan-template.service.ts +224 -0
  143. package/src/services/plan/plan-transaction-events.ts +33 -0
  144. package/src/services/plan/plan-validator.service.ts +907 -0
  145. package/src/services/plan/plan-workspace.service.ts +125 -0
  146. package/src/services/plugin-executor.service.ts +97 -68
  147. package/src/services/quality-metrics.service.ts +112 -94
  148. package/src/services/queue-job.service.ts +296 -230
  149. package/src/services/recent-activity-title.service.ts +65 -36
  150. package/src/services/recent-activity.service.ts +274 -259
  151. package/src/services/skill-resolver.service.ts +38 -12
  152. package/src/services/social-chat-history.service.ts +176 -125
  153. package/src/services/system-executor.service.ts +91 -61
  154. package/src/services/thread/thread-active-run.ts +203 -0
  155. package/src/services/thread/thread-bootstrap.ts +369 -0
  156. package/src/services/thread/thread-listing.ts +198 -0
  157. package/src/services/thread/thread-memory-block.ts +117 -0
  158. package/src/services/thread/thread-message.service.ts +363 -0
  159. package/src/services/thread/thread-record-store.ts +155 -0
  160. package/src/services/thread/thread-title.service.ts +74 -0
  161. package/src/services/thread/thread-turn-execution.ts +280 -0
  162. package/src/services/thread/thread-turn-message-context.ts +73 -0
  163. package/src/services/thread/thread-turn-preparation.service.ts +1146 -0
  164. package/src/services/thread/thread-turn-streaming.ts +402 -0
  165. package/src/services/thread/thread-turn-tracing.ts +35 -0
  166. package/src/services/thread/thread-turn.ts +343 -0
  167. package/src/services/thread/thread.service.ts +335 -0
  168. package/src/services/user.service.ts +82 -32
  169. package/src/services/write-intent-validator.service.ts +63 -51
  170. package/src/storage/attachment-parser.ts +69 -27
  171. package/src/storage/attachment-storage.service.ts +331 -275
  172. package/src/storage/generated-document-storage.service.ts +66 -34
  173. package/src/system-agents/agent-result.ts +3 -1
  174. package/src/system-agents/context-compaction.agent.ts +2 -2
  175. package/src/system-agents/delegated-agent-factory.ts +159 -90
  176. package/src/system-agents/memory-reranker.agent.ts +2 -2
  177. package/src/system-agents/memory.agent.ts +2 -2
  178. package/src/system-agents/recent-activity-title-refiner.agent.ts +2 -2
  179. package/src/system-agents/regular-chat-memory-digest.agent.ts +2 -2
  180. package/src/system-agents/skill-extractor.agent.ts +2 -2
  181. package/src/system-agents/skill-manager.agent.ts +2 -2
  182. package/src/system-agents/thread-router.agent.ts +157 -113
  183. package/src/system-agents/title-generator.agent.ts +2 -2
  184. package/src/tools/execution-plan.tool.ts +220 -161
  185. package/src/tools/fetch-webpage.tool.ts +21 -17
  186. package/src/tools/firecrawl-client.ts +16 -6
  187. package/src/tools/index.ts +1 -0
  188. package/src/tools/memory-block.tool.ts +14 -6
  189. package/src/tools/plan-approval.tool.ts +49 -47
  190. package/src/tools/read-file-parts.tool.ts +44 -33
  191. package/src/tools/remember-memory.tool.ts +65 -45
  192. package/src/tools/search-web.tool.ts +26 -22
  193. package/src/tools/search.tool.ts +41 -29
  194. package/src/tools/team-think.tool.ts +124 -83
  195. package/src/tools/user-questions.tool.ts +4 -3
  196. package/src/tools/web-tool-shared.ts +6 -0
  197. package/src/utils/async.ts +17 -23
  198. package/src/utils/crypto.ts +21 -0
  199. package/src/utils/date-time.ts +40 -1
  200. package/src/utils/errors.ts +95 -16
  201. package/src/utils/hono-error-handler.ts +24 -39
  202. package/src/utils/index.ts +2 -1
  203. package/src/utils/null-proto-record.ts +41 -0
  204. package/src/utils/sse-keepalive.ts +124 -21
  205. package/src/workers/bootstrap.ts +186 -51
  206. package/src/workers/memory-consolidation.worker.ts +325 -237
  207. package/src/workers/organization-learning.worker.ts +50 -16
  208. package/src/workers/regular-chat-memory-digest.helpers.ts +28 -27
  209. package/src/workers/regular-chat-memory-digest.runner.ts +175 -114
  210. package/src/workers/skill-extraction.runner.ts +176 -93
  211. package/src/workers/utils/file-section-chunker.ts +8 -10
  212. package/src/workers/utils/repo-structure-extractor.ts +349 -260
  213. package/src/workers/utils/repomix-file-sections.ts +2 -2
  214. package/src/workers/utils/thread-message-query.ts +97 -38
  215. package/src/workers/worker-utils.ts +56 -31
  216. package/src/config/debug-logger.ts +0 -47
  217. package/src/redis/connection-accessor.ts +0 -26
  218. package/src/runtime/context-compaction-runtime.ts +0 -87
  219. package/src/runtime/social-chat-agent-runner.ts +0 -118
  220. package/src/runtime/social-chat.ts +0 -516
  221. package/src/runtime/team-consultation-orchestrator.ts +0 -272
  222. package/src/services/adaptive-playbook.service.ts +0 -152
  223. package/src/services/artifact-provenance.service.ts +0 -172
  224. package/src/services/chat-attachments.service.ts +0 -17
  225. package/src/services/context-compaction-runtime.singleton.ts +0 -13
  226. package/src/services/execution-plan.service.ts +0 -1118
  227. package/src/services/memory.service.ts +0 -844
  228. package/src/services/plan-agent-heartbeat.service.ts +0 -136
  229. package/src/services/plan-agent-query.service.ts +0 -267
  230. package/src/services/plan-approval.service.ts +0 -83
  231. package/src/services/plan-artifact.service.ts +0 -50
  232. package/src/services/plan-builder.service.ts +0 -67
  233. package/src/services/plan-checkpoint.service.ts +0 -81
  234. package/src/services/plan-completion-side-effects.ts +0 -80
  235. package/src/services/plan-coordination.service.ts +0 -157
  236. package/src/services/plan-cycle.service.ts +0 -284
  237. package/src/services/plan-deadline.service.ts +0 -430
  238. package/src/services/plan-event-delivery.service.ts +0 -166
  239. package/src/services/plan-executor.service.ts +0 -1950
  240. package/src/services/plan-run.service.ts +0 -515
  241. package/src/services/plan-scheduler.service.ts +0 -240
  242. package/src/services/plan-template.service.ts +0 -177
  243. package/src/services/plan-validator.service.ts +0 -818
  244. package/src/services/plan-workspace.service.ts +0 -83
  245. package/src/services/thread-message.service.ts +0 -275
  246. package/src/services/thread-plan-registry.service.ts +0 -22
  247. package/src/services/thread-title.service.ts +0 -39
  248. package/src/services/thread-turn-preparation.service.ts +0 -1147
  249. package/src/services/thread-turn.ts +0 -172
  250. package/src/services/thread.service.ts +0 -869
  251. package/src/utils/env.ts +0 -8
  252. /package/src/runtime/{context-compaction-constants.ts → context-compaction/context-compaction-constants.ts} +0 -0
  253. /package/src/runtime/{memory-format.ts → memory/memory-format.ts} +0 -0
  254. /package/src/runtime/{memory-prompts-parse.ts → memory/memory-prompts-parse.ts} +0 -0
  255. /package/src/runtime/{memory-prompts-update.ts → memory/memory-prompts-update.ts} +0 -0
  256. /package/src/runtime/{social-chat-prompts.ts → social-chat/social-chat-prompts.ts} +0 -0
  257. /package/src/services/{plan-node-spec.ts → plan/plan-node-spec.ts} +0 -0
  258. /package/src/services/{thread-constants.ts → thread/thread-constants.ts} +0 -0
  259. /package/src/services/{thread.types.ts → thread/thread.types.ts} +0 -0
@@ -1,83 +0,0 @@
1
- import { z } from 'zod'
2
-
3
- import { getRedisConnection } from '../redis'
4
-
5
- const PlanWorkspaceEntrySchema = z.object({
6
- value: z.unknown(),
7
- version: z.number(),
8
- writeSequence: z.number(),
9
- nodeId: z.string(),
10
- timestamp: z.number(),
11
- })
12
-
13
- export type PlanWorkspaceEntry = z.infer<typeof PlanWorkspaceEntrySchema>
14
-
15
- class PlanWorkspaceService {
16
- private keyPrefix = 'plan-workspace:'
17
-
18
- async write(params: {
19
- runId: string
20
- nodeId: string
21
- key: string
22
- value: unknown
23
- version: number
24
- checkpointSequence: number
25
- }): Promise<void> {
26
- const redis = getRedisConnection()
27
- const hashKey = `${this.keyPrefix}${params.runId}`
28
- const fieldKey = `${params.nodeId}:${params.key}`
29
- const entry: PlanWorkspaceEntry = {
30
- value: params.value,
31
- version: params.version,
32
- writeSequence: params.checkpointSequence,
33
- nodeId: params.nodeId,
34
- timestamp: Date.now(),
35
- }
36
- await redis.hset(hashKey, fieldKey, JSON.stringify(entry))
37
- }
38
-
39
- async snapshotRead(params: {
40
- runId: string
41
- readerNodeId: string
42
- snapshotSequence?: number
43
- }): Promise<Record<string, PlanWorkspaceEntry>> {
44
- const redis = getRedisConnection()
45
- const hashKey = `${this.keyPrefix}${params.runId}`
46
- const nodePrefix = `${params.readerNodeId}:`
47
- const all = await redis.hgetall(hashKey)
48
- const result: Record<string, PlanWorkspaceEntry> = {}
49
- for (const [fieldKey, raw] of Object.entries(all)) {
50
- if (!fieldKey.startsWith(nodePrefix)) continue
51
- const entry = PlanWorkspaceEntrySchema.parse(JSON.parse(raw))
52
- if (params.snapshotSequence !== undefined && entry.writeSequence > params.snapshotSequence) {
53
- continue
54
- }
55
- result[fieldKey] = entry
56
- }
57
- return result
58
- }
59
-
60
- async currentRead(params: { runId: string; key?: string }): Promise<Record<string, PlanWorkspaceEntry>> {
61
- const redis = getRedisConnection()
62
- const hashKey = `${this.keyPrefix}${params.runId}`
63
- if (params.key) {
64
- // Support both raw field keys (e.g. 'nodeId:key') and plain keys
65
- const raw = await redis.hget(hashKey, params.key)
66
- if (!raw) return {}
67
- return { [params.key]: PlanWorkspaceEntrySchema.parse(JSON.parse(raw)) }
68
- }
69
- const all = await redis.hgetall(hashKey)
70
- const result: Record<string, PlanWorkspaceEntry> = {}
71
- for (const [k, v] of Object.entries(all)) {
72
- result[k] = PlanWorkspaceEntrySchema.parse(JSON.parse(v))
73
- }
74
- return result
75
- }
76
-
77
- async cleanup(runId: string): Promise<void> {
78
- const redis = getRedisConnection()
79
- await redis.del(`${this.keyPrefix}${runId}`)
80
- }
81
- }
82
-
83
- export const planWorkspaceService = new PlanWorkspaceService()
@@ -1,275 +0,0 @@
1
- import { parseRowMetadata, recordIdSchema, requireTimestamp, withCreatedAtMetadata } from '@lota-sdk/shared'
2
- import type { ChatMessage } from '@lota-sdk/shared'
3
- import { RecordId, surql } from 'surrealdb'
4
- import { z } from 'zod'
5
-
6
- import { agentDisplayNames } from '../config/agent-defaults'
7
- import { CursorRowSchema, listMessageHistoryPage } from '../db/cursor-pagination'
8
- import type { CursorPaginationConfig, MessageHistoryPage } from '../db/cursor-pagination'
9
- import { ensureRecordId, recordIdToString } from '../db/record-id'
10
- import type { RecordIdRef } from '../db/record-id'
11
- import { databaseService } from '../db/service'
12
- import { TABLES } from '../db/tables'
13
- import { ThreadMessageRowSchema } from '../db/thread-message-row'
14
- import type { ThreadMessageRow } from '../db/thread-message-row'
15
-
16
- const ThreadMessageExistingRowSchema = z.object({ id: recordIdSchema, createdAt: z.coerce.date() })
17
-
18
- function toMessageId(value: string | RecordIdRef): string {
19
- return recordIdToString(value, TABLES.THREAD_MESSAGE)
20
- }
21
-
22
- /**
23
- * Builds a collision-free row id by hashing the thread + message id pair.
24
- * Previous implementation replaced non-alphanumeric chars with '_', which was
25
- * lossy (e.g. "msg:foo" and "msg_foo" mapped to the same row id).
26
- * Now uses a 32-char SHA-256 hex prefix -- short enough for ergonomic ids,
27
- * long enough (128 bits) to make collisions negligible.
28
- */
29
- function toThreadMessageRowId(threadId: RecordIdRef, messageId: string): RecordId {
30
- const threadStr = recordIdToString(threadId, TABLES.THREAD)
31
- const digest = new Bun.CryptoHasher('sha256').update(`${threadStr}\0${messageId}`).digest('hex').slice(0, 32)
32
- return new RecordId(TABLES.THREAD_MESSAGE, digest)
33
- }
34
-
35
- function toThreadRef(threadId: RecordIdRef): RecordId {
36
- return ensureRecordId(threadId, TABLES.THREAD)
37
- }
38
-
39
- function toChatMessage(row: ThreadMessageRow): ChatMessage {
40
- const rowCreatedAt = requireTimestamp(row.createdAt)
41
- const metadata = withCreatedAtMetadata(parseRowMetadata(row.metadata), rowCreatedAt)
42
-
43
- return { id: row.messageId, role: row.role, parts: row.parts as ChatMessage['parts'], metadata }
44
- }
45
-
46
- const threadPaginationConfig: CursorPaginationConfig = {
47
- table: TABLES.THREAD_MESSAGE,
48
- parentFilterField: 'threadId',
49
- toRowId: toThreadMessageRowId,
50
- parseRow: (row: unknown) => ThreadMessageRowSchema.parse(row),
51
- toMessage: (row: unknown) => toChatMessage(ThreadMessageRowSchema.parse(row)),
52
- queryLatest: (parentId, limit) => surql`
53
- SELECT * FROM threadMessage
54
- WHERE threadId = ${parentId}
55
- ORDER BY createdAt DESC, id DESC
56
- LIMIT ${limit}
57
- `,
58
- queryBefore: (parentId, cursorCreatedAt, cursorId, limit) => surql`
59
- SELECT * FROM threadMessage
60
- WHERE threadId = ${parentId}
61
- AND (
62
- createdAt < ${cursorCreatedAt}
63
- OR (createdAt = ${cursorCreatedAt} AND id < ${cursorId})
64
- )
65
- ORDER BY createdAt DESC, id DESC
66
- LIMIT ${limit}
67
- `,
68
- }
69
-
70
- class ThreadMessageService {
71
- async upsertMessages(params: { threadId: RecordIdRef; messages: ChatMessage[] }): Promise<void> {
72
- const threadId = toThreadRef(params.threadId)
73
-
74
- const upsertPromises = params.messages.map(async (message) => {
75
- const messageId = message.id.trim()
76
- if (!messageId) return
77
-
78
- const role = message.role
79
- const parts = Array.isArray(message.parts)
80
- ? message.parts.map((part) => structuredClone(part) as Record<string, unknown>)
81
- : []
82
- if (parts.length === 0) {
83
- if (role === 'assistant') return
84
- throw new Error(`Refusing to persist thread message "${messageId}" with empty parts`)
85
- }
86
- const rowId = toThreadMessageRowId(threadId, messageId)
87
- const existingRow = await databaseService.findOne(
88
- TABLES.THREAD_MESSAGE,
89
- { threadId, messageId },
90
- ThreadMessageExistingRowSchema,
91
- )
92
- const persistedCreatedAt =
93
- existingRow === null ? requireTimestamp(message.metadata?.createdAt) : requireTimestamp(existingRow.createdAt)
94
- const metadata = withCreatedAtMetadata({ ...message.metadata, createdAt: persistedCreatedAt })
95
-
96
- await databaseService.upsert(
97
- TABLES.THREAD_MESSAGE,
98
- rowId,
99
- {
100
- threadId,
101
- messageId,
102
- role,
103
- parts,
104
- metadata,
105
- createdAt: existingRow ? existingRow.createdAt : new Date(persistedCreatedAt),
106
- },
107
- ThreadMessageRowSchema,
108
- { mutation: 'content' },
109
- )
110
- })
111
- await Promise.all(upsertPromises)
112
- }
113
-
114
- async listMessages(threadId: RecordIdRef): Promise<ChatMessage[]> {
115
- const threadRef = toThreadRef(threadId)
116
- const rows = await databaseService.query<unknown>(surql`
117
- SELECT * FROM threadMessage
118
- WHERE threadId = ${threadRef}
119
- ORDER BY createdAt ASC, id ASC
120
- `)
121
-
122
- return rows.map((row) => ThreadMessageRowSchema.parse(row)).map((row) => toChatMessage(row))
123
- }
124
-
125
- async listMessageHistoryPage(params: {
126
- threadId: RecordIdRef
127
- take: number
128
- beforeMessageId?: string
129
- }): Promise<MessageHistoryPage> {
130
- const threadRef = toThreadRef(params.threadId)
131
- return listMessageHistoryPage(threadPaginationConfig, {
132
- parentId: threadRef,
133
- take: params.take,
134
- beforeMessageId: params.beforeMessageId,
135
- })
136
- }
137
-
138
- async listMessagesAfterCursor(threadId: RecordIdRef, afterMessageId?: string): Promise<ChatMessage[]> {
139
- const threadRef = toThreadRef(threadId)
140
- const cursorMessageId = afterMessageId?.trim()
141
- if (!cursorMessageId) {
142
- return this.listMessages(threadRef)
143
- }
144
-
145
- const cursorRow = await databaseService.findOne(
146
- TABLES.THREAD_MESSAGE,
147
- { threadId: threadRef, messageId: cursorMessageId },
148
- CursorRowSchema,
149
- )
150
-
151
- if (!cursorRow) {
152
- throw new Error(`Thread cursor message not found: ${cursorMessageId}`)
153
- }
154
-
155
- const cursorCreatedAt = cursorRow.createdAt
156
- const cursorId = toThreadMessageRowId(threadRef, cursorMessageId)
157
- const rows = await databaseService.query<unknown>(surql`
158
- SELECT * FROM threadMessage
159
- WHERE threadId = ${threadRef}
160
- AND (
161
- createdAt > ${cursorCreatedAt}
162
- OR (createdAt = ${cursorCreatedAt} AND id > ${cursorId})
163
- )
164
- ORDER BY createdAt ASC, id ASC
165
- `)
166
-
167
- return rows.map((row) => ThreadMessageRowSchema.parse(row)).map((row) => toChatMessage(row))
168
- }
169
-
170
- async listRecentMessages(threadId: RecordIdRef, limit: number): Promise<ChatMessage[]> {
171
- const threadRef = toThreadRef(threadId)
172
- const rows = await databaseService.query<unknown>(surql`
173
- SELECT * FROM threadMessage
174
- WHERE threadId = ${threadRef}
175
- ORDER BY createdAt DESC, id DESC
176
- LIMIT ${Math.max(1, limit)}
177
- `)
178
-
179
- return rows
180
- .map((row) => ThreadMessageRowSchema.parse(row))
181
- .reverse()
182
- .map((row) => toChatMessage(row))
183
- }
184
-
185
- async searchMessages(params: {
186
- threadId: RecordIdRef
187
- role: 'user' | 'assistant'
188
- query: string
189
- limit: number
190
- }): Promise<Array<{ id: string; role: 'user' | 'assistant'; createdAt: string; content: string }>> {
191
- const normalizedQuery = params.query.trim().toLowerCase()
192
- if (!normalizedQuery) return []
193
-
194
- const messages = await this.listMessages(toThreadRef(params.threadId))
195
- return messages
196
- .filter((message) => message.role === params.role)
197
- .map((message) => ({
198
- id: message.id,
199
- role: message.role as 'user' | 'assistant',
200
- createdAt: new Date(requireTimestamp(message.metadata?.createdAt)).toISOString(),
201
- content: message.parts
202
- .flatMap((part) => (part.type === 'text' && typeof part.text === 'string' ? [part.text] : []))
203
- .join('\n')
204
- .trim(),
205
- }))
206
- .filter((item) => item.content.length > 0 && item.content.toLowerCase().includes(normalizedQuery))
207
- .slice(-Math.max(1, params.limit))
208
- }
209
-
210
- async addUserMessage(params: {
211
- messageId: RecordIdRef
212
- threadId: RecordIdRef
213
- content: string
214
- }): Promise<ChatMessage> {
215
- const threadRef = toThreadRef(params.threadId)
216
- const message: ChatMessage = {
217
- id: toMessageId(params.messageId),
218
- role: 'user',
219
- parts: [{ type: 'text', text: params.content }],
220
- metadata: { createdAt: Date.now() },
221
- }
222
-
223
- await this.upsertMessages({ threadId: threadRef, messages: [message] })
224
- return message
225
- }
226
-
227
- async addAgentMessage(params: {
228
- messageId: RecordIdRef
229
- threadId: RecordIdRef
230
- parts: ChatMessage['parts']
231
- metadata?: ChatMessage['metadata']
232
- }): Promise<ChatMessage> {
233
- const threadRef = toThreadRef(params.threadId)
234
- const message: ChatMessage = {
235
- id: toMessageId(params.messageId),
236
- role: 'assistant',
237
- parts: params.parts,
238
- metadata: withCreatedAtMetadata(params.metadata, Date.now()),
239
- }
240
-
241
- await this.upsertMessages({ threadId: threadRef, messages: [message] })
242
- return message
243
- }
244
-
245
- async ensureBootstrapWelcomeMessage(params: { threadId: RecordIdRef; agentId: string; text: string }): Promise<void> {
246
- const threadRef = toThreadRef(params.threadId)
247
- const existingRow = await databaseService.findOne(
248
- TABLES.THREAD_MESSAGE,
249
- { threadId: threadRef },
250
- ThreadMessageExistingRowSchema,
251
- )
252
- if (existingRow) return
253
-
254
- const messageText = params.text.trim()
255
- if (!messageText) return
256
-
257
- await this.upsertMessages({
258
- threadId: threadRef,
259
- messages: [
260
- {
261
- id: Bun.randomUUIDv7(),
262
- role: 'assistant',
263
- parts: [{ type: 'text', text: messageText }],
264
- metadata: {
265
- agentId: params.agentId,
266
- agentName: agentDisplayNames[params.agentId] ?? params.agentId,
267
- createdAt: Date.now(),
268
- },
269
- },
270
- ],
271
- })
272
- }
273
- }
274
-
275
- export const threadMessageService = new ThreadMessageService()
@@ -1,22 +0,0 @@
1
- import type { PlanRunRecord, SerializableExecutionPlan } from '@lota-sdk/shared'
2
-
3
- import type { RecordIdInput } from '../db/record-id'
4
- import { planRunService } from './plan-run.service'
5
-
6
- class ThreadPlanRegistryService {
7
- async listActiveRuns(threadId: RecordIdInput): Promise<PlanRunRecord[]> {
8
- return planRunService.getActiveRunRecords(threadId)
9
- }
10
-
11
- async countActiveRuns(threadId: RecordIdInput): Promise<number> {
12
- const runs = await this.listActiveRuns(threadId)
13
- return runs.length
14
- }
15
-
16
- async listActivePlans(threadId: RecordIdInput): Promise<SerializableExecutionPlan[]> {
17
- const runs = await this.listActiveRuns(threadId)
18
- return Promise.all(runs.map((run) => planRunService.toSerializablePlan(run)))
19
- }
20
- }
21
-
22
- export const threadPlanRegistryService = new ThreadPlanRegistryService()
@@ -1,39 +0,0 @@
1
- import { THREAD } from '@lota-sdk/shared'
2
-
3
- import { chatLogger } from '../config/logger'
4
- import type { RecordIdRef } from '../db/record-id'
5
- import { createHelperModelRuntime } from '../runtime/helper-model'
6
- import { deriveTitle, limitTitleWords, normalizeTitle } from '../runtime/title-helpers'
7
- import { createThreadTitleGeneratorAgent, THREAD_TITLE_GENERATOR_PROMPT } from '../system-agents/title-generator.agent'
8
- import { threadService } from './thread.service'
9
-
10
- const THREAD_TITLE_TIMEOUT_MS = 30_000
11
-
12
- class ThreadTitleService {
13
- helperRuntime = createHelperModelRuntime()
14
-
15
- async generateAndPersistTitle(threadId: RecordIdRef, sourceText: string): Promise<void> {
16
- let title = ''
17
- try {
18
- title = normalizeTitle(
19
- await this.helperRuntime.generateHelperText({
20
- tag: 'thread-title',
21
- createAgent: createThreadTitleGeneratorAgent,
22
- defaultSystemPrompt: THREAD_TITLE_GENERATOR_PROMPT,
23
- timeoutMs: THREAD_TITLE_TIMEOUT_MS,
24
- messages: [{ role: 'user', content: sourceText }],
25
- }),
26
- )
27
- } catch (error) {
28
- chatLogger.warn`Failed to generate thread title via LLM (non-fatal): ${error}`
29
- }
30
-
31
- if (!title) {
32
- title = limitTitleWords(deriveTitle(sourceText || THREAD.DEFAULT_TITLE))
33
- }
34
-
35
- await threadService.update(threadId, { title, nameGenerated: true })
36
- }
37
- }
38
-
39
- export const threadTitleService = new ThreadTitleService()