@lota-sdk/core 0.4.8 → 0.4.9

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