@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,31 +1,34 @@
1
+ import { Effect, Schema } from 'effect'
1
2
  import { z } from 'zod'
2
3
 
3
- import { agentDisplayNames, agentShortDisplayNames, resolveAgentNameAlias } from '../config/agent-defaults'
4
- import { compactWhitespace } from '../utils/string'
4
+ import { nowDate } from '../../utils/date-time'
5
+ import { compactWhitespace } from '../../utils/string'
6
+
7
+ export class MemoryBlockCompactError extends Schema.TaggedErrorClass<MemoryBlockCompactError>()(
8
+ 'MemoryBlockCompactError',
9
+ { message: Schema.String, cause: Schema.Defect },
10
+ ) {}
5
11
 
6
12
  function escapeRegex(value: string): string {
7
13
  return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
8
14
  }
9
15
 
10
16
  function normalizeMemoryBlockRole(role: string): string {
11
- const canonicalRole = resolveAgentNameAlias(role)
12
- if (canonicalRole) return canonicalRole
13
17
  return role.trim().toLowerCase()
14
18
  }
15
19
 
20
+ function toLabelAlias(role: string): string {
21
+ const trimmed = role.trim()
22
+ if (!trimmed) return ''
23
+ return trimmed.length === 1 ? trimmed.toUpperCase() : `${trimmed[0].toUpperCase()}${trimmed.slice(1).toLowerCase()}`
24
+ }
25
+
16
26
  function getMemoryBlockRoleAliases(role: string): string[] {
17
27
  const normalizedRole = role.trim()
18
28
  if (!normalizedRole) return []
19
-
20
- const canonicalRole = resolveAgentNameAlias(normalizedRole)
21
- if (!canonicalRole) return [normalizedRole]
22
-
23
- return [
24
- normalizedRole,
25
- canonicalRole,
26
- agentDisplayNames[canonicalRole],
27
- agentShortDisplayNames[canonicalRole],
28
- ].filter((value): value is string => typeof value === 'string' && value.trim().length > 0)
29
+ return [normalizedRole, normalizedRole.toLowerCase(), toLabelAlias(normalizedRole)].filter(
30
+ (value): value is string => typeof value === 'string' && value.trim().length > 0,
31
+ )
29
32
  }
30
33
 
31
34
  function createLabelPrefixRegex(labelRoles: readonly string[]): RegExp | null {
@@ -59,7 +62,10 @@ export interface CompactMemoryBlockEntriesParams {
59
62
  entries: MemoryBlockEntry[]
60
63
  triggerEntries: number
61
64
  chunkEntries: number
62
- compact: (params: { previousSummary: string; newEntriesText: string }) => Promise<string>
65
+ compact: (params: {
66
+ previousSummary: string
67
+ newEntriesText: string
68
+ }) => PromiseLike<string> | Effect.Effect<string, MemoryBlockCompactError>
63
69
  }
64
70
 
65
71
  export interface CompactMemoryBlockEntriesResult {
@@ -102,29 +108,37 @@ function toMemoryBlockEntriesSource(entries: Array<{ role: string; content: stri
102
108
  return entries.map((entry) => `${entry.role}: ${entry.content}`).join('\n')
103
109
  }
104
110
 
105
- export async function compactMemoryBlockEntries(
111
+ export function compactMemoryBlockEntries(
106
112
  params: CompactMemoryBlockEntriesParams,
107
- ): Promise<CompactMemoryBlockEntriesResult> {
108
- let summary = typeof params.previousSummary === 'string' ? params.previousSummary.trim() : ''
109
- let entries = [...params.entries]
110
- let compacted = false
111
-
112
- while (entries.length >= params.triggerEntries) {
113
- const chunk = entries.slice(0, params.chunkEntries)
114
- const nextSummary = (
115
- await params.compact({ previousSummary: summary, newEntriesText: toMemoryBlockEntriesSource(chunk) })
116
- ).trim()
117
-
118
- if (!nextSummary) {
119
- break
113
+ ): Effect.Effect<CompactMemoryBlockEntriesResult, MemoryBlockCompactError> {
114
+ return Effect.gen(function* () {
115
+ let summary = typeof params.previousSummary === 'string' ? params.previousSummary.trim() : ''
116
+ let entries = [...params.entries]
117
+ let compacted = false
118
+
119
+ while (entries.length >= params.triggerEntries) {
120
+ const chunk = entries.slice(0, params.chunkEntries)
121
+ const compactParams = { previousSummary: summary, newEntriesText: toMemoryBlockEntriesSource(chunk) }
122
+ const value = params.compact(compactParams)
123
+ const raw = yield* Effect.isEffect(value)
124
+ ? value
125
+ : Effect.tryPromise({
126
+ try: () => Promise.resolve(value),
127
+ catch: (cause) => new MemoryBlockCompactError({ message: 'compact callback failed', cause }),
128
+ })
129
+ const nextSummary = raw.trim()
130
+
131
+ if (!nextSummary) {
132
+ break
133
+ }
134
+
135
+ summary = nextSummary
136
+ entries = entries.slice(params.chunkEntries)
137
+ compacted = true
120
138
  }
121
139
 
122
- summary = nextSummary
123
- entries = entries.slice(params.chunkEntries)
124
- compacted = true
125
- }
126
-
127
- return { compacted, summary, entries }
140
+ return { compacted, summary, entries }
141
+ })
128
142
  }
129
143
 
130
144
  export function prepareMemoryBlockAppend(params: PrepareMemoryBlockAppendParams): PrepareMemoryBlockAppendResult {
@@ -158,7 +172,7 @@ export interface CreateMemoryBlockRuntimeOptions {
158
172
  export function createMemoryBlockRuntime(options: CreateMemoryBlockRuntimeOptions = {}): MemoryBlockRuntime {
159
173
  const labelPrefixRegex = createLabelPrefixRegex(options.labelRoles ?? [])
160
174
  const memoryBlockMaxChars = options.maxChars ?? 400
161
- const now = options.now ?? (() => new Date())
175
+ const now = options.now ?? nowDate
162
176
 
163
177
  const normalizeMemoryBlockEntry = (entry: string): string => {
164
178
  const normalizedLines = entry
@@ -1,4 +1,4 @@
1
- import { getBackgroundProcessingConfig, shouldRunAtFrequency } from '../config/background-processing'
1
+ import { getBackgroundProcessingConfig, shouldRunAtFrequency } from '../../config/background-processing'
2
2
 
3
3
  export function shouldEnqueueOnboardingPostChatMemory(params: {
4
4
  onboardingActive: boolean
@@ -1,4 +1,6 @@
1
- import { clampImportance, compactWhitespace } from '../utils/string'
1
+ import { Match } from 'effect'
2
+
3
+ import { clampImportance, compactWhitespace } from '../../utils/string'
2
4
 
3
5
  const SCORE_WEIGHTS = {
4
6
  durability: { core: 0.35, standard: 0.2, weak: 0.05 },
@@ -123,20 +125,17 @@ function scoreFact<T extends MemoryFactInput>(fact: T): number {
123
125
  const durability = fact.durability ?? 'standard'
124
126
  const type = fact.type ?? 'fact'
125
127
 
126
- const durabilityWeight =
127
- durability === 'core'
128
- ? SCORE_WEIGHTS.durability.core
129
- : durability === 'standard'
130
- ? SCORE_WEIGHTS.durability.standard
131
- : SCORE_WEIGHTS.durability.weak
132
- const typeWeight =
133
- type === 'decision'
134
- ? SCORE_WEIGHTS.type.decision
135
- : type === 'fact'
136
- ? SCORE_WEIGHTS.type.fact
137
- : type === 'preference'
138
- ? SCORE_WEIGHTS.type.preference
139
- : SCORE_WEIGHTS.type.default
128
+ const durabilityWeight = Match.value(durability).pipe(
129
+ Match.when('core', () => SCORE_WEIGHTS.durability.core),
130
+ Match.when('standard', () => SCORE_WEIGHTS.durability.standard),
131
+ Match.orElse(() => SCORE_WEIGHTS.durability.weak),
132
+ )
133
+ const typeWeight = Match.value(type).pipe(
134
+ Match.when('decision', () => SCORE_WEIGHTS.type.decision),
135
+ Match.when('fact', () => SCORE_WEIGHTS.type.fact),
136
+ Match.when('preference', () => SCORE_WEIGHTS.type.preference),
137
+ Match.orElse(() => SCORE_WEIGHTS.type.default),
138
+ )
140
139
  const lengthWeight =
141
140
  Math.min(fact.content.length, SCORE_WEIGHTS.maxContentLength) / SCORE_WEIGHTS.maxContentLength / 10
142
141
 
@@ -340,61 +339,49 @@ export function compileMemoryUpdatesFromDelta(params: {
340
339
  deleteSet.add(invalidateId)
341
340
  }
342
341
 
343
- if (item.classification === 'duplicate') {
344
- const targetId = item.targetMemoryIds[0]
345
- if (targetId) {
346
- updates.push({ id: targetId, text: existingById.get(targetId) ?? item.fact, event: 'NONE' })
347
- } else {
348
- updates.push({ id: `noop_${item.index}`, text: item.fact, event: 'NONE' })
349
- }
350
- continue
351
- }
352
-
353
- if (item.classification === 'enriches') {
354
- const targetId = item.targetMemoryIds[0]
355
- if (targetId) {
356
- const relations = buildRelations(item, 'new').filter((relation) => relation.memoryId !== targetId)
357
- updates.push({
358
- id: targetId,
359
- text: item.fact,
360
- event: 'UPDATE',
361
- oldMemory: existingById.get(targetId),
362
- ...(relations.length > 0 ? { relatesTo: relations } : {}),
363
- })
364
- continue
365
- }
342
+ const nextUpdates = Match.value(item.classification).pipe(
343
+ Match.when('duplicate', (): MemoryUpdateItemLike[] => {
344
+ const targetId = item.targetMemoryIds[0]
345
+ if (targetId) {
346
+ return [{ id: targetId, text: existingById.get(targetId) ?? item.fact, event: 'NONE' }]
347
+ }
348
+ return [{ id: `noop_${item.index}`, text: item.fact, event: 'NONE' }]
349
+ }),
350
+ Match.when('enriches', (): MemoryUpdateItemLike[] => {
351
+ const targetId = item.targetMemoryIds[0]
352
+ if (targetId) {
353
+ const relations = buildRelations(item, 'new').filter((relation) => relation.memoryId !== targetId)
354
+ return [
355
+ {
356
+ id: targetId,
357
+ text: item.fact,
358
+ event: 'UPDATE',
359
+ oldMemory: existingById.get(targetId),
360
+ ...(relations.length > 0 ? { relatesTo: relations } : {}),
361
+ },
362
+ ]
363
+ }
366
364
 
367
- const addId = addIdByFactIndex.get(item.index) ?? nextAddId()
368
- const relations = buildRelations(item, 'new')
369
- updates.push({
370
- id: addId,
371
- text: item.fact,
372
- event: 'ADD',
373
- ...(relations.length > 0 ? { relatesTo: relations } : {}),
374
- })
375
- continue
376
- }
365
+ const addId = addIdByFactIndex.get(item.index) ?? nextAddId()
366
+ const relations = buildRelations(item, 'new')
367
+ return [{ id: addId, text: item.fact, event: 'ADD', ...(relations.length > 0 ? { relatesTo: relations } : {}) }]
368
+ }),
369
+ Match.whenOr('supersedes', 'contradicts', (classification): MemoryUpdateItemLike[] => {
370
+ const addId = addIdByFactIndex.get(item.index) ?? nextAddId()
371
+ const relations = buildRelations(item, classification)
372
+ return [{ id: addId, text: item.fact, event: 'ADD', ...(relations.length > 0 ? { relatesTo: relations } : {}) }]
373
+ }),
374
+ Match.when('new', (): MemoryUpdateItemLike[] => {
375
+ const addId = addIdByFactIndex.get(item.index) ?? nextAddId()
376
+ const relations = buildRelations(item, 'new')
377
+ return [{ id: addId, text: item.fact, event: 'ADD', ...(relations.length > 0 ? { relatesTo: relations } : {}) }]
378
+ }),
379
+ Match.exhaustive,
380
+ )
377
381
 
378
- if (item.classification === 'supersedes' || item.classification === 'contradicts') {
379
- const addId = addIdByFactIndex.get(item.index) ?? nextAddId()
380
- const relations = buildRelations(item, item.classification)
381
- updates.push({
382
- id: addId,
383
- text: item.fact,
384
- event: 'ADD',
385
- ...(relations.length > 0 ? { relatesTo: relations } : {}),
386
- })
387
- continue
382
+ for (const update of nextUpdates) {
383
+ updates.push(update)
388
384
  }
389
-
390
- const addId = addIdByFactIndex.get(item.index) ?? nextAddId()
391
- const relations = buildRelations(item, 'new')
392
- updates.push({
393
- id: addId,
394
- text: item.fact,
395
- event: 'ADD',
396
- ...(relations.length > 0 ? { relatesTo: relations } : {}),
397
- })
398
385
  }
399
386
 
400
387
  const preservedRelationTargetIds = new Set<string>()
@@ -1,4 +1,4 @@
1
- import { formatUtcPromptDate } from '../utils/date-time'
1
+ import { formatUtcPromptDate, nowDate } from '../../utils/date-time'
2
2
 
3
3
  export function getFactRetrievalMessages(
4
4
  parsedMessages: string,
@@ -56,7 +56,7 @@ Hard rules:
56
56
  - Prefer returning fewer items. If uncertain, return an empty list.
57
57
  - Max ${maxFacts} facts.
58
58
 
59
- Today's date is ${formatUtcPromptDate(new Date())}.
59
+ Today's date is ${formatUtcPromptDate(nowDate())}.
60
60
  ${baseInstructions}`
61
61
 
62
62
  const userPrompt = `Conversation:\n${parsedMessages}`
@@ -0,0 +1,53 @@
1
+ import { Effect, Schema } from 'effect'
2
+
3
+ export const ORG_SCOPE_PREFIX = 'org'
4
+
5
+ const SCOPE_ID_MAX_LENGTH = 255
6
+ const SCOPE_PREFIX_REGEX = /^[a-z][a-z0-9_]*$/
7
+
8
+ export class MemoryScopeError extends Schema.TaggedErrorClass<MemoryScopeError>()('MemoryScopeError', {
9
+ message: Schema.String,
10
+ }) {}
11
+
12
+ function stripRecordPrefix(id: string): Effect.Effect<string, MemoryScopeError> {
13
+ if (typeof id !== 'string' || id.length === 0) {
14
+ return Effect.fail(new MemoryScopeError({ message: 'id must be a non-empty string' }))
15
+ }
16
+ const [, ...rest] = id.split(':')
17
+ return Effect.succeed(rest.length > 0 ? rest.join(':') : id)
18
+ }
19
+
20
+ export function scopeId(prefix: string, id: string): Effect.Effect<string, MemoryScopeError> {
21
+ return Effect.gen(function* () {
22
+ if (typeof prefix !== 'string' || prefix.length === 0) {
23
+ return yield* new MemoryScopeError({ message: 'prefix must be a non-empty string' })
24
+ }
25
+ if (!SCOPE_PREFIX_REGEX.test(prefix)) {
26
+ return yield* new MemoryScopeError({ message: `Invalid scope prefix: ${prefix}` })
27
+ }
28
+
29
+ const stripped = yield* stripRecordPrefix(id)
30
+ const scoped = `${prefix}:${stripped}`
31
+ if (scoped.length > SCOPE_ID_MAX_LENGTH) {
32
+ return yield* new MemoryScopeError({ message: 'scopeId exceeds maximum length' })
33
+ }
34
+
35
+ return scoped
36
+ })
37
+ }
38
+
39
+ export function agentScopeId(orgId: string, agentName: string): Effect.Effect<string, MemoryScopeError> {
40
+ return Effect.gen(function* () {
41
+ if (typeof agentName !== 'string' || agentName.trim().length === 0) {
42
+ return yield* new MemoryScopeError({ message: 'agentName must be a non-empty string' })
43
+ }
44
+ const strippedOrgId = yield* stripRecordPrefix(orgId)
45
+ const scoped = `agent:${strippedOrgId}:${agentName.trim()}`
46
+
47
+ if (scoped.length > SCOPE_ID_MAX_LENGTH) {
48
+ return yield* new MemoryScopeError({ message: 'scopeId exceeds maximum length' })
49
+ }
50
+
51
+ return scoped
52
+ })
53
+ }
@@ -1,36 +1,135 @@
1
- import { pluginRuntime } from '../config/agent-defaults'
1
+ import { Schema, Effect } from 'effect'
2
+
3
+ import { getPluginRuntime } from '../config/agent-defaults'
4
+ import type { RecordIdRef } from '../db/record-id'
2
5
  import type { LotaRuntimeIndexedRepositoriesContext } from './runtime-extensions'
3
6
  import { getRuntimeAdapters } from './runtime-extensions'
4
7
 
5
- export function getPluginService(path: string[]): ((...args: unknown[]) => unknown) | undefined {
6
- let current: unknown = pluginRuntime
7
- let owner: unknown = undefined
8
- for (const key of path) {
9
- if (current === null || current === undefined || typeof current !== 'object') return undefined
10
- owner = current
11
- current = (current as Record<string, unknown>)[key]
12
- }
13
- if (typeof current !== 'function') {
14
- return undefined
8
+ function isRecord(value: unknown): value is Record<string, unknown> {
9
+ return typeof value === 'object' && value !== null
10
+ }
11
+
12
+ class PluginResolutionError extends Schema.TaggedErrorClass<PluginResolutionError>()('PluginResolutionError', {
13
+ stage: Schema.Literals([
14
+ 'configuration',
15
+ 'linear-installation',
16
+ 'github-installation',
17
+ 'indexed-repositories-context',
18
+ ]),
19
+ message: Schema.String,
20
+ cause: Schema.Defect,
21
+ }) {}
22
+
23
+ function toPluginResolutionError(stage: PluginResolutionError['stage'], cause: unknown): PluginResolutionError {
24
+ return new PluginResolutionError({ stage, message: cause instanceof Error ? cause.message : String(cause), cause })
25
+ }
26
+
27
+ function readFunctionEffect(
28
+ value: unknown,
29
+ errorMessage: string,
30
+ ): Effect.Effect<(...args: unknown[]) => PromiseLike<unknown>, PluginResolutionError> {
31
+ if (typeof value !== 'function') {
32
+ return Effect.fail(new PluginResolutionError({ stage: 'configuration', message: errorMessage, cause: undefined }))
15
33
  }
16
34
 
17
- return owner && typeof owner === 'object'
18
- ? (current as (...args: unknown[]) => unknown).bind(owner)
19
- : (current as (...args: unknown[]) => unknown)
35
+ return Effect.succeed(value as (...args: unknown[]) => PromiseLike<unknown>)
36
+ }
37
+
38
+ function configError(message: string): PluginResolutionError {
39
+ return new PluginResolutionError({ stage: 'configuration', message, cause: undefined })
40
+ }
41
+
42
+ function requireRecord(value: unknown, message: string): Effect.Effect<Record<string, unknown>, PluginResolutionError> {
43
+ return isRecord(value) ? Effect.succeed(value) : Effect.fail(configError(message))
44
+ }
45
+
46
+ function resolvePluginServiceEffect(
47
+ pluginName: string,
48
+ serviceName: string,
49
+ methodName: string,
50
+ ): Effect.Effect<(...args: unknown[]) => PromiseLike<unknown>, PluginResolutionError> {
51
+ return Effect.fromNullishOr(getPluginRuntime()).pipe(
52
+ Effect.mapError(() => configError(`Plugin runtime is not configured. Missing "${pluginName}" integration.`)),
53
+ Effect.flatMap((pluginRuntime) =>
54
+ requireRecord(pluginRuntime[pluginName], `Plugin "${pluginName}" is not configured in the current runtime.`),
55
+ ),
56
+ Effect.flatMap((plugin) =>
57
+ requireRecord(plugin.services, `Plugin "${pluginName}" does not expose a services registry.`),
58
+ ),
59
+ Effect.flatMap((services) =>
60
+ requireRecord(services[serviceName], `Plugin "${pluginName}" service "${serviceName}" is not configured.`).pipe(
61
+ Effect.flatMap((service) =>
62
+ readFunctionEffect(
63
+ service[methodName],
64
+ `Plugin "${pluginName}" service "${serviceName}" is missing method "${methodName}".`,
65
+ ).pipe(
66
+ Effect.map(
67
+ (method) =>
68
+ (...args: unknown[]) =>
69
+ Reflect.apply(method, service, args),
70
+ ),
71
+ ),
72
+ ),
73
+ ),
74
+ ),
75
+ )
76
+ }
77
+
78
+ function tryResolvePluginServiceEffect(
79
+ pluginName: string,
80
+ serviceName: string,
81
+ methodName: string,
82
+ ): Effect.Effect<((...args: unknown[]) => PromiseLike<unknown>) | void, never, never> {
83
+ return Effect.catch(resolvePluginServiceEffect(pluginName, serviceName, methodName), () => Effect.void)
84
+ }
85
+
86
+ export function getLinearInstallationByOrgId(organizationId: RecordIdRef): Promise<unknown> {
87
+ return Effect.runPromise(
88
+ Effect.gen(function* () {
89
+ const fn = yield* tryResolvePluginServiceEffect('linear', 'linearService', 'getInstallationByOrgId')
90
+ if (!fn) return null
91
+ return yield* Effect.tryPromise({
92
+ try: () => fn(organizationId),
93
+ catch: (cause) => toPluginResolutionError('linear-installation', cause),
94
+ })
95
+ }),
96
+ )
97
+ }
98
+
99
+ export function getGithubInstallationForOrganization(organizationId: string): Promise<unknown> {
100
+ return Effect.runPromise(
101
+ Effect.gen(function* () {
102
+ const fn = yield* tryResolvePluginServiceEffect('github', 'githubService', 'getInstallationForOrganization')
103
+ if (!fn) return null
104
+ return yield* Effect.tryPromise({
105
+ try: () => fn(organizationId),
106
+ catch: (cause) => toPluginResolutionError('github-installation', cause),
107
+ })
108
+ }),
109
+ )
110
+ }
111
+
112
+ const EMPTY_INDEXED_REPO_CONTEXT: LotaRuntimeIndexedRepositoriesContext = {
113
+ provideRepoTool: false,
114
+ defaultSectionsByAgent: {},
115
+ context: '',
20
116
  }
21
117
 
22
- export async function buildIndexedRepositoriesContext(
118
+ export function buildIndexedRepositoriesContext(
23
119
  organizationId: string,
24
120
  ): Promise<LotaRuntimeIndexedRepositoriesContext> {
25
121
  const buildContext = getRuntimeAdapters().buildIndexedRepositoriesContext
26
- if (!buildContext) {
27
- return { provideRepoTool: false, defaultSectionsByAgent: {}, context: '' }
28
- }
29
-
30
- const result = await buildContext(organizationId)
31
- return {
32
- provideRepoTool: result.provideRepoTool,
33
- defaultSectionsByAgent: result.defaultSectionsByAgent,
34
- context: result.context ?? '',
35
- }
122
+ if (!buildContext) return Promise.resolve(EMPTY_INDEXED_REPO_CONTEXT)
123
+ return Effect.runPromise(
124
+ Effect.tryPromise({
125
+ try: () => buildContext(organizationId),
126
+ catch: (cause) => toPluginResolutionError('indexed-repositories-context', cause),
127
+ }).pipe(
128
+ Effect.map((result) => ({
129
+ provideRepoTool: result.provideRepoTool,
130
+ defaultSectionsByAgent: result.defaultSectionsByAgent,
131
+ context: result.context ?? '',
132
+ })),
133
+ ),
134
+ )
36
135
  }
@@ -33,7 +33,15 @@ export interface SystemNodeExecutor {
33
33
  executeNode(params: PluginNodeExecutionParams): Promise<PlanNodeResult>
34
34
  }
35
35
 
36
- export interface LotaPlugin<TServices = Record<string, unknown>, TTools = Record<string, unknown>> {
36
+ export interface PluginLifecycleServices {
37
+ connectDatabase?(): Promise<void>
38
+ disconnectDatabase?(): Promise<void>
39
+ }
40
+
41
+ export interface LotaPlugin<
42
+ TServices extends Record<string, unknown> = Record<string, unknown>,
43
+ TTools = Record<string, unknown>,
44
+ > {
37
45
  services: TServices
38
46
  nodeExecutor?: PluginNodeExecutor
39
47
  tools?: TTools