@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
@@ -0,0 +1,201 @@
1
+ import { Context, Effect, Layer } from 'effect'
2
+ import * as Schema from 'effect/Schema'
3
+ import { z } from 'zod'
4
+
5
+ import { OPENROUTER_FAST_RERANK_MODEL_ID } from '../../config/model-constants'
6
+ import { makeEffectTryPromiseWithOperation } from '../../effect/helpers'
7
+ import { RuntimeConfigServiceTag } from '../../effect/services'
8
+ import { toValidationError } from '../../effect/zod'
9
+ import { normalizeDirectOpenRouterModelId, resolveOpenRouterApiKey } from '../../openrouter/direct-provider'
10
+ import type { ResolvedLotaRuntimeConfig } from '../../runtime/runtime-config'
11
+
12
+ const OPENROUTER_RERANK_URL = 'https://openrouter.ai/api/v1/rerank' as const
13
+
14
+ class RerankServiceError extends Schema.TaggedErrorClass<RerankServiceError>()('RerankServiceError', {
15
+ operation: Schema.String,
16
+ message: Schema.String,
17
+ cause: Schema.Defect,
18
+ }) {}
19
+
20
+ function toRerankServiceError(operation: string, message: string, cause: unknown): RerankServiceError {
21
+ return new RerankServiceError({ operation, message, cause })
22
+ }
23
+
24
+ const tryRerankPromise = makeEffectTryPromiseWithOperation(toRerankServiceError)
25
+
26
+ const RerankResponseSchema = z
27
+ .object({
28
+ model: z.string().optional(),
29
+ results: z.array(
30
+ z
31
+ .object({
32
+ index: z.number().int().nonnegative().optional(),
33
+ relevance_score: z.number().optional(),
34
+ document: z.union([z.string(), z.object({ text: z.string().optional() }).passthrough()]).optional(),
35
+ })
36
+ .passthrough(),
37
+ ),
38
+ usage: z.object({ search_units: z.number().optional(), cost: z.number().optional() }).passthrough().optional(),
39
+ })
40
+ .passthrough()
41
+
42
+ export interface RerankDocument {
43
+ id: string
44
+ text: string
45
+ }
46
+
47
+ export interface RerankResultItem extends RerankDocument {
48
+ index: number
49
+ relevanceScore: number | null
50
+ }
51
+
52
+ export interface RerankUsage {
53
+ searchUnits?: number
54
+ cost?: number
55
+ }
56
+
57
+ export interface RerankResult {
58
+ modelId: string
59
+ results: RerankResultItem[]
60
+ usage?: RerankUsage
61
+ }
62
+
63
+ function clampTopN(topN: number | undefined, total: number): number {
64
+ if (!Number.isFinite(topN)) return total
65
+ return Math.max(1, Math.min(total, Math.trunc(topN as number)))
66
+ }
67
+
68
+ function matchRerankedDocument(
69
+ candidate: z.infer<typeof RerankResponseSchema>['results'][number],
70
+ documents: RerankDocument[],
71
+ ): RerankDocument | null {
72
+ if (typeof candidate.index === 'number' && candidate.index >= 0 && candidate.index < documents.length) {
73
+ return documents[candidate.index] ?? null
74
+ }
75
+
76
+ const text =
77
+ typeof candidate.document === 'string'
78
+ ? candidate.document
79
+ : typeof candidate.document?.text === 'string'
80
+ ? candidate.document.text
81
+ : null
82
+ if (!text) return null
83
+
84
+ return documents.find((document) => document.text === text) ?? null
85
+ }
86
+
87
+ export function makeRerankService(config: ResolvedLotaRuntimeConfig) {
88
+ function readConfiguredRerankModelId(): string | null {
89
+ const configured = config.memory.rerankerModelId
90
+ if (typeof configured !== 'string') return null
91
+ const trimmed = configured.trim()
92
+ return trimmed.length > 0 ? trimmed : null
93
+ }
94
+
95
+ function resolveRerankModelId(modelId?: string): string {
96
+ const explicit = modelId?.trim()
97
+ if (explicit) return normalizeDirectOpenRouterModelId(explicit)
98
+
99
+ const configured = readConfiguredRerankModelId()
100
+ if (configured) return normalizeDirectOpenRouterModelId(configured)
101
+
102
+ return OPENROUTER_FAST_RERANK_MODEL_ID
103
+ }
104
+
105
+ return {
106
+ rerankDocuments(params: { query: string; documents: RerankDocument[]; topN?: number; modelId?: string }) {
107
+ return Effect.gen(function* () {
108
+ if (params.documents.length === 0) {
109
+ return { modelId: resolveRerankModelId(params.modelId), results: [] as RerankResultItem[] }
110
+ }
111
+
112
+ const apiKey = resolveOpenRouterApiKey(config.aiGateway.openRouterApiKey)
113
+ const modelId = resolveRerankModelId(params.modelId)
114
+ const topN = clampTopN(params.topN, params.documents.length)
115
+
116
+ const response = yield* tryRerankPromise('fetch-rerank', 'Failed to fetch rerank results.', () =>
117
+ Bun.fetch(OPENROUTER_RERANK_URL, {
118
+ method: 'POST',
119
+ headers: { Authorization: `Bearer ${apiKey}`, 'Content-Type': 'application/json' },
120
+ body: JSON.stringify({
121
+ model: modelId,
122
+ query: params.query,
123
+ documents: params.documents.map((document) => document.text),
124
+ top_n: topN,
125
+ }),
126
+ }),
127
+ )
128
+
129
+ const responseText = yield* tryRerankPromise(
130
+ 'read-rerank-response',
131
+ 'Failed to read rerank response body.',
132
+ () => response.text(),
133
+ )
134
+
135
+ if (!response.ok) {
136
+ return yield* new RerankServiceError({
137
+ operation: 'fetch-rerank',
138
+ message: `OpenRouter rerank failed (${response.status}): ${responseText}`,
139
+ cause: responseText,
140
+ })
141
+ }
142
+
143
+ const payload = yield* Schema.decodeUnknownEffect(Schema.UnknownFromJsonString)(responseText).pipe(
144
+ Effect.mapError((cause) =>
145
+ toRerankServiceError(
146
+ 'parse-rerank-json',
147
+ `Rerank service returned invalid JSON (status ${response.status}): ${responseText.slice(0, 200)}`,
148
+ cause,
149
+ ),
150
+ ),
151
+ )
152
+
153
+ const parsedResponse = RerankResponseSchema.safeParse(payload)
154
+ if (!parsedResponse.success) {
155
+ return yield* toValidationError(parsedResponse.error, 'Rerank service returned an invalid response payload')
156
+ }
157
+ const parsed = parsedResponse.data
158
+ const seen = new Set<string>()
159
+ const results: RerankResultItem[] = []
160
+
161
+ for (const item of parsed.results) {
162
+ const matched = matchRerankedDocument(item, params.documents)
163
+ if (!matched || seen.has(matched.id)) continue
164
+ seen.add(matched.id)
165
+
166
+ results.push({
167
+ id: matched.id,
168
+ text: matched.text,
169
+ index: item.index ?? params.documents.findIndex((document) => document.id === matched.id),
170
+ relevanceScore: typeof item.relevance_score === 'number' ? item.relevance_score : null,
171
+ })
172
+
173
+ if (results.length >= topN) break
174
+ }
175
+
176
+ return {
177
+ modelId: parsed.model?.trim() || modelId,
178
+ results,
179
+ usage: parsed.usage
180
+ ? {
181
+ ...(parsed.usage.search_units !== undefined ? { searchUnits: parsed.usage.search_units } : {}),
182
+ ...(parsed.usage.cost !== undefined ? { cost: parsed.usage.cost } : {}),
183
+ }
184
+ : undefined,
185
+ }
186
+ })
187
+ },
188
+ }
189
+ }
190
+
191
+ export class RerankServiceTag extends Context.Service<RerankServiceTag, ReturnType<typeof makeRerankService>>()(
192
+ '@lota-sdk/core/RerankService',
193
+ ) {}
194
+
195
+ export const RerankServiceLive = Layer.effect(
196
+ RerankServiceTag,
197
+ Effect.gen(function* () {
198
+ const runtimeConfig = yield* RuntimeConfigServiceTag
199
+ return makeRerankService(runtimeConfig)
200
+ }),
201
+ )
@@ -1,86 +1,108 @@
1
1
  import type { MonitoringWindowConfig } from '@lota-sdk/shared'
2
+ import { Context, Effect, Layer } from 'effect'
2
3
 
3
- import { planSchedulerService } from './plan-scheduler.service'
4
+ import { ValidationError } from '../effect/errors'
5
+ import type { makePlanSchedulerService } from './plan/plan-scheduler.service'
6
+ import { PlanSchedulerServiceTag } from './plan/plan-scheduler.service'
4
7
 
5
- class MonitoringWindowService {
6
- computeCheckTimes(config: MonitoringWindowConfig): { offsetMs: number; intervalMs: number }[] {
7
- const times: { offsetMs: number; intervalMs: number }[] = []
8
- let currentInterval = config.initialIntervalMs
9
- let elapsed = 0
10
- while (elapsed < config.durationMs) {
11
- times.push({ offsetMs: elapsed, intervalMs: currentInterval })
12
- elapsed += currentInterval
13
- currentInterval = Math.min(currentInterval / config.decayFactor, config.maxIntervalMs)
14
- }
15
- return times
8
+ function computeCheckTimes(config: MonitoringWindowConfig): { offsetMs: number; intervalMs: number }[] {
9
+ const times: { offsetMs: number; intervalMs: number }[] = []
10
+ let currentInterval = config.initialIntervalMs
11
+ let elapsed = 0
12
+ while (elapsed < config.durationMs) {
13
+ times.push({ offsetMs: elapsed, intervalMs: currentInterval })
14
+ elapsed += currentInterval
15
+ currentInterval = Math.min(currentInterval / config.decayFactor, config.maxIntervalMs)
16
16
  }
17
+ return times
18
+ }
17
19
 
18
- async startMonitoringWindow(params: {
19
- runId: string
20
- nodeId: string
21
- config: MonitoringWindowConfig
22
- organizationId: string
23
- threadId: string
24
- }): Promise<void> {
25
- await planSchedulerService.createSchedule({
26
- organizationId: params.organizationId,
27
- threadId: params.threadId,
28
- runId: params.runId,
29
- nodeId: params.nodeId,
30
- scheduleSpec: {
31
- type: 'monitoring',
32
- intervalMs: params.config.initialIntervalMs,
33
- maxFires: this.computeCheckTimes(params.config).length,
34
- missedPolicy: 'skip',
35
- },
36
- })
37
- }
20
+ function evaluateTerminationEffect(params: {
21
+ samples: unknown[]
22
+ config: MonitoringWindowConfig
23
+ }): Effect.Effect<boolean, ValidationError> {
24
+ if (!params.config.earlyTermination) return Effect.succeed(false)
25
+ if (params.samples.length < params.config.earlyTermination.minSamples) return Effect.succeed(false)
38
26
 
39
- evaluateTermination(params: { samples: unknown[]; config: MonitoringWindowConfig }): boolean {
40
- if (!params.config.earlyTermination) return false
41
- if (params.samples.length < params.config.earlyTermination.minSamples) return false
27
+ const { condition, minSamples } = params.config.earlyTermination
28
+ const numericSamples = params.samples
29
+ .map((s) =>
30
+ typeof s === 'number'
31
+ ? s
32
+ : typeof s === 'object' && s !== null && 'value' in s
33
+ ? Number((s as Record<string, unknown>).value)
34
+ : NaN,
35
+ )
36
+ .filter((n) => !isNaN(n))
42
37
 
43
- const { condition, minSamples } = params.config.earlyTermination
44
- const numericSamples = params.samples
45
- .map((s) =>
46
- typeof s === 'number'
47
- ? s
48
- : typeof s === 'object' && s !== null && 'value' in s
49
- ? Number((s as Record<string, unknown>).value)
50
- : NaN,
51
- )
52
- .filter((n) => !isNaN(n))
38
+ if (numericSamples.length === 0) return Effect.succeed(false)
53
39
 
54
- // No numeric data to evaluate against — don't terminate
55
- if (numericSamples.length === 0) return false
40
+ if (condition === 'stable') {
41
+ const recent = numericSamples.slice(-minSamples)
42
+ if (recent.length < 2) return Effect.succeed(false)
43
+ const mean = recent.reduce((a, b) => a + b, 0) / recent.length
44
+ const variance = recent.reduce((a, b) => a + (b - mean) ** 2, 0) / recent.length
45
+ const cv = mean !== 0 ? Math.sqrt(variance) / Math.abs(mean) : 0
46
+ return Effect.succeed(cv < 0.1)
47
+ }
56
48
 
57
- if (condition === 'stable') {
58
- const recent = numericSamples.slice(-minSamples)
59
- if (recent.length < 2) return false
60
- const mean = recent.reduce((a, b) => a + b, 0) / recent.length
61
- const variance = recent.reduce((a, b) => a + (b - mean) ** 2, 0) / recent.length
62
- const cv = mean !== 0 ? Math.sqrt(variance) / Math.abs(mean) : 0
63
- return cv < 0.1
64
- }
49
+ const thresholdMatch = condition.match(/^threshold:(\d+(?:\.\d+)?)$/)
50
+ if (thresholdMatch?.[1]) {
51
+ const threshold = parseFloat(thresholdMatch[1])
52
+ return Effect.succeed(numericSamples.some((v) => v >= threshold))
53
+ }
65
54
 
66
- const thresholdMatch = condition.match(/^threshold:(\d+(?:\.\d+)?)$/)
67
- if (thresholdMatch?.[1]) {
68
- const threshold = parseFloat(thresholdMatch[1])
69
- return numericSamples.some((v) => v >= threshold)
70
- }
55
+ if (condition === 'trend:declining') {
56
+ const recent = numericSamples.slice(-minSamples)
57
+ return Effect.succeed(recent.length >= 2 && recent.every((v, i) => i === 0 || v <= (recent[i - 1] ?? v)))
58
+ }
71
59
 
72
- if (condition === 'trend:declining') {
73
- const recent = numericSamples.slice(-minSamples)
74
- return recent.length >= 2 && recent.every((v, i) => i === 0 || v <= (recent[i - 1] ?? v))
75
- }
60
+ if (condition === 'trend:increasing') {
61
+ const recent = numericSamples.slice(-minSamples)
62
+ return Effect.succeed(recent.length >= 2 && recent.every((v, i) => i === 0 || v >= (recent[i - 1] ?? v)))
63
+ }
76
64
 
77
- if (condition === 'trend:increasing') {
78
- const recent = numericSamples.slice(-minSamples)
79
- return recent.length >= 2 && recent.every((v, i) => i === 0 || v >= (recent[i - 1] ?? v))
80
- }
65
+ return Effect.fail(new ValidationError({ message: `Unknown monitoring termination condition: ${condition}` }))
66
+ }
81
67
 
82
- throw new Error(`Unknown monitoring termination condition: ${condition}`)
68
+ export function makeMonitoringWindowService(planSchedulerService: ReturnType<typeof makePlanSchedulerService>) {
69
+ return {
70
+ computeCheckTimes,
71
+ startMonitoringWindow(params: {
72
+ runId: string
73
+ nodeId: string
74
+ config: MonitoringWindowConfig
75
+ organizationId: string
76
+ threadId: string
77
+ }) {
78
+ return Effect.asVoid(
79
+ planSchedulerService.createSchedule({
80
+ organizationId: params.organizationId,
81
+ threadId: params.threadId,
82
+ runId: params.runId,
83
+ nodeId: params.nodeId,
84
+ scheduleSpec: {
85
+ type: 'monitoring',
86
+ intervalMs: params.config.initialIntervalMs,
87
+ maxFires: computeCheckTimes(params.config).length,
88
+ missedPolicy: 'skip',
89
+ },
90
+ }),
91
+ )
92
+ },
93
+ evaluateTermination: evaluateTerminationEffect,
83
94
  }
84
95
  }
85
96
 
86
- export const monitoringWindowService = new MonitoringWindowService()
97
+ export class MonitoringWindowServiceTag extends Context.Service<
98
+ MonitoringWindowServiceTag,
99
+ ReturnType<typeof makeMonitoringWindowService>
100
+ >()('@lota-sdk/core/MonitoringWindowService') {}
101
+
102
+ export const MonitoringWindowServiceLive = Layer.effect(
103
+ MonitoringWindowServiceTag,
104
+ Effect.gen(function* () {
105
+ const planSchedulerService = yield* PlanSchedulerServiceTag
106
+ return makeMonitoringWindowService(planSchedulerService)
107
+ }),
108
+ )
@@ -1,18 +1,13 @@
1
- import type { RecordIdRef } from '../db/record-id'
1
+ import { Context, Effect, Layer } from 'effect'
2
+
2
3
  import { ensureRecordId } from '../db/record-id'
3
4
  import { TABLES } from '../db/tables'
4
5
  import { extractMessageText } from '../runtime/thread-chat-helpers'
5
- import { threadMessageService } from './thread-message.service'
6
+ import type { makeThreadMessageService } from './thread/thread-message.service'
7
+ import { ThreadMessageServiceTag } from './thread/thread-message.service'
6
8
 
7
9
  const APPROVAL_VERIFICATION_MESSAGE_WINDOW = 20
8
10
 
9
- type VerifyMutatingApproval = (params: {
10
- threadId: string
11
- approvalReason: string
12
- approvalToken: string
13
- approvalMessageId?: string
14
- }) => Promise<{ ok: true } | { ok: false; reason: string }>
15
-
16
11
  function extractQuotedValue(value: string): string {
17
12
  const trimmed = value.trim()
18
13
  if (
@@ -58,50 +53,64 @@ function messageContainsExactApproval(messageText: string, approvalToken: string
58
53
  return extracted.token === approvalToken && extracted.reason === approvalReason
59
54
  }
60
55
 
61
- async function verifyMutatingApprovalForThread(
62
- threadId: RecordIdRef,
63
- params: { approvalReason: string; approvalToken: string; approvalMessageId?: string },
64
- ): Promise<{ ok: true } | { ok: false; reason: string }> {
65
- const token = extractQuotedValue(params.approvalToken)
66
- const reason = extractQuotedValue(params.approvalReason)
67
- const approvalMessageId = params.approvalMessageId?.trim()
68
- if (!token || !reason) {
69
- return { ok: false, reason: 'Mutating approval metadata is incomplete.' }
70
- }
71
- if (params.approvalMessageId !== undefined && !approvalMessageId) {
72
- return { ok: false, reason: 'approvalMessageId cannot be empty when provided.' }
73
- }
74
-
75
- const recentMessages = await threadMessageService.listRecentMessages(threadId, APPROVAL_VERIFICATION_MESSAGE_WINDOW)
76
- const userMessages = recentMessages.filter((message) => message.role === 'user')
77
- if (userMessages.length === 0) {
78
- return { ok: false, reason: 'No user message history available to verify mutating approval.' }
79
- }
80
-
81
- const latestUserMessage = userMessages.at(-1)
82
- if (!latestUserMessage) {
83
- return { ok: false, reason: 'Latest user message was not found for mutating approval verification.' }
84
- }
85
-
86
- if (approvalMessageId && approvalMessageId !== latestUserMessage.id) {
87
- return {
88
- ok: false,
89
- reason:
90
- 'approvalMessageId must reference the latest user message for mutating approval verification; stale approvals are not accepted.',
91
- }
56
+ export function makeMutatingApprovalService(threadMessageService: ReturnType<typeof makeThreadMessageService>) {
57
+ return (params: { threadId: string; approvalReason: string; approvalToken: string; approvalMessageId?: string }) => {
58
+ const threadId = ensureRecordId(params.threadId, TABLES.THREAD)
59
+ return Effect.gen(function* () {
60
+ const token = extractQuotedValue(params.approvalToken)
61
+ const reason = extractQuotedValue(params.approvalReason)
62
+ const approvalMessageId = params.approvalMessageId?.trim()
63
+ if (!token || !reason) {
64
+ return { ok: false as const, reason: 'Mutating approval metadata is incomplete.' }
65
+ }
66
+ if (params.approvalMessageId !== undefined && !approvalMessageId) {
67
+ return { ok: false as const, reason: 'approvalMessageId cannot be empty when provided.' }
68
+ }
69
+
70
+ const recentMessages = yield* threadMessageService.listRecentMessages(
71
+ threadId,
72
+ APPROVAL_VERIFICATION_MESSAGE_WINDOW,
73
+ )
74
+ const userMessages = recentMessages.filter((message) => message.role === 'user')
75
+ if (userMessages.length === 0) {
76
+ return { ok: false as const, reason: 'No user message history available to verify mutating approval.' }
77
+ }
78
+
79
+ const latestUserMessage = userMessages.at(-1)
80
+ if (!latestUserMessage) {
81
+ return { ok: false as const, reason: 'Latest user message was not found for mutating approval verification.' }
82
+ }
83
+
84
+ if (approvalMessageId && approvalMessageId !== latestUserMessage.id) {
85
+ return {
86
+ ok: false as const,
87
+ reason:
88
+ 'approvalMessageId must reference the latest user message for mutating approval verification; stale approvals are not accepted.',
89
+ }
90
+ }
91
+
92
+ if (!messageContainsExactApproval(extractMessageText(latestUserMessage), token, reason)) {
93
+ return {
94
+ ok: false as const,
95
+ reason:
96
+ 'Referenced approval message must contain exact approval fields: "Approval Token: <token>" and "Approval Reason: <reason>".',
97
+ }
98
+ }
99
+
100
+ return { ok: true as const }
101
+ })
92
102
  }
93
-
94
- if (!messageContainsExactApproval(extractMessageText(latestUserMessage), token, reason)) {
95
- return {
96
- ok: false,
97
- reason:
98
- 'Referenced approval message must contain exact approval fields: "Approval Token: <token>" and "Approval Reason: <reason>".',
99
- }
100
- }
101
-
102
- return { ok: true }
103
103
  }
104
104
 
105
- export const verifyMutatingApproval: VerifyMutatingApproval = async (params) => {
106
- return verifyMutatingApprovalForThread(ensureRecordId(params.threadId, TABLES.THREAD), params)
107
- }
105
+ export class MutatingApprovalServiceTag extends Context.Service<
106
+ MutatingApprovalServiceTag,
107
+ ReturnType<typeof makeMutatingApprovalService>
108
+ >()('@lota-sdk/core/MutatingApprovalService') {}
109
+
110
+ export const MutatingApprovalServiceLive = Layer.effect(
111
+ MutatingApprovalServiceTag,
112
+ Effect.gen(function* () {
113
+ const threadMessageService = yield* ThreadMessageServiceTag
114
+ return makeMutatingApprovalService(threadMessageService)
115
+ }),
116
+ )