@lota-sdk/core 0.4.7 → 0.4.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (259) hide show
  1. package/package.json +11 -12
  2. package/src/ai/embedding-cache.ts +94 -22
  3. package/src/ai-gateway/ai-gateway.ts +738 -223
  4. package/src/config/agent-defaults.ts +176 -75
  5. package/src/config/agent-types.ts +54 -4
  6. package/src/config/constants.ts +8 -2
  7. package/src/config/logger.ts +286 -19
  8. package/src/config/model-constants.ts +1 -0
  9. package/src/config/thread-defaults.ts +33 -21
  10. package/src/create-runtime.ts +725 -383
  11. package/src/db/base.service.ts +52 -28
  12. package/src/db/cursor-pagination.ts +71 -30
  13. package/src/db/memory-store.helpers.ts +4 -7
  14. package/src/db/memory-store.ts +856 -598
  15. package/src/db/memory.ts +398 -275
  16. package/src/db/record-id.ts +32 -10
  17. package/src/db/schema-fingerprint.ts +30 -12
  18. package/src/db/service-normalization.ts +255 -0
  19. package/src/db/service.ts +726 -761
  20. package/src/db/startup.ts +140 -66
  21. package/src/db/transaction-conflict.ts +15 -0
  22. package/src/effect/awaitable-effect.ts +87 -0
  23. package/src/effect/errors.ts +121 -0
  24. package/src/effect/helpers.ts +98 -0
  25. package/src/effect/index.ts +22 -0
  26. package/src/effect/layers.ts +228 -0
  27. package/src/effect/runtime-ref.ts +25 -0
  28. package/src/effect/runtime.ts +31 -0
  29. package/src/effect/services.ts +57 -0
  30. package/src/effect/zod.ts +43 -0
  31. package/src/embeddings/provider.ts +122 -71
  32. package/src/index.ts +46 -1
  33. package/src/openrouter/direct-provider.ts +29 -0
  34. package/src/queues/autonomous-job.queue.ts +130 -74
  35. package/src/queues/context-compaction.queue.ts +60 -15
  36. package/src/queues/delayed-node-promotion.queue.ts +52 -15
  37. package/src/queues/document-processor.queue.ts +52 -77
  38. package/src/queues/memory-consolidation.queue.ts +47 -32
  39. package/src/queues/organization-learning.queue.ts +13 -4
  40. package/src/queues/plan-agent-heartbeat.queue.ts +65 -21
  41. package/src/queues/plan-scheduler.queue.ts +107 -31
  42. package/src/queues/post-chat-memory.queue.ts +66 -24
  43. package/src/queues/queue-factory.ts +142 -52
  44. package/src/queues/standalone-worker.ts +39 -0
  45. package/src/queues/title-generation.queue.ts +54 -9
  46. package/src/redis/connection.ts +84 -32
  47. package/src/redis/index.ts +6 -8
  48. package/src/redis/org-memory-lock.ts +60 -27
  49. package/src/redis/redis-lease-lock.ts +200 -121
  50. package/src/redis/runtime-connection.ts +10 -0
  51. package/src/redis/stream-context.ts +84 -46
  52. package/src/runtime/agent-identity-overrides.ts +2 -2
  53. package/src/runtime/agent-runtime-policy.ts +4 -1
  54. package/src/runtime/agent-stream-helpers.ts +20 -9
  55. package/src/runtime/chat-run-orchestration.ts +102 -19
  56. package/src/runtime/chat-run-registry.ts +36 -2
  57. package/src/runtime/context-compaction/context-compaction-runtime.ts +107 -0
  58. package/src/runtime/{context-compaction.ts → context-compaction/context-compaction.ts} +114 -91
  59. package/src/runtime/execution-plan-visibility.ts +2 -2
  60. package/src/runtime/execution-plan.ts +42 -15
  61. package/src/runtime/graph-designer.ts +11 -7
  62. package/src/runtime/helper-model.ts +135 -48
  63. package/src/runtime/index.ts +7 -7
  64. package/src/runtime/indexed-repositories-policy.ts +3 -3
  65. package/src/runtime/{memory-block.ts → memory/memory-block.ts} +40 -36
  66. package/src/runtime/{memory-digest-policy.ts → memory/memory-digest-policy.ts} +1 -1
  67. package/src/runtime/{memory-pipeline.ts → memory/memory-pipeline.ts} +1 -1
  68. package/src/runtime/{memory-prompts-fact.ts → memory/memory-prompts-fact.ts} +2 -2
  69. package/src/runtime/{memory-scope.ts → memory/memory-scope.ts} +12 -6
  70. package/src/runtime/plugin-resolution.ts +144 -24
  71. package/src/runtime/plugin-types.ts +9 -1
  72. package/src/runtime/post-turn-side-effects.ts +197 -130
  73. package/src/runtime/retrieval-adapters.ts +38 -4
  74. package/src/runtime/runtime-config.ts +165 -61
  75. package/src/runtime/runtime-extensions.ts +21 -34
  76. package/src/runtime/social-chat/social-chat-agent-runner.ts +157 -0
  77. package/src/runtime/{social-chat-history.ts → social-chat/social-chat-history.ts} +42 -20
  78. package/src/runtime/social-chat/social-chat.ts +594 -0
  79. package/src/runtime/specialist-runner.ts +36 -10
  80. package/src/runtime/team-consultation/team-consultation-orchestrator.ts +427 -0
  81. package/src/runtime/{team-consultation-prompts.ts → team-consultation/team-consultation-prompts.ts} +6 -2
  82. package/src/runtime/thread-chat-helpers.ts +2 -2
  83. package/src/runtime/thread-plan-turn.ts +2 -1
  84. package/src/runtime/thread-turn-context.ts +172 -94
  85. package/src/runtime/turn-lifecycle.ts +93 -27
  86. package/src/services/agent-activity.service.ts +287 -203
  87. package/src/services/agent-executor.service.ts +329 -217
  88. package/src/services/artifact.service.ts +225 -148
  89. package/src/services/attachment.service.ts +137 -115
  90. package/src/services/autonomous-job.service.ts +888 -491
  91. package/src/services/chat-run-registry.service.ts +11 -1
  92. package/src/services/context-compaction.service.ts +136 -86
  93. package/src/services/document-chunk.service.ts +162 -90
  94. package/src/services/execution-plan/execution-plan-approval.ts +26 -0
  95. package/src/services/execution-plan/execution-plan-context.ts +29 -0
  96. package/src/services/execution-plan/execution-plan-graph.ts +256 -0
  97. package/src/services/execution-plan/execution-plan-schedule.ts +84 -0
  98. package/src/services/execution-plan/execution-plan-spec.ts +75 -0
  99. package/src/services/execution-plan/execution-plan.service.ts +1041 -0
  100. package/src/services/feedback-loop.service.ts +132 -76
  101. package/src/services/global-orchestrator.service.ts +80 -170
  102. package/src/services/graph-full-routing.ts +182 -0
  103. package/src/services/index.ts +18 -20
  104. package/src/services/institutional-memory.service.ts +220 -123
  105. package/src/services/learned-skill.service.ts +364 -259
  106. package/src/services/memory/memory-conversation.ts +95 -0
  107. package/src/services/memory/memory-org-memory.ts +39 -0
  108. package/src/services/memory/memory-preseeded.ts +80 -0
  109. package/src/services/memory/memory-rerank.ts +297 -0
  110. package/src/services/{memory-utils.ts → memory/memory-utils.ts} +5 -5
  111. package/src/services/memory/memory.service.ts +692 -0
  112. package/src/services/memory/rerank.service.ts +209 -0
  113. package/src/services/monitoring-window.service.ts +92 -70
  114. package/src/services/mutating-approval.service.ts +62 -53
  115. package/src/services/node-workspace.service.ts +141 -98
  116. package/src/services/notification.service.ts +17 -16
  117. package/src/services/organization-member.service.ts +120 -66
  118. package/src/services/organization.service.ts +144 -51
  119. package/src/services/ownership-dispatcher.service.ts +415 -264
  120. package/src/services/plan/plan-agent-heartbeat.service.ts +234 -0
  121. package/src/services/plan/plan-agent-query.service.ts +322 -0
  122. package/src/services/plan/plan-approval.service.ts +102 -0
  123. package/src/services/plan/plan-artifact.service.ts +60 -0
  124. package/src/services/plan/plan-builder.service.ts +76 -0
  125. package/src/services/plan/plan-checkpoint.service.ts +103 -0
  126. package/src/services/{plan-compiler.service.ts → plan/plan-compiler.service.ts} +26 -9
  127. package/src/services/plan/plan-completion-side-effects.ts +175 -0
  128. package/src/services/plan/plan-coordination.service.ts +181 -0
  129. package/src/services/plan/plan-cycle.service.ts +398 -0
  130. package/src/services/plan/plan-deadline.service.ts +547 -0
  131. package/src/services/plan/plan-event-delivery.service.ts +261 -0
  132. package/src/services/plan/plan-executor-context.ts +35 -0
  133. package/src/services/plan/plan-executor-graph.ts +475 -0
  134. package/src/services/plan/plan-executor-helpers.ts +322 -0
  135. package/src/services/plan/plan-executor-persistence.ts +209 -0
  136. package/src/services/plan/plan-executor.service.ts +1654 -0
  137. package/src/services/{plan-helpers.ts → plan/plan-helpers.ts} +1 -1
  138. package/src/services/{plan-run-data.ts → plan/plan-run-data.ts} +4 -4
  139. package/src/services/plan/plan-run-serialization.ts +15 -0
  140. package/src/services/plan/plan-run.service.ts +644 -0
  141. package/src/services/plan/plan-scheduler.service.ts +385 -0
  142. package/src/services/plan/plan-template.service.ts +224 -0
  143. package/src/services/plan/plan-transaction-events.ts +33 -0
  144. package/src/services/plan/plan-validator.service.ts +907 -0
  145. package/src/services/plan/plan-workspace.service.ts +125 -0
  146. package/src/services/plugin-executor.service.ts +97 -68
  147. package/src/services/quality-metrics.service.ts +112 -94
  148. package/src/services/queue-job.service.ts +296 -230
  149. package/src/services/recent-activity-title.service.ts +65 -36
  150. package/src/services/recent-activity.service.ts +274 -259
  151. package/src/services/skill-resolver.service.ts +38 -12
  152. package/src/services/social-chat-history.service.ts +176 -125
  153. package/src/services/system-executor.service.ts +91 -61
  154. package/src/services/thread/thread-active-run.ts +203 -0
  155. package/src/services/thread/thread-bootstrap.ts +369 -0
  156. package/src/services/thread/thread-listing.ts +198 -0
  157. package/src/services/thread/thread-memory-block.ts +117 -0
  158. package/src/services/thread/thread-message.service.ts +363 -0
  159. package/src/services/thread/thread-record-store.ts +155 -0
  160. package/src/services/thread/thread-title.service.ts +74 -0
  161. package/src/services/thread/thread-turn-execution.ts +280 -0
  162. package/src/services/thread/thread-turn-message-context.ts +73 -0
  163. package/src/services/thread/thread-turn-preparation.service.ts +1146 -0
  164. package/src/services/thread/thread-turn-streaming.ts +402 -0
  165. package/src/services/thread/thread-turn-tracing.ts +35 -0
  166. package/src/services/thread/thread-turn.ts +343 -0
  167. package/src/services/thread/thread.service.ts +335 -0
  168. package/src/services/user.service.ts +82 -32
  169. package/src/services/write-intent-validator.service.ts +63 -51
  170. package/src/storage/attachment-parser.ts +69 -27
  171. package/src/storage/attachment-storage.service.ts +331 -275
  172. package/src/storage/generated-document-storage.service.ts +66 -34
  173. package/src/system-agents/agent-result.ts +3 -1
  174. package/src/system-agents/context-compaction.agent.ts +2 -2
  175. package/src/system-agents/delegated-agent-factory.ts +159 -90
  176. package/src/system-agents/memory-reranker.agent.ts +2 -2
  177. package/src/system-agents/memory.agent.ts +2 -2
  178. package/src/system-agents/recent-activity-title-refiner.agent.ts +2 -2
  179. package/src/system-agents/regular-chat-memory-digest.agent.ts +2 -2
  180. package/src/system-agents/skill-extractor.agent.ts +2 -2
  181. package/src/system-agents/skill-manager.agent.ts +2 -2
  182. package/src/system-agents/thread-router.agent.ts +157 -113
  183. package/src/system-agents/title-generator.agent.ts +2 -2
  184. package/src/tools/execution-plan.tool.ts +220 -161
  185. package/src/tools/fetch-webpage.tool.ts +21 -17
  186. package/src/tools/firecrawl-client.ts +16 -6
  187. package/src/tools/index.ts +1 -0
  188. package/src/tools/memory-block.tool.ts +14 -6
  189. package/src/tools/plan-approval.tool.ts +49 -47
  190. package/src/tools/read-file-parts.tool.ts +44 -33
  191. package/src/tools/remember-memory.tool.ts +65 -45
  192. package/src/tools/search-web.tool.ts +26 -22
  193. package/src/tools/search.tool.ts +41 -29
  194. package/src/tools/team-think.tool.ts +124 -83
  195. package/src/tools/user-questions.tool.ts +4 -3
  196. package/src/tools/web-tool-shared.ts +6 -0
  197. package/src/utils/async.ts +17 -23
  198. package/src/utils/crypto.ts +21 -0
  199. package/src/utils/date-time.ts +40 -1
  200. package/src/utils/errors.ts +95 -16
  201. package/src/utils/hono-error-handler.ts +24 -39
  202. package/src/utils/index.ts +2 -1
  203. package/src/utils/null-proto-record.ts +41 -0
  204. package/src/utils/sse-keepalive.ts +124 -21
  205. package/src/workers/bootstrap.ts +186 -51
  206. package/src/workers/memory-consolidation.worker.ts +325 -237
  207. package/src/workers/organization-learning.worker.ts +50 -16
  208. package/src/workers/regular-chat-memory-digest.helpers.ts +28 -27
  209. package/src/workers/regular-chat-memory-digest.runner.ts +175 -114
  210. package/src/workers/skill-extraction.runner.ts +176 -93
  211. package/src/workers/utils/file-section-chunker.ts +8 -10
  212. package/src/workers/utils/repo-structure-extractor.ts +349 -260
  213. package/src/workers/utils/repomix-file-sections.ts +2 -2
  214. package/src/workers/utils/thread-message-query.ts +97 -38
  215. package/src/workers/worker-utils.ts +56 -31
  216. package/src/config/debug-logger.ts +0 -47
  217. package/src/redis/connection-accessor.ts +0 -26
  218. package/src/runtime/context-compaction-runtime.ts +0 -87
  219. package/src/runtime/social-chat-agent-runner.ts +0 -118
  220. package/src/runtime/social-chat.ts +0 -516
  221. package/src/runtime/team-consultation-orchestrator.ts +0 -272
  222. package/src/services/adaptive-playbook.service.ts +0 -152
  223. package/src/services/artifact-provenance.service.ts +0 -172
  224. package/src/services/chat-attachments.service.ts +0 -17
  225. package/src/services/context-compaction-runtime.singleton.ts +0 -13
  226. package/src/services/execution-plan.service.ts +0 -1118
  227. package/src/services/memory.service.ts +0 -844
  228. package/src/services/plan-agent-heartbeat.service.ts +0 -136
  229. package/src/services/plan-agent-query.service.ts +0 -267
  230. package/src/services/plan-approval.service.ts +0 -83
  231. package/src/services/plan-artifact.service.ts +0 -50
  232. package/src/services/plan-builder.service.ts +0 -67
  233. package/src/services/plan-checkpoint.service.ts +0 -81
  234. package/src/services/plan-completion-side-effects.ts +0 -80
  235. package/src/services/plan-coordination.service.ts +0 -157
  236. package/src/services/plan-cycle.service.ts +0 -284
  237. package/src/services/plan-deadline.service.ts +0 -430
  238. package/src/services/plan-event-delivery.service.ts +0 -166
  239. package/src/services/plan-executor.service.ts +0 -1950
  240. package/src/services/plan-run.service.ts +0 -515
  241. package/src/services/plan-scheduler.service.ts +0 -240
  242. package/src/services/plan-template.service.ts +0 -177
  243. package/src/services/plan-validator.service.ts +0 -818
  244. package/src/services/plan-workspace.service.ts +0 -83
  245. package/src/services/thread-message.service.ts +0 -275
  246. package/src/services/thread-plan-registry.service.ts +0 -22
  247. package/src/services/thread-title.service.ts +0 -39
  248. package/src/services/thread-turn-preparation.service.ts +0 -1147
  249. package/src/services/thread-turn.ts +0 -172
  250. package/src/services/thread.service.ts +0 -869
  251. package/src/utils/env.ts +0 -8
  252. /package/src/runtime/{context-compaction-constants.ts → context-compaction/context-compaction-constants.ts} +0 -0
  253. /package/src/runtime/{memory-format.ts → memory/memory-format.ts} +0 -0
  254. /package/src/runtime/{memory-prompts-parse.ts → memory/memory-prompts-parse.ts} +0 -0
  255. /package/src/runtime/{memory-prompts-update.ts → memory/memory-prompts-update.ts} +0 -0
  256. /package/src/runtime/{social-chat-prompts.ts → social-chat/social-chat-prompts.ts} +0 -0
  257. /package/src/services/{plan-node-spec.ts → plan/plan-node-spec.ts} +0 -0
  258. /package/src/services/{thread-constants.ts → thread/thread-constants.ts} +0 -0
  259. /package/src/services/{thread.types.ts → thread/thread.types.ts} +0 -0
@@ -1,7 +1,15 @@
1
1
  import type { ChatMessage } from '@lota-sdk/shared'
2
+ import { Schema, Effect } from 'effect'
2
3
  import { z } from 'zod'
3
4
 
4
- import { CHARS_PER_TOKEN_ESTIMATE, compactWhitespace, readRecord, readString, stringifyUnknown } from '../utils/string'
5
+ import { nowEpochMillis } from '../../utils/date-time'
6
+ import {
7
+ CHARS_PER_TOKEN_ESTIMATE,
8
+ compactWhitespace,
9
+ readRecord,
10
+ readString,
11
+ stringifyUnknown,
12
+ } from '../../utils/string'
5
13
  import {
6
14
  COMPACTION_CHUNK_MAX_CHARS,
7
15
  CONTEXT_COMPACTION_INCLUDED_TOOL_NAMES,
@@ -51,6 +59,11 @@ export interface ContextCompactionRunnerParams {
51
59
  transcript: string
52
60
  }
53
61
 
62
+ class CompactionError extends Schema.TaggedErrorClass<CompactionError>()('CompactionError', {
63
+ message: Schema.String,
64
+ cause: Schema.optional(Schema.Defect),
65
+ }) {}
66
+
54
67
  export interface ContextCompactionPromptParams {
55
68
  previousSummary: string
56
69
  transcript: string
@@ -216,8 +229,8 @@ export function buildMemoryBlockCompactionPrompt(params: MemoryBlockCompactionPr
216
229
  export function createContextCompactionRuntime(
217
230
  options: CreateContextCompactionRuntimeOptions,
218
231
  ): ContextCompactionRuntime {
219
- const now = options.now ?? (() => Date.now())
220
- const randomId = options.randomId ?? (() => crypto.randomUUID())
232
+ const now = options.now ?? nowEpochMillis
233
+ const randomId = options.randomId ?? (() => Bun.randomUUIDv7())
221
234
  const thresholdRatio = options.thresholdRatio ?? CONTEXT_COMPACTION_THRESHOLD_RATIO
222
235
  const outputReserveTokens = options.outputReserveTokens ?? CONTEXT_OUTPUT_RESERVE_TOKENS
223
236
  const safetyMarginTokens = options.safetyMarginTokens ?? CONTEXT_SAFETY_MARGIN_TOKENS
@@ -312,111 +325,121 @@ export function createContextCompactionRuntime(
312
325
  return { estimatedTokens, threshold, shouldCompact: estimatedTokens >= threshold }
313
326
  }
314
327
 
315
- const compactContextMessages = async (params: {
316
- previousSummary: string
317
- newMessages: ContextMessage[]
318
- }): Promise<CompactionOutput> => {
319
- const chunks = splitByCharBudget(params.newMessages, compactionChunkMaxChars)
320
- let summary = normalizeSummary(params.previousSummary)
321
-
322
- for (const chunk of chunks) {
323
- const transcript = toCompactionTranscript(chunk)
324
- const output = await options.runCompacter({ previousSummary: summary, chunk, transcript })
325
- summary = normalizeSummary(output.summary)
326
- }
327
-
328
- return { summary }
329
- }
330
-
331
- const rollupSummaryIfOversized = async (summary: string): Promise<string> => {
332
- if (estimateTokens(summary) <= summaryRollupMaxTokens) {
333
- return summary
334
- }
328
+ const compactContextMessagesEffect = (params: { previousSummary: string; newMessages: ContextMessage[] }) =>
329
+ Effect.gen(function* () {
330
+ const chunks = splitByCharBudget(params.newMessages, compactionChunkMaxChars)
331
+ let summary = normalizeSummary(params.previousSummary)
332
+
333
+ for (const chunk of chunks) {
334
+ const transcript = toCompactionTranscript(chunk)
335
+ const output = yield* Effect.tryPromise({
336
+ try: () => options.runCompacter({ previousSummary: summary, chunk, transcript }),
337
+ catch: (error: unknown) => new CompactionError({ message: String(error), cause: error }),
338
+ })
339
+ summary = normalizeSummary(output.summary)
340
+ }
335
341
 
336
- const output = await compactContextMessages({
337
- previousSummary: '',
338
- newMessages: [{ role: 'assistant', text: summary, sourceMessageId: 'summary-rollup' }],
342
+ return { summary }
339
343
  })
340
344
 
341
- return normalizeSummary(output.summary)
342
- }
343
-
344
- const compactHistory = async (params: CompactHistoryParams): Promise<CompactHistoryResult> => {
345
- let summaryText = normalizeSummary(params.summaryText)
346
- let remainingMessages = [...params.liveMessages]
347
- let compactedMessages: ChatMessage[] = []
348
- let lastCompactedMessageId: string | undefined
349
- const summaryPayload = buildSyntheticSummaryPayload(summaryText)
350
- const initialPayload = JSON.stringify([...(summaryPayload ? [summaryPayload] : []), ...remainingMessages])
351
- const inputChars = initialPayload.length
352
-
353
- const buildEarlyExitResult = (estimatedTokens: number): CompactHistoryResult => {
354
- const exitSummaryPayload = buildSyntheticSummaryPayload(summaryText)
355
- const outputPayload = JSON.stringify([...(exitSummaryPayload ? [exitSummaryPayload] : []), ...remainingMessages])
356
- return {
357
- compacted: compactedMessages.length > 0,
358
- summaryText,
359
- ...(lastCompactedMessageId ? { lastCompactedMessageId } : {}),
360
- compactedMessages,
361
- compactedMessageCount: compactedMessages.length,
362
- remainingMessageCount: remainingMessages.length,
363
- estimatedTokens,
364
- inputChars,
365
- outputChars: outputPayload.length,
345
+ const rollupSummaryIfOversizedEffect = (summary: string) =>
346
+ Effect.gen(function* () {
347
+ if (estimateTokens(summary) <= summaryRollupMaxTokens) {
348
+ return summary
366
349
  }
367
- }
368
350
 
369
- for (;;) {
370
- const assessment = shouldCompactHistory({
371
- summaryText,
372
- liveMessages: remainingMessages,
373
- contextSize: params.contextSize,
351
+ const output = yield* compactContextMessagesEffect({
352
+ previousSummary: '',
353
+ newMessages: [{ role: 'assistant', text: summary, sourceMessageId: 'summary-rollup' }],
374
354
  })
375
355
 
376
- if (!assessment.shouldCompact) {
377
- return buildEarlyExitResult(assessment.estimatedTokens)
378
- }
356
+ return normalizeSummary(output.summary)
357
+ })
379
358
 
380
- const boundary = Math.max(0, remainingMessages.length - params.tailMessageCount)
381
- if (boundary <= 0) {
382
- return buildEarlyExitResult(assessment.estimatedTokens)
359
+ const compactHistoryEffect = (params: CompactHistoryParams) =>
360
+ Effect.gen(function* () {
361
+ let summaryText = normalizeSummary(params.summaryText)
362
+ let remainingMessages = [...params.liveMessages]
363
+ let compactedMessages: ChatMessage[] = []
364
+ let lastCompactedMessageId: string | undefined
365
+ const summaryPayload = buildSyntheticSummaryPayload(summaryText)
366
+ const initialPayload = stringifyUnknown([...(summaryPayload ? [summaryPayload] : []), ...remainingMessages]) ?? ''
367
+ const inputChars = initialPayload.length
368
+
369
+ const buildEarlyExitResult = (estimatedTokens: number): CompactHistoryResult => {
370
+ const exitSummaryPayload = buildSyntheticSummaryPayload(summaryText)
371
+ const outputPayload =
372
+ stringifyUnknown([...(exitSummaryPayload ? [exitSummaryPayload] : []), ...remainingMessages]) ?? ''
373
+ return {
374
+ compacted: compactedMessages.length > 0,
375
+ summaryText,
376
+ ...(lastCompactedMessageId ? { lastCompactedMessageId } : {}),
377
+ compactedMessages,
378
+ compactedMessageCount: compactedMessages.length,
379
+ remainingMessageCount: remainingMessages.length,
380
+ estimatedTokens,
381
+ inputChars,
382
+ outputChars: outputPayload.length,
383
+ }
383
384
  }
384
385
 
385
- const candidatePrefix = remainingMessages.slice(0, boundary)
386
- const messagesToCompact = candidatePrefix.filter((message) => !readIsCompacted(message))
387
- const contextMessages = messagesToCompact
388
- .map(toContextMessageFromChatMessage)
389
- .filter((message) => compactWhitespace(message.text).length > 0)
390
- const sourceText = toCompactionTranscript(contextMessages)
386
+ for (;;) {
387
+ const assessment = shouldCompactHistory({
388
+ summaryText,
389
+ liveMessages: remainingMessages,
390
+ contextSize: params.contextSize,
391
+ })
391
392
 
392
- if (!compactWhitespace(sourceText)) {
393
- return buildEarlyExitResult(assessment.estimatedTokens)
394
- }
393
+ if (!assessment.shouldCompact) {
394
+ return buildEarlyExitResult(assessment.estimatedTokens)
395
+ }
396
+
397
+ const boundary = Math.max(0, remainingMessages.length - params.tailMessageCount)
398
+ if (boundary <= 0) {
399
+ return buildEarlyExitResult(assessment.estimatedTokens)
400
+ }
395
401
 
396
- let nextSummary = normalizeSummary(
397
- (await compactContextMessages({ previousSummary: summaryText, newMessages: contextMessages })).summary,
398
- )
399
- nextSummary = await rollupSummaryIfOversized(nextSummary)
402
+ const candidatePrefix = remainingMessages.slice(0, boundary)
403
+ const messagesToCompact = candidatePrefix.filter((message) => !readIsCompacted(message))
404
+ const contextMessages = messagesToCompact
405
+ .map(toContextMessageFromChatMessage)
406
+ .filter((message) => compactWhitespace(message.text).length > 0)
407
+ const sourceText = toCompactionTranscript(contextMessages)
400
408
 
401
- if (nextSummary.length >= sourceText.length) {
402
- throw new Error('Compaction summary is not shorter than compacted source')
403
- }
409
+ if (!compactWhitespace(sourceText)) {
410
+ return buildEarlyExitResult(assessment.estimatedTokens)
411
+ }
404
412
 
405
- summaryText = nextSummary
406
- compactedMessages = [
407
- ...compactedMessages,
408
- ...candidatePrefix.map((message) => markMessageCompacted(message, now)),
409
- ]
410
- lastCompactedMessageId = candidatePrefix.at(-1)?.id ?? lastCompactedMessageId
411
- remainingMessages = remainingMessages.slice(boundary)
413
+ let nextSummary = normalizeSummary(
414
+ (yield* compactContextMessagesEffect({ previousSummary: summaryText, newMessages: contextMessages })).summary,
415
+ )
416
+ nextSummary = yield* rollupSummaryIfOversizedEffect(nextSummary)
412
417
 
413
- if (remainingMessages.length <= params.tailMessageCount) {
414
- continue
418
+ if (nextSummary.length >= sourceText.length) {
419
+ return yield* new CompactionError({ message: 'Compaction summary is not shorter than compacted source' })
420
+ }
421
+
422
+ summaryText = nextSummary
423
+ compactedMessages = [
424
+ ...compactedMessages,
425
+ ...candidatePrefix.map((message) => markMessageCompacted(message, now)),
426
+ ]
427
+ lastCompactedMessageId = candidatePrefix.at(-1)?.id ?? lastCompactedMessageId
428
+ remainingMessages = remainingMessages.slice(boundary)
429
+
430
+ if (remainingMessages.length <= params.tailMessageCount) {
431
+ continue
432
+ }
415
433
  }
416
- }
417
- }
434
+ })
418
435
 
419
- return { createSummaryMessage, prependSummaryMessage, estimateThreshold, shouldCompactHistory, compactHistory }
436
+ return {
437
+ createSummaryMessage,
438
+ prependSummaryMessage,
439
+ estimateThreshold,
440
+ shouldCompactHistory,
441
+ compactHistory: (params) => Effect.runPromise(compactHistoryEffect(params)),
442
+ }
420
443
  }
421
444
 
422
445
  export function parseCompactionOutput(value: unknown): CompactionOutput {
@@ -1,6 +1,6 @@
1
1
  import type { PlanExecutionVisibility, PlanNodeSpecRecord, PlanSpecRecord } from '@lota-sdk/shared'
2
2
 
3
- import { agentRoster } from '../config/agent-defaults'
3
+ import { isAgentName } from '../config/agent-defaults'
4
4
 
5
5
  export function resolvePlanNodeExecutionVisibility(
6
6
  plan: Pick<PlanSpecRecord, 'defaultExecutionVisibility'>,
@@ -12,7 +12,7 @@ export function resolvePlanNodeExecutionVisibility(
12
12
  return configuredVisibility
13
13
  }
14
14
 
15
- return node.owner.executorType === 'agent' && agentRoster.includes(node.owner.ref) ? 'visible' : 'silent'
15
+ return node.owner.executorType === 'agent' && isAgentName(node.owner.ref) ? 'visible' : 'silent'
16
16
  }
17
17
 
18
18
  export function shouldPlanNodeUseVisibleTurn(
@@ -1,4 +1,5 @@
1
1
  import type { SerializableExecutionPlan } from '@lota-sdk/shared'
2
+ import { Schema, Duration, Effect } from 'effect'
2
3
 
3
4
  type ExecutionPlanPromptSummary = Pick<SerializableExecutionPlan, 'runId' | 'title'>
4
5
 
@@ -24,6 +25,15 @@ function formatExecutionPlansForPrompt(plans: SerializableExecutionPlan[]): stri
24
25
  return ['<execution-plan-state>', JSON.stringify(payload, null, 2), '</execution-plan-state>'].join('\n')
25
26
  }
26
27
 
28
+ export class ExecutionPlanCacheError extends Schema.TaggedErrorClass<ExecutionPlanCacheError>()(
29
+ 'ExecutionPlanCacheError',
30
+ { message: Schema.String, cause: Schema.Defect },
31
+ ) {}
32
+
33
+ export function toExecutionPlanCacheError(cause: unknown): ExecutionPlanCacheError {
34
+ return new ExecutionPlanCacheError({ message: cause instanceof Error ? cause.message : String(cause), cause })
35
+ }
36
+
27
37
  export function buildExecutionPlanInstructionSections(plans: SerializableExecutionPlan[] | null | undefined): string[] {
28
38
  const normalized = plans ?? []
29
39
  return [EXECUTION_PLAN_AGENT_PROTOCOL_PROMPT, formatExecutionPlansForPrompt(normalized)]
@@ -31,27 +41,44 @@ export function buildExecutionPlanInstructionSections(plans: SerializableExecuti
31
41
 
32
42
  export function createExecutionPlanInstructionSectionCache(params: {
33
43
  disabled?: boolean
34
- loadPlans: () => Promise<SerializableExecutionPlan[]>
44
+ loadPlansEffect: () => Effect.Effect<SerializableExecutionPlan[], ExecutionPlanCacheError>
35
45
  }) {
36
- let plansPromise: Promise<SerializableExecutionPlan[]> | null = null
37
- let sectionsPromise: Promise<string[] | undefined> | null = null
46
+ const [getPlansCachedEffect, invalidatePlansEffect] = Effect.runSync(
47
+ Effect.cachedInvalidateWithTTL(
48
+ Effect.suspend(() => (params.disabled ? Effect.succeed([]) : params.loadPlansEffect())),
49
+ Duration.infinity,
50
+ ),
51
+ )
52
+ const [getSectionsCachedEffect, invalidateSectionsEffect] = Effect.runSync(
53
+ Effect.cachedInvalidateWithTTL(
54
+ Effect.gen(function* () {
55
+ if (params.disabled) {
56
+ return undefined
57
+ }
58
+
59
+ const plans = yield* getPlansCachedEffect
60
+ return buildExecutionPlanInstructionSections(plans)
61
+ }),
62
+ Duration.infinity,
63
+ ),
64
+ )
65
+
66
+ const getPlansEffect = (): Effect.Effect<SerializableExecutionPlan[], ExecutionPlanCacheError> => getPlansCachedEffect
67
+
68
+ const getSectionsEffect = (): Effect.Effect<string[] | undefined, ExecutionPlanCacheError> => getSectionsCachedEffect
38
69
 
39
70
  return {
40
71
  invalidate() {
41
- plansPromise = null
42
- sectionsPromise = null
72
+ Effect.runSync(invalidatePlansEffect)
73
+ Effect.runSync(invalidateSectionsEffect)
43
74
  },
44
- async getPlans(): Promise<SerializableExecutionPlan[]> {
45
- if (params.disabled) return []
46
-
47
- plansPromise ??= params.loadPlans()
48
- return plansPromise
75
+ getPlansEffect,
76
+ getSectionsEffect,
77
+ getPlans() {
78
+ return Effect.runPromise(getPlansEffect())
49
79
  },
50
- async getSections(): Promise<string[] | undefined> {
51
- if (params.disabled) return undefined
52
-
53
- sectionsPromise ??= this.getPlans().then((plans) => buildExecutionPlanInstructionSections(plans))
54
- return sectionsPromise
80
+ getSections() {
81
+ return Effect.runPromise(getSectionsEffect())
55
82
  },
56
83
  }
57
84
  }
@@ -1,15 +1,19 @@
1
1
  import type { GraphDesignRequest, GraphDesignResponse } from '@lota-sdk/shared'
2
+ import { Effect } from 'effect'
3
+
4
+ import { getOptionalCurrentRuntime } from '../effect/runtime-ref'
5
+ import { RuntimeConfigServiceTag } from '../effect/services'
2
6
 
3
7
  export interface GraphDesigner {
4
8
  designGraph(request: GraphDesignRequest): Promise<GraphDesignResponse>
5
9
  }
6
10
 
7
- let _graphDesigner: GraphDesigner | null = null
8
-
9
- export function configureGraphDesigner(designer: GraphDesigner): void {
10
- _graphDesigner = designer
11
- }
12
-
13
11
  export function getGraphDesigner(): GraphDesigner | null {
14
- return _graphDesigner
12
+ const runtime = getOptionalCurrentRuntime()
13
+ if (!runtime) {
14
+ return null
15
+ }
16
+
17
+ const runtimeConfig = runtime.runSync(Effect.service(RuntimeConfigServiceTag))
18
+ return runtimeConfig.graphDesigner ?? null
15
19
  }
@@ -5,8 +5,13 @@ import type {
5
5
  ToolLoopAgentOnStepFinishCallback,
6
6
  ToolSet,
7
7
  } from 'ai'
8
+ import { Context, Duration, Effect, Layer, Metric } from 'effect'
8
9
  import type { ZodSchema } from 'zod'
9
10
 
11
+ import type { ValidationError } from '../effect/errors'
12
+ import { AiGenerationError, BadRequestError } from '../effect/errors'
13
+ import { summarizeZodIssues, toValidationError } from '../effect/zod'
14
+ import { nowEpochMillis } from '../utils/date-time'
10
15
  import { isRecord, stringifyUnknown } from '../utils/string'
11
16
 
12
17
  export interface HelperToolLoopAgentOptions {
@@ -70,7 +75,7 @@ function isRateLimitError(error: unknown): boolean {
70
75
  return getErrorStatus(error) === 429
71
76
  }
72
77
 
73
- function formatError(tag: string, error: unknown): Error {
78
+ function formatGenerationError(tag: string, error: unknown): AiGenerationError {
74
79
  const status = getErrorStatus(error)
75
80
  const rateLimited = isRateLimitError(error)
76
81
  const message = error instanceof Error ? error.message : String(error)
@@ -87,7 +92,73 @@ function formatError(tag: string, error: unknown): Error {
87
92
  if (responseBody) parts.push(`response_body=${responseBody}`)
88
93
  if (requestUrl) parts.push(`url=${requestUrl}`)
89
94
 
90
- return new Error(parts.join(' '))
95
+ return new AiGenerationError({
96
+ source: tag,
97
+ message: parts.join(' '),
98
+ ...(status !== null ? { status } : {}),
99
+ ...(rateLimited ? { rateLimited: true } : {}),
100
+ ...(responseData ? { providerData: responseData } : {}),
101
+ ...(responseBody ? { responseBody } : {}),
102
+ ...(requestUrl ? { url: requestUrl } : {}),
103
+ })
104
+ }
105
+
106
+ function emptyHelperMessagesError(tag: string): BadRequestError {
107
+ return new BadRequestError({ message: `[${tag}] Empty helper messages` })
108
+ }
109
+
110
+ function parseStructuredRootCandidate<T>(params: {
111
+ tag: string
112
+ schema: ZodSchema<T>
113
+ candidate: unknown
114
+ }): { data: T } | { error: ValidationError } {
115
+ const directParsed = params.schema.safeParse(params.candidate)
116
+ if (directParsed.success) {
117
+ return { data: directParsed.data }
118
+ }
119
+
120
+ return {
121
+ error: toValidationError(
122
+ directParsed.error,
123
+ `[${params.tag}] Structured output failed schema validation: ${summarizeZodIssues(directParsed.error.issues)}`,
124
+ ),
125
+ }
126
+ }
127
+
128
+ function createHelperAgent(params: {
129
+ tag: string
130
+ createAgent: CreateHelperAgentFn
131
+ systemPrompt?: string
132
+ temperature?: number
133
+ maxOutputTokens?: number
134
+ output?: Output.Output
135
+ }): Effect.Effect<HelperAgent, AiGenerationError> {
136
+ return Effect.try({
137
+ try: () =>
138
+ params.createAgent({
139
+ ...(params.systemPrompt ? { instructions: params.systemPrompt } : {}),
140
+ ...(params.output ? { output: params.output } : {}),
141
+ ...(typeof params.temperature === 'number' ? { temperature: params.temperature } : {}),
142
+ ...(typeof params.maxOutputTokens === 'number' ? { maxOutputTokens: params.maxOutputTokens } : {}),
143
+ maxRetries: 2,
144
+ }),
145
+ catch: (error) => formatGenerationError(params.tag, error),
146
+ })
147
+ }
148
+
149
+ function generateWithHelperAgent(
150
+ tag: string,
151
+ agent: HelperAgent,
152
+ params: { messages: HelperMessage[]; timeoutMs?: number },
153
+ ): Effect.Effect<{ text?: unknown; output?: unknown }, AiGenerationError> {
154
+ return Effect.tryPromise({
155
+ try: () =>
156
+ agent.generate({
157
+ messages: toModelMessages(params.messages),
158
+ ...(typeof params.timeoutMs === 'number' ? { timeout: params.timeoutMs } : {}),
159
+ }),
160
+ catch: (error) => formatGenerationError(tag, error),
161
+ })
91
162
  }
92
163
 
93
164
  function toModelMessages(messages: HelperMessage[]): Array<{ role: 'user' | 'assistant'; content: string }> {
@@ -118,13 +189,6 @@ function resolveStructuredSystemPrompt(systemPrompt?: string): string {
118
189
  return parts.join('\n\n')
119
190
  }
120
191
 
121
- function formatSchemaIssueSummary(issues: Array<{ path: PropertyKey[]; message: string }>): string {
122
- return issues
123
- .slice(0, 5)
124
- .map((issue) => `${issue.path.map((segment) => String(segment)).join('.') || 'root'}: ${issue.message}`)
125
- .join('; ')
126
- }
127
-
128
192
  function parseStructuredCandidate<T>(params: {
129
193
  schema: ZodSchema<T>
130
194
  candidate: unknown
@@ -147,40 +211,54 @@ function parseStructuredCandidate<T>(params: {
147
211
  return null
148
212
  }
149
213
 
214
+ const helperModelDuration = Metric.timer('helper_model_duration')
215
+
216
+ function recordHelperModelDuration(tag: string, startedAt: number): Effect.Effect<void, never, never> {
217
+ return Metric.update(
218
+ Metric.withAttributes(helperModelDuration, { tag }),
219
+ Duration.millis(Math.max(0, nowEpochMillis() - startedAt)),
220
+ )
221
+ }
222
+
150
223
  export function createHelperModelRuntime() {
151
- async function generateHelperText(params: GenerateHelperTextParams): Promise<string> {
224
+ function generateHelperTextEffect(
225
+ params: GenerateHelperTextParams,
226
+ ): Effect.Effect<string, AiGenerationError | BadRequestError | ValidationError> {
152
227
  const systemPrompt = resolveSystemPrompt({
153
228
  explicitSystemPrompt: params.systemPrompt,
154
229
  defaultSystemPrompt: params.defaultSystemPrompt,
155
230
  })
156
231
 
157
232
  if (!systemPrompt && params.messages.length === 0) {
158
- throw new Error(`[${params.tag}] Empty helper messages`)
233
+ return Effect.fail(emptyHelperMessagesError(params.tag))
159
234
  }
160
235
 
161
- try {
162
- const agent = params.createAgent({
163
- ...(systemPrompt ? { instructions: systemPrompt } : {}),
164
- ...(typeof params.temperature === 'number' ? { temperature: params.temperature } : {}),
165
- ...(typeof params.maxOutputTokens === 'number' ? { maxOutputTokens: params.maxOutputTokens } : {}),
166
- maxRetries: 2,
167
- })
236
+ const startedAt = nowEpochMillis()
168
237
 
169
- const result = await agent.generate({
170
- messages: toModelMessages(params.messages),
171
- ...(typeof params.timeoutMs === 'number' ? { timeout: params.timeoutMs } : {}),
238
+ return Effect.gen(function* () {
239
+ const agent = yield* createHelperAgent({
240
+ tag: params.tag,
241
+ createAgent: params.createAgent,
242
+ systemPrompt,
243
+ temperature: params.temperature,
244
+ maxOutputTokens: params.maxOutputTokens,
172
245
  })
246
+ const result = yield* generateWithHelperAgent(params.tag, agent, params)
173
247
  const text = typeof result.text === 'string' ? result.text.trim() : ''
174
248
  if (!text) {
175
- throw new Error('Empty helper model output')
249
+ return yield* formatGenerationError(params.tag, new Error('Empty helper model output'))
176
250
  }
177
251
  return text
178
- } catch (error) {
179
- throw formatError(params.tag, error)
180
- }
252
+ }).pipe(Effect.ensuring(recordHelperModelDuration(params.tag, startedAt)))
181
253
  }
182
254
 
183
- async function generateHelperStructured<T>(params: GenerateHelperStructuredParams<T>): Promise<T> {
255
+ function generateHelperText(params: GenerateHelperTextParams): Promise<string> {
256
+ return Effect.runPromise(generateHelperTextEffect(params))
257
+ }
258
+
259
+ function generateHelperStructuredEffect<T>(
260
+ params: GenerateHelperStructuredParams<T>,
261
+ ): Effect.Effect<T, AiGenerationError | BadRequestError | ValidationError> {
184
262
  const baseSystemPrompt = resolveSystemPrompt({
185
263
  explicitSystemPrompt: params.systemPrompt,
186
264
  defaultSystemPrompt: params.defaultSystemPrompt,
@@ -188,39 +266,48 @@ export function createHelperModelRuntime() {
188
266
  const systemPrompt = resolveStructuredSystemPrompt(baseSystemPrompt)
189
267
 
190
268
  if (!baseSystemPrompt && params.messages.length === 0) {
191
- throw new Error(`[${params.tag}] Empty helper messages`)
269
+ return Effect.fail(emptyHelperMessagesError(params.tag))
192
270
  }
193
271
 
194
- try {
195
- const agent = params.createAgent({
196
- output: Output.object({ schema: params.schema }),
197
- ...(systemPrompt ? { instructions: systemPrompt } : {}),
198
- ...(typeof params.temperature === 'number' ? { temperature: params.temperature } : {}),
199
- ...(typeof params.maxOutputTokens === 'number' ? { maxOutputTokens: params.maxOutputTokens } : {}),
200
- maxRetries: 2,
201
- })
272
+ const startedAt = nowEpochMillis()
202
273
 
203
- const result = await agent.generate({
204
- messages: toModelMessages(params.messages),
205
- ...(typeof params.timeoutMs === 'number' ? { timeout: params.timeoutMs } : {}),
274
+ return Effect.gen(function* () {
275
+ const agent = yield* createHelperAgent({
276
+ tag: params.tag,
277
+ createAgent: params.createAgent,
278
+ systemPrompt,
279
+ temperature: params.temperature,
280
+ maxOutputTokens: params.maxOutputTokens,
281
+ output: Output.object({ schema: params.schema }),
206
282
  })
283
+ const result = yield* generateWithHelperAgent(params.tag, agent, params)
207
284
  const parsed = parseStructuredCandidate({ schema: params.schema, candidate: result.output })
208
285
  if (parsed) {
209
286
  return parsed.data
210
287
  }
211
288
 
212
- const directParsed = params.schema.safeParse(result.output)
213
- if (!directParsed.success) {
214
- throw new Error(
215
- `Structured output failed schema validation: ${formatSchemaIssueSummary(directParsed.error.issues)}`,
216
- )
289
+ const validated = parseStructuredRootCandidate({
290
+ tag: params.tag,
291
+ schema: params.schema,
292
+ candidate: result.output,
293
+ })
294
+ if ('error' in validated) {
295
+ return yield* validated.error
217
296
  }
218
297
 
219
- return directParsed.data
220
- } catch (error) {
221
- throw formatError(params.tag, error)
222
- }
298
+ return validated.data
299
+ }).pipe(Effect.ensuring(recordHelperModelDuration(params.tag, startedAt)))
300
+ }
301
+
302
+ function generateHelperStructured<T>(params: GenerateHelperStructuredParams<T>): Promise<T> {
303
+ return Effect.runPromise(generateHelperStructuredEffect(params))
223
304
  }
224
305
 
225
- return { generateHelperText, generateHelperStructured }
306
+ return { generateHelperTextEffect, generateHelperText, generateHelperStructuredEffect, generateHelperStructured }
226
307
  }
308
+
309
+ export type HelperModelRuntime = ReturnType<typeof createHelperModelRuntime>
310
+
311
+ export class HelperModelTag extends Context.Service<HelperModelTag, HelperModelRuntime>()('HelperModel') {}
312
+
313
+ export const HelperModelLive = Layer.succeed(HelperModelTag, createHelperModelRuntime())
@@ -4,15 +4,15 @@ export * from './agent-stream-helpers'
4
4
  export * from './agent-types'
5
5
  export * from './chat-request-routing'
6
6
  export * from './chat-run-registry'
7
- export * from './context-compaction'
7
+ export * from './context-compaction/context-compaction'
8
8
  export * from './execution-plan'
9
9
  export * from './graph-designer'
10
10
  export * from './helper-model'
11
11
  export * from './indexed-repositories-policy'
12
12
  export * from './instruction-sections'
13
- export * from './memory-block'
14
- export * from './memory-digest-policy'
15
- export * from './memory-scope'
13
+ export * from './memory/memory-block'
14
+ export * from './memory/memory-digest-policy'
15
+ export * from './memory/memory-scope'
16
16
  export * from './llm-content'
17
17
  export * from './plugin-resolution'
18
18
  export * from './plugin-types'
@@ -20,8 +20,8 @@ export * from './runtime-config'
20
20
  export * from './runtime-extensions'
21
21
  export * from './runtime-worker-registry'
22
22
  export * from './skill-extraction-policy'
23
- export * from './social-chat'
24
- export * from './team-consultation-orchestrator'
25
- export * from './team-consultation-prompts'
23
+ export * from './social-chat/social-chat'
24
+ export * from './team-consultation/team-consultation-orchestrator'
25
+ export * from './team-consultation/team-consultation-prompts'
26
26
  export * from './turn-lifecycle'
27
27
  export * from './thread-chat-helpers'