@lota-sdk/core 0.4.8 → 0.4.10

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 (272) hide show
  1. package/package.json +11 -12
  2. package/src/ai/embedding-cache.ts +96 -22
  3. package/src/ai-gateway/ai-gateway.ts +766 -223
  4. package/src/config/agent-defaults.ts +189 -75
  5. package/src/config/agent-types.ts +54 -4
  6. package/src/config/background-processing.ts +1 -1
  7. package/src/config/constants.ts +8 -2
  8. package/src/config/index.ts +0 -1
  9. package/src/config/logger.ts +299 -19
  10. package/src/config/thread-defaults.ts +40 -20
  11. package/src/create-runtime.ts +200 -449
  12. package/src/db/base.service.ts +52 -28
  13. package/src/db/cursor-pagination.ts +71 -30
  14. package/src/db/memory-query-builder.ts +2 -1
  15. package/src/db/memory-store.helpers.ts +4 -7
  16. package/src/db/memory-store.ts +868 -601
  17. package/src/db/memory.ts +396 -280
  18. package/src/db/record-id.ts +32 -10
  19. package/src/db/schema-fingerprint.ts +30 -12
  20. package/src/db/service-normalization.ts +288 -0
  21. package/src/db/service.ts +912 -779
  22. package/src/db/startup.ts +153 -68
  23. package/src/db/transaction-conflict.ts +15 -0
  24. package/src/effect/awaitable-effect.ts +96 -0
  25. package/src/effect/errors.ts +121 -0
  26. package/src/effect/helpers.ts +123 -0
  27. package/src/effect/index.ts +24 -0
  28. package/src/effect/layers.ts +238 -0
  29. package/src/effect/runtime-ref.ts +25 -0
  30. package/src/effect/runtime.ts +46 -0
  31. package/src/effect/services.ts +61 -0
  32. package/src/effect/zod.ts +43 -0
  33. package/src/embeddings/provider.ts +128 -83
  34. package/src/index.ts +48 -1
  35. package/src/openrouter/direct-provider.ts +11 -35
  36. package/src/queues/autonomous-job.queue.ts +117 -73
  37. package/src/queues/context-compaction.queue.ts +50 -17
  38. package/src/queues/delayed-node-promotion.queue.ts +46 -17
  39. package/src/queues/document-processor.queue.ts +52 -77
  40. package/src/queues/memory-consolidation.queue.ts +47 -32
  41. package/src/queues/organization-learning.queue.ts +26 -4
  42. package/src/queues/plan-agent-heartbeat.queue.ts +71 -24
  43. package/src/queues/plan-scheduler.queue.ts +97 -33
  44. package/src/queues/post-chat-memory.queue.ts +56 -26
  45. package/src/queues/queue-factory.ts +227 -59
  46. package/src/queues/standalone-worker.ts +39 -0
  47. package/src/queues/title-generation.queue.ts +45 -11
  48. package/src/redis/connection.ts +182 -113
  49. package/src/redis/index.ts +6 -8
  50. package/src/redis/org-memory-lock.ts +60 -27
  51. package/src/redis/redis-lease-lock.ts +200 -121
  52. package/src/redis/runtime-connection.ts +20 -0
  53. package/src/redis/stream-context.ts +92 -46
  54. package/src/runtime/agent-identity-overrides.ts +2 -2
  55. package/src/runtime/agent-runtime-policy.ts +5 -2
  56. package/src/runtime/agent-stream-helpers.ts +24 -9
  57. package/src/runtime/chat-run-orchestration.ts +102 -19
  58. package/src/runtime/chat-run-registry.ts +36 -2
  59. package/src/runtime/context-compaction/context-compaction-runtime.ts +107 -0
  60. package/src/runtime/{context-compaction.ts → context-compaction/context-compaction.ts} +161 -94
  61. package/src/runtime/domain-layer.ts +192 -0
  62. package/src/runtime/execution-plan-visibility.ts +2 -2
  63. package/src/runtime/execution-plan.ts +42 -15
  64. package/src/runtime/graph-designer.ts +16 -4
  65. package/src/runtime/helper-model.ts +139 -48
  66. package/src/runtime/index.ts +7 -8
  67. package/src/runtime/indexed-repositories-policy.ts +3 -3
  68. package/src/runtime/{memory-block.ts → memory/memory-block.ts} +50 -36
  69. package/src/runtime/{memory-digest-policy.ts → memory/memory-digest-policy.ts} +1 -1
  70. package/src/runtime/{memory-pipeline.ts → memory/memory-pipeline.ts} +54 -67
  71. package/src/runtime/{memory-prompts-fact.ts → memory/memory-prompts-fact.ts} +2 -2
  72. package/src/runtime/memory/memory-scope.ts +53 -0
  73. package/src/runtime/plugin-resolution.ts +124 -25
  74. package/src/runtime/plugin-types.ts +9 -1
  75. package/src/runtime/post-turn-side-effects.ts +177 -130
  76. package/src/runtime/retrieval-adapters.ts +40 -6
  77. package/src/runtime/runtime-accessors.ts +92 -0
  78. package/src/runtime/runtime-config.ts +150 -61
  79. package/src/runtime/runtime-extensions.ts +23 -25
  80. package/src/runtime/runtime-lifecycle.ts +124 -0
  81. package/src/runtime/runtime-services.ts +386 -0
  82. package/src/runtime/runtime-token.ts +47 -0
  83. package/src/runtime/social-chat/social-chat-agent-runner.ts +159 -0
  84. package/src/runtime/{social-chat-history.ts → social-chat/social-chat-history.ts} +51 -20
  85. package/src/runtime/social-chat/social-chat.ts +630 -0
  86. package/src/runtime/specialist-runner.ts +36 -10
  87. package/src/runtime/team-consultation/team-consultation-orchestrator.ts +433 -0
  88. package/src/runtime/{team-consultation-prompts.ts → team-consultation/team-consultation-prompts.ts} +6 -2
  89. package/src/runtime/thread-chat-helpers.ts +2 -2
  90. package/src/runtime/thread-plan-turn.ts +2 -1
  91. package/src/runtime/thread-turn-context.ts +183 -111
  92. package/src/runtime/turn-lifecycle.ts +93 -27
  93. package/src/services/agent-activity.service.ts +287 -203
  94. package/src/services/agent-executor.service.ts +253 -149
  95. package/src/services/artifact.service.ts +231 -149
  96. package/src/services/attachment.service.ts +171 -115
  97. package/src/services/autonomous-job.service.ts +890 -491
  98. package/src/services/background-work.service.ts +54 -0
  99. package/src/services/chat-run-registry.service.ts +13 -1
  100. package/src/services/context-compaction.service.ts +136 -86
  101. package/src/services/document-chunk.service.ts +151 -88
  102. package/src/services/execution-plan/execution-plan-approval.ts +26 -0
  103. package/src/services/execution-plan/execution-plan-context.ts +29 -0
  104. package/src/services/execution-plan/execution-plan-graph.ts +278 -0
  105. package/src/services/execution-plan/execution-plan-schedule.ts +84 -0
  106. package/src/services/execution-plan/execution-plan-spec.ts +75 -0
  107. package/src/services/execution-plan/execution-plan.service.ts +1041 -0
  108. package/src/services/feedback-loop.service.ts +132 -76
  109. package/src/services/global-orchestrator.service.ts +101 -168
  110. package/src/services/graph-full-routing.ts +193 -0
  111. package/src/services/index.ts +19 -21
  112. package/src/services/institutional-memory.service.ts +213 -125
  113. package/src/services/learned-skill.service.ts +368 -260
  114. package/src/services/memory/memory-conversation.ts +95 -0
  115. package/src/services/memory/memory-errors.ts +27 -0
  116. package/src/services/memory/memory-org-memory.ts +50 -0
  117. package/src/services/memory/memory-preseeded.ts +86 -0
  118. package/src/services/memory/memory-rerank.ts +297 -0
  119. package/src/services/{memory-utils.ts → memory/memory-utils.ts} +6 -5
  120. package/src/services/memory/memory.service.ts +674 -0
  121. package/src/services/memory/rerank.service.ts +201 -0
  122. package/src/services/monitoring-window.service.ts +92 -70
  123. package/src/services/mutating-approval.service.ts +62 -53
  124. package/src/services/node-workspace.service.ts +141 -98
  125. package/src/services/notification.service.ts +29 -16
  126. package/src/services/organization-member.service.ts +120 -66
  127. package/src/services/organization.service.ts +153 -77
  128. package/src/services/ownership-dispatcher.service.ts +456 -263
  129. package/src/services/plan/plan-agent-heartbeat.service.ts +234 -0
  130. package/src/services/plan/plan-agent-query.service.ts +322 -0
  131. package/src/services/{plan-approval.service.ts → plan/plan-approval.service.ts} +45 -22
  132. package/src/services/plan/plan-artifact.service.ts +60 -0
  133. package/src/services/plan/plan-builder.service.ts +76 -0
  134. package/src/services/plan/plan-checkpoint.service.ts +103 -0
  135. package/src/services/{plan-compiler.service.ts → plan/plan-compiler.service.ts} +26 -9
  136. package/src/services/plan/plan-completion-side-effects.ts +169 -0
  137. package/src/services/plan/plan-coordination.service.ts +181 -0
  138. package/src/services/plan/plan-cycle.service.ts +405 -0
  139. package/src/services/plan/plan-deadline.service.ts +533 -0
  140. package/src/services/plan/plan-event-delivery.service.ts +266 -0
  141. package/src/services/plan/plan-executor-context.ts +35 -0
  142. package/src/services/plan/plan-executor-graph.ts +522 -0
  143. package/src/services/plan/plan-executor-helpers.ts +307 -0
  144. package/src/services/plan/plan-executor-persistence.ts +209 -0
  145. package/src/services/plan/plan-executor.service.ts +1737 -0
  146. package/src/services/{plan-helpers.ts → plan/plan-helpers.ts} +1 -1
  147. package/src/services/{plan-run-data.ts → plan/plan-run-data.ts} +4 -4
  148. package/src/services/plan/plan-run-serialization.ts +15 -0
  149. package/src/services/plan/plan-run.service.ts +637 -0
  150. package/src/services/plan/plan-scheduler.service.ts +379 -0
  151. package/src/services/plan/plan-template.service.ts +224 -0
  152. package/src/services/plan/plan-transaction-events.ts +36 -0
  153. package/src/services/plan/plan-validator.service.ts +907 -0
  154. package/src/services/plan/plan-workspace.service.ts +131 -0
  155. package/src/services/plugin-executor.service.ts +102 -68
  156. package/src/services/quality-metrics.service.ts +112 -94
  157. package/src/services/queue-job.service.ts +288 -231
  158. package/src/services/recent-activity-title.service.ts +73 -36
  159. package/src/services/recent-activity.service.ts +274 -259
  160. package/src/services/skill-resolver.service.ts +38 -12
  161. package/src/services/social-chat-history.service.ts +190 -122
  162. package/src/services/system-executor.service.ts +96 -61
  163. package/src/services/thread/thread-active-run.ts +203 -0
  164. package/src/services/thread/thread-bootstrap.ts +385 -0
  165. package/src/services/thread/thread-listing.ts +199 -0
  166. package/src/services/thread/thread-memory-block.ts +130 -0
  167. package/src/services/thread/thread-message.service.ts +379 -0
  168. package/src/services/thread/thread-record-store.ts +155 -0
  169. package/src/services/thread/thread-title.service.ts +74 -0
  170. package/src/services/thread/thread-turn-execution.ts +280 -0
  171. package/src/services/thread/thread-turn-message-context.ts +73 -0
  172. package/src/services/thread/thread-turn-preparation.service.ts +1148 -0
  173. package/src/services/thread/thread-turn-streaming.ts +403 -0
  174. package/src/services/thread/thread-turn-tracing.ts +35 -0
  175. package/src/services/thread/thread-turn.ts +376 -0
  176. package/src/services/thread/thread.service.ts +344 -0
  177. package/src/services/user.service.ts +82 -32
  178. package/src/services/write-intent-validator.service.ts +63 -51
  179. package/src/storage/attachment-parser.ts +69 -27
  180. package/src/storage/attachment-storage.service.ts +334 -275
  181. package/src/storage/generated-document-storage.service.ts +66 -34
  182. package/src/system-agents/agent-result.ts +3 -1
  183. package/src/system-agents/context-compaction.agent.ts +3 -3
  184. package/src/system-agents/delegated-agent-factory.ts +159 -90
  185. package/src/system-agents/helper-agent-options.ts +1 -1
  186. package/src/system-agents/memory-reranker.agent.ts +3 -3
  187. package/src/system-agents/memory.agent.ts +3 -3
  188. package/src/system-agents/recent-activity-title-refiner.agent.ts +3 -3
  189. package/src/system-agents/regular-chat-memory-digest.agent.ts +3 -3
  190. package/src/system-agents/skill-extractor.agent.ts +3 -3
  191. package/src/system-agents/skill-manager.agent.ts +3 -3
  192. package/src/system-agents/thread-router.agent.ts +157 -113
  193. package/src/system-agents/title-generator.agent.ts +3 -3
  194. package/src/tools/execution-plan.tool.ts +241 -171
  195. package/src/tools/fetch-webpage.tool.ts +29 -18
  196. package/src/tools/firecrawl-client.ts +26 -6
  197. package/src/tools/index.ts +1 -0
  198. package/src/tools/memory-block.tool.ts +14 -6
  199. package/src/tools/plan-approval.tool.ts +57 -47
  200. package/src/tools/read-file-parts.tool.ts +44 -33
  201. package/src/tools/remember-memory.tool.ts +65 -45
  202. package/src/tools/search-web.tool.ts +33 -22
  203. package/src/tools/search.tool.ts +41 -29
  204. package/src/tools/team-think.tool.ts +125 -84
  205. package/src/tools/user-questions.tool.ts +4 -3
  206. package/src/tools/web-tool-shared.ts +6 -0
  207. package/src/utils/async.ts +25 -22
  208. package/src/utils/crypto.ts +21 -0
  209. package/src/utils/date-time.ts +40 -1
  210. package/src/utils/errors.ts +111 -20
  211. package/src/utils/hono-error-handler.ts +24 -39
  212. package/src/utils/index.ts +2 -1
  213. package/src/utils/null-proto-record.ts +41 -0
  214. package/src/utils/sse-keepalive.ts +124 -21
  215. package/src/workers/bootstrap.ts +164 -52
  216. package/src/workers/memory-consolidation.worker.ts +325 -237
  217. package/src/workers/organization-learning.worker.ts +50 -16
  218. package/src/workers/regular-chat-memory-digest.helpers.ts +28 -27
  219. package/src/workers/regular-chat-memory-digest.runner.ts +185 -114
  220. package/src/workers/skill-extraction.runner.ts +176 -93
  221. package/src/workers/utils/file-section-chunker.ts +8 -10
  222. package/src/workers/utils/repo-structure-extractor.ts +349 -260
  223. package/src/workers/utils/repomix-file-sections.ts +2 -2
  224. package/src/workers/utils/thread-message-query.ts +97 -38
  225. package/src/workers/worker-utils.ts +74 -31
  226. package/src/config/debug-logger.ts +0 -47
  227. package/src/config/search.ts +0 -3
  228. package/src/redis/connection-accessor.ts +0 -26
  229. package/src/runtime/agent-types.ts +0 -1
  230. package/src/runtime/context-compaction-runtime.ts +0 -87
  231. package/src/runtime/memory-scope.ts +0 -43
  232. package/src/runtime/social-chat-agent-runner.ts +0 -118
  233. package/src/runtime/social-chat.ts +0 -516
  234. package/src/runtime/team-consultation-orchestrator.ts +0 -272
  235. package/src/services/adaptive-playbook.service.ts +0 -152
  236. package/src/services/artifact-provenance.service.ts +0 -172
  237. package/src/services/chat-attachments.service.ts +0 -17
  238. package/src/services/context-compaction-runtime.singleton.ts +0 -13
  239. package/src/services/execution-plan.service.ts +0 -1118
  240. package/src/services/memory.service.ts +0 -914
  241. package/src/services/plan-agent-heartbeat.service.ts +0 -136
  242. package/src/services/plan-agent-query.service.ts +0 -267
  243. package/src/services/plan-artifact.service.ts +0 -50
  244. package/src/services/plan-builder.service.ts +0 -67
  245. package/src/services/plan-checkpoint.service.ts +0 -81
  246. package/src/services/plan-completion-side-effects.ts +0 -80
  247. package/src/services/plan-coordination.service.ts +0 -157
  248. package/src/services/plan-cycle.service.ts +0 -284
  249. package/src/services/plan-deadline.service.ts +0 -430
  250. package/src/services/plan-event-delivery.service.ts +0 -166
  251. package/src/services/plan-executor.service.ts +0 -1950
  252. package/src/services/plan-run.service.ts +0 -515
  253. package/src/services/plan-scheduler.service.ts +0 -240
  254. package/src/services/plan-template.service.ts +0 -177
  255. package/src/services/plan-validator.service.ts +0 -818
  256. package/src/services/plan-workspace.service.ts +0 -83
  257. package/src/services/rerank.service.ts +0 -156
  258. package/src/services/thread-message.service.ts +0 -275
  259. package/src/services/thread-plan-registry.service.ts +0 -22
  260. package/src/services/thread-title.service.ts +0 -39
  261. package/src/services/thread-turn-preparation.service.ts +0 -1147
  262. package/src/services/thread-turn.ts +0 -172
  263. package/src/services/thread.service.ts +0 -869
  264. package/src/utils/env.ts +0 -8
  265. /package/src/runtime/{context-compaction-constants.ts → context-compaction/context-compaction-constants.ts} +0 -0
  266. /package/src/runtime/{memory-format.ts → memory/memory-format.ts} +0 -0
  267. /package/src/runtime/{memory-prompts-parse.ts → memory/memory-prompts-parse.ts} +0 -0
  268. /package/src/runtime/{memory-prompts-update.ts → memory/memory-prompts-update.ts} +0 -0
  269. /package/src/runtime/{social-chat-prompts.ts → social-chat/social-chat-prompts.ts} +0 -0
  270. /package/src/services/{plan-node-spec.ts → plan/plan-node-spec.ts} +0 -0
  271. /package/src/services/{thread-constants.ts → thread/thread-constants.ts} +0 -0
  272. /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()