@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
|
@@ -1,96 +1,152 @@
|
|
|
1
1
|
import type { Recommendation } from '@lota-sdk/shared'
|
|
2
|
+
import { Context, Schema, Effect, Layer } from 'effect'
|
|
2
3
|
|
|
3
4
|
import { ensureRecordId, recordIdToString } from '../db/record-id'
|
|
4
5
|
import { TABLES } from '../db/tables'
|
|
5
|
-
import { toIsoDateTimeString } from '../utils/date-time'
|
|
6
|
-
import {
|
|
6
|
+
import { toIsoDateTimeString, unsafeDateFrom } from '../utils/date-time'
|
|
7
|
+
import type { makePlanRunService } from './plan/plan-run.service'
|
|
8
|
+
import { PlanRunServiceTag } from './plan/plan-run.service'
|
|
7
9
|
|
|
8
|
-
class
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
const attempts = await planRunService.listAttempts(run.id)
|
|
13
|
-
const nodeSpecs = await planRunService.listNodeSpecs(ensureRecordId(run.planSpecId, TABLES.PLAN_SPEC))
|
|
14
|
-
const nodeSpecsByNodeId = new Map(nodeSpecs.map((ns) => [ns.nodeId, ns]))
|
|
10
|
+
class FeedbackLoopServiceError extends Schema.TaggedErrorClass<FeedbackLoopServiceError>()('FeedbackLoopServiceError', {
|
|
11
|
+
message: Schema.String,
|
|
12
|
+
cause: Schema.Defect,
|
|
13
|
+
}) {}
|
|
15
14
|
|
|
16
|
-
|
|
15
|
+
export function makeFeedbackLoopService(planRunService: ReturnType<typeof makePlanRunService>) {
|
|
16
|
+
return {
|
|
17
|
+
analyzeOutcomes(params: { runId: string; organizationId: string }) {
|
|
18
|
+
return Effect.gen(function* () {
|
|
19
|
+
const run = yield* planRunService
|
|
20
|
+
.getRunById(params.runId)
|
|
21
|
+
.pipe(
|
|
22
|
+
Effect.mapError(
|
|
23
|
+
(cause) =>
|
|
24
|
+
new FeedbackLoopServiceError({ message: 'Failed to load plan run for feedback analysis.', cause }),
|
|
25
|
+
),
|
|
26
|
+
)
|
|
27
|
+
const [nodeRuns, attempts, nodeSpecs] = yield* Effect.all([
|
|
28
|
+
planRunService
|
|
29
|
+
.listNodeRuns(run.id)
|
|
30
|
+
.pipe(
|
|
31
|
+
Effect.mapError(
|
|
32
|
+
(cause) =>
|
|
33
|
+
new FeedbackLoopServiceError({ message: 'Failed to load node runs for feedback analysis.', cause }),
|
|
34
|
+
),
|
|
35
|
+
),
|
|
36
|
+
planRunService
|
|
37
|
+
.listAttempts(run.id)
|
|
38
|
+
.pipe(
|
|
39
|
+
Effect.mapError(
|
|
40
|
+
(cause) =>
|
|
41
|
+
new FeedbackLoopServiceError({
|
|
42
|
+
message: 'Failed to load node attempts for feedback analysis.',
|
|
43
|
+
cause,
|
|
44
|
+
}),
|
|
45
|
+
),
|
|
46
|
+
),
|
|
47
|
+
planRunService
|
|
48
|
+
.listNodeSpecs(ensureRecordId(run.planSpecId, TABLES.PLAN_SPEC))
|
|
49
|
+
.pipe(
|
|
50
|
+
Effect.mapError(
|
|
51
|
+
(cause) =>
|
|
52
|
+
new FeedbackLoopServiceError({ message: 'Failed to load node specs for feedback analysis.', cause }),
|
|
53
|
+
),
|
|
54
|
+
),
|
|
55
|
+
])
|
|
17
56
|
|
|
18
|
-
|
|
19
|
-
const nodeSpec = nodeSpecsByNodeId.get(nodeRun.nodeId)
|
|
20
|
-
if (!nodeSpec) continue
|
|
57
|
+
const nodeSpecsByNodeId = new Map(nodeSpecs.map((ns) => [ns.nodeId, ns]))
|
|
21
58
|
|
|
22
|
-
|
|
23
|
-
const failedAttempts = nodeAttempts.filter((a) => a.status === 'failed')
|
|
59
|
+
const recommendations: Recommendation[] = []
|
|
24
60
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
target: 'node',
|
|
29
|
-
targetId: nodeRun.nodeId,
|
|
30
|
-
description: `Node "${nodeSpec.label}" failed ${failedAttempts.length} times before ${nodeRun.status === 'completed' || nodeRun.status === 'partial' ? 'succeeding' : 'giving up'}. Consider adjusting retry policy or node instructions.`,
|
|
31
|
-
evidence: failedAttempts.map((a) => ({
|
|
32
|
-
sourceType: 'metric',
|
|
33
|
-
sourceId: recordIdToString(a.id, TABLES.PLAN_NODE_ATTEMPT),
|
|
34
|
-
summary: `Attempt failed with class: ${a.failureClass ?? 'unknown'}`,
|
|
35
|
-
confidence: 0.9,
|
|
36
|
-
})),
|
|
37
|
-
confidence: Math.min(0.5 + failedAttempts.length * 0.15, 0.95),
|
|
38
|
-
})
|
|
39
|
-
}
|
|
40
|
-
}
|
|
61
|
+
for (const nodeRun of nodeRuns) {
|
|
62
|
+
const nodeSpec = nodeSpecsByNodeId.get(nodeRun.nodeId)
|
|
63
|
+
if (!nodeSpec) continue
|
|
41
64
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
65
|
+
const nodeAttempts = attempts.filter((a) => a.nodeId === nodeRun.nodeId)
|
|
66
|
+
const failedAttempts = nodeAttempts.filter((a) => a.status === 'failed')
|
|
67
|
+
|
|
68
|
+
if (failedAttempts.length >= 2) {
|
|
69
|
+
recommendations.push({
|
|
70
|
+
type: 'warning',
|
|
71
|
+
target: 'node',
|
|
72
|
+
targetId: nodeRun.nodeId,
|
|
73
|
+
description: `Node "${nodeSpec.label}" failed ${failedAttempts.length} times before ${nodeRun.status === 'completed' || nodeRun.status === 'partial' ? 'succeeding' : 'giving up'}. Consider adjusting retry policy or node instructions.`,
|
|
74
|
+
evidence: failedAttempts.map((a) => ({
|
|
75
|
+
sourceType: 'metric',
|
|
76
|
+
sourceId: recordIdToString(a.id, TABLES.PLAN_NODE_ATTEMPT),
|
|
77
|
+
summary: `Attempt failed with class: ${a.failureClass ?? 'unknown'}`,
|
|
78
|
+
confidence: 0.9,
|
|
79
|
+
})),
|
|
80
|
+
confidence: Math.min(0.5 + failedAttempts.length * 0.15, 0.95),
|
|
81
|
+
})
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const completedNodeRuns = nodeRuns.filter(
|
|
86
|
+
(nr) => nr.startedAt && nr.completedAt && (nr.status === 'completed' || nr.status === 'partial'),
|
|
87
|
+
)
|
|
88
|
+
if (completedNodeRuns.length >= 2) {
|
|
89
|
+
const durations = completedNodeRuns.map((nr) => {
|
|
90
|
+
const start = unsafeDateFrom(toIsoDateTimeString(nr.startedAt)).getTime()
|
|
91
|
+
const end = unsafeDateFrom(toIsoDateTimeString(nr.completedAt)).getTime()
|
|
92
|
+
return { nodeId: nr.nodeId, durationMs: end - start }
|
|
93
|
+
})
|
|
51
94
|
|
|
52
|
-
|
|
95
|
+
const avgDuration = durations.reduce((sum, d) => sum + d.durationMs, 0) / durations.length
|
|
53
96
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
97
|
+
for (const d of durations) {
|
|
98
|
+
if (d.durationMs > avgDuration * 2.5 && d.durationMs > 5000) {
|
|
99
|
+
const nodeSpec = nodeSpecsByNodeId.get(d.nodeId)
|
|
100
|
+
recommendations.push({
|
|
101
|
+
type: 'optimization',
|
|
102
|
+
target: 'node',
|
|
103
|
+
targetId: d.nodeId,
|
|
104
|
+
description: `Node "${nodeSpec?.label ?? d.nodeId}" took ${Math.round(d.durationMs / 1000)}s, which is ${Math.round((d.durationMs / avgDuration) * 10) / 10}x the average. Consider splitting or optimizing.`,
|
|
105
|
+
evidence: [
|
|
106
|
+
{
|
|
107
|
+
sourceType: 'metric',
|
|
108
|
+
sourceId: d.nodeId,
|
|
109
|
+
summary: `Execution time: ${d.durationMs}ms vs average ${Math.round(avgDuration)}ms`,
|
|
110
|
+
confidence: 0.85,
|
|
111
|
+
},
|
|
112
|
+
],
|
|
113
|
+
confidence: 0.7,
|
|
114
|
+
})
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const skippedNodes = nodeRuns.filter((nr) => nr.status === 'skipped')
|
|
120
|
+
if (skippedNodes.length > 0 && skippedNodes.length >= nodeRuns.length * 0.3) {
|
|
57
121
|
recommendations.push({
|
|
58
|
-
type: '
|
|
59
|
-
target: '
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
},
|
|
69
|
-
],
|
|
70
|
-
confidence: 0.7,
|
|
122
|
+
type: 'pattern',
|
|
123
|
+
target: 'plan',
|
|
124
|
+
description: `${skippedNodes.length} of ${nodeRuns.length} nodes were skipped. The plan may have overly broad conditional branches.`,
|
|
125
|
+
evidence: skippedNodes.map((nr) => ({
|
|
126
|
+
sourceType: 'pattern',
|
|
127
|
+
sourceId: nr.nodeId,
|
|
128
|
+
summary: `Node "${nodeSpecsByNodeId.get(nr.nodeId)?.label ?? nr.nodeId}" was skipped`,
|
|
129
|
+
confidence: 0.8,
|
|
130
|
+
})),
|
|
131
|
+
confidence: 0.65,
|
|
71
132
|
})
|
|
72
133
|
}
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
134
|
|
|
76
|
-
|
|
77
|
-
if (skippedNodes.length > 0 && skippedNodes.length >= nodeRuns.length * 0.3) {
|
|
78
|
-
recommendations.push({
|
|
79
|
-
type: 'pattern',
|
|
80
|
-
target: 'plan',
|
|
81
|
-
description: `${skippedNodes.length} of ${nodeRuns.length} nodes were skipped. The plan may have overly broad conditional branches.`,
|
|
82
|
-
evidence: skippedNodes.map((nr) => ({
|
|
83
|
-
sourceType: 'pattern',
|
|
84
|
-
sourceId: nr.nodeId,
|
|
85
|
-
summary: `Node "${nodeSpecsByNodeId.get(nr.nodeId)?.label ?? nr.nodeId}" was skipped`,
|
|
86
|
-
confidence: 0.8,
|
|
87
|
-
})),
|
|
88
|
-
confidence: 0.65,
|
|
135
|
+
return recommendations
|
|
89
136
|
})
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
return recommendations
|
|
137
|
+
},
|
|
93
138
|
}
|
|
94
139
|
}
|
|
95
140
|
|
|
96
|
-
export
|
|
141
|
+
export class FeedbackLoopServiceTag extends Context.Service<
|
|
142
|
+
FeedbackLoopServiceTag,
|
|
143
|
+
ReturnType<typeof makeFeedbackLoopService>
|
|
144
|
+
>()('FeedbackLoopService') {}
|
|
145
|
+
|
|
146
|
+
export const FeedbackLoopServiceLive = Layer.effect(
|
|
147
|
+
FeedbackLoopServiceTag,
|
|
148
|
+
Effect.gen(function* () {
|
|
149
|
+
const planRunService = yield* PlanRunServiceTag
|
|
150
|
+
return makeFeedbackLoopService(planRunService)
|
|
151
|
+
}),
|
|
152
|
+
)
|
|
@@ -1,180 +1,90 @@
|
|
|
1
|
-
import type { ConvergenceState
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
1
|
+
import type { ConvergenceState } from '@lota-sdk/shared'
|
|
2
|
+
import { Context, Effect, Layer } from 'effect'
|
|
3
|
+
|
|
4
|
+
import { runPromise } from '../effect/runtime'
|
|
5
|
+
import { getCurrentRuntime } from '../effect/runtime-ref'
|
|
6
|
+
import { routeGraphFullEffect } from './graph-full-routing'
|
|
7
|
+
import type { makeOwnershipDispatcherService } from './ownership-dispatcher.service'
|
|
8
|
+
import { OwnershipDispatcherServiceTag } from './ownership-dispatcher.service'
|
|
9
|
+
import type { makePlanExecutorService } from './plan/plan-executor.service'
|
|
10
|
+
import { PlanExecutorServiceTag } from './plan/plan-executor.service'
|
|
11
|
+
import type { makePlanRunService } from './plan/plan-run.service'
|
|
12
|
+
import { PlanRunServiceTag } from './plan/plan-run.service'
|
|
13
|
+
|
|
14
|
+
function detectConvergence(params: {
|
|
15
|
+
totalNodes: number
|
|
16
|
+
completedNodes: number
|
|
17
|
+
failedNodes: number
|
|
18
|
+
previousCompletedNodes?: number
|
|
19
|
+
previousFailedNodes?: number
|
|
20
|
+
}): ConvergenceState {
|
|
21
|
+
const completionRatio = params.totalNodes > 0 ? params.completedNodes / params.totalNodes : 0
|
|
22
|
+
const failureRatio = params.totalNodes > 0 ? params.failedNodes / params.totalNodes : 0
|
|
23
|
+
|
|
24
|
+
if (params.previousCompletedNodes !== undefined) {
|
|
25
|
+
const completionVelocity = params.completedNodes - params.previousCompletedNodes
|
|
26
|
+
const failureVelocity = params.failedNodes - (params.previousFailedNodes ?? 0)
|
|
27
|
+
|
|
28
|
+
if (completionVelocity > 0 && failureVelocity === 0) return 'converging'
|
|
29
|
+
if (completionVelocity === 0 && failureVelocity === 0) return 'stalled'
|
|
30
|
+
if (failureVelocity > 0) return 'diverging'
|
|
31
|
+
}
|
|
7
32
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
if (ownerType === 'plugin' || ownerType === 'system') return 'external_system_unavailable'
|
|
12
|
-
return 'non_recoverable_logic_error'
|
|
33
|
+
if (completionRatio > 0.5 && failureRatio === 0) return 'converging'
|
|
34
|
+
if (failureRatio > 0.3) return 'diverging'
|
|
35
|
+
return 'progressing'
|
|
13
36
|
}
|
|
14
37
|
|
|
15
|
-
function
|
|
16
|
-
|
|
38
|
+
function decideRerouteAction(params: {
|
|
39
|
+
failedNodeId: string
|
|
40
|
+
retryCount: number
|
|
41
|
+
maxRetries: number
|
|
42
|
+
convergenceState: ConvergenceState
|
|
43
|
+
}): 'retry' | 'skip' | 'abort' {
|
|
44
|
+
if (params.retryCount < params.maxRetries) return 'retry'
|
|
45
|
+
if (params.convergenceState === 'converging') return 'skip'
|
|
46
|
+
if (params.convergenceState === 'diverging') return 'abort'
|
|
47
|
+
return 'skip'
|
|
17
48
|
}
|
|
18
49
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
completedNodes: number
|
|
25
|
-
failedNodes: number
|
|
26
|
-
previousCompletedNodes?: number
|
|
27
|
-
previousFailedNodes?: number
|
|
28
|
-
}): ConvergenceState {
|
|
29
|
-
const completionRatio = params.totalNodes > 0 ? params.completedNodes / params.totalNodes : 0
|
|
30
|
-
const failureRatio = params.totalNodes > 0 ? params.failedNodes / params.totalNodes : 0
|
|
31
|
-
|
|
32
|
-
if (params.previousCompletedNodes !== undefined) {
|
|
33
|
-
const completionVelocity = params.completedNodes - params.previousCompletedNodes
|
|
34
|
-
const failureVelocity = params.failedNodes - (params.previousFailedNodes ?? 0)
|
|
35
|
-
|
|
36
|
-
if (completionVelocity > 0 && failureVelocity === 0) return 'converging'
|
|
37
|
-
if (completionVelocity === 0 && failureVelocity === 0) return 'stalled'
|
|
38
|
-
if (failureVelocity > 0) return 'diverging'
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
if (completionRatio > 0.5 && failureRatio === 0) return 'converging'
|
|
42
|
-
if (failureRatio > 0.3) return 'diverging'
|
|
43
|
-
return 'progressing'
|
|
44
|
-
}
|
|
50
|
+
interface GlobalOrchestratorDeps {
|
|
51
|
+
ownershipDispatcherService: ReturnType<typeof makeOwnershipDispatcherService>
|
|
52
|
+
planExecutorService: ReturnType<typeof makePlanExecutorService>
|
|
53
|
+
planRunService: ReturnType<typeof makePlanRunService>
|
|
54
|
+
}
|
|
45
55
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
+
export function makeGlobalOrchestratorService(deps: GlobalOrchestratorDeps) {
|
|
57
|
+
return {
|
|
58
|
+
detectConvergence,
|
|
59
|
+
decideRerouteAction,
|
|
60
|
+
routeGraphFull: (params: { threadId: string; runId: string }) =>
|
|
61
|
+
routeGraphFullEffect(params, {
|
|
62
|
+
dispatchReadyNode: (dispatchParams) => deps.ownershipDispatcherService.dispatchReadyNode(dispatchParams),
|
|
63
|
+
planExecutorService: deps.planExecutorService,
|
|
64
|
+
planRunService: deps.planRunService,
|
|
65
|
+
}),
|
|
56
66
|
}
|
|
67
|
+
}
|
|
57
68
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
// Find ready action nodes (not structural, not human — those are handled by syncRunGraph)
|
|
77
|
-
const readyNodes = nodeRuns.filter((nr) => {
|
|
78
|
-
if (nr.status !== 'ready') return false
|
|
79
|
-
const ns = nodeSpecs.find((s) => s.nodeId === nr.nodeId)
|
|
80
|
-
return ns && ns.owner.executorType !== 'user' && !STRUCTURAL_TYPES.has(ns.type)
|
|
81
|
-
})
|
|
82
|
-
if (readyNodes.length === 0) break
|
|
83
|
-
|
|
84
|
-
// Split into silent (dispatch now) and visible (enqueue heartbeat wake)
|
|
85
|
-
const silentNodes = readyNodes.filter((nr) => {
|
|
86
|
-
const ns = nodeSpecs.find((s) => s.nodeId === nr.nodeId)
|
|
87
|
-
return ns && !shouldPlanNodeUseVisibleTurn(spec, ns)
|
|
88
|
-
})
|
|
89
|
-
const visibleNodes = readyNodes.filter((nr) => {
|
|
90
|
-
const ns = nodeSpecs.find((s) => s.nodeId === nr.nodeId)
|
|
91
|
-
return ns && shouldPlanNodeUseVisibleTurn(spec, ns)
|
|
92
|
-
})
|
|
93
|
-
|
|
94
|
-
// Transition all ready nodes to 'running' BEFORE dispatching
|
|
95
|
-
for (const nodeRun of readyNodes) {
|
|
96
|
-
await planExecutorService.transitionNodeToRunning({ runId: params.runId, nodeId: nodeRun.nodeId })
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
// Enqueue heartbeat wakes for visible agent nodes — they need a streaming turn
|
|
100
|
-
if (visibleNodes.length > 0) {
|
|
101
|
-
const { enqueuePlanAgentHeartbeatWake } = await import('../queues/plan-agent-heartbeat.queue')
|
|
102
|
-
const updatedRunForWake = await planRunService.getRunById(params.runId)
|
|
103
|
-
for (const nodeRun of visibleNodes) {
|
|
104
|
-
const ns = nodeSpecs.find((s) => s.nodeId === nodeRun.nodeId)
|
|
105
|
-
if (!ns || ns.owner.executorType !== 'agent') continue
|
|
106
|
-
await enqueuePlanAgentHeartbeatWake({
|
|
107
|
-
organizationId: recordIdToString(updatedRunForWake.organizationId, TABLES.ORGANIZATION),
|
|
108
|
-
threadId: recordIdToString(updatedRunForWake.threadId, TABLES.THREAD),
|
|
109
|
-
runId: recordIdToString(updatedRunForWake.id, TABLES.PLAN_RUN),
|
|
110
|
-
nodeId: nodeRun.nodeId,
|
|
111
|
-
agentId: ns.owner.ref,
|
|
112
|
-
reason: 'graph-full-visible',
|
|
113
|
-
})
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
// If no silent nodes to dispatch, break and let heartbeat handle visible ones
|
|
118
|
-
if (silentNodes.length === 0) break
|
|
119
|
-
|
|
120
|
-
// Re-fetch run after transitions for accurate state in dispatch context
|
|
121
|
-
const updatedRun = await planRunService.getRunById(params.runId)
|
|
122
|
-
|
|
123
|
-
// Dispatch silent nodes in parallel with LINEAR mode override (prevents recursion)
|
|
124
|
-
const results = await Promise.allSettled(
|
|
125
|
-
silentNodes.map(async (nodeRun) => {
|
|
126
|
-
const nodeSpecRecord = nodeSpecs.find((ns) => ns.nodeId === nodeRun.nodeId)
|
|
127
|
-
if (!nodeSpecRecord) {
|
|
128
|
-
throw new Error(`Node spec not found for node "${nodeRun.nodeId}".`)
|
|
129
|
-
}
|
|
130
|
-
// Re-fetch the node run to get the updated 'running' state with resolvedInput
|
|
131
|
-
const updatedNodeRun = await planRunService.getNodeRunByNodeId(updatedRun.id, nodeRun.nodeId)
|
|
132
|
-
const result = await ownershipDispatcherService.dispatchReadyNode({
|
|
133
|
-
run: updatedRun,
|
|
134
|
-
nodeSpecRecord,
|
|
135
|
-
nodeRun: updatedNodeRun,
|
|
136
|
-
spec,
|
|
137
|
-
executionModeOverride: 'linear',
|
|
138
|
-
})
|
|
139
|
-
return { nodeId: nodeRun.nodeId, ownerRef: nodeSpecRecord.owner.ref, result }
|
|
140
|
-
}),
|
|
141
|
-
)
|
|
142
|
-
|
|
143
|
-
const threadId = recordIdToString(updatedRun.threadId, TABLES.THREAD)
|
|
144
|
-
const runId = recordIdToString(updatedRun.id, TABLES.PLAN_RUN)
|
|
145
|
-
|
|
146
|
-
// Submit results sequentially (each triggers syncRunGraph internally)
|
|
147
|
-
for (let i = 0; i < results.length; i++) {
|
|
148
|
-
const settled = results[i]
|
|
149
|
-
const nodeRun = silentNodes[i]
|
|
150
|
-
const nodeSpecRecord = nodeSpecs.find((ns) => ns.nodeId === nodeRun.nodeId)
|
|
151
|
-
|
|
152
|
-
if (settled.status === 'fulfilled') {
|
|
153
|
-
await planExecutorService.submitNodeResult({
|
|
154
|
-
threadId,
|
|
155
|
-
runId,
|
|
156
|
-
nodeId: settled.value.nodeId,
|
|
157
|
-
emittedBy: settled.value.ownerRef,
|
|
158
|
-
result: settled.value.result,
|
|
159
|
-
})
|
|
160
|
-
} else {
|
|
161
|
-
serverLogger.warn`routeGraphFull: dispatch failed for node "${nodeRun.nodeId}": ${settled.reason}`
|
|
162
|
-
await planExecutorService.blockNodeOnDispatchFailure({
|
|
163
|
-
threadId,
|
|
164
|
-
runId,
|
|
165
|
-
nodeId: nodeRun.nodeId,
|
|
166
|
-
emittedBy: nodeSpecRecord?.owner.ref ?? 'unknown',
|
|
167
|
-
message: formatDispatchError(settled.reason),
|
|
168
|
-
failureClass: classifyDispatchFailure(nodeSpecRecord?.owner.executorType ?? 'agent', settled.reason),
|
|
169
|
-
})
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
if (round === MAX_ROUNDS - 1) {
|
|
175
|
-
serverLogger.warn`graph-full execution reached max rounds (${MAX_ROUNDS}) for run ${params.runId} — possible non-converging graph`
|
|
176
|
-
}
|
|
177
|
-
}
|
|
69
|
+
export class GlobalOrchestratorServiceTag extends Context.Service<
|
|
70
|
+
GlobalOrchestratorServiceTag,
|
|
71
|
+
ReturnType<typeof makeGlobalOrchestratorService>
|
|
72
|
+
>()('GlobalOrchestratorService') {}
|
|
73
|
+
|
|
74
|
+
export const GlobalOrchestratorServiceLive = Layer.effect(
|
|
75
|
+
GlobalOrchestratorServiceTag,
|
|
76
|
+
Effect.gen(function* () {
|
|
77
|
+
const ownershipDispatcherService = yield* OwnershipDispatcherServiceTag
|
|
78
|
+
const planExecutorService = yield* PlanExecutorServiceTag
|
|
79
|
+
const planRunService = yield* PlanRunServiceTag
|
|
80
|
+
return makeGlobalOrchestratorService({ ownershipDispatcherService, planExecutorService, planRunService })
|
|
81
|
+
}),
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
function getGlobalOrchestratorService() {
|
|
85
|
+
return getCurrentRuntime().runSync(Effect.service(GlobalOrchestratorServiceTag))
|
|
178
86
|
}
|
|
179
87
|
|
|
180
|
-
export
|
|
88
|
+
export function routeGraphFull(params: { threadId: string; runId: string }): Promise<void> {
|
|
89
|
+
return runPromise(getGlobalOrchestratorService().routeGraphFull(params))
|
|
90
|
+
}
|