@lota-sdk/core 0.4.8 → 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/thread-defaults.ts +33 -21
  9. package/src/create-runtime.ts +725 -387
  10. package/src/db/base.service.ts +52 -28
  11. package/src/db/cursor-pagination.ts +71 -30
  12. package/src/db/memory-store.helpers.ts +4 -7
  13. package/src/db/memory-store.ts +856 -598
  14. package/src/db/memory.ts +398 -275
  15. package/src/db/record-id.ts +32 -10
  16. package/src/db/schema-fingerprint.ts +30 -12
  17. package/src/db/service-normalization.ts +255 -0
  18. package/src/db/service.ts +726 -761
  19. package/src/db/startup.ts +140 -66
  20. package/src/db/transaction-conflict.ts +15 -0
  21. package/src/effect/awaitable-effect.ts +87 -0
  22. package/src/effect/errors.ts +121 -0
  23. package/src/effect/helpers.ts +98 -0
  24. package/src/effect/index.ts +22 -0
  25. package/src/effect/layers.ts +228 -0
  26. package/src/effect/runtime-ref.ts +25 -0
  27. package/src/effect/runtime.ts +31 -0
  28. package/src/effect/services.ts +57 -0
  29. package/src/effect/zod.ts +43 -0
  30. package/src/embeddings/provider.ts +122 -76
  31. package/src/index.ts +46 -1
  32. package/src/openrouter/direct-provider.ts +11 -35
  33. package/src/queues/autonomous-job.queue.ts +130 -74
  34. package/src/queues/context-compaction.queue.ts +60 -15
  35. package/src/queues/delayed-node-promotion.queue.ts +52 -15
  36. package/src/queues/document-processor.queue.ts +52 -77
  37. package/src/queues/memory-consolidation.queue.ts +47 -32
  38. package/src/queues/organization-learning.queue.ts +13 -4
  39. package/src/queues/plan-agent-heartbeat.queue.ts +65 -21
  40. package/src/queues/plan-scheduler.queue.ts +107 -31
  41. package/src/queues/post-chat-memory.queue.ts +66 -24
  42. package/src/queues/queue-factory.ts +142 -52
  43. package/src/queues/standalone-worker.ts +39 -0
  44. package/src/queues/title-generation.queue.ts +54 -9
  45. package/src/redis/connection.ts +84 -32
  46. package/src/redis/index.ts +6 -8
  47. package/src/redis/org-memory-lock.ts +60 -27
  48. package/src/redis/redis-lease-lock.ts +200 -121
  49. package/src/redis/runtime-connection.ts +10 -0
  50. package/src/redis/stream-context.ts +84 -46
  51. package/src/runtime/agent-identity-overrides.ts +2 -2
  52. package/src/runtime/agent-runtime-policy.ts +4 -1
  53. package/src/runtime/agent-stream-helpers.ts +20 -9
  54. package/src/runtime/chat-run-orchestration.ts +102 -19
  55. package/src/runtime/chat-run-registry.ts +36 -2
  56. package/src/runtime/context-compaction/context-compaction-runtime.ts +107 -0
  57. package/src/runtime/{context-compaction.ts → context-compaction/context-compaction.ts} +114 -91
  58. package/src/runtime/execution-plan-visibility.ts +2 -2
  59. package/src/runtime/execution-plan.ts +42 -15
  60. package/src/runtime/graph-designer.ts +11 -7
  61. package/src/runtime/helper-model.ts +135 -48
  62. package/src/runtime/index.ts +7 -7
  63. package/src/runtime/indexed-repositories-policy.ts +3 -3
  64. package/src/runtime/{memory-block.ts → memory/memory-block.ts} +40 -36
  65. package/src/runtime/{memory-digest-policy.ts → memory/memory-digest-policy.ts} +1 -1
  66. package/src/runtime/{memory-pipeline.ts → memory/memory-pipeline.ts} +1 -1
  67. package/src/runtime/{memory-prompts-fact.ts → memory/memory-prompts-fact.ts} +2 -2
  68. package/src/runtime/{memory-scope.ts → memory/memory-scope.ts} +12 -6
  69. package/src/runtime/plugin-resolution.ts +144 -24
  70. package/src/runtime/plugin-types.ts +9 -1
  71. package/src/runtime/post-turn-side-effects.ts +197 -130
  72. package/src/runtime/retrieval-adapters.ts +38 -4
  73. package/src/runtime/runtime-config.ts +150 -61
  74. package/src/runtime/runtime-extensions.ts +21 -34
  75. package/src/runtime/social-chat/social-chat-agent-runner.ts +157 -0
  76. package/src/runtime/{social-chat-history.ts → social-chat/social-chat-history.ts} +42 -20
  77. package/src/runtime/social-chat/social-chat.ts +594 -0
  78. package/src/runtime/specialist-runner.ts +36 -10
  79. package/src/runtime/team-consultation/team-consultation-orchestrator.ts +427 -0
  80. package/src/runtime/{team-consultation-prompts.ts → team-consultation/team-consultation-prompts.ts} +6 -2
  81. package/src/runtime/thread-chat-helpers.ts +2 -2
  82. package/src/runtime/thread-plan-turn.ts +2 -1
  83. package/src/runtime/thread-turn-context.ts +172 -94
  84. package/src/runtime/turn-lifecycle.ts +93 -27
  85. package/src/services/agent-activity.service.ts +287 -203
  86. package/src/services/agent-executor.service.ts +329 -217
  87. package/src/services/artifact.service.ts +225 -148
  88. package/src/services/attachment.service.ts +137 -115
  89. package/src/services/autonomous-job.service.ts +888 -491
  90. package/src/services/chat-run-registry.service.ts +11 -1
  91. package/src/services/context-compaction.service.ts +136 -86
  92. package/src/services/document-chunk.service.ts +162 -90
  93. package/src/services/execution-plan/execution-plan-approval.ts +26 -0
  94. package/src/services/execution-plan/execution-plan-context.ts +29 -0
  95. package/src/services/execution-plan/execution-plan-graph.ts +256 -0
  96. package/src/services/execution-plan/execution-plan-schedule.ts +84 -0
  97. package/src/services/execution-plan/execution-plan-spec.ts +75 -0
  98. package/src/services/execution-plan/execution-plan.service.ts +1041 -0
  99. package/src/services/feedback-loop.service.ts +132 -76
  100. package/src/services/global-orchestrator.service.ts +80 -170
  101. package/src/services/graph-full-routing.ts +182 -0
  102. package/src/services/index.ts +18 -21
  103. package/src/services/institutional-memory.service.ts +220 -123
  104. package/src/services/learned-skill.service.ts +364 -259
  105. package/src/services/memory/memory-conversation.ts +95 -0
  106. package/src/services/memory/memory-org-memory.ts +39 -0
  107. package/src/services/memory/memory-preseeded.ts +80 -0
  108. package/src/services/memory/memory-rerank.ts +297 -0
  109. package/src/services/{memory-utils.ts → memory/memory-utils.ts} +5 -5
  110. package/src/services/memory/memory.service.ts +692 -0
  111. package/src/services/memory/rerank.service.ts +209 -0
  112. package/src/services/monitoring-window.service.ts +92 -70
  113. package/src/services/mutating-approval.service.ts +62 -53
  114. package/src/services/node-workspace.service.ts +141 -98
  115. package/src/services/notification.service.ts +17 -16
  116. package/src/services/organization-member.service.ts +120 -66
  117. package/src/services/organization.service.ts +144 -51
  118. package/src/services/ownership-dispatcher.service.ts +415 -264
  119. package/src/services/plan/plan-agent-heartbeat.service.ts +234 -0
  120. package/src/services/plan/plan-agent-query.service.ts +322 -0
  121. package/src/services/plan/plan-approval.service.ts +102 -0
  122. package/src/services/plan/plan-artifact.service.ts +60 -0
  123. package/src/services/plan/plan-builder.service.ts +76 -0
  124. package/src/services/plan/plan-checkpoint.service.ts +103 -0
  125. package/src/services/{plan-compiler.service.ts → plan/plan-compiler.service.ts} +26 -9
  126. package/src/services/plan/plan-completion-side-effects.ts +175 -0
  127. package/src/services/plan/plan-coordination.service.ts +181 -0
  128. package/src/services/plan/plan-cycle.service.ts +398 -0
  129. package/src/services/plan/plan-deadline.service.ts +547 -0
  130. package/src/services/plan/plan-event-delivery.service.ts +261 -0
  131. package/src/services/plan/plan-executor-context.ts +35 -0
  132. package/src/services/plan/plan-executor-graph.ts +475 -0
  133. package/src/services/plan/plan-executor-helpers.ts +322 -0
  134. package/src/services/plan/plan-executor-persistence.ts +209 -0
  135. package/src/services/plan/plan-executor.service.ts +1654 -0
  136. package/src/services/{plan-helpers.ts → plan/plan-helpers.ts} +1 -1
  137. package/src/services/{plan-run-data.ts → plan/plan-run-data.ts} +4 -4
  138. package/src/services/plan/plan-run-serialization.ts +15 -0
  139. package/src/services/plan/plan-run.service.ts +644 -0
  140. package/src/services/plan/plan-scheduler.service.ts +385 -0
  141. package/src/services/plan/plan-template.service.ts +224 -0
  142. package/src/services/plan/plan-transaction-events.ts +33 -0
  143. package/src/services/plan/plan-validator.service.ts +907 -0
  144. package/src/services/plan/plan-workspace.service.ts +125 -0
  145. package/src/services/plugin-executor.service.ts +97 -68
  146. package/src/services/quality-metrics.service.ts +112 -94
  147. package/src/services/queue-job.service.ts +296 -230
  148. package/src/services/recent-activity-title.service.ts +65 -36
  149. package/src/services/recent-activity.service.ts +274 -259
  150. package/src/services/skill-resolver.service.ts +38 -12
  151. package/src/services/social-chat-history.service.ts +176 -125
  152. package/src/services/system-executor.service.ts +91 -61
  153. package/src/services/thread/thread-active-run.ts +203 -0
  154. package/src/services/thread/thread-bootstrap.ts +369 -0
  155. package/src/services/thread/thread-listing.ts +198 -0
  156. package/src/services/thread/thread-memory-block.ts +117 -0
  157. package/src/services/thread/thread-message.service.ts +363 -0
  158. package/src/services/thread/thread-record-store.ts +155 -0
  159. package/src/services/thread/thread-title.service.ts +74 -0
  160. package/src/services/thread/thread-turn-execution.ts +280 -0
  161. package/src/services/thread/thread-turn-message-context.ts +73 -0
  162. package/src/services/thread/thread-turn-preparation.service.ts +1146 -0
  163. package/src/services/thread/thread-turn-streaming.ts +402 -0
  164. package/src/services/thread/thread-turn-tracing.ts +35 -0
  165. package/src/services/thread/thread-turn.ts +343 -0
  166. package/src/services/thread/thread.service.ts +335 -0
  167. package/src/services/user.service.ts +82 -32
  168. package/src/services/write-intent-validator.service.ts +63 -51
  169. package/src/storage/attachment-parser.ts +69 -27
  170. package/src/storage/attachment-storage.service.ts +331 -275
  171. package/src/storage/generated-document-storage.service.ts +66 -34
  172. package/src/system-agents/agent-result.ts +3 -1
  173. package/src/system-agents/context-compaction.agent.ts +2 -2
  174. package/src/system-agents/delegated-agent-factory.ts +159 -90
  175. package/src/system-agents/memory-reranker.agent.ts +2 -2
  176. package/src/system-agents/memory.agent.ts +2 -2
  177. package/src/system-agents/recent-activity-title-refiner.agent.ts +2 -2
  178. package/src/system-agents/regular-chat-memory-digest.agent.ts +2 -2
  179. package/src/system-agents/skill-extractor.agent.ts +2 -2
  180. package/src/system-agents/skill-manager.agent.ts +2 -2
  181. package/src/system-agents/thread-router.agent.ts +157 -113
  182. package/src/system-agents/title-generator.agent.ts +2 -2
  183. package/src/tools/execution-plan.tool.ts +220 -161
  184. package/src/tools/fetch-webpage.tool.ts +21 -17
  185. package/src/tools/firecrawl-client.ts +16 -6
  186. package/src/tools/index.ts +1 -0
  187. package/src/tools/memory-block.tool.ts +14 -6
  188. package/src/tools/plan-approval.tool.ts +49 -47
  189. package/src/tools/read-file-parts.tool.ts +44 -33
  190. package/src/tools/remember-memory.tool.ts +65 -45
  191. package/src/tools/search-web.tool.ts +26 -22
  192. package/src/tools/search.tool.ts +41 -29
  193. package/src/tools/team-think.tool.ts +124 -83
  194. package/src/tools/user-questions.tool.ts +4 -3
  195. package/src/tools/web-tool-shared.ts +6 -0
  196. package/src/utils/async.ts +17 -23
  197. package/src/utils/crypto.ts +21 -0
  198. package/src/utils/date-time.ts +40 -1
  199. package/src/utils/errors.ts +95 -16
  200. package/src/utils/hono-error-handler.ts +24 -39
  201. package/src/utils/index.ts +2 -1
  202. package/src/utils/null-proto-record.ts +41 -0
  203. package/src/utils/sse-keepalive.ts +124 -21
  204. package/src/workers/bootstrap.ts +186 -51
  205. package/src/workers/memory-consolidation.worker.ts +325 -237
  206. package/src/workers/organization-learning.worker.ts +50 -16
  207. package/src/workers/regular-chat-memory-digest.helpers.ts +28 -27
  208. package/src/workers/regular-chat-memory-digest.runner.ts +175 -114
  209. package/src/workers/skill-extraction.runner.ts +176 -93
  210. package/src/workers/utils/file-section-chunker.ts +8 -10
  211. package/src/workers/utils/repo-structure-extractor.ts +349 -260
  212. package/src/workers/utils/repomix-file-sections.ts +2 -2
  213. package/src/workers/utils/thread-message-query.ts +97 -38
  214. package/src/workers/worker-utils.ts +56 -31
  215. package/src/config/debug-logger.ts +0 -47
  216. package/src/redis/connection-accessor.ts +0 -26
  217. package/src/runtime/context-compaction-runtime.ts +0 -87
  218. package/src/runtime/social-chat-agent-runner.ts +0 -118
  219. package/src/runtime/social-chat.ts +0 -516
  220. package/src/runtime/team-consultation-orchestrator.ts +0 -272
  221. package/src/services/adaptive-playbook.service.ts +0 -152
  222. package/src/services/artifact-provenance.service.ts +0 -172
  223. package/src/services/chat-attachments.service.ts +0 -17
  224. package/src/services/context-compaction-runtime.singleton.ts +0 -13
  225. package/src/services/execution-plan.service.ts +0 -1118
  226. package/src/services/memory.service.ts +0 -914
  227. package/src/services/plan-agent-heartbeat.service.ts +0 -136
  228. package/src/services/plan-agent-query.service.ts +0 -267
  229. package/src/services/plan-approval.service.ts +0 -83
  230. package/src/services/plan-artifact.service.ts +0 -50
  231. package/src/services/plan-builder.service.ts +0 -67
  232. package/src/services/plan-checkpoint.service.ts +0 -81
  233. package/src/services/plan-completion-side-effects.ts +0 -80
  234. package/src/services/plan-coordination.service.ts +0 -157
  235. package/src/services/plan-cycle.service.ts +0 -284
  236. package/src/services/plan-deadline.service.ts +0 -430
  237. package/src/services/plan-event-delivery.service.ts +0 -166
  238. package/src/services/plan-executor.service.ts +0 -1950
  239. package/src/services/plan-run.service.ts +0 -515
  240. package/src/services/plan-scheduler.service.ts +0 -240
  241. package/src/services/plan-template.service.ts +0 -177
  242. package/src/services/plan-validator.service.ts +0 -818
  243. package/src/services/plan-workspace.service.ts +0 -83
  244. package/src/services/rerank.service.ts +0 -156
  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,156 +0,0 @@
1
- import { z } from 'zod'
2
-
3
- import { OPENROUTER_FAST_RERANK_MODEL_ID } from '../config/model-constants'
4
- import { normalizeDirectOpenRouterModelId, resolveOpenRouterApiKey } from '../openrouter/direct-provider'
5
- import { getRuntimeConfig } from '../runtime/runtime-config'
6
-
7
- const OPENROUTER_RERANK_URL = 'https://openrouter.ai/api/v1/rerank' as const
8
-
9
- const RerankResponseSchema = z
10
- .object({
11
- model: z.string().optional(),
12
- results: z.array(
13
- z
14
- .object({
15
- index: z.number().int().nonnegative().optional(),
16
- relevance_score: z.number().optional(),
17
- document: z.union([z.string(), z.object({ text: z.string().optional() }).passthrough()]).optional(),
18
- })
19
- .passthrough(),
20
- ),
21
- usage: z.object({ search_units: z.number().optional(), cost: z.number().optional() }).passthrough().optional(),
22
- })
23
- .passthrough()
24
-
25
- export interface RerankDocument {
26
- id: string
27
- text: string
28
- }
29
-
30
- export interface RerankResultItem extends RerankDocument {
31
- index: number
32
- relevanceScore: number | null
33
- }
34
-
35
- export interface RerankUsage {
36
- searchUnits?: number
37
- cost?: number
38
- }
39
-
40
- export interface RerankResult {
41
- modelId: string
42
- results: RerankResultItem[]
43
- usage?: RerankUsage
44
- }
45
-
46
- function clampTopN(topN: number | undefined, total: number): number {
47
- if (!Number.isFinite(topN)) return total
48
- return Math.max(1, Math.min(total, Math.trunc(topN as number)))
49
- }
50
-
51
- function readConfiguredRerankModelId(): string | null {
52
- try {
53
- return getRuntimeConfig().memory.rerankerModelId.trim()
54
- } catch {
55
- return null
56
- }
57
- }
58
-
59
- function resolveRerankModelId(modelId?: string): string {
60
- const explicit = modelId?.trim()
61
- if (explicit) return normalizeDirectOpenRouterModelId(explicit)
62
-
63
- const configured = readConfiguredRerankModelId()
64
- if (configured) return normalizeDirectOpenRouterModelId(configured)
65
-
66
- return OPENROUTER_FAST_RERANK_MODEL_ID
67
- }
68
-
69
- function matchRerankedDocument(
70
- candidate: z.infer<typeof RerankResponseSchema>['results'][number],
71
- documents: RerankDocument[],
72
- ): RerankDocument | null {
73
- if (typeof candidate.index === 'number' && candidate.index >= 0 && candidate.index < documents.length) {
74
- return documents[candidate.index] ?? null
75
- }
76
-
77
- const text =
78
- typeof candidate.document === 'string'
79
- ? candidate.document
80
- : typeof candidate.document?.text === 'string'
81
- ? candidate.document.text
82
- : null
83
- if (!text) return null
84
-
85
- return documents.find((document) => document.text === text) ?? null
86
- }
87
-
88
- class RerankService {
89
- async rerankDocuments(params: {
90
- query: string
91
- documents: RerankDocument[]
92
- topN?: number
93
- modelId?: string
94
- }): Promise<RerankResult> {
95
- if (params.documents.length === 0) {
96
- return { modelId: resolveRerankModelId(params.modelId), results: [] }
97
- }
98
-
99
- const apiKey = resolveOpenRouterApiKey()
100
- const modelId = resolveRerankModelId(params.modelId)
101
- const topN = clampTopN(params.topN, params.documents.length)
102
-
103
- const response = await fetch(OPENROUTER_RERANK_URL, {
104
- method: 'POST',
105
- headers: { Authorization: `Bearer ${apiKey}`, 'Content-Type': 'application/json' },
106
- body: JSON.stringify({
107
- model: modelId,
108
- query: params.query,
109
- documents: params.documents.map((document) => document.text),
110
- top_n: topN,
111
- }),
112
- })
113
-
114
- const responseText = await response.text()
115
- let payload: unknown = responseText
116
- try {
117
- payload = JSON.parse(responseText)
118
- } catch {}
119
-
120
- if (!response.ok) {
121
- throw new Error(`OpenRouter rerank failed (${response.status}): ${responseText}`)
122
- }
123
-
124
- const parsed = RerankResponseSchema.parse(payload)
125
- const seen = new Set<string>()
126
- const results: RerankResultItem[] = []
127
-
128
- for (const item of parsed.results) {
129
- const matched = matchRerankedDocument(item, params.documents)
130
- if (!matched || seen.has(matched.id)) continue
131
- seen.add(matched.id)
132
-
133
- results.push({
134
- id: matched.id,
135
- text: matched.text,
136
- index: item.index ?? params.documents.findIndex((document) => document.id === matched.id),
137
- relevanceScore: typeof item.relevance_score === 'number' ? item.relevance_score : null,
138
- })
139
-
140
- if (results.length >= topN) break
141
- }
142
-
143
- return {
144
- modelId: parsed.model?.trim() || modelId,
145
- results,
146
- usage: parsed.usage
147
- ? {
148
- ...(parsed.usage.search_units !== undefined ? { searchUnits: parsed.usage.search_units } : {}),
149
- ...(parsed.usage.cost !== undefined ? { cost: parsed.usage.cost } : {}),
150
- }
151
- : undefined,
152
- }
153
- }
154
- }
155
-
156
- export const rerankService = new RerankService()
@@ -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()