@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,27 +1,307 @@
1
- import type { LogLevel } from '@logtape/logtape'
2
- import { configure, getAnsiColorFormatter, getConsoleSink, getLogger as getLogTapeLogger } from '@logtape/logtape'
1
+ import { Effect, Logger, References } from 'effect'
2
+
3
+ import { getOptionalCurrentRuntime } from '../effect/runtime-ref'
3
4
 
4
5
  const LOG_CATEGORY = 'lota-sdk'
5
6
 
6
- export async function configureLotaLogger(logLevel: LogLevel = 'info'): Promise<void> {
7
- const formatter = getAnsiColorFormatter({ level: 'FULL' })
7
+ export type LotaLogLevel = 'trace' | 'debug' | 'info' | 'warning' | 'error' | 'fatal'
8
+
9
+ type LogFields = Record<string, unknown>
10
+ type LotaLogEffect = Effect.Effect<void, never, never>
11
+ type EffectLogLevel = 'Trace' | 'Debug' | 'Info' | 'Warn' | 'Error' | 'Fatal'
8
12
 
9
- await configure({
10
- reset: true,
11
- sinks: { console: getConsoleSink({ formatter }) },
12
- loggers: [
13
- { category: ['logtape', 'meta'], lowestLevel: 'warning', sinks: ['console'] },
14
- { category: ['server'], lowestLevel: logLevel, sinks: ['console'] },
15
- { category: [LOG_CATEGORY], lowestLevel: logLevel, sinks: ['console'] },
16
- { category: ['hono'], lowestLevel: logLevel, sinks: ['console'] },
17
- ],
18
- })
13
+ export interface LotaLoggerMethod {
14
+ (message: string, fields?: LogFields): void
15
+ (strings: TemplateStringsArray, ...values: Array<unknown>): void
19
16
  }
20
17
 
21
- export function getLogger(category: readonly string[]) {
22
- return getLogTapeLogger([...category])
18
+ export interface LotaLogger {
19
+ trace: LotaLoggerMethod
20
+ debug: LotaLoggerMethod
21
+ info: LotaLoggerMethod
22
+ warn: LotaLoggerMethod
23
+ error: LotaLoggerMethod
24
+ fatal: LotaLoggerMethod
25
+ }
26
+
27
+ const LOG_LEVEL_PRIORITY: Record<LotaLogLevel, number> = { trace: 0, debug: 1, info: 2, warning: 3, error: 4, fatal: 5 }
28
+
29
+ export function resolveLotaLogLevel(value: string | null | undefined): LotaLogLevel | null {
30
+ const normalized = value?.trim().toLowerCase()
31
+ switch (normalized) {
32
+ case 'trace':
33
+ case 'debug':
34
+ case 'info':
35
+ case 'warning':
36
+ case 'error':
37
+ case 'fatal':
38
+ return normalized
39
+ default:
40
+ return null
41
+ }
23
42
  }
24
43
 
25
- export const serverLogger = getLogger([LOG_CATEGORY])
26
- export const chatLogger = getLogger([LOG_CATEGORY, 'chat'])
27
- export const aiLogger = getLogger([LOG_CATEGORY, 'ai'])
44
+ function readEnvLogLevel(): LotaLogLevel | null {
45
+ const value = Bun.env.LOG_LEVEL?.trim().toLowerCase()
46
+ return resolveLotaLogLevel(value)
47
+ }
48
+
49
+ // Module-level cache: loggers expose a sync `.info(...)` API that must work
50
+ // from both Effect and non-Effect call sites, so the level cannot live in
51
+ // Effect.Config (which requires Effect context to read). `configureLotaLogger`
52
+ // is called once from `buildInfrastructureLayer` with the resolved value.
53
+ let configuredLogLevel: LotaLogLevel = readEnvLogLevel() ?? 'info'
54
+
55
+ export function configureLotaLogger(logLevel: LotaLogLevel = 'info'): void {
56
+ configuredLogLevel = logLevel
57
+ }
58
+
59
+ export function getConfiguredLogLevel(): LotaLogLevel {
60
+ return configuredLogLevel
61
+ }
62
+
63
+ export function toEffectLogLevel(logLevel: LotaLogLevel): EffectLogLevel {
64
+ switch (logLevel) {
65
+ case 'trace':
66
+ return 'Trace'
67
+ case 'debug':
68
+ return 'Debug'
69
+ case 'info':
70
+ return 'Info'
71
+ case 'warning':
72
+ return 'Warn'
73
+ case 'error':
74
+ return 'Error'
75
+ case 'fatal':
76
+ return 'Fatal'
77
+ }
78
+ }
79
+
80
+ function shouldLog(level: LotaLogLevel): boolean {
81
+ return LOG_LEVEL_PRIORITY[level] >= LOG_LEVEL_PRIORITY[configuredLogLevel]
82
+ }
83
+
84
+ function isTemplateStringsArray(value: unknown): value is TemplateStringsArray {
85
+ return Array.isArray(value) && Object.prototype.hasOwnProperty.call(value, 'raw')
86
+ }
87
+
88
+ function describeObject(value: object): string {
89
+ return Object.prototype.toString.call(value)
90
+ }
91
+
92
+ function describeScalar(value: unknown): string {
93
+ if (value === null) return 'null'
94
+ if (value === undefined) return 'undefined'
95
+ if (typeof value === 'symbol') {
96
+ return value.description ? `Symbol(${value.description})` : 'Symbol'
97
+ }
98
+ if (typeof value === 'function') {
99
+ return `[Function ${value.name || 'anonymous'}]`
100
+ }
101
+
102
+ return JSON.stringify(value)
103
+ }
104
+
105
+ function normalizeJsonValue(value: unknown): unknown {
106
+ if (typeof value === 'bigint') return value.toString()
107
+ if (value instanceof Date) return value.toISOString()
108
+ if (value instanceof Error) {
109
+ return { name: value.name, message: value.message, stack: value.stack }
110
+ }
111
+ return value
112
+ }
113
+
114
+ function normalizeAnnotationValue(value: unknown): unknown {
115
+ if (value === null || value === undefined) return value
116
+ if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') return value
117
+ if (typeof value === 'bigint') return value.toString()
118
+ if (value instanceof Date) return value.toISOString()
119
+ if (value instanceof Error) {
120
+ return { name: value.name, message: value.message, stack: value.stack }
121
+ }
122
+
123
+ try {
124
+ return JSON.parse(JSON.stringify(value, (_key, nestedValue) => normalizeJsonValue(nestedValue)))
125
+ } catch {
126
+ return typeof value === 'object' ? describeObject(value) : describeScalar(value)
127
+ }
128
+ }
129
+
130
+ function formatLogValue(value: unknown): string {
131
+ if (value instanceof Error) {
132
+ return `${value.name}: ${value.message}`
133
+ }
134
+ if (typeof value === 'string') return value
135
+ if (typeof value === 'number' || typeof value === 'boolean' || typeof value === 'bigint') return String(value)
136
+ if (value === null) return 'null'
137
+ if (value === undefined) return 'undefined'
138
+ if (value instanceof Date) return value.toISOString()
139
+
140
+ try {
141
+ return JSON.stringify(normalizeAnnotationValue(value))
142
+ } catch {
143
+ return typeof value === 'object' ? describeObject(value) : describeScalar(value)
144
+ }
145
+ }
146
+
147
+ function formatTemplate(strings: TemplateStringsArray, values: Array<unknown>): string {
148
+ let output = ''
149
+
150
+ for (let index = 0; index < strings.length; index++) {
151
+ output += strings[index]
152
+ if (index < values.length) {
153
+ output += formatLogValue(values[index])
154
+ }
155
+ }
156
+
157
+ return output
158
+ }
159
+
160
+ function applyFieldPlaceholders(message: string, fields?: LogFields): string {
161
+ if (!fields) return message
162
+
163
+ let output = message
164
+ for (const [key, value] of Object.entries(fields)) {
165
+ output = output.replaceAll(`{${key}}`, formatLogValue(value))
166
+ }
167
+ return output
168
+ }
169
+
170
+ function buildLogContext(fields?: LogFields): Record<string, unknown> {
171
+ const context: Record<string, unknown> = {}
172
+ if (!fields) {
173
+ return context
174
+ }
175
+
176
+ for (const [key, value] of Object.entries(fields)) {
177
+ if (value === undefined) continue
178
+ context[key] = normalizeAnnotationValue(value)
179
+ }
180
+
181
+ return context
182
+ }
183
+
184
+ function getConsoleMethod(level: LotaLogLevel): (...args: Array<unknown>) => void {
185
+ switch (level) {
186
+ case 'trace':
187
+ return console.trace
188
+ case 'debug':
189
+ return console.debug
190
+ case 'info':
191
+ return console.info
192
+ case 'warning':
193
+ return console.warn
194
+ case 'error':
195
+ return console.error
196
+ case 'fatal':
197
+ return console.error
198
+ }
199
+ }
200
+
201
+ function getLogEffect(level: LotaLogLevel, message: string): LotaLogEffect {
202
+ switch (level) {
203
+ case 'trace':
204
+ return Effect.logTrace(message)
205
+ case 'debug':
206
+ return Effect.logDebug(message)
207
+ case 'info':
208
+ return Effect.logInfo(message)
209
+ case 'warning':
210
+ return Effect.logWarning(message)
211
+ case 'error':
212
+ return Effect.logError(message)
213
+ case 'fatal':
214
+ return Effect.logFatal(message)
215
+ }
216
+ }
217
+
218
+ function runStandaloneLogEffect(effect: LotaLogEffect): void {
219
+ Effect.runSync(
220
+ effect.pipe(
221
+ Effect.provide(Logger.layer([Logger.consolePretty()])),
222
+ Effect.provideService(References.MinimumLogLevel, toEffectLogLevel(configuredLogLevel)),
223
+ ),
224
+ )
225
+ }
226
+
227
+ function emitConsoleFallback(
228
+ level: LotaLogLevel,
229
+ category: readonly string[],
230
+ message: string,
231
+ annotations: Record<string, unknown>,
232
+ ): void {
233
+ const formattedMessage = `[${category.join(':')}] ${message}`
234
+ const sink = getConsoleMethod(level)
235
+ const { lotaLogger: _lotaLogger, ...rest } = annotations
236
+ if (Object.keys(rest).length === 0) {
237
+ sink(formattedMessage)
238
+ return
239
+ }
240
+
241
+ sink(formattedMessage, rest)
242
+ }
243
+
244
+ function emit(level: LotaLogLevel, category: readonly string[], message: string, fields?: LogFields): void {
245
+ if (!shouldLog(level)) return
246
+
247
+ const formattedMessage = applyFieldPlaceholders(message, fields)
248
+ const annotations = { lotaLogger: category.join(':'), ...buildLogContext(fields) }
249
+ const effect = getLogEffect(level, formattedMessage).pipe(Effect.annotateLogs(annotations))
250
+
251
+ // Route through the seated managed runtime so log timestamps stay on the
252
+ // caller's fiber and inherit its span/log annotations. Pre-bootstrap (no
253
+ // runtime seated) uses the standalone Effect logger so output stays
254
+ // consistent with post-bootstrap formatting.
255
+ const runtime = getOptionalCurrentRuntime()
256
+ try {
257
+ if (runtime) {
258
+ runtime.runFork(effect)
259
+ } else {
260
+ runStandaloneLogEffect(effect)
261
+ }
262
+ } catch {
263
+ emitConsoleFallback(level, category, formattedMessage, annotations)
264
+ }
265
+ }
266
+
267
+ function createLoggerMethod(level: LotaLogLevel, category: readonly string[]): LotaLoggerMethod {
268
+ return (messageOrStrings: string | TemplateStringsArray, ...values: Array<unknown>) => {
269
+ if (isTemplateStringsArray(messageOrStrings)) {
270
+ emit(level, category, formatTemplate(messageOrStrings, values))
271
+ return
272
+ }
273
+
274
+ const [candidateFields] = values
275
+ const fields =
276
+ candidateFields && typeof candidateFields === 'object' && !Array.isArray(candidateFields)
277
+ ? (candidateFields as LogFields)
278
+ : undefined
279
+
280
+ emit(level, category, messageOrStrings, fields)
281
+ }
282
+ }
283
+
284
+ export function getLogger(category: readonly string[]): LotaLogger {
285
+ return {
286
+ trace: createLoggerMethod('trace', category),
287
+ debug: createLoggerMethod('debug', category),
288
+ info: createLoggerMethod('info', category),
289
+ warn: createLoggerMethod('warning', category),
290
+ error: createLoggerMethod('error', category),
291
+ fatal: createLoggerMethod('fatal', category),
292
+ }
293
+ }
294
+
295
+ export const serverLogger: LotaLogger = getLogger([LOG_CATEGORY])
296
+ export const chatLogger: LotaLogger = getLogger([LOG_CATEGORY, 'chat'])
297
+ export const aiLogger: LotaLogger = getLogger([LOG_CATEGORY, 'ai'])
298
+
299
+ export interface LotaLoggerSet {
300
+ server: LotaLogger
301
+ chat: LotaLogger
302
+ ai: LotaLogger
303
+ }
304
+
305
+ export function getLotaLoggers(): LotaLoggerSet {
306
+ return { server: serverLogger, chat: chatLogger, ai: aiLogger }
307
+ }
@@ -1,3 +1,6 @@
1
+ import { resolveLotaService } from '../effect/runtime'
2
+ import { ThreadConfigServiceTag } from '../effect/services'
3
+
1
4
  export interface ThreadBootstrapWelcomeConfig {
2
5
  defaultAgentId: string
3
6
  buildMessageText: (params: { userName?: string | null }) => string
@@ -23,16 +26,19 @@ interface ResolvedThreadBootstrapConfig {
23
26
  onboardingWelcome?: ThreadBootstrapWelcomeConfig
24
27
  }
25
28
 
26
- const DEFAULT_THREAD_BOOTSTRAP_CONFIG: ResolvedThreadBootstrapConfig = {
27
- onboardingDefaultAgents: [],
28
- completedDefaultAgents: [],
29
- threadTypesAfterOnboarding: [],
30
- ensureDefaultGroupOnCompleted: true,
29
+ export type { ResolvedThreadBootstrapConfig }
30
+
31
+ let currentThreadBootstrapConfig: ResolvedThreadBootstrapConfig | null = null
32
+
33
+ export function configureThreadRuntimeDefaults(config: ResolvedThreadBootstrapConfig): void {
34
+ currentThreadBootstrapConfig = config
31
35
  }
32
36
 
33
- let resolvedThreadBootstrapConfig: ResolvedThreadBootstrapConfig = DEFAULT_THREAD_BOOTSTRAP_CONFIG
37
+ export function clearThreadRuntimeDefaults(): void {
38
+ currentThreadBootstrapConfig = null
39
+ }
34
40
 
35
- function withDedupedStrings(values: readonly string[]): string[] {
41
+ function normalizeThreadAgentIds(values: readonly string[]): readonly string[] {
36
42
  const seen = new Set<string>()
37
43
  const deduped: string[] = []
38
44
 
@@ -46,27 +52,41 @@ function withDedupedStrings(values: readonly string[]): string[] {
46
52
  return deduped
47
53
  }
48
54
 
49
- export function configureThreads(params: { agentRoster: readonly string[]; config?: LotaThreadConfig }): void {
55
+ function resolveBootstrapAgentIds(
56
+ agentRoster: readonly string[],
57
+ onboardingAgents: readonly string[] | undefined,
58
+ welcomeAgentId: string | undefined,
59
+ ): readonly string[] {
60
+ return normalizeThreadAgentIds([...(onboardingAgents ?? agentRoster), ...(welcomeAgentId ? [welcomeAgentId] : [])])
61
+ }
62
+
63
+ export function resolveThreadConfig(params: {
64
+ agentRoster: readonly string[]
65
+ config?: LotaThreadConfig
66
+ }): ResolvedThreadBootstrapConfig {
50
67
  const bootstrap = params.config?.bootstrap
51
68
  const onboardingWelcome = bootstrap?.onboardingWelcome
52
- const onboardingDefaultAgents = withDedupedStrings([
53
- ...(bootstrap?.onboardingDefaultAgents ?? params.agentRoster),
54
- ...(onboardingWelcome ? [onboardingWelcome.defaultAgentId] : []),
55
- ])
56
-
57
- resolvedThreadBootstrapConfig = {
58
- onboardingDefaultAgents,
59
- completedDefaultAgents: withDedupedStrings(bootstrap?.completedDefaultAgents ?? params.agentRoster),
60
- threadTypesAfterOnboarding: withDedupedStrings(bootstrap?.threadTypesAfterOnboarding ?? []),
69
+
70
+ return {
71
+ onboardingDefaultAgents: resolveBootstrapAgentIds(
72
+ params.agentRoster,
73
+ bootstrap?.onboardingDefaultAgents,
74
+ onboardingWelcome?.defaultAgentId,
75
+ ),
76
+ completedDefaultAgents: normalizeThreadAgentIds(bootstrap?.completedDefaultAgents ?? params.agentRoster),
77
+ threadTypesAfterOnboarding: normalizeThreadAgentIds(bootstrap?.threadTypesAfterOnboarding ?? []),
61
78
  ensureDefaultGroupOnCompleted: bootstrap?.ensureDefaultGroupOnCompleted ?? true,
62
79
  ...(onboardingWelcome ? { onboardingWelcome } : {}),
63
80
  }
64
81
  }
65
82
 
66
83
  export function getThreadBootstrapConfig(): ResolvedThreadBootstrapConfig {
67
- return resolvedThreadBootstrapConfig
84
+ return currentThreadBootstrapConfig ?? resolveLotaService(ThreadConfigServiceTag)
68
85
  }
69
86
 
70
- export function resolveOnboardingOwnerAgentId(defaultLeadAgentId: string): string {
71
- return resolvedThreadBootstrapConfig.onboardingWelcome?.defaultAgentId ?? defaultLeadAgentId
87
+ export function resolveOnboardingOwnerAgentId(
88
+ defaultLeadAgentId: string,
89
+ config: ResolvedThreadBootstrapConfig = getThreadBootstrapConfig(),
90
+ ): string {
91
+ return config.onboardingWelcome?.defaultAgentId ?? defaultLeadAgentId
72
92
  }