@lota-sdk/core 0.4.8 → 0.4.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (272) hide show
  1. package/package.json +11 -12
  2. package/src/ai/embedding-cache.ts +96 -22
  3. package/src/ai-gateway/ai-gateway.ts +766 -223
  4. package/src/config/agent-defaults.ts +189 -75
  5. package/src/config/agent-types.ts +54 -4
  6. package/src/config/background-processing.ts +1 -1
  7. package/src/config/constants.ts +8 -2
  8. package/src/config/index.ts +0 -1
  9. package/src/config/logger.ts +299 -19
  10. package/src/config/thread-defaults.ts +40 -20
  11. package/src/create-runtime.ts +200 -449
  12. package/src/db/base.service.ts +52 -28
  13. package/src/db/cursor-pagination.ts +71 -30
  14. package/src/db/memory-query-builder.ts +2 -1
  15. package/src/db/memory-store.helpers.ts +4 -7
  16. package/src/db/memory-store.ts +868 -601
  17. package/src/db/memory.ts +396 -280
  18. package/src/db/record-id.ts +32 -10
  19. package/src/db/schema-fingerprint.ts +30 -12
  20. package/src/db/service-normalization.ts +288 -0
  21. package/src/db/service.ts +912 -779
  22. package/src/db/startup.ts +153 -68
  23. package/src/db/transaction-conflict.ts +15 -0
  24. package/src/effect/awaitable-effect.ts +96 -0
  25. package/src/effect/errors.ts +121 -0
  26. package/src/effect/helpers.ts +123 -0
  27. package/src/effect/index.ts +24 -0
  28. package/src/effect/layers.ts +238 -0
  29. package/src/effect/runtime-ref.ts +25 -0
  30. package/src/effect/runtime.ts +46 -0
  31. package/src/effect/services.ts +61 -0
  32. package/src/effect/zod.ts +43 -0
  33. package/src/embeddings/provider.ts +128 -83
  34. package/src/index.ts +48 -1
  35. package/src/openrouter/direct-provider.ts +11 -35
  36. package/src/queues/autonomous-job.queue.ts +117 -73
  37. package/src/queues/context-compaction.queue.ts +50 -17
  38. package/src/queues/delayed-node-promotion.queue.ts +46 -17
  39. package/src/queues/document-processor.queue.ts +52 -77
  40. package/src/queues/memory-consolidation.queue.ts +47 -32
  41. package/src/queues/organization-learning.queue.ts +26 -4
  42. package/src/queues/plan-agent-heartbeat.queue.ts +71 -24
  43. package/src/queues/plan-scheduler.queue.ts +97 -33
  44. package/src/queues/post-chat-memory.queue.ts +56 -26
  45. package/src/queues/queue-factory.ts +227 -59
  46. package/src/queues/standalone-worker.ts +39 -0
  47. package/src/queues/title-generation.queue.ts +45 -11
  48. package/src/redis/connection.ts +182 -113
  49. package/src/redis/index.ts +6 -8
  50. package/src/redis/org-memory-lock.ts +60 -27
  51. package/src/redis/redis-lease-lock.ts +200 -121
  52. package/src/redis/runtime-connection.ts +20 -0
  53. package/src/redis/stream-context.ts +92 -46
  54. package/src/runtime/agent-identity-overrides.ts +2 -2
  55. package/src/runtime/agent-runtime-policy.ts +5 -2
  56. package/src/runtime/agent-stream-helpers.ts +24 -9
  57. package/src/runtime/chat-run-orchestration.ts +102 -19
  58. package/src/runtime/chat-run-registry.ts +36 -2
  59. package/src/runtime/context-compaction/context-compaction-runtime.ts +107 -0
  60. package/src/runtime/{context-compaction.ts → context-compaction/context-compaction.ts} +161 -94
  61. package/src/runtime/domain-layer.ts +192 -0
  62. package/src/runtime/execution-plan-visibility.ts +2 -2
  63. package/src/runtime/execution-plan.ts +42 -15
  64. package/src/runtime/graph-designer.ts +16 -4
  65. package/src/runtime/helper-model.ts +139 -48
  66. package/src/runtime/index.ts +7 -8
  67. package/src/runtime/indexed-repositories-policy.ts +3 -3
  68. package/src/runtime/{memory-block.ts → memory/memory-block.ts} +50 -36
  69. package/src/runtime/{memory-digest-policy.ts → memory/memory-digest-policy.ts} +1 -1
  70. package/src/runtime/{memory-pipeline.ts → memory/memory-pipeline.ts} +54 -67
  71. package/src/runtime/{memory-prompts-fact.ts → memory/memory-prompts-fact.ts} +2 -2
  72. package/src/runtime/memory/memory-scope.ts +53 -0
  73. package/src/runtime/plugin-resolution.ts +124 -25
  74. package/src/runtime/plugin-types.ts +9 -1
  75. package/src/runtime/post-turn-side-effects.ts +177 -130
  76. package/src/runtime/retrieval-adapters.ts +40 -6
  77. package/src/runtime/runtime-accessors.ts +92 -0
  78. package/src/runtime/runtime-config.ts +150 -61
  79. package/src/runtime/runtime-extensions.ts +23 -25
  80. package/src/runtime/runtime-lifecycle.ts +124 -0
  81. package/src/runtime/runtime-services.ts +386 -0
  82. package/src/runtime/runtime-token.ts +47 -0
  83. package/src/runtime/social-chat/social-chat-agent-runner.ts +159 -0
  84. package/src/runtime/{social-chat-history.ts → social-chat/social-chat-history.ts} +51 -20
  85. package/src/runtime/social-chat/social-chat.ts +630 -0
  86. package/src/runtime/specialist-runner.ts +36 -10
  87. package/src/runtime/team-consultation/team-consultation-orchestrator.ts +433 -0
  88. package/src/runtime/{team-consultation-prompts.ts → team-consultation/team-consultation-prompts.ts} +6 -2
  89. package/src/runtime/thread-chat-helpers.ts +2 -2
  90. package/src/runtime/thread-plan-turn.ts +2 -1
  91. package/src/runtime/thread-turn-context.ts +183 -111
  92. package/src/runtime/turn-lifecycle.ts +93 -27
  93. package/src/services/agent-activity.service.ts +287 -203
  94. package/src/services/agent-executor.service.ts +253 -149
  95. package/src/services/artifact.service.ts +231 -149
  96. package/src/services/attachment.service.ts +171 -115
  97. package/src/services/autonomous-job.service.ts +890 -491
  98. package/src/services/background-work.service.ts +54 -0
  99. package/src/services/chat-run-registry.service.ts +13 -1
  100. package/src/services/context-compaction.service.ts +136 -86
  101. package/src/services/document-chunk.service.ts +151 -88
  102. package/src/services/execution-plan/execution-plan-approval.ts +26 -0
  103. package/src/services/execution-plan/execution-plan-context.ts +29 -0
  104. package/src/services/execution-plan/execution-plan-graph.ts +278 -0
  105. package/src/services/execution-plan/execution-plan-schedule.ts +84 -0
  106. package/src/services/execution-plan/execution-plan-spec.ts +75 -0
  107. package/src/services/execution-plan/execution-plan.service.ts +1041 -0
  108. package/src/services/feedback-loop.service.ts +132 -76
  109. package/src/services/global-orchestrator.service.ts +101 -168
  110. package/src/services/graph-full-routing.ts +193 -0
  111. package/src/services/index.ts +19 -21
  112. package/src/services/institutional-memory.service.ts +213 -125
  113. package/src/services/learned-skill.service.ts +368 -260
  114. package/src/services/memory/memory-conversation.ts +95 -0
  115. package/src/services/memory/memory-errors.ts +27 -0
  116. package/src/services/memory/memory-org-memory.ts +50 -0
  117. package/src/services/memory/memory-preseeded.ts +86 -0
  118. package/src/services/memory/memory-rerank.ts +297 -0
  119. package/src/services/{memory-utils.ts → memory/memory-utils.ts} +6 -5
  120. package/src/services/memory/memory.service.ts +674 -0
  121. package/src/services/memory/rerank.service.ts +201 -0
  122. package/src/services/monitoring-window.service.ts +92 -70
  123. package/src/services/mutating-approval.service.ts +62 -53
  124. package/src/services/node-workspace.service.ts +141 -98
  125. package/src/services/notification.service.ts +29 -16
  126. package/src/services/organization-member.service.ts +120 -66
  127. package/src/services/organization.service.ts +153 -77
  128. package/src/services/ownership-dispatcher.service.ts +456 -263
  129. package/src/services/plan/plan-agent-heartbeat.service.ts +234 -0
  130. package/src/services/plan/plan-agent-query.service.ts +322 -0
  131. package/src/services/{plan-approval.service.ts → plan/plan-approval.service.ts} +45 -22
  132. package/src/services/plan/plan-artifact.service.ts +60 -0
  133. package/src/services/plan/plan-builder.service.ts +76 -0
  134. package/src/services/plan/plan-checkpoint.service.ts +103 -0
  135. package/src/services/{plan-compiler.service.ts → plan/plan-compiler.service.ts} +26 -9
  136. package/src/services/plan/plan-completion-side-effects.ts +169 -0
  137. package/src/services/plan/plan-coordination.service.ts +181 -0
  138. package/src/services/plan/plan-cycle.service.ts +405 -0
  139. package/src/services/plan/plan-deadline.service.ts +533 -0
  140. package/src/services/plan/plan-event-delivery.service.ts +266 -0
  141. package/src/services/plan/plan-executor-context.ts +35 -0
  142. package/src/services/plan/plan-executor-graph.ts +522 -0
  143. package/src/services/plan/plan-executor-helpers.ts +307 -0
  144. package/src/services/plan/plan-executor-persistence.ts +209 -0
  145. package/src/services/plan/plan-executor.service.ts +1737 -0
  146. package/src/services/{plan-helpers.ts → plan/plan-helpers.ts} +1 -1
  147. package/src/services/{plan-run-data.ts → plan/plan-run-data.ts} +4 -4
  148. package/src/services/plan/plan-run-serialization.ts +15 -0
  149. package/src/services/plan/plan-run.service.ts +637 -0
  150. package/src/services/plan/plan-scheduler.service.ts +379 -0
  151. package/src/services/plan/plan-template.service.ts +224 -0
  152. package/src/services/plan/plan-transaction-events.ts +36 -0
  153. package/src/services/plan/plan-validator.service.ts +907 -0
  154. package/src/services/plan/plan-workspace.service.ts +131 -0
  155. package/src/services/plugin-executor.service.ts +102 -68
  156. package/src/services/quality-metrics.service.ts +112 -94
  157. package/src/services/queue-job.service.ts +288 -231
  158. package/src/services/recent-activity-title.service.ts +73 -36
  159. package/src/services/recent-activity.service.ts +274 -259
  160. package/src/services/skill-resolver.service.ts +38 -12
  161. package/src/services/social-chat-history.service.ts +190 -122
  162. package/src/services/system-executor.service.ts +96 -61
  163. package/src/services/thread/thread-active-run.ts +203 -0
  164. package/src/services/thread/thread-bootstrap.ts +385 -0
  165. package/src/services/thread/thread-listing.ts +199 -0
  166. package/src/services/thread/thread-memory-block.ts +130 -0
  167. package/src/services/thread/thread-message.service.ts +379 -0
  168. package/src/services/thread/thread-record-store.ts +155 -0
  169. package/src/services/thread/thread-title.service.ts +74 -0
  170. package/src/services/thread/thread-turn-execution.ts +280 -0
  171. package/src/services/thread/thread-turn-message-context.ts +73 -0
  172. package/src/services/thread/thread-turn-preparation.service.ts +1148 -0
  173. package/src/services/thread/thread-turn-streaming.ts +403 -0
  174. package/src/services/thread/thread-turn-tracing.ts +35 -0
  175. package/src/services/thread/thread-turn.ts +376 -0
  176. package/src/services/thread/thread.service.ts +344 -0
  177. package/src/services/user.service.ts +82 -32
  178. package/src/services/write-intent-validator.service.ts +63 -51
  179. package/src/storage/attachment-parser.ts +69 -27
  180. package/src/storage/attachment-storage.service.ts +334 -275
  181. package/src/storage/generated-document-storage.service.ts +66 -34
  182. package/src/system-agents/agent-result.ts +3 -1
  183. package/src/system-agents/context-compaction.agent.ts +3 -3
  184. package/src/system-agents/delegated-agent-factory.ts +159 -90
  185. package/src/system-agents/helper-agent-options.ts +1 -1
  186. package/src/system-agents/memory-reranker.agent.ts +3 -3
  187. package/src/system-agents/memory.agent.ts +3 -3
  188. package/src/system-agents/recent-activity-title-refiner.agent.ts +3 -3
  189. package/src/system-agents/regular-chat-memory-digest.agent.ts +3 -3
  190. package/src/system-agents/skill-extractor.agent.ts +3 -3
  191. package/src/system-agents/skill-manager.agent.ts +3 -3
  192. package/src/system-agents/thread-router.agent.ts +157 -113
  193. package/src/system-agents/title-generator.agent.ts +3 -3
  194. package/src/tools/execution-plan.tool.ts +241 -171
  195. package/src/tools/fetch-webpage.tool.ts +29 -18
  196. package/src/tools/firecrawl-client.ts +26 -6
  197. package/src/tools/index.ts +1 -0
  198. package/src/tools/memory-block.tool.ts +14 -6
  199. package/src/tools/plan-approval.tool.ts +57 -47
  200. package/src/tools/read-file-parts.tool.ts +44 -33
  201. package/src/tools/remember-memory.tool.ts +65 -45
  202. package/src/tools/search-web.tool.ts +33 -22
  203. package/src/tools/search.tool.ts +41 -29
  204. package/src/tools/team-think.tool.ts +125 -84
  205. package/src/tools/user-questions.tool.ts +4 -3
  206. package/src/tools/web-tool-shared.ts +6 -0
  207. package/src/utils/async.ts +25 -22
  208. package/src/utils/crypto.ts +21 -0
  209. package/src/utils/date-time.ts +40 -1
  210. package/src/utils/errors.ts +111 -20
  211. package/src/utils/hono-error-handler.ts +24 -39
  212. package/src/utils/index.ts +2 -1
  213. package/src/utils/null-proto-record.ts +41 -0
  214. package/src/utils/sse-keepalive.ts +124 -21
  215. package/src/workers/bootstrap.ts +164 -52
  216. package/src/workers/memory-consolidation.worker.ts +325 -237
  217. package/src/workers/organization-learning.worker.ts +50 -16
  218. package/src/workers/regular-chat-memory-digest.helpers.ts +28 -27
  219. package/src/workers/regular-chat-memory-digest.runner.ts +185 -114
  220. package/src/workers/skill-extraction.runner.ts +176 -93
  221. package/src/workers/utils/file-section-chunker.ts +8 -10
  222. package/src/workers/utils/repo-structure-extractor.ts +349 -260
  223. package/src/workers/utils/repomix-file-sections.ts +2 -2
  224. package/src/workers/utils/thread-message-query.ts +97 -38
  225. package/src/workers/worker-utils.ts +74 -31
  226. package/src/config/debug-logger.ts +0 -47
  227. package/src/config/search.ts +0 -3
  228. package/src/redis/connection-accessor.ts +0 -26
  229. package/src/runtime/agent-types.ts +0 -1
  230. package/src/runtime/context-compaction-runtime.ts +0 -87
  231. package/src/runtime/memory-scope.ts +0 -43
  232. package/src/runtime/social-chat-agent-runner.ts +0 -118
  233. package/src/runtime/social-chat.ts +0 -516
  234. package/src/runtime/team-consultation-orchestrator.ts +0 -272
  235. package/src/services/adaptive-playbook.service.ts +0 -152
  236. package/src/services/artifact-provenance.service.ts +0 -172
  237. package/src/services/chat-attachments.service.ts +0 -17
  238. package/src/services/context-compaction-runtime.singleton.ts +0 -13
  239. package/src/services/execution-plan.service.ts +0 -1118
  240. package/src/services/memory.service.ts +0 -914
  241. package/src/services/plan-agent-heartbeat.service.ts +0 -136
  242. package/src/services/plan-agent-query.service.ts +0 -267
  243. package/src/services/plan-artifact.service.ts +0 -50
  244. package/src/services/plan-builder.service.ts +0 -67
  245. package/src/services/plan-checkpoint.service.ts +0 -81
  246. package/src/services/plan-completion-side-effects.ts +0 -80
  247. package/src/services/plan-coordination.service.ts +0 -157
  248. package/src/services/plan-cycle.service.ts +0 -284
  249. package/src/services/plan-deadline.service.ts +0 -430
  250. package/src/services/plan-event-delivery.service.ts +0 -166
  251. package/src/services/plan-executor.service.ts +0 -1950
  252. package/src/services/plan-run.service.ts +0 -515
  253. package/src/services/plan-scheduler.service.ts +0 -240
  254. package/src/services/plan-template.service.ts +0 -177
  255. package/src/services/plan-validator.service.ts +0 -818
  256. package/src/services/plan-workspace.service.ts +0 -83
  257. package/src/services/rerank.service.ts +0 -156
  258. package/src/services/thread-message.service.ts +0 -275
  259. package/src/services/thread-plan-registry.service.ts +0 -22
  260. package/src/services/thread-title.service.ts +0 -39
  261. package/src/services/thread-turn-preparation.service.ts +0 -1147
  262. package/src/services/thread-turn.ts +0 -172
  263. package/src/services/thread.service.ts +0 -869
  264. package/src/utils/env.ts +0 -8
  265. /package/src/runtime/{context-compaction-constants.ts → context-compaction/context-compaction-constants.ts} +0 -0
  266. /package/src/runtime/{memory-format.ts → memory/memory-format.ts} +0 -0
  267. /package/src/runtime/{memory-prompts-parse.ts → memory/memory-prompts-parse.ts} +0 -0
  268. /package/src/runtime/{memory-prompts-update.ts → memory/memory-prompts-update.ts} +0 -0
  269. /package/src/runtime/{social-chat-prompts.ts → social-chat/social-chat-prompts.ts} +0 -0
  270. /package/src/services/{plan-node-spec.ts → plan/plan-node-spec.ts} +0 -0
  271. /package/src/services/{thread-constants.ts → thread/thread-constants.ts} +0 -0
  272. /package/src/services/{thread.types.ts → thread/thread.types.ts} +0 -0
@@ -0,0 +1,433 @@
1
+ import { ConsultTeamArgsSchema, withMessageCreatedAt } from '@lota-sdk/shared'
2
+ import type { ChatMessage, ConsultTeamResultData } from '@lota-sdk/shared'
3
+ import { convertToModelMessages, tool as createTool } from 'ai'
4
+ import { Schema, Deferred, Effect, Exit, Fiber, Queue, Ref, Scope } from 'effect'
5
+
6
+ import { getAgentDisplayNames, getTeamConsultParticipants } from '../../config/agent-defaults'
7
+ import { nowEpochMillis } from '../../utils/date-time'
8
+ import { createTimedAbortSignal } from '../agent-stream-helpers'
9
+ import { buildModelInputMessagesWithUploadMetadata, buildReadableUploadMetadataText } from '../chat-attachments'
10
+ import type { ReadableUploadMetadataLike } from '../chat-attachments'
11
+ import type { RepoSectionName } from '../indexed-repositories-policy'
12
+ import { extractMessageText } from '../thread-chat-helpers'
13
+ import { buildTeamConsultationFailureMessage } from './team-consultation-prompts'
14
+
15
+ export type DefaultRepoSections = RepoSectionName[]
16
+ const TEAM_CONSULTATION_TIMEOUT_MS = 90_000
17
+
18
+ class TeamConsultationError extends Schema.TaggedErrorClass<TeamConsultationError>()('TeamConsultationError', {
19
+ message: Schema.String,
20
+ cause: Schema.optional(Schema.Defect),
21
+ }) {}
22
+
23
+ function buildSnapshot(responses: ConsultTeamResultData['responses']): ConsultTeamResultData {
24
+ return { responses: responses.map((response) => ({ ...response })) }
25
+ }
26
+
27
+ function getConsultTeamOutput(output: unknown): ConsultTeamResultData | undefined {
28
+ if (output && typeof output === 'object' && Array.isArray((output as { responses?: unknown }).responses)) {
29
+ return output as ConsultTeamResultData
30
+ }
31
+
32
+ if (Array.isArray(output)) {
33
+ for (let index = output.length - 1; index >= 0; index -= 1) {
34
+ const candidate = getConsultTeamOutput(output[index])
35
+ if (candidate) return candidate
36
+ }
37
+ }
38
+
39
+ return undefined
40
+ }
41
+
42
+ function findLastUserMessage(
43
+ messages: ChatMessage[],
44
+ predicate: (message: ChatMessage) => boolean,
45
+ ): ChatMessage | null {
46
+ for (let index = messages.length - 1; index >= 0; index -= 1) {
47
+ const message = messages[index]
48
+ if (predicate(message)) {
49
+ return message
50
+ }
51
+ }
52
+
53
+ return null
54
+ }
55
+
56
+ function selectTeamConsultationContextMessages(messages: ChatMessage[], latestUserMessageId: string): ChatMessage[] {
57
+ const latestUserMessage =
58
+ findLastUserMessage(messages, (message) => message.id === latestUserMessageId && message.role === 'user') ??
59
+ findLastUserMessage(messages, (message) => message.role === 'user')
60
+
61
+ return latestUserMessage ? [latestUserMessage] : []
62
+ }
63
+
64
+ interface ParticipantObserver {
65
+ run<T>(fn: () => T | Promise<T>): Promise<T>
66
+ recordError?: (error: unknown) => void
67
+ recordAbort?: (error: unknown) => void
68
+ }
69
+
70
+ export interface TeamConsultationParticipantRunner {
71
+ buildParticipantAgent(
72
+ agentId: string,
73
+ params: {
74
+ task: string
75
+ systemWorkspaceDetails?: string
76
+ preSeededMemoriesSection?: string
77
+ retrievedKnowledgeSection?: string
78
+ },
79
+ ): Promise<{
80
+ agent: {
81
+ generate(params: Record<string, unknown>): Promise<{ text: string }>
82
+ stream(
83
+ params: Record<string, unknown>,
84
+ ): Promise<{
85
+ consumeStream(): unknown
86
+ toUIMessageStream(options: Record<string, unknown>): ReadableStream<unknown>
87
+ }>
88
+ }
89
+ observer: ParticipantObserver
90
+ }>
91
+ }
92
+
93
+ export interface CreateConsultTeamToolParams {
94
+ historyMessages: ChatMessage[]
95
+ latestUserMessageId: string
96
+ availableUploads: ReadableUploadMetadataLike[]
97
+ defaultRepoSectionsByAgent: Record<string, DefaultRepoSections | undefined>
98
+ systemWorkspaceDetails?: string
99
+ getPreSeededMemoriesSection: (agentId: string) => Promise<string | undefined>
100
+ retrievedKnowledgeSection?: string
101
+ displayNamesById?: Partial<Record<string, string>>
102
+ abortSignal: AbortSignal
103
+ participantRunner: TeamConsultationParticipantRunner
104
+ onReadError?: (agentId: string, error: unknown) => void
105
+ }
106
+
107
+ /** Sentinel pushed into the snapshot queue to signal that all workers are done. */
108
+ const DONE = Symbol('done')
109
+
110
+ type ConsultTeamSnapshotItem = ConsultTeamResultData | typeof DONE
111
+
112
+ interface ConsultTeamStreamResources {
113
+ snapshotQueue: Queue.Queue<ConsultTeamSnapshotItem>
114
+ workerFiber: Fiber.Fiber<unknown, unknown>
115
+ }
116
+
117
+ function createConsultTeamStreamResources(params: {
118
+ task: string
119
+ uploadMetadataText: string
120
+ teamConsultParticipants: readonly string[]
121
+ resolveDisplayName: (agentId: string) => string
122
+ historyMessages: ChatMessage[]
123
+ latestUserMessageId: string
124
+ availableUploads: ReadableUploadMetadataLike[]
125
+ systemWorkspaceDetails?: string
126
+ getPreSeededMemoriesSection: (agentId: string) => Promise<string | undefined>
127
+ retrievedKnowledgeSection?: string
128
+ abortSignal: AbortSignal
129
+ participantRunner: TeamConsultationParticipantRunner
130
+ onReadError?: (agentId: string, error: unknown) => void
131
+ }): Effect.Effect<ConsultTeamStreamResources, TeamConsultationError> {
132
+ return Effect.gen(function* () {
133
+ const initialResponses: ConsultTeamResultData['responses'] = params.teamConsultParticipants.map((agentId) => ({
134
+ agentId,
135
+ agentName: params.resolveDisplayName(agentId),
136
+ status: 'running',
137
+ }))
138
+
139
+ const snapshotQueue = yield* Queue.bounded<ConsultTeamSnapshotItem>(params.teamConsultParticipants.length + 2)
140
+ const responsesRef = yield* Ref.make([...initialResponses])
141
+
142
+ const pushSnapshot = Effect.flatMap(Ref.get(responsesRef), (current) =>
143
+ Queue.offer(snapshotQueue, buildSnapshot(current)),
144
+ )
145
+
146
+ const runParticipantEffect = (agentId: string, index: number) => {
147
+ const agentName = params.resolveDisplayName(agentId)
148
+ const timedAbort = createTimedAbortSignal(params.abortSignal, TEAM_CONSULTATION_TIMEOUT_MS)
149
+ const toTeamConsultationError = (message: string, cause?: unknown) =>
150
+ new TeamConsultationError({ message, cause })
151
+
152
+ const generateAndRecord = Effect.gen(function* () {
153
+ const preSeededMemoriesSection = yield* Effect.tryPromise({
154
+ try: () => params.getPreSeededMemoriesSection(agentId),
155
+ catch: (error) => toTeamConsultationError(`Failed to load pre-seeded memories for ${agentName}.`, error),
156
+ })
157
+
158
+ const { agent, observer } = yield* Effect.tryPromise({
159
+ try: () =>
160
+ params.participantRunner.buildParticipantAgent(agentId, {
161
+ task: params.task,
162
+ systemWorkspaceDetails: params.systemWorkspaceDetails,
163
+ preSeededMemoriesSection,
164
+ retrievedKnowledgeSection: params.retrievedKnowledgeSection,
165
+ }),
166
+ catch: (error) => toTeamConsultationError(`Failed to build team participant agent for ${agentName}.`, error),
167
+ })
168
+
169
+ const modelMessages = yield* Effect.tryPromise({
170
+ try: () =>
171
+ convertToModelMessages(
172
+ buildModelInputMessagesWithUploadMetadata({
173
+ messages: selectTeamConsultationContextMessages(params.historyMessages, params.latestUserMessageId),
174
+ latestUserMessageId: params.latestUserMessageId,
175
+ uploadMetadataText: params.uploadMetadataText,
176
+ }),
177
+ { ignoreIncompleteToolCalls: true },
178
+ ),
179
+ catch: (error) => toTeamConsultationError(`Failed to build model messages for ${agentName}.`, error),
180
+ })
181
+
182
+ const result = yield* Effect.tryPromise({
183
+ try: () =>
184
+ observer.run(() =>
185
+ agent.generate({
186
+ messages: modelMessages,
187
+ abortSignal: timedAbort.signal,
188
+ timeout: TEAM_CONSULTATION_TIMEOUT_MS,
189
+ }),
190
+ ),
191
+ catch: (error) => {
192
+ if (params.abortSignal.aborted || timedAbort.signal.aborted) {
193
+ observer.recordAbort?.(error)
194
+ } else {
195
+ observer.recordError?.(error)
196
+ }
197
+ return toTeamConsultationError(`Team participant ${agentName} failed.`, error)
198
+ },
199
+ })
200
+
201
+ const responseText = result.text.trim()
202
+ if (!responseText) {
203
+ return yield* new TeamConsultationError({
204
+ message: `Team participant ${agentName} did not produce a response.`,
205
+ })
206
+ }
207
+
208
+ const message = withMessageCreatedAt(
209
+ {
210
+ id: Bun.randomUUIDv7(),
211
+ role: 'assistant',
212
+ parts: [{ type: 'text', text: responseText }],
213
+ metadata: { agentId, agentName },
214
+ } satisfies ChatMessage,
215
+ nowEpochMillis(),
216
+ )
217
+
218
+ yield* Ref.update(responsesRef, (prev) => {
219
+ const next = [...prev]
220
+ next[index] = {
221
+ agentId,
222
+ agentName,
223
+ status: 'complete',
224
+ summary: extractMessageText(message).trim() || undefined,
225
+ message,
226
+ }
227
+ return next
228
+ })
229
+ yield* pushSnapshot
230
+ })
231
+
232
+ return generateAndRecord.pipe(
233
+ Effect.catch((error) => {
234
+ // Parent abort is fatal — re-fail so structured concurrency interrupts siblings.
235
+ if (params.abortSignal.aborted) {
236
+ return Effect.fail(error)
237
+ }
238
+
239
+ return Ref.update(responsesRef, (prev) => {
240
+ const next = [...prev]
241
+ next[index] = {
242
+ agentId,
243
+ agentName,
244
+ status: 'error',
245
+ error: buildTeamConsultationFailureMessage(
246
+ agentName,
247
+ TEAM_CONSULTATION_TIMEOUT_MS,
248
+ timedAbort.didTimeout(),
249
+ error,
250
+ ),
251
+ }
252
+ return next
253
+ }).pipe(Effect.andThen(pushSnapshot))
254
+ }),
255
+ Effect.ensuring(Effect.sync(() => timedAbort.dispose())),
256
+ )
257
+ }
258
+
259
+ const workersEffect = Effect.forEach(
260
+ params.teamConsultParticipants,
261
+ (agentId, index) => runParticipantEffect(agentId, index),
262
+ { concurrency: 'unbounded' },
263
+ ).pipe(Effect.ensuring(Queue.offer(snapshotQueue, DONE)))
264
+
265
+ yield* pushSnapshot
266
+ const workerFiber = yield* Effect.forkChild(workersEffect)
267
+
268
+ return { snapshotQueue, workerFiber }
269
+ })
270
+ }
271
+
272
+ class ConsultTeamSnapshotIterable implements AsyncIterableIterator<ConsultTeamResultData> {
273
+ private readonly scope: Scope.Closeable
274
+ private readonly resourcesDeferred: Deferred.Deferred<ConsultTeamStreamResources, TeamConsultationError>
275
+ private readonly buildEffect: Effect.Effect<ConsultTeamStreamResources, TeamConsultationError>
276
+ private initStarted = false
277
+ private closed = false
278
+ private completed = false
279
+
280
+ constructor(params: CreateConsultTeamToolParams, task: string) {
281
+ const teamConsultParticipants = getTeamConsultParticipants()
282
+ const uploadMetadataText = buildReadableUploadMetadataText(params.availableUploads)
283
+ const agentDisplayNames = getAgentDisplayNames()
284
+ const resolveDisplayName = (agentId: string) => {
285
+ if (params.displayNamesById && Object.hasOwn(params.displayNamesById, agentId)) {
286
+ const override = params.displayNamesById[agentId]
287
+ if (override !== undefined) {
288
+ return override
289
+ }
290
+ }
291
+
292
+ return agentDisplayNames[agentId] ?? agentId
293
+ }
294
+
295
+ this.scope = Scope.makeUnsafe()
296
+ this.resourcesDeferred = Deferred.makeUnsafe<ConsultTeamStreamResources, TeamConsultationError>()
297
+ this.buildEffect = createConsultTeamStreamResources({
298
+ task,
299
+ uploadMetadataText,
300
+ teamConsultParticipants,
301
+ resolveDisplayName,
302
+ historyMessages: params.historyMessages,
303
+ latestUserMessageId: params.latestUserMessageId,
304
+ availableUploads: params.availableUploads,
305
+ systemWorkspaceDetails: params.systemWorkspaceDetails,
306
+ getPreSeededMemoriesSection: params.getPreSeededMemoriesSection,
307
+ retrievedKnowledgeSection: params.retrievedKnowledgeSection,
308
+ abortSignal: params.abortSignal,
309
+ participantRunner: params.participantRunner,
310
+ onReadError: params.onReadError,
311
+ })
312
+ }
313
+
314
+ /**
315
+ * Acquire-release: on first call, build the stream resources and register
316
+ * cleanup as a finalizer in the iterable's scope. Subsequent calls await the
317
+ * cached `Deferred`. If init fails, no finalizer is registered, so closing
318
+ * the scope is a no-op for the leaked-resource path.
319
+ */
320
+ private acquireResourcesEffect(): Effect.Effect<ConsultTeamStreamResources, TeamConsultationError> {
321
+ if (this.initStarted) {
322
+ return Deferred.await(this.resourcesDeferred)
323
+ }
324
+ this.initStarted = true
325
+
326
+ const acquire = Effect.acquireRelease(this.buildEffect, ({ snapshotQueue, workerFiber }) =>
327
+ Fiber.interrupt(workerFiber).pipe(Effect.andThen(Queue.shutdown(snapshotQueue))),
328
+ ).pipe(Scope.provide(this.scope))
329
+
330
+ return acquire.pipe(
331
+ Effect.exit,
332
+ Effect.flatMap((exit) =>
333
+ Deferred.done(this.resourcesDeferred, exit).pipe(Effect.andThen(Deferred.await(this.resourcesDeferred))),
334
+ ),
335
+ )
336
+ }
337
+
338
+ /** Close the scope idempotently. Runs cleanup finalizers iff init succeeded. */
339
+ private closeScopeEffect(): Effect.Effect<void, never> {
340
+ return Scope.close(this.scope, Exit.void).pipe(Effect.catchCause(() => Effect.void))
341
+ }
342
+
343
+ [Symbol.asyncIterator](): AsyncIterableIterator<ConsultTeamResultData> {
344
+ return this
345
+ }
346
+
347
+ next(): Promise<IteratorResult<ConsultTeamResultData>> {
348
+ if (this.closed) {
349
+ return Promise.resolve({ done: true, value: undefined as never })
350
+ }
351
+
352
+ return Effect.runPromise(
353
+ Effect.gen(
354
+ function* (this: ConsultTeamSnapshotIterable) {
355
+ const { snapshotQueue } = yield* this.acquireResourcesEffect()
356
+
357
+ if (this.completed) {
358
+ return { done: true, value: undefined as never }
359
+ }
360
+
361
+ const item = yield* Queue.take(snapshotQueue)
362
+ if (item === DONE) {
363
+ this.completed = true
364
+ this.closed = true
365
+ yield* this.closeScopeEffect()
366
+ return { done: true, value: undefined as never }
367
+ }
368
+
369
+ return { done: false, value: item }
370
+ }.bind(this),
371
+ ).pipe(
372
+ Effect.tapCause(() =>
373
+ Effect.sync(() => {
374
+ this.closed = true
375
+ }).pipe(Effect.andThen(this.closeScopeEffect())),
376
+ ),
377
+ ),
378
+ )
379
+ }
380
+
381
+ return(): Promise<IteratorResult<ConsultTeamResultData>> {
382
+ this.closed = true
383
+ return Effect.runPromise(this.closeScopeEffect().pipe(Effect.as({ done: true, value: undefined as never })))
384
+ }
385
+
386
+ throw(error: unknown): Promise<never> {
387
+ this.closed = true
388
+ return Effect.runPromise(this.closeScopeEffect().pipe(Effect.andThen(Effect.fail(error as never))))
389
+ }
390
+ }
391
+
392
+ export function createConsultTeamTool(params: CreateConsultTeamToolParams) {
393
+ const teamConsultParticipants = getTeamConsultParticipants()
394
+ const agentDisplayNames = getAgentDisplayNames()
395
+ const resolveDisplayName = (agentId: string) => {
396
+ if (params.displayNamesById && Object.hasOwn(params.displayNamesById, agentId)) {
397
+ const override = params.displayNamesById[agentId]
398
+ if (override !== undefined) {
399
+ return override
400
+ }
401
+ }
402
+
403
+ return agentDisplayNames[agentId] ?? agentId
404
+ }
405
+ const participantNames = teamConsultParticipants
406
+ .map((agentId) => resolveDisplayName(agentId))
407
+ .filter((value) => value.trim().length > 0)
408
+ const participantSummary =
409
+ participantNames.length > 0 ? participantNames.join(', ') : 'the configured specialist participants'
410
+
411
+ return createTool({
412
+ description: `Consult the specialist team in parallel before replying. Use this when the answer benefits from structured input across ${participantSummary}.`,
413
+ inputSchema: ConsultTeamArgsSchema,
414
+ execute: ({ task }) => new ConsultTeamSnapshotIterable(params, task),
415
+ toModelOutput: ({ output }) => {
416
+ const result = getConsultTeamOutput(output)
417
+ const summaryLines =
418
+ result?.responses
419
+ .map((response) => {
420
+ const summary =
421
+ typeof response.error === 'string' && response.error.trim().length > 0
422
+ ? response.error.trim()
423
+ : typeof response.summary === 'string' && response.summary.trim().length > 0
424
+ ? response.summary.trim()
425
+ : 'No response.'
426
+ return `${response.agentName}: ${summary}`
427
+ })
428
+ .join('\n') ?? 'Team consultation completed.'
429
+
430
+ return { type: 'text', value: summaryLines }
431
+ },
432
+ })
433
+ }
@@ -1,7 +1,11 @@
1
- import { agentDisplayNames, getLeadAgentDisplayName } from '../config/agent-defaults'
1
+ import { getAgentDisplayNames, getLeadAgentDisplayName } from '../../config/agent-defaults'
2
+
3
+ function resolveDisplayName(agentId: string): string {
4
+ return getAgentDisplayNames()[agentId] ?? agentId
5
+ }
2
6
 
3
7
  export function buildTeamConsultationResponseGuard(params: { agentId: string; task: string }) {
4
- const agentName = agentDisplayNames[params.agentId] ?? params.agentId
8
+ const agentName = resolveDisplayName(params.agentId)
5
9
  const leadAgentDisplayName = getLeadAgentDisplayName()
6
10
  const mentorConstraint =
7
11
  params.agentId === 'mentor'
@@ -1,7 +1,7 @@
1
1
  import { toTimestamp } from '@lota-sdk/shared'
2
2
  import type { ChatMessage } from '@lota-sdk/shared'
3
3
 
4
- import { agentDisplayNames, resolveAgentNameAlias } from '../config/agent-defaults'
4
+ import { getAgentDisplayNames, resolveAgentNameAlias } from '../config/agent-defaults'
5
5
  import type { ChatMessageLike, ReadableUploadMetadataLike } from './chat-types'
6
6
 
7
7
  export interface ThreadHistoryMessage {
@@ -35,7 +35,7 @@ function getAgentName(message: ChatMessageLike): string | undefined {
35
35
  const value = (metadata as Record<string, unknown>).agentName
36
36
  if (typeof value !== 'string' || !value.trim()) return undefined
37
37
  const resolvedAgentName = resolveAgentNameAlias(value)
38
- return resolvedAgentName ? (agentDisplayNames[resolvedAgentName] ?? value.trim()) : value.trim()
38
+ return resolvedAgentName ? (getAgentDisplayNames()[resolvedAgentName] ?? value.trim()) : value.trim()
39
39
  }
40
40
 
41
41
  export function extractMessageText(message: ChatMessageLike): string {
@@ -1,6 +1,7 @@
1
1
  import { SUBMIT_PLAN_TURN_RESULT_TOOL_NAME, withMessageCreatedAt } from '@lota-sdk/shared'
2
2
  import type { ChatMessage, PlanArtifactSubmission, PlanNodeRunRecord, PlanNodeSpecRecord } from '@lota-sdk/shared'
3
3
 
4
+ import { nowEpochMillis } from '../utils/date-time'
4
5
  import { buildCompletionCheckStructuredOutputHints } from './agent-runtime-policy'
5
6
  import { mergeInstructionSections } from './instruction-sections'
6
7
 
@@ -125,6 +126,6 @@ export function buildPlanTurnPromptMessage(planTurn: ThreadPlanTurnContext): Cha
125
126
  ],
126
127
  metadata: { trigger: 'plan-turn', planRunId: planTurn.runId, planNodeId: planTurn.nodeId },
127
128
  },
128
- Date.now(),
129
+ nowEpochMillis(),
129
130
  )
130
131
  }