@lota-sdk/core 0.4.7 → 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.
- package/package.json +11 -12
- package/src/ai/embedding-cache.ts +94 -22
- package/src/ai-gateway/ai-gateway.ts +738 -223
- package/src/config/agent-defaults.ts +176 -75
- package/src/config/agent-types.ts +54 -4
- package/src/config/constants.ts +8 -2
- package/src/config/logger.ts +286 -19
- package/src/config/model-constants.ts +1 -0
- package/src/config/thread-defaults.ts +33 -21
- package/src/create-runtime.ts +725 -383
- package/src/db/base.service.ts +52 -28
- package/src/db/cursor-pagination.ts +71 -30
- package/src/db/memory-store.helpers.ts +4 -7
- package/src/db/memory-store.ts +856 -598
- package/src/db/memory.ts +398 -275
- package/src/db/record-id.ts +32 -10
- package/src/db/schema-fingerprint.ts +30 -12
- package/src/db/service-normalization.ts +255 -0
- package/src/db/service.ts +726 -761
- package/src/db/startup.ts +140 -66
- package/src/db/transaction-conflict.ts +15 -0
- package/src/effect/awaitable-effect.ts +87 -0
- package/src/effect/errors.ts +121 -0
- package/src/effect/helpers.ts +98 -0
- package/src/effect/index.ts +22 -0
- package/src/effect/layers.ts +228 -0
- package/src/effect/runtime-ref.ts +25 -0
- package/src/effect/runtime.ts +31 -0
- package/src/effect/services.ts +57 -0
- package/src/effect/zod.ts +43 -0
- package/src/embeddings/provider.ts +122 -71
- package/src/index.ts +46 -1
- package/src/openrouter/direct-provider.ts +29 -0
- package/src/queues/autonomous-job.queue.ts +130 -74
- package/src/queues/context-compaction.queue.ts +60 -15
- package/src/queues/delayed-node-promotion.queue.ts +52 -15
- package/src/queues/document-processor.queue.ts +52 -77
- package/src/queues/memory-consolidation.queue.ts +47 -32
- package/src/queues/organization-learning.queue.ts +13 -4
- package/src/queues/plan-agent-heartbeat.queue.ts +65 -21
- package/src/queues/plan-scheduler.queue.ts +107 -31
- package/src/queues/post-chat-memory.queue.ts +66 -24
- package/src/queues/queue-factory.ts +142 -52
- package/src/queues/standalone-worker.ts +39 -0
- package/src/queues/title-generation.queue.ts +54 -9
- package/src/redis/connection.ts +84 -32
- package/src/redis/index.ts +6 -8
- package/src/redis/org-memory-lock.ts +60 -27
- package/src/redis/redis-lease-lock.ts +200 -121
- package/src/redis/runtime-connection.ts +10 -0
- package/src/redis/stream-context.ts +84 -46
- package/src/runtime/agent-identity-overrides.ts +2 -2
- package/src/runtime/agent-runtime-policy.ts +4 -1
- package/src/runtime/agent-stream-helpers.ts +20 -9
- package/src/runtime/chat-run-orchestration.ts +102 -19
- package/src/runtime/chat-run-registry.ts +36 -2
- package/src/runtime/context-compaction/context-compaction-runtime.ts +107 -0
- package/src/runtime/{context-compaction.ts → context-compaction/context-compaction.ts} +114 -91
- package/src/runtime/execution-plan-visibility.ts +2 -2
- package/src/runtime/execution-plan.ts +42 -15
- package/src/runtime/graph-designer.ts +11 -7
- package/src/runtime/helper-model.ts +135 -48
- package/src/runtime/index.ts +7 -7
- package/src/runtime/indexed-repositories-policy.ts +3 -3
- package/src/runtime/{memory-block.ts → memory/memory-block.ts} +40 -36
- package/src/runtime/{memory-digest-policy.ts → memory/memory-digest-policy.ts} +1 -1
- package/src/runtime/{memory-pipeline.ts → memory/memory-pipeline.ts} +1 -1
- package/src/runtime/{memory-prompts-fact.ts → memory/memory-prompts-fact.ts} +2 -2
- package/src/runtime/{memory-scope.ts → memory/memory-scope.ts} +12 -6
- package/src/runtime/plugin-resolution.ts +144 -24
- package/src/runtime/plugin-types.ts +9 -1
- package/src/runtime/post-turn-side-effects.ts +197 -130
- package/src/runtime/retrieval-adapters.ts +38 -4
- package/src/runtime/runtime-config.ts +165 -61
- package/src/runtime/runtime-extensions.ts +21 -34
- package/src/runtime/social-chat/social-chat-agent-runner.ts +157 -0
- package/src/runtime/{social-chat-history.ts → social-chat/social-chat-history.ts} +42 -20
- package/src/runtime/social-chat/social-chat.ts +594 -0
- package/src/runtime/specialist-runner.ts +36 -10
- package/src/runtime/team-consultation/team-consultation-orchestrator.ts +427 -0
- package/src/runtime/{team-consultation-prompts.ts → team-consultation/team-consultation-prompts.ts} +6 -2
- package/src/runtime/thread-chat-helpers.ts +2 -2
- package/src/runtime/thread-plan-turn.ts +2 -1
- package/src/runtime/thread-turn-context.ts +172 -94
- package/src/runtime/turn-lifecycle.ts +93 -27
- package/src/services/agent-activity.service.ts +287 -203
- package/src/services/agent-executor.service.ts +329 -217
- package/src/services/artifact.service.ts +225 -148
- package/src/services/attachment.service.ts +137 -115
- package/src/services/autonomous-job.service.ts +888 -491
- package/src/services/chat-run-registry.service.ts +11 -1
- package/src/services/context-compaction.service.ts +136 -86
- package/src/services/document-chunk.service.ts +162 -90
- package/src/services/execution-plan/execution-plan-approval.ts +26 -0
- package/src/services/execution-plan/execution-plan-context.ts +29 -0
- package/src/services/execution-plan/execution-plan-graph.ts +256 -0
- package/src/services/execution-plan/execution-plan-schedule.ts +84 -0
- package/src/services/execution-plan/execution-plan-spec.ts +75 -0
- package/src/services/execution-plan/execution-plan.service.ts +1041 -0
- package/src/services/feedback-loop.service.ts +132 -76
- package/src/services/global-orchestrator.service.ts +80 -170
- package/src/services/graph-full-routing.ts +182 -0
- package/src/services/index.ts +18 -20
- package/src/services/institutional-memory.service.ts +220 -123
- package/src/services/learned-skill.service.ts +364 -259
- package/src/services/memory/memory-conversation.ts +95 -0
- package/src/services/memory/memory-org-memory.ts +39 -0
- package/src/services/memory/memory-preseeded.ts +80 -0
- package/src/services/memory/memory-rerank.ts +297 -0
- package/src/services/{memory-utils.ts → memory/memory-utils.ts} +5 -5
- package/src/services/memory/memory.service.ts +692 -0
- package/src/services/memory/rerank.service.ts +209 -0
- package/src/services/monitoring-window.service.ts +92 -70
- package/src/services/mutating-approval.service.ts +62 -53
- package/src/services/node-workspace.service.ts +141 -98
- package/src/services/notification.service.ts +17 -16
- package/src/services/organization-member.service.ts +120 -66
- package/src/services/organization.service.ts +144 -51
- package/src/services/ownership-dispatcher.service.ts +415 -264
- package/src/services/plan/plan-agent-heartbeat.service.ts +234 -0
- package/src/services/plan/plan-agent-query.service.ts +322 -0
- package/src/services/plan/plan-approval.service.ts +102 -0
- package/src/services/plan/plan-artifact.service.ts +60 -0
- package/src/services/plan/plan-builder.service.ts +76 -0
- package/src/services/plan/plan-checkpoint.service.ts +103 -0
- package/src/services/{plan-compiler.service.ts → plan/plan-compiler.service.ts} +26 -9
- package/src/services/plan/plan-completion-side-effects.ts +175 -0
- package/src/services/plan/plan-coordination.service.ts +181 -0
- package/src/services/plan/plan-cycle.service.ts +398 -0
- package/src/services/plan/plan-deadline.service.ts +547 -0
- package/src/services/plan/plan-event-delivery.service.ts +261 -0
- package/src/services/plan/plan-executor-context.ts +35 -0
- package/src/services/plan/plan-executor-graph.ts +475 -0
- package/src/services/plan/plan-executor-helpers.ts +322 -0
- package/src/services/plan/plan-executor-persistence.ts +209 -0
- package/src/services/plan/plan-executor.service.ts +1654 -0
- package/src/services/{plan-helpers.ts → plan/plan-helpers.ts} +1 -1
- package/src/services/{plan-run-data.ts → plan/plan-run-data.ts} +4 -4
- package/src/services/plan/plan-run-serialization.ts +15 -0
- package/src/services/plan/plan-run.service.ts +644 -0
- package/src/services/plan/plan-scheduler.service.ts +385 -0
- package/src/services/plan/plan-template.service.ts +224 -0
- package/src/services/plan/plan-transaction-events.ts +33 -0
- package/src/services/plan/plan-validator.service.ts +907 -0
- package/src/services/plan/plan-workspace.service.ts +125 -0
- package/src/services/plugin-executor.service.ts +97 -68
- package/src/services/quality-metrics.service.ts +112 -94
- package/src/services/queue-job.service.ts +296 -230
- package/src/services/recent-activity-title.service.ts +65 -36
- package/src/services/recent-activity.service.ts +274 -259
- package/src/services/skill-resolver.service.ts +38 -12
- package/src/services/social-chat-history.service.ts +176 -125
- package/src/services/system-executor.service.ts +91 -61
- package/src/services/thread/thread-active-run.ts +203 -0
- package/src/services/thread/thread-bootstrap.ts +369 -0
- package/src/services/thread/thread-listing.ts +198 -0
- package/src/services/thread/thread-memory-block.ts +117 -0
- package/src/services/thread/thread-message.service.ts +363 -0
- package/src/services/thread/thread-record-store.ts +155 -0
- package/src/services/thread/thread-title.service.ts +74 -0
- package/src/services/thread/thread-turn-execution.ts +280 -0
- package/src/services/thread/thread-turn-message-context.ts +73 -0
- package/src/services/thread/thread-turn-preparation.service.ts +1146 -0
- package/src/services/thread/thread-turn-streaming.ts +402 -0
- package/src/services/thread/thread-turn-tracing.ts +35 -0
- package/src/services/thread/thread-turn.ts +343 -0
- package/src/services/thread/thread.service.ts +335 -0
- package/src/services/user.service.ts +82 -32
- package/src/services/write-intent-validator.service.ts +63 -51
- package/src/storage/attachment-parser.ts +69 -27
- package/src/storage/attachment-storage.service.ts +331 -275
- package/src/storage/generated-document-storage.service.ts +66 -34
- package/src/system-agents/agent-result.ts +3 -1
- package/src/system-agents/context-compaction.agent.ts +2 -2
- package/src/system-agents/delegated-agent-factory.ts +159 -90
- package/src/system-agents/memory-reranker.agent.ts +2 -2
- package/src/system-agents/memory.agent.ts +2 -2
- package/src/system-agents/recent-activity-title-refiner.agent.ts +2 -2
- package/src/system-agents/regular-chat-memory-digest.agent.ts +2 -2
- package/src/system-agents/skill-extractor.agent.ts +2 -2
- package/src/system-agents/skill-manager.agent.ts +2 -2
- package/src/system-agents/thread-router.agent.ts +157 -113
- package/src/system-agents/title-generator.agent.ts +2 -2
- package/src/tools/execution-plan.tool.ts +220 -161
- package/src/tools/fetch-webpage.tool.ts +21 -17
- package/src/tools/firecrawl-client.ts +16 -6
- package/src/tools/index.ts +1 -0
- package/src/tools/memory-block.tool.ts +14 -6
- package/src/tools/plan-approval.tool.ts +49 -47
- package/src/tools/read-file-parts.tool.ts +44 -33
- package/src/tools/remember-memory.tool.ts +65 -45
- package/src/tools/search-web.tool.ts +26 -22
- package/src/tools/search.tool.ts +41 -29
- package/src/tools/team-think.tool.ts +124 -83
- package/src/tools/user-questions.tool.ts +4 -3
- package/src/tools/web-tool-shared.ts +6 -0
- package/src/utils/async.ts +17 -23
- package/src/utils/crypto.ts +21 -0
- package/src/utils/date-time.ts +40 -1
- package/src/utils/errors.ts +95 -16
- package/src/utils/hono-error-handler.ts +24 -39
- package/src/utils/index.ts +2 -1
- package/src/utils/null-proto-record.ts +41 -0
- package/src/utils/sse-keepalive.ts +124 -21
- package/src/workers/bootstrap.ts +186 -51
- package/src/workers/memory-consolidation.worker.ts +325 -237
- package/src/workers/organization-learning.worker.ts +50 -16
- package/src/workers/regular-chat-memory-digest.helpers.ts +28 -27
- package/src/workers/regular-chat-memory-digest.runner.ts +175 -114
- package/src/workers/skill-extraction.runner.ts +176 -93
- package/src/workers/utils/file-section-chunker.ts +8 -10
- package/src/workers/utils/repo-structure-extractor.ts +349 -260
- package/src/workers/utils/repomix-file-sections.ts +2 -2
- package/src/workers/utils/thread-message-query.ts +97 -38
- package/src/workers/worker-utils.ts +56 -31
- package/src/config/debug-logger.ts +0 -47
- package/src/redis/connection-accessor.ts +0 -26
- package/src/runtime/context-compaction-runtime.ts +0 -87
- package/src/runtime/social-chat-agent-runner.ts +0 -118
- package/src/runtime/social-chat.ts +0 -516
- package/src/runtime/team-consultation-orchestrator.ts +0 -272
- package/src/services/adaptive-playbook.service.ts +0 -152
- package/src/services/artifact-provenance.service.ts +0 -172
- package/src/services/chat-attachments.service.ts +0 -17
- package/src/services/context-compaction-runtime.singleton.ts +0 -13
- package/src/services/execution-plan.service.ts +0 -1118
- package/src/services/memory.service.ts +0 -844
- package/src/services/plan-agent-heartbeat.service.ts +0 -136
- package/src/services/plan-agent-query.service.ts +0 -267
- package/src/services/plan-approval.service.ts +0 -83
- package/src/services/plan-artifact.service.ts +0 -50
- package/src/services/plan-builder.service.ts +0 -67
- package/src/services/plan-checkpoint.service.ts +0 -81
- package/src/services/plan-completion-side-effects.ts +0 -80
- package/src/services/plan-coordination.service.ts +0 -157
- package/src/services/plan-cycle.service.ts +0 -284
- package/src/services/plan-deadline.service.ts +0 -430
- package/src/services/plan-event-delivery.service.ts +0 -166
- package/src/services/plan-executor.service.ts +0 -1950
- package/src/services/plan-run.service.ts +0 -515
- package/src/services/plan-scheduler.service.ts +0 -240
- package/src/services/plan-template.service.ts +0 -177
- package/src/services/plan-validator.service.ts +0 -818
- package/src/services/plan-workspace.service.ts +0 -83
- package/src/services/thread-message.service.ts +0 -275
- package/src/services/thread-plan-registry.service.ts +0 -22
- package/src/services/thread-title.service.ts +0 -39
- package/src/services/thread-turn-preparation.service.ts +0 -1147
- package/src/services/thread-turn.ts +0 -172
- package/src/services/thread.service.ts +0 -869
- package/src/utils/env.ts +0 -8
- /package/src/runtime/{context-compaction-constants.ts → context-compaction/context-compaction-constants.ts} +0 -0
- /package/src/runtime/{memory-format.ts → memory/memory-format.ts} +0 -0
- /package/src/runtime/{memory-prompts-parse.ts → memory/memory-prompts-parse.ts} +0 -0
- /package/src/runtime/{memory-prompts-update.ts → memory/memory-prompts-update.ts} +0 -0
- /package/src/runtime/{social-chat-prompts.ts → social-chat/social-chat-prompts.ts} +0 -0
- /package/src/services/{plan-node-spec.ts → plan/plan-node-spec.ts} +0 -0
- /package/src/services/{thread-constants.ts → thread/thread-constants.ts} +0 -0
- /package/src/services/{thread.types.ts → thread/thread.types.ts} +0 -0
|
@@ -4,35 +4,62 @@ import type {
|
|
|
4
4
|
PlanArtifactRecord,
|
|
5
5
|
PlanArtifactSubmission,
|
|
6
6
|
PlanFailureClass,
|
|
7
|
-
PlanNodeResultSubmission,
|
|
8
7
|
PlanNodeRunRecord,
|
|
9
8
|
PlanNodeSpec,
|
|
10
9
|
PlanNodeSpecRecord,
|
|
11
10
|
PlanRunRecord,
|
|
12
11
|
PlanSchemaRegistry,
|
|
13
12
|
PlanSpecRecord,
|
|
13
|
+
PlanNodeOwner,
|
|
14
14
|
PlanDraft,
|
|
15
|
-
SerializableExecutionPlan,
|
|
16
15
|
UpstreamHandoff,
|
|
17
16
|
} from '@lota-sdk/shared'
|
|
17
|
+
import { Cause, Context, Effect, Layer, Match } from 'effect'
|
|
18
18
|
|
|
19
|
-
import {
|
|
19
|
+
import type { ResolvedAgentConfig } from '../config/agent-defaults'
|
|
20
20
|
import type { RecordIdInput } from '../db/record-id'
|
|
21
21
|
import { ensureRecordId, recordIdToString } from '../db/record-id'
|
|
22
|
-
import {
|
|
22
|
+
import type { SurrealDBService } from '../db/service'
|
|
23
23
|
import { TABLES } from '../db/tables'
|
|
24
|
+
import { BadRequestError, ConfigurationError, DatabaseError } from '../effect/errors'
|
|
25
|
+
import { isPromiseLike } from '../effect/helpers'
|
|
26
|
+
import { AgentConfigServiceTag, DatabaseServiceTag, RuntimeAdaptersServiceTag } from '../effect/services'
|
|
24
27
|
import { resolvePlanNodeExecutionVisibility, shouldPlanNodeUseVisibleTurn } from '../runtime/execution-plan-visibility'
|
|
25
|
-
import {
|
|
26
|
-
import {
|
|
27
|
-
import {
|
|
28
|
-
import {
|
|
29
|
-
import {
|
|
30
|
-
import
|
|
31
|
-
import {
|
|
32
|
-
import {
|
|
33
|
-
import {
|
|
34
|
-
import {
|
|
35
|
-
import {
|
|
28
|
+
import type { LotaRuntimeAdapters } from '../runtime/runtime-extensions'
|
|
29
|
+
import type { makeAgentExecutorService } from './agent-executor.service'
|
|
30
|
+
import { AgentExecutorServiceTag } from './agent-executor.service'
|
|
31
|
+
import { routeGraphFullEffect } from './graph-full-routing'
|
|
32
|
+
import type { makeMonitoringWindowService } from './monitoring-window.service'
|
|
33
|
+
import { MonitoringWindowServiceTag } from './monitoring-window.service'
|
|
34
|
+
import type { makePlanExecutorService } from './plan/plan-executor.service'
|
|
35
|
+
import { PlanExecutorServiceTag } from './plan/plan-executor.service'
|
|
36
|
+
import { serializeRunFull } from './plan/plan-run-serialization'
|
|
37
|
+
import type { makePlanRunService } from './plan/plan-run.service'
|
|
38
|
+
import { PlanRunServiceTag } from './plan/plan-run.service'
|
|
39
|
+
import type { PlanValidationIssueInput } from './plan/plan-validator.service'
|
|
40
|
+
import type { makePluginExecutorService } from './plugin-executor.service'
|
|
41
|
+
import { PluginExecutorServiceTag } from './plugin-executor.service'
|
|
42
|
+
import type { SkillResolverService } from './skill-resolver.service'
|
|
43
|
+
import { SkillResolverServiceTag } from './skill-resolver.service'
|
|
44
|
+
import type { makeSystemExecutorService } from './system-executor.service'
|
|
45
|
+
import { SystemExecutorServiceTag } from './system-executor.service'
|
|
46
|
+
import { ThreadSchema } from './thread/thread.types'
|
|
47
|
+
import type { makeUserService } from './user.service'
|
|
48
|
+
import { UserServiceTag } from './user.service'
|
|
49
|
+
|
|
50
|
+
interface OwnershipDispatcherDeps {
|
|
51
|
+
db: SurrealDBService
|
|
52
|
+
agentConfig: ResolvedAgentConfig
|
|
53
|
+
runtimeAdapters: LotaRuntimeAdapters
|
|
54
|
+
agentExecutor: ReturnType<typeof makeAgentExecutorService>
|
|
55
|
+
monitoringWindow: ReturnType<typeof makeMonitoringWindowService>
|
|
56
|
+
planExecutor: ReturnType<typeof makePlanExecutorService>
|
|
57
|
+
planRun: ReturnType<typeof makePlanRunService>
|
|
58
|
+
pluginExecutor: ReturnType<typeof makePluginExecutorService>
|
|
59
|
+
skillResolver: SkillResolverService
|
|
60
|
+
systemExecutor: ReturnType<typeof makeSystemExecutorService>
|
|
61
|
+
user: ReturnType<typeof makeUserService>
|
|
62
|
+
}
|
|
36
63
|
|
|
37
64
|
const STABLE_RUN_STATUSES = new Set(['pending-approval', 'awaiting-human', 'blocked', 'failed', 'completed', 'aborted'])
|
|
38
65
|
const MAX_DISPATCH_ITERATIONS = 64
|
|
@@ -101,204 +128,89 @@ function formatDispatchError(error: unknown): string {
|
|
|
101
128
|
return error instanceof Error ? error.message : String(error)
|
|
102
129
|
}
|
|
103
130
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
for (const node of draft.nodes) {
|
|
109
|
-
if (node.owner.executorType === 'agent') {
|
|
110
|
-
if (!agentRoster.includes(node.owner.ref)) {
|
|
111
|
-
issues.push({
|
|
112
|
-
severity: 'blocking',
|
|
113
|
-
code: 'agent_executor_missing',
|
|
114
|
-
message: `Node "${node.label}" references unknown agent executor "${node.owner.ref}".`,
|
|
115
|
-
nodeId: node.id,
|
|
116
|
-
detail: { agentId: node.owner.ref },
|
|
117
|
-
})
|
|
118
|
-
}
|
|
119
|
-
continue
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
if (node.owner.executorType === 'plugin') {
|
|
123
|
-
issues.push(...pluginExecutorService.validateOwner(node.owner, node.id))
|
|
124
|
-
continue
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
if (node.owner.executorType === 'system') {
|
|
128
|
-
issues.push(...systemExecutorService.validateOwner(node.owner, node.id))
|
|
129
|
-
continue
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
if (node.owner.executorType === 'skill') {
|
|
133
|
-
// Skill owners are validated at execution time via skillResolverService.
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
return issues
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
async dispatchRunToStableBoundary(params: {
|
|
141
|
-
runId: RecordIdInput
|
|
142
|
-
emittedBy: string
|
|
143
|
-
}): Promise<SerializableExecutionPlan> {
|
|
144
|
-
const initialRun = await planRunService.getRunById(params.runId)
|
|
145
|
-
const autoDispatchEnabled = await this.shouldAutoDispatch(initialRun)
|
|
146
|
-
if (!autoDispatchEnabled) {
|
|
147
|
-
return this.serializeRun(initialRun.id)
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
let iteration = 0
|
|
151
|
-
while (iteration < MAX_DISPATCH_ITERATIONS) {
|
|
152
|
-
const run = await planRunService.getRunById(params.runId)
|
|
153
|
-
if (STABLE_RUN_STATUSES.has(run.status) || run.status !== 'running' || !run.currentNodeId) {
|
|
154
|
-
return this.serializeRun(run.id)
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
const spec = await planRunService.getPlanSpecById(run.planSpecId)
|
|
158
|
-
|
|
159
|
-
if (spec.executionMode === 'graph-full') {
|
|
160
|
-
const { globalOrchestratorService } = await import('./global-orchestrator.service')
|
|
161
|
-
await globalOrchestratorService.routeGraphFull({
|
|
162
|
-
threadId: recordIdToString(run.threadId, TABLES.THREAD),
|
|
163
|
-
runId: recordIdToString(run.id, TABLES.PLAN_RUN),
|
|
164
|
-
})
|
|
165
|
-
return this.serializeRun(run.id)
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
const nodeSpecRecord = await planRunService.getNodeSpecByNodeId(spec.id, run.currentNodeId)
|
|
169
|
-
const planNode = toPlanNodeSpec(nodeSpecRecord)
|
|
170
|
-
if (planNode.owner.executorType === 'user') {
|
|
171
|
-
return this.serializeRun(run.id)
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
const nodeRun = await planRunService.getNodeRunByNodeId(run.id, nodeSpecRecord.nodeId)
|
|
175
|
-
if (nodeRun.status === 'monitoring') {
|
|
176
|
-
// Monitoring nodes are managed by the scheduler — treat as stable
|
|
177
|
-
return this.serializeRun(run.id)
|
|
178
|
-
}
|
|
179
|
-
if (nodeRun.status !== 'running') {
|
|
180
|
-
return this.serializeRun(run.id)
|
|
181
|
-
}
|
|
182
|
-
if (shouldPlanNodeUseVisibleTurn(spec, nodeSpecRecord)) {
|
|
183
|
-
return this.serializeRun(run.id)
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
const [artifacts, dispatchContext] = await Promise.all([
|
|
187
|
-
planRunService.listArtifacts(run.id),
|
|
188
|
-
this.buildDispatchContext(run, spec, nodeSpecRecord),
|
|
189
|
-
])
|
|
190
|
-
const inputArtifacts = artifacts
|
|
191
|
-
.filter((artifact) => nodeSpecRecord.upstreamNodeIds.includes(artifact.nodeId))
|
|
192
|
-
.map((artifact) => toArtifactSubmission(artifact))
|
|
193
|
-
|
|
194
|
-
try {
|
|
195
|
-
const result = await this.dispatchNode({
|
|
196
|
-
nodeSpec: planNode,
|
|
197
|
-
resolvedInput: nodeRun.resolvedInput ?? {},
|
|
198
|
-
inputArtifacts,
|
|
199
|
-
context: { ...dispatchContext, nodeId: planNode.id },
|
|
200
|
-
executionMode: spec.executionMode,
|
|
201
|
-
schemaRegistry: spec.schemaRegistry,
|
|
202
|
-
})
|
|
131
|
+
function toDispatchDatabaseError(message: string, cause: unknown) {
|
|
132
|
+
return new DatabaseError({ message, cause })
|
|
133
|
+
}
|
|
203
134
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
135
|
+
const matchDraftExecutor = (deps: OwnershipDispatcherDeps, node: { id: string; label: string }) =>
|
|
136
|
+
Match.type<PlanNodeOwner>().pipe(
|
|
137
|
+
Match.discriminator('executorType')('agent', (owner): PlanValidationIssueInput[] =>
|
|
138
|
+
deps.agentConfig.roster.includes(owner.ref)
|
|
139
|
+
? []
|
|
140
|
+
: [
|
|
141
|
+
{
|
|
142
|
+
severity: 'blocking',
|
|
143
|
+
code: 'agent_executor_missing',
|
|
144
|
+
message: `Node "${node.label}" references unknown agent executor "${owner.ref}".`,
|
|
145
|
+
nodeId: node.id,
|
|
146
|
+
detail: { agentId: owner.ref },
|
|
147
|
+
},
|
|
148
|
+
],
|
|
149
|
+
),
|
|
150
|
+
Match.discriminator('executorType')('plugin', (owner): PlanValidationIssueInput[] =>
|
|
151
|
+
deps.pluginExecutor.validateOwner(owner, node.id),
|
|
152
|
+
),
|
|
153
|
+
Match.discriminator('executorType')('system', (owner): PlanValidationIssueInput[] =>
|
|
154
|
+
deps.systemExecutor.validateOwner(owner, node.id),
|
|
155
|
+
),
|
|
156
|
+
Match.discriminator('executorType')('skill', (): PlanValidationIssueInput[] => []),
|
|
157
|
+
Match.discriminator('executorType')('user', (): PlanValidationIssueInput[] => []),
|
|
158
|
+
Match.exhaustive,
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
function validateDraftExecutors(deps: OwnershipDispatcherDeps, draft: PlanDraft): PlanValidationIssueInput[] {
|
|
162
|
+
return draft.nodes.flatMap((node) => matchDraftExecutor(deps, node)(node.owner))
|
|
163
|
+
}
|
|
222
164
|
|
|
223
|
-
|
|
165
|
+
const shouldAutoDispatchEffect = (deps: OwnershipDispatcherDeps, run: PlanRunRecord) =>
|
|
166
|
+
Effect.gen(function* () {
|
|
167
|
+
const workspaceProvider = deps.runtimeAdapters.workspaceProvider
|
|
168
|
+
if (!workspaceProvider) {
|
|
169
|
+
return true
|
|
224
170
|
}
|
|
225
171
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
ensureRecordId(params.runId, TABLES.PLAN_RUN),
|
|
229
|
-
TABLES.PLAN_RUN,
|
|
230
|
-
)}.`,
|
|
172
|
+
const workspace = yield* Effect.tryPromise(() =>
|
|
173
|
+
workspaceProvider.getWorkspace(ensureRecordId(run.organizationId, TABLES.ORGANIZATION)),
|
|
231
174
|
)
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
async dispatchReadyNode(params: {
|
|
235
|
-
run: PlanRunRecord
|
|
236
|
-
nodeSpecRecord: PlanNodeSpecRecord
|
|
237
|
-
nodeRun: PlanNodeRunRecord
|
|
238
|
-
spec: PlanSpecRecord
|
|
239
|
-
executionModeOverride?: ExecutionMode
|
|
240
|
-
}): Promise<PlanNodeResultSubmission> {
|
|
241
|
-
if (shouldPlanNodeUseVisibleTurn(params.spec, params.nodeSpecRecord)) {
|
|
242
|
-
throw new Error(
|
|
243
|
-
`Node "${params.nodeSpecRecord.nodeId}" requires a visible plan turn and cannot be silently dispatched.`,
|
|
244
|
-
)
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
const planNode = toPlanNodeSpec(params.nodeSpecRecord)
|
|
248
|
-
const [artifacts, dispatchContext] = await Promise.all([
|
|
249
|
-
planRunService.listArtifacts(params.run.id),
|
|
250
|
-
this.buildDispatchContext(params.run, params.spec, params.nodeSpecRecord),
|
|
251
|
-
])
|
|
252
|
-
const inputArtifacts = artifacts
|
|
253
|
-
.filter((artifact) => params.nodeSpecRecord.upstreamNodeIds.includes(artifact.nodeId))
|
|
254
|
-
.map((artifact) => toArtifactSubmission(artifact))
|
|
255
|
-
|
|
256
|
-
return this.dispatchNode({
|
|
257
|
-
nodeSpec: planNode,
|
|
258
|
-
resolvedInput: params.nodeRun.resolvedInput ?? {},
|
|
259
|
-
inputArtifacts,
|
|
260
|
-
context: { ...dispatchContext, nodeId: planNode.id },
|
|
261
|
-
executionMode: params.spec.executionMode,
|
|
262
|
-
executionModeOverride: params.executionModeOverride,
|
|
263
|
-
schemaRegistry: params.spec.schemaRegistry,
|
|
264
|
-
})
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
private async shouldAutoDispatch(run: PlanRunRecord): Promise<boolean> {
|
|
268
|
-
const workspaceProvider = getRuntimeAdapters().workspaceProvider
|
|
269
|
-
if (!workspaceProvider) {
|
|
175
|
+
if (!workspaceProvider.getLifecycleState) {
|
|
270
176
|
return true
|
|
271
177
|
}
|
|
272
178
|
|
|
273
|
-
const
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
resolveExecutionVisibility(params: { spec: PlanSpecRecord; nodeSpecRecord: PlanNodeSpecRecord }) {
|
|
279
|
-
return resolvePlanNodeExecutionVisibility(params.spec, params.nodeSpecRecord)
|
|
280
|
-
}
|
|
179
|
+
const lifecycleState = yield* Effect.gen(function* () {
|
|
180
|
+
const result = workspaceProvider.getLifecycleState?.call(workspaceProvider, workspace)
|
|
181
|
+
if (isPromiseLike(result)) {
|
|
182
|
+
return yield* Effect.tryPromise(() => result)
|
|
183
|
+
}
|
|
281
184
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
185
|
+
return result
|
|
186
|
+
})
|
|
187
|
+
return lifecycleState ? lifecycleState.bootstrapActive !== true : true
|
|
188
|
+
})
|
|
189
|
+
|
|
190
|
+
const buildDispatchContextEffect = (
|
|
191
|
+
deps: OwnershipDispatcherDeps,
|
|
192
|
+
run: PlanRunRecord,
|
|
193
|
+
spec: PlanSpecRecord,
|
|
194
|
+
nodeSpecRecord: PlanNodeSpecRecord,
|
|
195
|
+
) =>
|
|
196
|
+
Effect.gen(function* () {
|
|
287
197
|
const organizationId = recordIdToString(run.organizationId, TABLES.ORGANIZATION)
|
|
288
198
|
const threadId = recordIdToString(run.threadId, TABLES.THREAD)
|
|
289
199
|
const planId = recordIdToString(run.id, TABLES.PLAN_RUN)
|
|
290
|
-
const [thread, nodeSpecs, nodeRuns] =
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
200
|
+
const [thread, nodeSpecs, nodeRuns] = yield* Effect.all([
|
|
201
|
+
deps.db
|
|
202
|
+
.findOne(TABLES.THREAD, { id: ensureRecordId(run.threadId, TABLES.THREAD) }, ThreadSchema)
|
|
203
|
+
.pipe(Effect.mapError((cause) => toDispatchDatabaseError('Failed to load thread context.', cause))),
|
|
204
|
+
deps.planRun
|
|
205
|
+
.listNodeSpecs(spec.id)
|
|
206
|
+
.pipe(Effect.mapError((cause) => toDispatchDatabaseError('Failed to load plan node specs.', cause))),
|
|
207
|
+
deps.planRun
|
|
208
|
+
.listNodeRuns(run.id)
|
|
209
|
+
.pipe(Effect.mapError((cause) => toDispatchDatabaseError('Failed to load plan node runs.', cause))),
|
|
294
210
|
])
|
|
295
211
|
const userId = thread?.userId ? recordIdToString(thread.userId, TABLES.USER) : undefined
|
|
296
|
-
const
|
|
297
|
-
|
|
298
|
-
.getUser(userId)
|
|
299
|
-
.then((user) => user.name)
|
|
300
|
-
.catch(() => undefined)
|
|
301
|
-
: undefined
|
|
212
|
+
const userResult = userId ? yield* Effect.exit(deps.user.getUser(userId)) : undefined
|
|
213
|
+
const userName = userResult?._tag === 'Success' ? userResult.value.name : undefined
|
|
302
214
|
const nodeSpecsById = new Map(nodeSpecs.map((candidate) => [candidate.nodeId, candidate]))
|
|
303
215
|
const upstreamHandoffs: UpstreamHandoff[] = nodeRuns
|
|
304
216
|
.filter(
|
|
@@ -324,19 +236,17 @@ class OwnershipDispatcherService {
|
|
|
324
236
|
...(userName ? { userName } : {}),
|
|
325
237
|
...(upstreamHandoffs.length > 0 ? { upstreamHandoffs } : {}),
|
|
326
238
|
}
|
|
327
|
-
}
|
|
239
|
+
})
|
|
328
240
|
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
includeCheckpoints: true,
|
|
335
|
-
includeValidationIssues: true,
|
|
336
|
-
})
|
|
337
|
-
}
|
|
241
|
+
const serializeRunEffect = (deps: OwnershipDispatcherDeps, runId: RecordIdInput) =>
|
|
242
|
+
Effect.gen(function* () {
|
|
243
|
+
const run = yield* deps.planRun.getRunById(runId)
|
|
244
|
+
return yield* serializeRunFull(deps.planRun, run)
|
|
245
|
+
})
|
|
338
246
|
|
|
339
|
-
|
|
247
|
+
const dispatchNodeEffect = (
|
|
248
|
+
deps: OwnershipDispatcherDeps,
|
|
249
|
+
params: {
|
|
340
250
|
nodeSpec: PlanNodeSpec
|
|
341
251
|
resolvedInput: Record<string, unknown>
|
|
342
252
|
inputArtifacts: PlanArtifactSubmission[]
|
|
@@ -344,83 +254,324 @@ class OwnershipDispatcherService {
|
|
|
344
254
|
executionMode?: ExecutionMode
|
|
345
255
|
executionModeOverride?: ExecutionMode
|
|
346
256
|
schemaRegistry?: PlanSchemaRegistry
|
|
347
|
-
}
|
|
257
|
+
},
|
|
258
|
+
) =>
|
|
259
|
+
Effect.gen(function* () {
|
|
348
260
|
const effectiveExecutionMode = params.executionModeOverride ?? params.executionMode
|
|
349
261
|
if (params.nodeSpec.type === 'monitoring' && params.nodeSpec.monitoringConfig) {
|
|
350
|
-
|
|
262
|
+
const monitoringConfig = params.nodeSpec.monitoringConfig
|
|
263
|
+
yield* deps.monitoringWindow.startMonitoringWindow({
|
|
351
264
|
runId: params.context.planId,
|
|
352
265
|
nodeId: params.nodeSpec.id,
|
|
353
|
-
config:
|
|
266
|
+
config: monitoringConfig,
|
|
354
267
|
organizationId: params.context.organizationId,
|
|
355
268
|
threadId: params.context.threadId,
|
|
356
269
|
})
|
|
357
270
|
return {
|
|
358
271
|
notes: `Monitoring window started for node "${params.nodeSpec.label}".`,
|
|
359
|
-
structuredOutput: { status: 'monitoring-started', config:
|
|
272
|
+
structuredOutput: { status: 'monitoring-started', config: monitoringConfig },
|
|
360
273
|
artifacts: [],
|
|
361
274
|
}
|
|
362
275
|
}
|
|
363
276
|
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
inputArtifacts: params.inputArtifacts,
|
|
369
|
-
context: params.context,
|
|
370
|
-
executionMode: effectiveExecutionMode,
|
|
371
|
-
schemaRegistry: params.schemaRegistry,
|
|
372
|
-
})
|
|
373
|
-
}
|
|
374
|
-
if (params.nodeSpec.owner.executorType === 'plugin') {
|
|
375
|
-
return pluginExecutorService.executeNode({
|
|
376
|
-
nodeSpec: params.nodeSpec,
|
|
377
|
-
resolvedInput: params.resolvedInput,
|
|
378
|
-
context: params.context,
|
|
379
|
-
})
|
|
380
|
-
}
|
|
381
|
-
if (params.nodeSpec.owner.executorType === 'system') {
|
|
382
|
-
return systemExecutorService.executeNode({
|
|
383
|
-
nodeSpec: params.nodeSpec,
|
|
384
|
-
resolvedInput: params.resolvedInput,
|
|
385
|
-
context: params.context,
|
|
386
|
-
})
|
|
387
|
-
}
|
|
388
|
-
if (params.nodeSpec.owner.executorType === 'skill') {
|
|
389
|
-
const resolved = await skillResolverService.resolve({
|
|
390
|
-
skillRef: params.nodeSpec.owner.ref,
|
|
391
|
-
organizationId: params.context.organizationId,
|
|
392
|
-
})
|
|
393
|
-
if (!resolved) {
|
|
394
|
-
throw new Error(`Skill "${params.nodeSpec.owner.ref}" could not be resolved. This is a configuration error.`)
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
if (resolved.executorType === 'agent') {
|
|
398
|
-
const skillNodeSpec = { ...params.nodeSpec, owner: { executorType: 'agent' as const, ref: resolved.ref } }
|
|
399
|
-
return agentExecutorService.executeNode({
|
|
400
|
-
nodeSpec: skillNodeSpec,
|
|
277
|
+
switch (params.nodeSpec.owner.executorType) {
|
|
278
|
+
case 'agent':
|
|
279
|
+
return yield* deps.agentExecutor.executeNode({
|
|
280
|
+
nodeSpec: params.nodeSpec,
|
|
401
281
|
resolvedInput: params.resolvedInput,
|
|
402
282
|
inputArtifacts: params.inputArtifacts,
|
|
403
283
|
context: params.context,
|
|
404
284
|
executionMode: effectiveExecutionMode,
|
|
405
285
|
schemaRegistry: params.schemaRegistry,
|
|
406
286
|
})
|
|
287
|
+
case 'plugin':
|
|
288
|
+
return yield* deps.pluginExecutor.executeNode({
|
|
289
|
+
nodeSpec: params.nodeSpec,
|
|
290
|
+
resolvedInput: params.resolvedInput,
|
|
291
|
+
context: params.context,
|
|
292
|
+
})
|
|
293
|
+
case 'system':
|
|
294
|
+
return yield* deps.systemExecutor.executeNode({
|
|
295
|
+
nodeSpec: params.nodeSpec,
|
|
296
|
+
resolvedInput: params.resolvedInput,
|
|
297
|
+
context: params.context,
|
|
298
|
+
})
|
|
299
|
+
case 'skill': {
|
|
300
|
+
const resolved = yield* deps.skillResolver.resolve({
|
|
301
|
+
skillRef: params.nodeSpec.owner.ref,
|
|
302
|
+
organizationId: params.context.organizationId,
|
|
303
|
+
})
|
|
304
|
+
if (!resolved) {
|
|
305
|
+
return yield* new ConfigurationError({
|
|
306
|
+
message: `Skill "${params.nodeSpec.owner.ref}" could not be resolved. This is a configuration error.`,
|
|
307
|
+
})
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
if (resolved.executorType === 'agent') {
|
|
311
|
+
const skillNodeSpec = { ...params.nodeSpec, owner: { executorType: 'agent' as const, ref: resolved.ref } }
|
|
312
|
+
return yield* deps.agentExecutor.executeNode({
|
|
313
|
+
nodeSpec: skillNodeSpec,
|
|
314
|
+
resolvedInput: params.resolvedInput,
|
|
315
|
+
inputArtifacts: params.inputArtifacts,
|
|
316
|
+
context: params.context,
|
|
317
|
+
executionMode: effectiveExecutionMode,
|
|
318
|
+
schemaRegistry: params.schemaRegistry,
|
|
319
|
+
})
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
return yield* deps.pluginExecutor.executeNode({
|
|
323
|
+
nodeSpec: {
|
|
324
|
+
...params.nodeSpec,
|
|
325
|
+
owner: {
|
|
326
|
+
executorType: 'plugin' as const,
|
|
327
|
+
ref: resolved.ref,
|
|
328
|
+
operation: resolved.operation ?? params.nodeSpec.owner.ref,
|
|
329
|
+
},
|
|
330
|
+
},
|
|
331
|
+
resolvedInput: params.resolvedInput,
|
|
332
|
+
context: params.context,
|
|
333
|
+
})
|
|
334
|
+
}
|
|
335
|
+
case 'user':
|
|
336
|
+
return yield* new BadRequestError({
|
|
337
|
+
message: `User-owned node "${params.nodeSpec.id}" cannot be auto-dispatched.`,
|
|
338
|
+
})
|
|
339
|
+
}
|
|
340
|
+
})
|
|
341
|
+
|
|
342
|
+
const dispatchRunToStableBoundaryEffect = (
|
|
343
|
+
deps: OwnershipDispatcherDeps,
|
|
344
|
+
params: { runId: RecordIdInput; emittedBy: string },
|
|
345
|
+
) =>
|
|
346
|
+
Effect.gen(function* () {
|
|
347
|
+
const initialRun = yield* deps.planRun.getRunById(params.runId)
|
|
348
|
+
const autoDispatchEnabled = yield* shouldAutoDispatchEffect(deps, initialRun)
|
|
349
|
+
if (!autoDispatchEnabled) {
|
|
350
|
+
return yield* serializeRunEffect(deps, initialRun.id)
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
for (let iteration = 0; iteration < MAX_DISPATCH_ITERATIONS; iteration += 1) {
|
|
354
|
+
const run = yield* deps.planRun.getRunById(params.runId)
|
|
355
|
+
if (STABLE_RUN_STATUSES.has(run.status) || run.status !== 'running' || !run.currentNodeId) {
|
|
356
|
+
return yield* serializeRunEffect(deps, run.id)
|
|
407
357
|
}
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
358
|
+
|
|
359
|
+
const spec = yield* deps.planRun.getPlanSpecById(run.planSpecId)
|
|
360
|
+
|
|
361
|
+
if (spec.executionMode === 'graph-full') {
|
|
362
|
+
yield* routeGraphFullEffect(
|
|
363
|
+
{ threadId: recordIdToString(run.threadId, TABLES.THREAD), runId: recordIdToString(run.id, TABLES.PLAN_RUN) },
|
|
364
|
+
{
|
|
365
|
+
dispatchReadyNode: (dispatchParams) => dispatchReadyNodeEffect(deps, dispatchParams),
|
|
366
|
+
planExecutorService: deps.planExecutor,
|
|
367
|
+
planRunService: deps.planRun,
|
|
415
368
|
},
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
369
|
+
).pipe(
|
|
370
|
+
Effect.catch(() =>
|
|
371
|
+
Effect.fail(
|
|
372
|
+
new ConfigurationError({
|
|
373
|
+
message: `Failed to route graph-full execution for run ${recordIdToString(run.id, TABLES.PLAN_RUN)}.`,
|
|
374
|
+
}),
|
|
375
|
+
),
|
|
376
|
+
),
|
|
377
|
+
)
|
|
378
|
+
return yield* serializeRunEffect(deps, run.id)
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
const currentNodeId = run.currentNodeId
|
|
382
|
+
if (!currentNodeId) {
|
|
383
|
+
return yield* serializeRunEffect(deps, run.id)
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
const nodeSpecRecord = yield* deps.planRun.getNodeSpecByNodeId(spec.id, currentNodeId)
|
|
387
|
+
const planNode = toPlanNodeSpec(nodeSpecRecord)
|
|
388
|
+
if (planNode.owner.executorType === 'user') {
|
|
389
|
+
return yield* serializeRunEffect(deps, run.id)
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
const nodeId = nodeSpecRecord.nodeId
|
|
393
|
+
if (!nodeId) {
|
|
394
|
+
return yield* new ConfigurationError({
|
|
395
|
+
message: `Node spec "${nodeSpecRecord.label}" is missing an executable node id.`,
|
|
396
|
+
})
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
const nodeRun = yield* deps.planRun.getNodeRunByNodeId(run.id, nodeId)
|
|
400
|
+
if (nodeRun.status === 'monitoring') {
|
|
401
|
+
return yield* serializeRunEffect(deps, run.id)
|
|
402
|
+
}
|
|
403
|
+
if (nodeRun.status !== 'running') {
|
|
404
|
+
return yield* serializeRunEffect(deps, run.id)
|
|
405
|
+
}
|
|
406
|
+
if (shouldPlanNodeUseVisibleTurn(spec, nodeSpecRecord)) {
|
|
407
|
+
return yield* serializeRunEffect(deps, run.id)
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
const [artifacts, dispatchContext] = yield* Effect.all([
|
|
411
|
+
deps.planRun
|
|
412
|
+
.listArtifacts(run.id)
|
|
413
|
+
.pipe(Effect.mapError((cause) => toDispatchDatabaseError('Failed to load run artifacts.', cause))),
|
|
414
|
+
buildDispatchContextEffect(deps, run, spec, nodeSpecRecord),
|
|
415
|
+
])
|
|
416
|
+
const inputArtifacts = artifacts
|
|
417
|
+
.filter((artifact) => nodeSpecRecord.upstreamNodeIds.includes(artifact.nodeId))
|
|
418
|
+
.map((artifact) => toArtifactSubmission(artifact))
|
|
419
|
+
|
|
420
|
+
const dispatchExit = yield* Effect.exit(
|
|
421
|
+
Effect.gen(function* () {
|
|
422
|
+
const result = yield* dispatchNodeEffect(deps, {
|
|
423
|
+
nodeSpec: planNode,
|
|
424
|
+
resolvedInput: nodeRun.resolvedInput ?? {},
|
|
425
|
+
inputArtifacts,
|
|
426
|
+
context: { ...dispatchContext, nodeId: planNode.id },
|
|
427
|
+
executionMode: spec.executionMode,
|
|
428
|
+
schemaRegistry: spec.schemaRegistry,
|
|
429
|
+
})
|
|
430
|
+
|
|
431
|
+
yield* Effect.tryPromise(() =>
|
|
432
|
+
deps.planExecutor.submitNodeResult({
|
|
433
|
+
threadId: run.threadId,
|
|
434
|
+
runId: recordIdToString(run.id, TABLES.PLAN_RUN),
|
|
435
|
+
nodeId: planNode.id,
|
|
436
|
+
emittedBy: planNode.owner.ref,
|
|
437
|
+
result,
|
|
438
|
+
}),
|
|
439
|
+
)
|
|
440
|
+
}),
|
|
441
|
+
)
|
|
442
|
+
|
|
443
|
+
if (dispatchExit._tag === 'Failure') {
|
|
444
|
+
const failure = Cause.squash(dispatchExit.cause)
|
|
445
|
+
yield* Effect.tryPromise(() =>
|
|
446
|
+
deps.planExecutor.blockNodeOnDispatchFailure({
|
|
447
|
+
threadId: run.threadId,
|
|
448
|
+
runId: recordIdToString(run.id, TABLES.PLAN_RUN),
|
|
449
|
+
nodeId: planNode.id,
|
|
450
|
+
emittedBy: planNode.owner.ref,
|
|
451
|
+
message: formatDispatchError(failure),
|
|
452
|
+
failureClass: classifyDispatchFailure({ ownerType: planNode.owner.executorType, error: failure }),
|
|
453
|
+
}),
|
|
454
|
+
)
|
|
455
|
+
return yield* serializeRunEffect(deps, run.id)
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
return yield* new ConfigurationError({
|
|
460
|
+
message: `Ownership dispatch exceeded ${MAX_DISPATCH_ITERATIONS} iterations for run ${recordIdToString(
|
|
461
|
+
ensureRecordId(params.runId, TABLES.PLAN_RUN),
|
|
462
|
+
TABLES.PLAN_RUN,
|
|
463
|
+
)}.`,
|
|
464
|
+
})
|
|
465
|
+
})
|
|
466
|
+
|
|
467
|
+
const dispatchReadyNodeEffect = (
|
|
468
|
+
deps: OwnershipDispatcherDeps,
|
|
469
|
+
params: {
|
|
470
|
+
run: PlanRunRecord
|
|
471
|
+
nodeSpecRecord: PlanNodeSpecRecord
|
|
472
|
+
nodeRun: PlanNodeRunRecord
|
|
473
|
+
spec: PlanSpecRecord
|
|
474
|
+
executionModeOverride?: ExecutionMode
|
|
475
|
+
},
|
|
476
|
+
) =>
|
|
477
|
+
Effect.gen(function* () {
|
|
478
|
+
if (shouldPlanNodeUseVisibleTurn(params.spec, params.nodeSpecRecord)) {
|
|
479
|
+
return yield* new BadRequestError({
|
|
480
|
+
message: `Node "${params.nodeSpecRecord.nodeId}" requires a visible plan turn and cannot be silently dispatched.`,
|
|
419
481
|
})
|
|
420
482
|
}
|
|
421
483
|
|
|
422
|
-
|
|
423
|
-
|
|
484
|
+
const planNode = toPlanNodeSpec(params.nodeSpecRecord)
|
|
485
|
+
const [artifacts, dispatchContext] = yield* Effect.all([
|
|
486
|
+
deps.planRun
|
|
487
|
+
.listArtifacts(params.run.id)
|
|
488
|
+
.pipe(Effect.mapError((cause) => toDispatchDatabaseError('Failed to load run artifacts.', cause))),
|
|
489
|
+
buildDispatchContextEffect(deps, params.run, params.spec, params.nodeSpecRecord),
|
|
490
|
+
])
|
|
491
|
+
const inputArtifacts = artifacts
|
|
492
|
+
.filter((artifact) => params.nodeSpecRecord.upstreamNodeIds.includes(artifact.nodeId))
|
|
493
|
+
.map((artifact) => toArtifactSubmission(artifact))
|
|
494
|
+
|
|
495
|
+
return yield* dispatchNodeEffect(deps, {
|
|
496
|
+
nodeSpec: planNode,
|
|
497
|
+
resolvedInput: params.nodeRun.resolvedInput ?? {},
|
|
498
|
+
inputArtifacts,
|
|
499
|
+
context: { ...dispatchContext, nodeId: planNode.id },
|
|
500
|
+
executionMode: params.spec.executionMode,
|
|
501
|
+
executionModeOverride: params.executionModeOverride,
|
|
502
|
+
schemaRegistry: params.spec.schemaRegistry,
|
|
503
|
+
})
|
|
504
|
+
})
|
|
505
|
+
|
|
506
|
+
function resolveExecutionVisibility(params: { spec: PlanSpecRecord; nodeSpecRecord: PlanNodeSpecRecord }) {
|
|
507
|
+
return resolvePlanNodeExecutionVisibility(params.spec, params.nodeSpecRecord)
|
|
424
508
|
}
|
|
425
509
|
|
|
426
|
-
export
|
|
510
|
+
export function makeOwnershipDispatcherService(deps: OwnershipDispatcherDeps) {
|
|
511
|
+
return {
|
|
512
|
+
validateDraftExecutors(draft: PlanDraft) {
|
|
513
|
+
return validateDraftExecutors(deps, draft)
|
|
514
|
+
},
|
|
515
|
+
dispatchRunToStableBoundary(params: { runId: RecordIdInput; emittedBy: string }) {
|
|
516
|
+
return dispatchRunToStableBoundaryEffect(deps, params)
|
|
517
|
+
},
|
|
518
|
+
dispatchReadyNode(params: {
|
|
519
|
+
run: PlanRunRecord
|
|
520
|
+
nodeSpecRecord: PlanNodeSpecRecord
|
|
521
|
+
nodeRun: PlanNodeRunRecord
|
|
522
|
+
spec: PlanSpecRecord
|
|
523
|
+
executionModeOverride?: ExecutionMode
|
|
524
|
+
}) {
|
|
525
|
+
return dispatchReadyNodeEffect(deps, params)
|
|
526
|
+
},
|
|
527
|
+
resolveExecutionVisibility(params: { spec: PlanSpecRecord; nodeSpecRecord: PlanNodeSpecRecord }) {
|
|
528
|
+
return resolveExecutionVisibility(params)
|
|
529
|
+
},
|
|
530
|
+
dispatchNode(params: {
|
|
531
|
+
nodeSpec: PlanNodeSpec
|
|
532
|
+
resolvedInput: Record<string, unknown>
|
|
533
|
+
inputArtifacts: PlanArtifactSubmission[]
|
|
534
|
+
context: OwnershipDispatchContext
|
|
535
|
+
executionMode?: ExecutionMode
|
|
536
|
+
executionModeOverride?: ExecutionMode
|
|
537
|
+
schemaRegistry?: PlanSchemaRegistry
|
|
538
|
+
}) {
|
|
539
|
+
return dispatchNodeEffect(deps, params)
|
|
540
|
+
},
|
|
541
|
+
} as const
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
export class OwnershipDispatcherServiceTag extends Context.Service<
|
|
545
|
+
OwnershipDispatcherServiceTag,
|
|
546
|
+
ReturnType<typeof makeOwnershipDispatcherService>
|
|
547
|
+
>()('OwnershipDispatcherService') {}
|
|
548
|
+
|
|
549
|
+
export const OwnershipDispatcherServiceLive = Layer.effect(
|
|
550
|
+
OwnershipDispatcherServiceTag,
|
|
551
|
+
Effect.gen(function* () {
|
|
552
|
+
const db = yield* DatabaseServiceTag
|
|
553
|
+
const agentConfig = yield* AgentConfigServiceTag
|
|
554
|
+
const runtimeAdapters = yield* RuntimeAdaptersServiceTag
|
|
555
|
+
const planRun = yield* PlanRunServiceTag
|
|
556
|
+
const agentExecutor = yield* AgentExecutorServiceTag
|
|
557
|
+
const monitoringWindow = yield* MonitoringWindowServiceTag
|
|
558
|
+
const planExecutor = yield* PlanExecutorServiceTag
|
|
559
|
+
const pluginExecutor = yield* PluginExecutorServiceTag
|
|
560
|
+
const skillResolver = yield* SkillResolverServiceTag
|
|
561
|
+
const systemExecutor = yield* SystemExecutorServiceTag
|
|
562
|
+
const user = yield* UserServiceTag
|
|
563
|
+
return makeOwnershipDispatcherService({
|
|
564
|
+
db,
|
|
565
|
+
agentConfig,
|
|
566
|
+
runtimeAdapters,
|
|
567
|
+
agentExecutor,
|
|
568
|
+
monitoringWindow,
|
|
569
|
+
planExecutor,
|
|
570
|
+
planRun,
|
|
571
|
+
pluginExecutor,
|
|
572
|
+
skillResolver,
|
|
573
|
+
systemExecutor,
|
|
574
|
+
user,
|
|
575
|
+
})
|
|
576
|
+
}),
|
|
577
|
+
)
|