@lota-sdk/core 0.4.10 → 0.4.12
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.
- package/package.json +3 -3
- package/src/ai-gateway/ai-gateway.ts +214 -98
- package/src/ai-gateway/index.ts +16 -1
- package/src/config/agent-defaults.ts +4 -120
- package/src/config/logger.ts +18 -34
- package/src/config/model-constants.ts +1 -0
- package/src/config/thread-defaults.ts +1 -18
- package/src/create-runtime.ts +90 -28
- package/src/db/base.service.ts +30 -38
- package/src/db/service.ts +489 -545
- package/src/effect/index.ts +0 -2
- package/src/effect/layers.ts +6 -13
- package/src/embeddings/provider.ts +2 -7
- package/src/index.ts +4 -5
- package/src/queues/autonomous-job.queue.ts +159 -113
- package/src/queues/context-compaction.queue.ts +39 -25
- package/src/queues/delayed-node-promotion.queue.ts +56 -29
- package/src/queues/document-processor.queue.ts +5 -3
- package/src/queues/index.ts +1 -0
- package/src/queues/memory-consolidation.queue.ts +79 -53
- package/src/queues/organization-learning.queue.ts +63 -39
- package/src/queues/plan-agent-heartbeat.queue.ts +104 -79
- package/src/queues/plan-scheduler.queue.ts +100 -84
- package/src/queues/post-chat-memory.queue.ts +55 -33
- package/src/queues/queue-factory.ts +40 -41
- package/src/queues/queues.service.ts +61 -0
- package/src/queues/title-generation.queue.ts +42 -31
- package/src/redis/org-memory-lock.ts +24 -9
- package/src/redis/redis-lease-lock.ts +8 -1
- package/src/runtime/agent-identity-overrides.ts +7 -3
- package/src/runtime/agent-runtime-policy.ts +9 -4
- package/src/runtime/agent-stream-helpers.ts +9 -4
- package/src/runtime/context-compaction/context-compaction-runtime.ts +28 -32
- package/src/runtime/context-compaction/context-compaction.ts +9 -7
- package/src/runtime/domain-layer.ts +15 -4
- package/src/runtime/execution-plan-visibility.ts +5 -2
- package/src/runtime/graph-designer.ts +0 -22
- package/src/runtime/index.ts +2 -0
- package/src/runtime/indexed-repositories-policy.ts +2 -6
- package/src/runtime/live-turn-trace.ts +344 -0
- package/src/runtime/plugin-resolution.ts +29 -12
- package/src/runtime/post-turn-side-effects.ts +139 -141
- package/src/runtime/runtime-config.ts +0 -6
- package/src/runtime/runtime-extensions.ts +0 -54
- package/src/runtime/runtime-lifecycle.ts +4 -4
- package/src/runtime/runtime-services.ts +125 -53
- package/src/runtime/runtime-worker-registry.ts +113 -30
- package/src/runtime/social-chat/social-chat-agent-runner.ts +6 -3
- package/src/runtime/social-chat/social-chat-history.ts +3 -1
- package/src/runtime/social-chat/social-chat.ts +35 -20
- package/src/runtime/team-consultation/team-consultation-orchestrator.ts +6 -5
- package/src/runtime/team-consultation/team-consultation-prompts.ts +11 -6
- package/src/runtime/thread-chat-helpers.ts +18 -9
- package/src/runtime/thread-turn-context.ts +7 -47
- package/src/runtime/turn-lifecycle.ts +6 -14
- package/src/services/agent-activity.service.ts +168 -175
- package/src/services/agent-executor.service.ts +35 -16
- package/src/services/attachment.service.ts +4 -70
- package/src/services/autonomous-job.service.ts +53 -61
- package/src/services/context-compaction.service.ts +7 -9
- package/src/services/execution-plan/execution-plan-graph.ts +106 -115
- package/src/services/execution-plan/execution-plan-schedule.ts +1 -15
- package/src/services/execution-plan/execution-plan.service.ts +67 -50
- package/src/services/global-orchestrator.service.ts +18 -7
- package/src/services/graph-full-routing.ts +7 -6
- package/src/services/memory/memory-conversation.ts +10 -5
- package/src/services/memory/memory.service.ts +11 -8
- package/src/services/ownership-dispatcher.service.ts +16 -5
- package/src/services/plan/plan-agent-heartbeat.service.ts +29 -15
- package/src/services/plan/plan-agent-query.service.ts +12 -8
- package/src/services/plan/plan-completion-side-effects.ts +93 -101
- package/src/services/plan/plan-cycle.service.ts +7 -45
- package/src/services/plan/plan-deadline.service.ts +28 -17
- package/src/services/plan/plan-event-delivery.service.ts +47 -40
- package/src/services/plan/plan-executor-context.ts +2 -0
- package/src/services/plan/plan-executor-graph.ts +366 -391
- package/src/services/plan/plan-executor.service.ts +13 -91
- package/src/services/plan/plan-scheduler.service.ts +62 -49
- package/src/services/plan/plan-transaction-events.ts +1 -1
- package/src/services/recent-activity-title.service.ts +6 -2
- package/src/services/thread/thread-bootstrap.ts +11 -9
- package/src/services/thread/thread-message.service.ts +6 -5
- package/src/services/thread/thread-turn-execution.ts +86 -82
- package/src/services/thread/thread-turn-preparation.service.ts +92 -45
- package/src/services/thread/thread-turn-streaming.ts +60 -28
- package/src/services/thread/thread-turn.ts +212 -46
- package/src/services/thread/thread.service.ts +21 -6
- package/src/system-agents/recent-activity-title-refiner.agent.ts +8 -5
- package/src/system-agents/thread-router.agent.ts +23 -20
- package/src/tools/execution-plan.tool.ts +8 -3
- package/src/tools/fetch-webpage.tool.ts +10 -9
- package/src/tools/firecrawl-client.ts +0 -15
- package/src/tools/remember-memory.tool.ts +3 -6
- package/src/tools/research-topic.tool.ts +12 -3
- package/src/tools/search-web.tool.ts +10 -9
- package/src/tools/search.tool.ts +4 -5
- package/src/tools/team-think.tool.ts +139 -121
- package/src/workers/bootstrap.ts +9 -10
- package/src/workers/memory-consolidation.worker.ts +4 -1
- package/src/workers/organization-learning.worker.ts +15 -2
- package/src/workers/regular-chat-memory-digest.helpers.ts +3 -4
- package/src/workers/regular-chat-memory-digest.runner.ts +21 -14
- package/src/workers/skill-extraction.runner.ts +13 -15
- package/src/workers/worker-utils.ts +6 -18
- package/src/effect/awaitable-effect.ts +0 -96
- package/src/effect/runtime-ref.ts +0 -25
- package/src/effect/runtime.ts +0 -46
- package/src/redis/runtime-connection.ts +0 -20
- package/src/runtime/runtime-accessors.ts +0 -92
- package/src/runtime/runtime-token.ts +0 -47
|
@@ -9,9 +9,9 @@ import type {
|
|
|
9
9
|
} from '@lota-sdk/shared'
|
|
10
10
|
import { Context, Schema, Effect, Layer } from 'effect'
|
|
11
11
|
|
|
12
|
-
import {
|
|
12
|
+
import type { ResolvedAgentConfig } from '../config/agent-defaults'
|
|
13
13
|
import { serverLogger } from '../config/logger'
|
|
14
|
-
import {
|
|
14
|
+
import { AgentConfigServiceTag } from '../effect/services'
|
|
15
15
|
import { unsafeDateFrom } from '../utils/date-time'
|
|
16
16
|
import type { makeExecutionPlanService } from './execution-plan/execution-plan.service'
|
|
17
17
|
import { ExecutionPlanServiceTag } from './execution-plan/execution-plan.service'
|
|
@@ -37,6 +37,7 @@ type ActivePlanEntry = {
|
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
type AgentActivityDeps = {
|
|
40
|
+
agentConfig: ResolvedAgentConfig
|
|
40
41
|
executionPlanService: Pick<ReturnType<typeof makeExecutionPlanService>, 'getActivePlansForThread'>
|
|
41
42
|
threadService: Pick<ReturnType<typeof makeThreadService>, 'listThreads'>
|
|
42
43
|
}
|
|
@@ -154,158 +155,97 @@ function buildPlanViewNode(
|
|
|
154
155
|
}
|
|
155
156
|
|
|
156
157
|
export function getBoard(userRef: string, orgRef: string, deps: AgentActivityDeps) {
|
|
157
|
-
return
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
}
|
|
180
|
-
}),
|
|
181
|
-
)
|
|
158
|
+
return Effect.gen(function* () {
|
|
159
|
+
const activePlans = yield* getAllActivePlans(userRef, orgRef, deps)
|
|
160
|
+
const cards = activePlans
|
|
161
|
+
.filter(({ plan }) => !isPendingPlanApproval(plan))
|
|
162
|
+
.flatMap(({ plan, thread }) => plan.nodes.map((node) => planNodeToCard(node, plan, thread.id, thread.title)))
|
|
163
|
+
|
|
164
|
+
const columns: PlanBoardColumn[] = BOARD_COLUMN_ORDER.map((status) => ({
|
|
165
|
+
status,
|
|
166
|
+
label: COLUMN_LABELS[status],
|
|
167
|
+
nodes: cards.filter((card) => card.status === status),
|
|
168
|
+
}))
|
|
169
|
+
|
|
170
|
+
return {
|
|
171
|
+
columns,
|
|
172
|
+
summary: {
|
|
173
|
+
totalNodes: cards.length,
|
|
174
|
+
completedNodes: cards.filter((card) => card.status === 'completed').length,
|
|
175
|
+
activePlanCount: activePlans.length,
|
|
176
|
+
pendingApprovalCount: countPendingPlanApprovals(activePlans) + cards.filter((card) => card.hasApproval).length,
|
|
177
|
+
},
|
|
178
|
+
}
|
|
179
|
+
})
|
|
182
180
|
}
|
|
183
181
|
|
|
184
182
|
export function getPlanView(orgRef: string, planRunId: string, userRef: string, deps: AgentActivityDeps) {
|
|
185
|
-
return
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
}),
|
|
203
|
-
)
|
|
183
|
+
return Effect.gen(function* () {
|
|
184
|
+
const activePlans = yield* getAllActivePlans(userRef, orgRef, deps)
|
|
185
|
+
const match = activePlans.find(({ plan }) => plan.runId === planRunId)
|
|
186
|
+
if (!match) return null
|
|
187
|
+
|
|
188
|
+
const { plan, thread } = match
|
|
189
|
+
return {
|
|
190
|
+
planRunId: plan.runId,
|
|
191
|
+
title: plan.title,
|
|
192
|
+
objective: plan.objective,
|
|
193
|
+
status: plan.status,
|
|
194
|
+
leadAgentId: plan.leadAgentId,
|
|
195
|
+
progress: { completed: plan.progress.completed + plan.progress.partial, total: plan.progress.total },
|
|
196
|
+
nodes: plan.nodes.map((node) => buildPlanViewNode(node, plan, thread.id, thread.title)),
|
|
197
|
+
edges: plan.edges.map((edge) => ({ from: edge.source, to: edge.target })),
|
|
198
|
+
}
|
|
199
|
+
})
|
|
204
200
|
}
|
|
205
201
|
|
|
206
202
|
export function getMyTasks(userRef: string, orgRef: string, deps: AgentActivityDeps) {
|
|
207
|
-
return
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
const pendingPlanApprovalCount = countPendingPlanApprovals(activePlans)
|
|
212
|
-
|
|
213
|
-
for (const { plan, thread } of activePlans) {
|
|
214
|
-
if (isPendingPlanApproval(plan)) {
|
|
215
|
-
continue
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
for (const node of plan.nodes) {
|
|
219
|
-
const humanOwned = node.owner.executorType === 'user'
|
|
220
|
-
const awaitingHuman = node.status === 'awaiting-human'
|
|
221
|
-
if (!humanOwned && !awaitingHuman) continue
|
|
203
|
+
return Effect.gen(function* () {
|
|
204
|
+
const activePlans = yield* getAllActivePlans(userRef, orgRef, deps)
|
|
205
|
+
const tasks: PlanNodeCard[] = []
|
|
206
|
+
const pendingPlanApprovalCount = countPendingPlanApprovals(activePlans)
|
|
222
207
|
|
|
223
|
-
|
|
224
|
-
|
|
208
|
+
for (const { plan, thread } of activePlans) {
|
|
209
|
+
if (isPendingPlanApproval(plan)) {
|
|
210
|
+
continue
|
|
225
211
|
}
|
|
226
212
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
213
|
+
for (const node of plan.nodes) {
|
|
214
|
+
const humanOwned = node.owner.executorType === 'user'
|
|
215
|
+
const awaitingHuman = node.status === 'awaiting-human'
|
|
216
|
+
if (!humanOwned && !awaitingHuman) continue
|
|
231
217
|
|
|
232
|
-
|
|
233
|
-
return toAwaitableEffect(
|
|
234
|
-
Effect.gen(function* () {
|
|
235
|
-
const [activePlans, userTasks] = yield* Effect.all([
|
|
236
|
-
getAllActivePlans(userRef, orgRef, deps),
|
|
237
|
-
getMyTasks(userRef, orgRef, deps),
|
|
238
|
-
])
|
|
239
|
-
const activityByAgent = new Map<string, AgentActivityEntry>()
|
|
240
|
-
|
|
241
|
-
const agentRoster = getAgentRoster()
|
|
242
|
-
for (const agentId of agentRoster) {
|
|
243
|
-
activityByAgent.set(agentId, {
|
|
244
|
-
agentId,
|
|
245
|
-
counts: createEmptyCounts(),
|
|
246
|
-
tasks: [],
|
|
247
|
-
projects: [],
|
|
248
|
-
isLeadingActivePlan: false,
|
|
249
|
-
isRunning: false,
|
|
250
|
-
lastActiveAt: null,
|
|
251
|
-
})
|
|
218
|
+
tasks.push(planNodeToCard(node, plan, thread.id, thread.title))
|
|
252
219
|
}
|
|
220
|
+
}
|
|
253
221
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
const leadEntry = activityByAgent.get(plan.leadAgentId) ?? {
|
|
258
|
-
agentId: plan.leadAgentId,
|
|
259
|
-
counts: createEmptyCounts(),
|
|
260
|
-
tasks: [],
|
|
261
|
-
projects: [],
|
|
262
|
-
isLeadingActivePlan: false,
|
|
263
|
-
isRunning: false,
|
|
264
|
-
lastActiveAt: null,
|
|
265
|
-
}
|
|
266
|
-
activityByAgent.set(plan.leadAgentId, leadEntry)
|
|
267
|
-
leadEntry.isLeadingActivePlan = true
|
|
268
|
-
if (!leadEntry.projects.some((project) => project.planRunId === plan.runId)) {
|
|
269
|
-
leadEntry.projects.push({
|
|
270
|
-
threadId: thread.id,
|
|
271
|
-
threadTitle: thread.title,
|
|
272
|
-
planRunId: plan.runId,
|
|
273
|
-
planTitle: plan.title,
|
|
274
|
-
status: plan.status,
|
|
275
|
-
})
|
|
276
|
-
}
|
|
277
|
-
leadEntry.isRunning = leadEntry.isRunning || thread.isRunning
|
|
278
|
-
leadEntry.lastActiveAt = maxIsoDate(leadEntry.lastActiveAt, thread.updatedAt)
|
|
279
|
-
}
|
|
280
|
-
continue
|
|
281
|
-
}
|
|
222
|
+
return { tasks, pendingApprovalCount: pendingPlanApprovalCount + tasks.filter((task) => task.hasApproval).length }
|
|
223
|
+
})
|
|
224
|
+
}
|
|
282
225
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
if (!isCompletedStatus(node.status)) {
|
|
305
|
-
entry.tasks.push(planNodeToCard(node, plan, thread.id, thread.title))
|
|
306
|
-
}
|
|
307
|
-
}
|
|
226
|
+
export function getAgentActivity(userRef: string, orgRef: string, deps: AgentActivityDeps) {
|
|
227
|
+
return Effect.gen(function* () {
|
|
228
|
+
const [activePlans, userTasks] = yield* Effect.all([
|
|
229
|
+
getAllActivePlans(userRef, orgRef, deps),
|
|
230
|
+
getMyTasks(userRef, orgRef, deps),
|
|
231
|
+
])
|
|
232
|
+
const activityByAgent = new Map<string, AgentActivityEntry>()
|
|
233
|
+
|
|
234
|
+
const agentRoster = deps.agentConfig.roster
|
|
235
|
+
for (const agentId of agentRoster) {
|
|
236
|
+
activityByAgent.set(agentId, {
|
|
237
|
+
agentId,
|
|
238
|
+
counts: createEmptyCounts(),
|
|
239
|
+
tasks: [],
|
|
240
|
+
projects: [],
|
|
241
|
+
isLeadingActivePlan: false,
|
|
242
|
+
isRunning: false,
|
|
243
|
+
lastActiveAt: null,
|
|
244
|
+
})
|
|
245
|
+
}
|
|
308
246
|
|
|
247
|
+
for (const { plan, thread } of activePlans) {
|
|
248
|
+
if (isPendingPlanApproval(plan)) {
|
|
309
249
|
if (plan.leadAgentId.trim()) {
|
|
310
250
|
const leadEntry = activityByAgent.get(plan.leadAgentId) ?? {
|
|
311
251
|
agentId: plan.leadAgentId,
|
|
@@ -318,15 +258,8 @@ export function getAgentActivity(userRef: string, orgRef: string, deps: AgentAct
|
|
|
318
258
|
}
|
|
319
259
|
activityByAgent.set(plan.leadAgentId, leadEntry)
|
|
320
260
|
leadEntry.isLeadingActivePlan = true
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
for (const agentId of involvedAgents) {
|
|
325
|
-
const entry = activityByAgent.get(agentId)
|
|
326
|
-
if (!entry) continue
|
|
327
|
-
|
|
328
|
-
if (!entry.projects.some((project) => project.planRunId === plan.runId)) {
|
|
329
|
-
entry.projects.push({
|
|
261
|
+
if (!leadEntry.projects.some((project) => project.planRunId === plan.runId)) {
|
|
262
|
+
leadEntry.projects.push({
|
|
330
263
|
threadId: thread.id,
|
|
331
264
|
threadTitle: thread.title,
|
|
332
265
|
planRunId: plan.runId,
|
|
@@ -334,38 +267,97 @@ export function getAgentActivity(userRef: string, orgRef: string, deps: AgentAct
|
|
|
334
267
|
status: plan.status,
|
|
335
268
|
})
|
|
336
269
|
}
|
|
270
|
+
leadEntry.isRunning = leadEntry.isRunning || thread.isRunning
|
|
271
|
+
leadEntry.lastActiveAt = maxIsoDate(leadEntry.lastActiveAt, thread.updatedAt)
|
|
272
|
+
}
|
|
273
|
+
continue
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
const involvedAgents = new Set<string>()
|
|
277
|
+
|
|
278
|
+
for (const node of plan.nodes) {
|
|
279
|
+
if (node.owner.executorType !== 'agent') continue
|
|
280
|
+
|
|
281
|
+
const agentId = node.owner.ref
|
|
282
|
+
const entry =
|
|
283
|
+
activityByAgent.get(agentId) ??
|
|
284
|
+
({
|
|
285
|
+
agentId,
|
|
286
|
+
counts: createEmptyCounts(),
|
|
287
|
+
tasks: [],
|
|
288
|
+
projects: [],
|
|
289
|
+
isLeadingActivePlan: false,
|
|
290
|
+
isRunning: false,
|
|
291
|
+
lastActiveAt: null,
|
|
292
|
+
} as AgentActivityEntry)
|
|
293
|
+
activityByAgent.set(agentId, entry)
|
|
294
|
+
involvedAgents.add(agentId)
|
|
295
|
+
incrementCounts(entry.counts, node.status)
|
|
337
296
|
|
|
338
|
-
|
|
339
|
-
entry.
|
|
297
|
+
if (!isCompletedStatus(node.status)) {
|
|
298
|
+
entry.tasks.push(planNodeToCard(node, plan, thread.id, thread.title))
|
|
340
299
|
}
|
|
341
300
|
}
|
|
342
301
|
|
|
343
|
-
|
|
344
|
-
const
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
302
|
+
if (plan.leadAgentId.trim()) {
|
|
303
|
+
const leadEntry = activityByAgent.get(plan.leadAgentId) ?? {
|
|
304
|
+
agentId: plan.leadAgentId,
|
|
305
|
+
counts: createEmptyCounts(),
|
|
306
|
+
tasks: [],
|
|
307
|
+
projects: [],
|
|
308
|
+
isLeadingActivePlan: false,
|
|
309
|
+
isRunning: false,
|
|
310
|
+
lastActiveAt: null,
|
|
350
311
|
}
|
|
351
|
-
|
|
352
|
-
|
|
312
|
+
activityByAgent.set(plan.leadAgentId, leadEntry)
|
|
313
|
+
leadEntry.isLeadingActivePlan = true
|
|
314
|
+
involvedAgents.add(plan.leadAgentId)
|
|
315
|
+
}
|
|
353
316
|
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
317
|
+
for (const agentId of involvedAgents) {
|
|
318
|
+
const entry = activityByAgent.get(agentId)
|
|
319
|
+
if (!entry) continue
|
|
320
|
+
|
|
321
|
+
if (!entry.projects.some((project) => project.planRunId === plan.runId)) {
|
|
322
|
+
entry.projects.push({
|
|
323
|
+
threadId: thread.id,
|
|
324
|
+
threadTitle: thread.title,
|
|
325
|
+
planRunId: plan.runId,
|
|
326
|
+
planTitle: plan.title,
|
|
327
|
+
status: plan.status,
|
|
328
|
+
})
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
entry.isRunning = entry.isRunning || thread.isRunning
|
|
332
|
+
entry.lastActiveAt = maxIsoDate(entry.lastActiveAt, thread.updatedAt)
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
const agents = [...activityByAgent.values()].sort((left, right) => {
|
|
337
|
+
const leftIndex = agentRoster.indexOf(left.agentId)
|
|
338
|
+
const rightIndex = agentRoster.indexOf(right.agentId)
|
|
339
|
+
if (leftIndex !== -1 || rightIndex !== -1) {
|
|
340
|
+
if (leftIndex === -1) return 1
|
|
341
|
+
if (rightIndex === -1) return -1
|
|
342
|
+
return leftIndex - rightIndex
|
|
366
343
|
}
|
|
367
|
-
|
|
368
|
-
|
|
344
|
+
return left.agentId.localeCompare(right.agentId)
|
|
345
|
+
})
|
|
346
|
+
|
|
347
|
+
return {
|
|
348
|
+
agents,
|
|
349
|
+
userActivity: {
|
|
350
|
+
taskCount: userTasks.tasks.length,
|
|
351
|
+
pendingApprovalCount: userTasks.pendingApprovalCount,
|
|
352
|
+
awaitingHumanCount: userTasks.tasks.filter((task) => task.status === 'awaiting-human').length,
|
|
353
|
+
lastActiveAt: activePlans.reduce<string | null>(
|
|
354
|
+
(latest, entry) => maxIsoDate(latest, entry.thread.updatedAt),
|
|
355
|
+
null,
|
|
356
|
+
),
|
|
357
|
+
},
|
|
358
|
+
totalActivePlans: activePlans.length,
|
|
359
|
+
}
|
|
360
|
+
})
|
|
369
361
|
}
|
|
370
362
|
|
|
371
363
|
export function getAllActivePlans(userRef: string, orgRef: string, deps: AgentActivityDeps) {
|
|
@@ -452,8 +444,9 @@ export class AgentActivityServiceTag extends Context.Service<AgentActivityServic
|
|
|
452
444
|
export const AgentActivityServiceLive = Layer.effect(
|
|
453
445
|
AgentActivityServiceTag,
|
|
454
446
|
Effect.gen(function* () {
|
|
447
|
+
const agentConfig = yield* AgentConfigServiceTag
|
|
455
448
|
const executionPlanService = yield* ExecutionPlanServiceTag
|
|
456
449
|
const threadService = yield* ThreadServiceTag
|
|
457
|
-
return createAgentActivityService({ executionPlanService, threadService })
|
|
450
|
+
return createAgentActivityService({ agentConfig, executionPlanService, threadService })
|
|
458
451
|
}),
|
|
459
452
|
)
|
|
@@ -12,14 +12,19 @@ import { stepCountIs, tool } from 'ai'
|
|
|
12
12
|
import type { ToolSet } from 'ai'
|
|
13
13
|
import { Context, Schema, Effect, Layer } from 'effect'
|
|
14
14
|
|
|
15
|
-
import {
|
|
15
|
+
import type { ResolvedAgentConfig, ResolvedAgentFactoryConfig } from '../config/agent-defaults'
|
|
16
16
|
import { ensureRecordId } from '../db/record-id'
|
|
17
17
|
import type { SurrealDBService } from '../db/service'
|
|
18
18
|
import { TABLES } from '../db/tables'
|
|
19
19
|
import { BadRequestError, NotFoundError } from '../effect/errors'
|
|
20
20
|
import { makeEffectTryPromiseWithOperation } from '../effect/helpers'
|
|
21
|
-
import {
|
|
22
|
-
|
|
21
|
+
import {
|
|
22
|
+
AgentConfigServiceTag,
|
|
23
|
+
AgentFactoryServiceTag,
|
|
24
|
+
DatabaseServiceTag,
|
|
25
|
+
RuntimeAdaptersServiceTag,
|
|
26
|
+
TurnHooksServiceTag,
|
|
27
|
+
} from '../effect/services'
|
|
23
28
|
import { toValidationError } from '../effect/zod'
|
|
24
29
|
import {
|
|
25
30
|
OWNERSHIP_DISPATCH_BLOCKED_TOOL_NAMES,
|
|
@@ -33,7 +38,6 @@ import {
|
|
|
33
38
|
getGithubInstallationForOrganization,
|
|
34
39
|
getLinearInstallationByOrgId,
|
|
35
40
|
} from '../runtime/plugin-resolution'
|
|
36
|
-
import { getTurnHooks } from '../runtime/runtime-extensions'
|
|
37
41
|
import { readInstructionSections, readOptionalString } from '../runtime/thread-chat-helpers'
|
|
38
42
|
import { nowDate } from '../utils/date-time'
|
|
39
43
|
import type { makeNodeWorkspaceService } from './node-workspace.service'
|
|
@@ -91,8 +95,8 @@ export function buildWriteIntentDispatchPrompt(nodeSpec: PlanNodeSpec): string {
|
|
|
91
95
|
|
|
92
96
|
const MAX_SELF_CORRECTION_RETRIES = 3
|
|
93
97
|
|
|
94
|
-
function validateOwner(agentId: string, nodeId: string): PlanValidationIssueInput[] {
|
|
95
|
-
if (!
|
|
98
|
+
function validateOwner(agentConfig: ResolvedAgentConfig, agentId: string, nodeId: string): PlanValidationIssueInput[] {
|
|
99
|
+
if (!agentConfig.roster.includes(agentId)) {
|
|
96
100
|
return [
|
|
97
101
|
{
|
|
98
102
|
severity: 'blocking',
|
|
@@ -140,7 +144,7 @@ function buildWriteIntentTool(params: {
|
|
|
140
144
|
'Write a validated artifact or structured output field. Call this for each deliverable. If validation fails, correct your payload and try again.',
|
|
141
145
|
inputSchema: WriteIntentSchema,
|
|
142
146
|
execute: (intent: WriteIntent) =>
|
|
143
|
-
runPromise(
|
|
147
|
+
Effect.runPromise(
|
|
144
148
|
Effect.gen(function* () {
|
|
145
149
|
const correctionCount = params.workspace.sys.correctionCounts.get(intent.targetPath) ?? 0
|
|
146
150
|
|
|
@@ -193,7 +197,7 @@ function createExecuteNode(deps: AgentExecutorDeps) {
|
|
|
193
197
|
}
|
|
194
198
|
|
|
195
199
|
const agentId = owner.ref
|
|
196
|
-
if (!
|
|
200
|
+
if (!deps.agentConfig.roster.includes(agentId)) {
|
|
197
201
|
return yield* new BadRequestError({ message: `Agent executor "${agentId}" is not registered.` })
|
|
198
202
|
}
|
|
199
203
|
|
|
@@ -217,21 +221,23 @@ function createExecuteNode(deps: AgentExecutorDeps) {
|
|
|
217
221
|
}
|
|
218
222
|
const userRef = ensureRecordId(userRefSource, TABLES.USER)
|
|
219
223
|
const userName = params.context.userName ?? 'User'
|
|
224
|
+
const runtimeAdapters = yield* RuntimeAdaptersServiceTag
|
|
220
225
|
const { linearInstallation, githubInstallation, indexedRepoContext } = yield* Effect.all({
|
|
221
226
|
linearInstallation: tryAgentExecutorTask(
|
|
222
227
|
'get-linear-installation',
|
|
223
228
|
`Failed to load Linear installation for org ${params.context.organizationId}.`,
|
|
224
|
-
() => getLinearInstallationByOrgId(organizationRef),
|
|
229
|
+
() => getLinearInstallationByOrgId(deps.agentFactoryConfig.pluginRuntime, organizationRef),
|
|
225
230
|
),
|
|
226
231
|
githubInstallation: tryAgentExecutorTask(
|
|
227
232
|
'get-github-installation',
|
|
228
233
|
`Failed to load GitHub installation for org ${params.context.organizationId}.`,
|
|
229
|
-
() =>
|
|
234
|
+
() =>
|
|
235
|
+
getGithubInstallationForOrganization(deps.agentFactoryConfig.pluginRuntime, params.context.organizationId),
|
|
230
236
|
),
|
|
231
237
|
indexedRepoContext: tryAgentExecutorTask(
|
|
232
238
|
'build-indexed-repositories-context',
|
|
233
239
|
`Failed to build indexed repository context for org ${params.context.organizationId}.`,
|
|
234
|
-
() => buildIndexedRepositoriesContext(params.context.organizationId),
|
|
240
|
+
() => buildIndexedRepositoriesContext(runtimeAdapters, params.context.organizationId),
|
|
235
241
|
),
|
|
236
242
|
}).pipe(Effect.withSpan('AgentExecutor.loadInstallations'))
|
|
237
243
|
|
|
@@ -246,7 +252,7 @@ function createExecuteNode(deps: AgentExecutorDeps) {
|
|
|
246
252
|
}),
|
|
247
253
|
]
|
|
248
254
|
|
|
249
|
-
const turnHooks =
|
|
255
|
+
const turnHooks = yield* TurnHooksServiceTag
|
|
250
256
|
const agentResolutionValue = yield* tryAgentExecutorPromise(
|
|
251
257
|
'resolve-agent',
|
|
252
258
|
`Failed to resolve agent "${agentId}" for dispatched execution.`,
|
|
@@ -270,7 +276,7 @@ function createExecuteNode(deps: AgentExecutorDeps) {
|
|
|
270
276
|
)
|
|
271
277
|
const agentResolution = isRecord(agentResolutionValue) ? agentResolutionValue : null
|
|
272
278
|
const resolvedAgentId = readOptionalString(agentResolution?.agentId) ?? agentId
|
|
273
|
-
const runtimeConfig = getAgentRuntimeConfig({
|
|
279
|
+
const runtimeConfig = deps.agentFactoryConfig.getAgentRuntimeConfig({
|
|
274
280
|
agentId: resolvedAgentId,
|
|
275
281
|
threadType: thread.type,
|
|
276
282
|
mode: dispatchMode,
|
|
@@ -283,7 +289,7 @@ function createExecuteNode(deps: AgentExecutorDeps) {
|
|
|
283
289
|
responseGuardSection: buildOwnershipDispatchResponseGuard({ node: params.nodeSpec, executionMode: mode }),
|
|
284
290
|
})
|
|
285
291
|
|
|
286
|
-
const agentFactoryConfig =
|
|
292
|
+
const agentFactoryConfig = deps.agentFactoryConfig
|
|
287
293
|
const rawTools = yield* tryAgentExecutorPromise(
|
|
288
294
|
'build-agent-tools',
|
|
289
295
|
`Failed to build agent tools for "${resolvedAgentId}".`,
|
|
@@ -400,13 +406,18 @@ function createExecuteNode(deps: AgentExecutorDeps) {
|
|
|
400
406
|
}
|
|
401
407
|
|
|
402
408
|
interface AgentExecutorDeps {
|
|
409
|
+
agentConfig: ResolvedAgentConfig
|
|
410
|
+
agentFactoryConfig: ResolvedAgentFactoryConfig
|
|
403
411
|
db: SurrealDBService
|
|
404
412
|
nodeWorkspaceService: ReturnType<typeof makeNodeWorkspaceService>
|
|
405
413
|
writeIntentValidatorService: ReturnType<typeof makeWriteIntentValidatorService>
|
|
406
414
|
}
|
|
407
415
|
|
|
408
416
|
export function makeAgentExecutorService(deps: AgentExecutorDeps) {
|
|
409
|
-
return {
|
|
417
|
+
return {
|
|
418
|
+
validateOwner: (agentId: string, nodeId: string) => validateOwner(deps.agentConfig, agentId, nodeId),
|
|
419
|
+
executeNode: createExecuteNode(deps),
|
|
420
|
+
}
|
|
410
421
|
}
|
|
411
422
|
|
|
412
423
|
export class AgentExecutorServiceTag extends Context.Service<
|
|
@@ -417,9 +428,17 @@ export class AgentExecutorServiceTag extends Context.Service<
|
|
|
417
428
|
export const AgentExecutorServiceLive = Layer.effect(
|
|
418
429
|
AgentExecutorServiceTag,
|
|
419
430
|
Effect.gen(function* () {
|
|
431
|
+
const agentConfig = yield* AgentConfigServiceTag
|
|
432
|
+
const agentFactoryConfig = yield* AgentFactoryServiceTag
|
|
420
433
|
const db = yield* DatabaseServiceTag
|
|
421
434
|
const nodeWorkspaceService = yield* NodeWorkspaceServiceTag
|
|
422
435
|
const writeIntentValidatorService = yield* WriteIntentValidatorServiceTag
|
|
423
|
-
return makeAgentExecutorService({
|
|
436
|
+
return makeAgentExecutorService({
|
|
437
|
+
agentConfig,
|
|
438
|
+
agentFactoryConfig,
|
|
439
|
+
db,
|
|
440
|
+
nodeWorkspaceService,
|
|
441
|
+
writeIntentValidatorService,
|
|
442
|
+
})
|
|
424
443
|
}),
|
|
425
444
|
)
|
|
@@ -3,11 +3,7 @@ import { Context, Effect, Layer } from 'effect'
|
|
|
3
3
|
import type { RecordIdRef } from '../db/record-id'
|
|
4
4
|
import { recordIdToString } from '../db/record-id'
|
|
5
5
|
import { TABLES } from '../db/tables'
|
|
6
|
-
import {
|
|
7
|
-
import type {
|
|
8
|
-
makeAttachmentStorageService,
|
|
9
|
-
UploadedThreadAttachment as SdkUploadedThreadAttachment,
|
|
10
|
-
} from '../storage/attachment-storage.service'
|
|
6
|
+
import type { makeAttachmentStorageService } from '../storage/attachment-storage.service'
|
|
11
7
|
import { AttachmentStorageServiceTag } from '../storage/attachment-storage.service'
|
|
12
8
|
import type { MessagePartLike, ReadableUploadMetadata as SdkReadableUploadMetadata } from '../storage/attachment-types'
|
|
13
9
|
|
|
@@ -55,35 +51,11 @@ export function makeAttachmentService(attachmentStorage: ReturnType<typeof makeA
|
|
|
55
51
|
})
|
|
56
52
|
},
|
|
57
53
|
|
|
58
|
-
extractStoredAttachmentText({
|
|
59
|
-
storageKey,
|
|
60
|
-
name,
|
|
61
|
-
contentType,
|
|
62
|
-
}: {
|
|
63
|
-
storageKey: string
|
|
64
|
-
name: string
|
|
65
|
-
contentType: string
|
|
66
|
-
}): Promise<string> {
|
|
67
|
-
return runPromise(attachmentStorage.extractStoredAttachmentText({ storageKey, name, contentType }))
|
|
68
|
-
},
|
|
69
|
-
|
|
70
|
-
extractStoredAttachmentTextEffect(params: { storageKey: string; name: string; contentType: string }) {
|
|
54
|
+
extractStoredAttachmentText(params: { storageKey: string; name: string; contentType: string }) {
|
|
71
55
|
return attachmentStorage.extractStoredAttachmentText(params)
|
|
72
56
|
},
|
|
73
57
|
|
|
74
|
-
extractStoredAttachmentPages({
|
|
75
|
-
storageKey,
|
|
76
|
-
name,
|
|
77
|
-
contentType,
|
|
78
|
-
}: {
|
|
79
|
-
storageKey: string
|
|
80
|
-
name: string
|
|
81
|
-
contentType: string
|
|
82
|
-
}): Promise<{ pageMode: 'logical' | 'pdf'; pages: string[] }> {
|
|
83
|
-
return runPromise(attachmentStorage.extractStoredAttachmentPages({ storageKey, name, contentType }))
|
|
84
|
-
},
|
|
85
|
-
|
|
86
|
-
extractStoredAttachmentPagesEffect(params: { storageKey: string; name: string; contentType: string }) {
|
|
58
|
+
extractStoredAttachmentPages(params: { storageKey: string; name: string; contentType: string }) {
|
|
87
59
|
return attachmentStorage.extractStoredAttachmentPages(params)
|
|
88
60
|
},
|
|
89
61
|
|
|
@@ -125,30 +97,6 @@ export function makeAttachmentService(attachmentStorage: ReturnType<typeof makeA
|
|
|
125
97
|
relativePath: string
|
|
126
98
|
content: string
|
|
127
99
|
contentType: string
|
|
128
|
-
}): Promise<{ storageKey: string; sizeBytes: number }> {
|
|
129
|
-
return runPromise(
|
|
130
|
-
attachmentStorage.writeOrganizationDocument({
|
|
131
|
-
orgId: toOrgId(orgId),
|
|
132
|
-
namespace,
|
|
133
|
-
relativePath,
|
|
134
|
-
content,
|
|
135
|
-
contentType,
|
|
136
|
-
}),
|
|
137
|
-
)
|
|
138
|
-
},
|
|
139
|
-
|
|
140
|
-
writeOrganizationDocumentEffect({
|
|
141
|
-
orgId,
|
|
142
|
-
namespace,
|
|
143
|
-
relativePath,
|
|
144
|
-
content,
|
|
145
|
-
contentType,
|
|
146
|
-
}: {
|
|
147
|
-
orgId: RecordIdRef
|
|
148
|
-
namespace: string
|
|
149
|
-
relativePath: string
|
|
150
|
-
content: string
|
|
151
|
-
contentType: string
|
|
152
100
|
}) {
|
|
153
101
|
return attachmentStorage.writeOrganizationDocument({
|
|
154
102
|
orgId: toOrgId(orgId),
|
|
@@ -173,21 +121,7 @@ export function makeAttachmentService(attachmentStorage: ReturnType<typeof makeA
|
|
|
173
121
|
return attachmentStorage.uploadOrganizationDocument({ file, orgId: toOrgId(orgId), namespace, relativePath })
|
|
174
122
|
},
|
|
175
123
|
|
|
176
|
-
uploadThreadAttachment({
|
|
177
|
-
file,
|
|
178
|
-
orgId,
|
|
179
|
-
userId,
|
|
180
|
-
}: {
|
|
181
|
-
file: File
|
|
182
|
-
orgId: RecordIdRef
|
|
183
|
-
userId: RecordIdRef
|
|
184
|
-
}): Promise<SdkUploadedThreadAttachment> {
|
|
185
|
-
return runPromise(
|
|
186
|
-
attachmentStorage.uploadThreadAttachment({ file, orgId: toOrgId(orgId), userId: toUserId(userId) }),
|
|
187
|
-
)
|
|
188
|
-
},
|
|
189
|
-
|
|
190
|
-
uploadThreadAttachmentEffect({ file, orgId, userId }: { file: File; orgId: RecordIdRef; userId: RecordIdRef }) {
|
|
124
|
+
uploadThreadAttachment({ file, orgId, userId }: { file: File; orgId: RecordIdRef; userId: RecordIdRef }) {
|
|
191
125
|
return attachmentStorage.uploadThreadAttachment({ file, orgId: toOrgId(orgId), userId: toUserId(userId) })
|
|
192
126
|
},
|
|
193
127
|
}
|