@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,80 +0,0 @@
|
|
|
1
|
-
import type { PlanNodeOwner, PlanNodeType } from '@lota-sdk/shared'
|
|
2
|
-
import { PlanEventSchema } from '@lota-sdk/shared'
|
|
3
|
-
|
|
4
|
-
import { aiLogger } from '../config/logger'
|
|
5
|
-
import { ensureRecordId } from '../db/record-id'
|
|
6
|
-
import { databaseService } from '../db/service'
|
|
7
|
-
import { TABLES } from '../db/tables'
|
|
8
|
-
import { feedbackLoopService } from './feedback-loop.service'
|
|
9
|
-
import { institutionalMemoryService } from './institutional-memory.service'
|
|
10
|
-
import { planEventDeliveryService } from './plan-event-delivery.service'
|
|
11
|
-
import { planRunService } from './plan-run.service'
|
|
12
|
-
import type { PlanValidationIssueInput } from './plan-validator.service'
|
|
13
|
-
import { qualityMetricsService } from './quality-metrics.service'
|
|
14
|
-
|
|
15
|
-
export async function runPlanNodeCompletionSideEffects(params: {
|
|
16
|
-
runId: string
|
|
17
|
-
organizationId: string
|
|
18
|
-
nodeId: string
|
|
19
|
-
nodeLabel: string
|
|
20
|
-
nodeOwnerRef: string
|
|
21
|
-
nodeOwnerType: PlanNodeOwner['executorType']
|
|
22
|
-
nodeType: PlanNodeType
|
|
23
|
-
nodeStartedAt?: string | Date | null
|
|
24
|
-
nodeAttemptCount: number
|
|
25
|
-
artifactCount: number
|
|
26
|
-
validationIssues: PlanValidationIssueInput[]
|
|
27
|
-
}): Promise<void> {
|
|
28
|
-
const executionTimeMs = params.nodeStartedAt ? Date.now() - new Date(params.nodeStartedAt).getTime() : 0
|
|
29
|
-
await qualityMetricsService.recordNodeMetrics({
|
|
30
|
-
organizationId: params.organizationId,
|
|
31
|
-
runId: params.runId,
|
|
32
|
-
nodeId: params.nodeId,
|
|
33
|
-
metrics: {
|
|
34
|
-
executionTimeMs: Math.max(0, executionTimeMs),
|
|
35
|
-
attemptCount: params.nodeAttemptCount,
|
|
36
|
-
artifactCount: params.artifactCount,
|
|
37
|
-
validationIssueCount: params.validationIssues.length,
|
|
38
|
-
ownerRef: params.nodeOwnerRef,
|
|
39
|
-
ownerType: params.nodeOwnerType,
|
|
40
|
-
nodeType: params.nodeType,
|
|
41
|
-
},
|
|
42
|
-
})
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
async function runPlanCompletionSideEffects(params: { runId: string; organizationId: string }): Promise<void> {
|
|
46
|
-
await qualityMetricsService.recordCycleMetrics({ organizationId: params.organizationId, runId: params.runId })
|
|
47
|
-
|
|
48
|
-
const recommendations = await feedbackLoopService.analyzeOutcomes({
|
|
49
|
-
runId: params.runId,
|
|
50
|
-
organizationId: params.organizationId,
|
|
51
|
-
})
|
|
52
|
-
if (recommendations.length > 0) {
|
|
53
|
-
const run = await planRunService.getRunById(params.runId)
|
|
54
|
-
const specRecord = await planRunService.getPlanSpecById(run.planSpecId)
|
|
55
|
-
const event = await databaseService.create(
|
|
56
|
-
TABLES.PLAN_EVENT,
|
|
57
|
-
{
|
|
58
|
-
planSpecId: ensureRecordId(specRecord.id, TABLES.PLAN_SPEC),
|
|
59
|
-
runId: ensureRecordId(run.id, TABLES.PLAN_RUN),
|
|
60
|
-
eventType: 'feedback-analyzed',
|
|
61
|
-
message: `Feedback analysis produced ${recommendations.length} recommendation(s).`,
|
|
62
|
-
detail: { recommendations },
|
|
63
|
-
emittedBy: 'system',
|
|
64
|
-
},
|
|
65
|
-
PlanEventSchema,
|
|
66
|
-
)
|
|
67
|
-
await planEventDeliveryService.dispatchEvent(event)
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
await institutionalMemoryService.extractPatterns({ organizationId: params.organizationId, runId: params.runId })
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
export async function runPlanCompletionSideEffectsSafely(params: {
|
|
74
|
-
runId: string
|
|
75
|
-
organizationId: string
|
|
76
|
-
}): Promise<void> {
|
|
77
|
-
await runPlanCompletionSideEffects(params).catch((error) => {
|
|
78
|
-
aiLogger.warn`Plan completion side effects failed for run ${params.runId}: ${error instanceof Error ? error.message : String(error)}`
|
|
79
|
-
})
|
|
80
|
-
}
|
|
@@ -1,157 +0,0 @@
|
|
|
1
|
-
import type { PlanArtifactRecord, PlanDependency } from '@lota-sdk/shared'
|
|
2
|
-
|
|
3
|
-
import { serverLogger } from '../config/logger'
|
|
4
|
-
import { recordIdToString } from '../db/record-id'
|
|
5
|
-
import { TABLES } from '../db/tables'
|
|
6
|
-
import type { PlanValidationIssueInput } from './plan-validator.service'
|
|
7
|
-
|
|
8
|
-
export interface DependencyResolutionResult {
|
|
9
|
-
resolved: Map<string, PlanArtifactRecord>
|
|
10
|
-
unresolved: PlanDependency[]
|
|
11
|
-
notifications: Array<{ dependency: PlanDependency; reason: string }>
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
class PlanCoordinationService {
|
|
15
|
-
/**
|
|
16
|
-
* Resolve cross-plan artifact dependencies.
|
|
17
|
-
* For each dependency:
|
|
18
|
-
* 1. Find the source plan by spec id in the thread
|
|
19
|
-
* 2. Find the artifact by (nodeId, artifactName) in that plan's run
|
|
20
|
-
* 3. Check staleness if maxStalenessMs set
|
|
21
|
-
* 4. Based on triggerMode:
|
|
22
|
-
* 'block' -> unresolved if missing/stale
|
|
23
|
-
* 'notify' -> proceed but record notification
|
|
24
|
-
* 'best-effort' -> proceed regardless, no notification
|
|
25
|
-
*/
|
|
26
|
-
async resolveDependencies(params: {
|
|
27
|
-
dependencies: PlanDependency[]
|
|
28
|
-
threadId: string
|
|
29
|
-
}): Promise<DependencyResolutionResult> {
|
|
30
|
-
const { planRunService } = await import('./plan-run.service')
|
|
31
|
-
|
|
32
|
-
const resolved = new Map<string, PlanArtifactRecord>()
|
|
33
|
-
const unresolved: PlanDependency[] = []
|
|
34
|
-
const notifications: DependencyResolutionResult['notifications'] = []
|
|
35
|
-
const specs = await planRunService.listPlanSpecsByThread(params.threadId)
|
|
36
|
-
|
|
37
|
-
for (const dep of params.dependencies) {
|
|
38
|
-
const depKey = `${dep.sourcePlanSpecId}:${dep.sourceNodeId}:${dep.artifactName}`
|
|
39
|
-
|
|
40
|
-
const sourceSpec = specs.find((s) => recordIdToString(s.id, TABLES.PLAN_SPEC) === dep.sourcePlanSpecId)
|
|
41
|
-
if (!sourceSpec) {
|
|
42
|
-
const reason = `Source plan "${dep.sourcePlanSpecId}" not found in thread.`
|
|
43
|
-
if (dep.triggerMode === 'block') {
|
|
44
|
-
unresolved.push(dep)
|
|
45
|
-
} else if (dep.triggerMode === 'notify') {
|
|
46
|
-
notifications.push({ dependency: dep, reason })
|
|
47
|
-
serverLogger.warn`Dependency unmet (notify): ${reason}`
|
|
48
|
-
}
|
|
49
|
-
// best-effort: silently proceed
|
|
50
|
-
continue
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const runs = await planRunService.listRunsBySpec(sourceSpec.id)
|
|
54
|
-
const activeRun = runs.find((r) => r.status === 'completed' || r.status === 'running')
|
|
55
|
-
if (!activeRun) {
|
|
56
|
-
const reason = `No active run found for plan "${sourceSpec.title}".`
|
|
57
|
-
if (dep.triggerMode === 'block') {
|
|
58
|
-
unresolved.push(dep)
|
|
59
|
-
} else if (dep.triggerMode === 'notify') {
|
|
60
|
-
notifications.push({ dependency: dep, reason })
|
|
61
|
-
serverLogger.warn`Dependency unmet (notify): ${reason}`
|
|
62
|
-
}
|
|
63
|
-
continue
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
const artifacts = await planRunService.listArtifacts(activeRun.id)
|
|
67
|
-
const artifact = artifacts.find((a) => a.nodeId === dep.sourceNodeId && a.name === dep.artifactName)
|
|
68
|
-
|
|
69
|
-
if (!artifact) {
|
|
70
|
-
const reason = `Artifact "${dep.artifactName}" not found on node "${dep.sourceNodeId}" in plan "${sourceSpec.title}".`
|
|
71
|
-
if (dep.triggerMode === 'block') {
|
|
72
|
-
unresolved.push(dep)
|
|
73
|
-
} else if (dep.triggerMode === 'notify') {
|
|
74
|
-
notifications.push({ dependency: dep, reason })
|
|
75
|
-
serverLogger.warn`Dependency unmet (notify): ${reason}`
|
|
76
|
-
}
|
|
77
|
-
continue
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
if (dep.maxStalenessMs && this.isStale(artifact, dep.maxStalenessMs)) {
|
|
81
|
-
const reason = `Artifact "${dep.artifactName}" from plan "${sourceSpec.title}" is stale.`
|
|
82
|
-
if (dep.triggerMode === 'block') {
|
|
83
|
-
unresolved.push(dep)
|
|
84
|
-
continue
|
|
85
|
-
}
|
|
86
|
-
if (dep.triggerMode === 'notify') {
|
|
87
|
-
notifications.push({ dependency: dep, reason })
|
|
88
|
-
serverLogger.warn`Dependency stale (notify): ${reason}`
|
|
89
|
-
}
|
|
90
|
-
// best-effort and notify: use stale artifact anyway
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
resolved.set(depKey, artifact)
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
return { resolved, unresolved, notifications }
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
/** Check if an artifact has exceeded the staleness window. */
|
|
100
|
-
isStale(artifact: Pick<PlanArtifactRecord, 'createdAt'>, maxStalenessMs: number): boolean {
|
|
101
|
-
if (!maxStalenessMs) return false
|
|
102
|
-
return Date.now() - artifact.createdAt.getTime() > maxStalenessMs
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* Validate no circular dependencies exist using Kahn's algorithm.
|
|
107
|
-
* Build adjacency: planSpecId -> depends on upstream planSpecIds
|
|
108
|
-
* Run topological sort; if not all visited -> cycle exists.
|
|
109
|
-
*/
|
|
110
|
-
validateNoCycles(
|
|
111
|
-
specs: Array<{ id: string; title?: string; dependencies?: PlanDependency[] }>,
|
|
112
|
-
): PlanValidationIssueInput[] {
|
|
113
|
-
const adj = new Map<string, Set<string>>()
|
|
114
|
-
const inDegree = new Map<string, number>()
|
|
115
|
-
const labels = new Map(specs.map((spec) => [spec.id, spec.title ?? spec.id]))
|
|
116
|
-
|
|
117
|
-
for (const spec of specs) {
|
|
118
|
-
if (!adj.has(spec.id)) adj.set(spec.id, new Set())
|
|
119
|
-
if (!inDegree.has(spec.id)) inDegree.set(spec.id, 0)
|
|
120
|
-
|
|
121
|
-
for (const dep of spec.dependencies ?? []) {
|
|
122
|
-
if (!adj.has(dep.sourcePlanSpecId)) adj.set(dep.sourcePlanSpecId, new Set())
|
|
123
|
-
if (!inDegree.has(dep.sourcePlanSpecId)) inDegree.set(dep.sourcePlanSpecId, 0)
|
|
124
|
-
|
|
125
|
-
adj.get(dep.sourcePlanSpecId)?.add(spec.id)
|
|
126
|
-
inDegree.set(spec.id, (inDegree.get(spec.id) ?? 0) + 1)
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
const queue = [...inDegree.entries()].filter(([, d]) => d === 0).map(([t]) => t)
|
|
131
|
-
const visited = new Set<string>()
|
|
132
|
-
|
|
133
|
-
while (queue.length > 0) {
|
|
134
|
-
const node = queue.shift()
|
|
135
|
-
if (!node) break
|
|
136
|
-
visited.add(node)
|
|
137
|
-
for (const dep of adj.get(node) ?? []) {
|
|
138
|
-
const d = (inDegree.get(dep) ?? 0) - 1
|
|
139
|
-
inDegree.set(dep, d)
|
|
140
|
-
if (d === 0) queue.push(dep)
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
const unvisited = specs.filter((s) => !visited.has(s.id))
|
|
145
|
-
if (unvisited.length === 0) return []
|
|
146
|
-
|
|
147
|
-
return [
|
|
148
|
-
{
|
|
149
|
-
severity: 'blocking',
|
|
150
|
-
code: 'circular_dependency',
|
|
151
|
-
message: `Circular plan dependencies detected involving: ${unvisited.map((s) => labels.get(s.id) ?? s.id).join(', ')}`,
|
|
152
|
-
},
|
|
153
|
-
]
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
export const planCoordinationService = new PlanCoordinationService()
|
|
@@ -1,284 +0,0 @@
|
|
|
1
|
-
import { PlanArtifactSchema, PlanCycleRecordSchema, PlanRunSchema } from '@lota-sdk/shared'
|
|
2
|
-
import type {
|
|
3
|
-
CarryForwardPolicy,
|
|
4
|
-
CycleSchedule,
|
|
5
|
-
PlanArtifactRecord,
|
|
6
|
-
PlanCycleRecord,
|
|
7
|
-
PlanDraft,
|
|
8
|
-
PlanRunStatus,
|
|
9
|
-
PlanScheduleSpec,
|
|
10
|
-
PlanTemplateRecord,
|
|
11
|
-
} from '@lota-sdk/shared'
|
|
12
|
-
|
|
13
|
-
import type { RecordIdInput } from '../db/record-id'
|
|
14
|
-
import { ensureRecordId, recordIdToString } from '../db/record-id'
|
|
15
|
-
import { databaseService } from '../db/service'
|
|
16
|
-
import { TABLES } from '../db/tables'
|
|
17
|
-
import { planSchedulerService } from './plan-scheduler.service'
|
|
18
|
-
import { planTemplateService } from './plan-template.service'
|
|
19
|
-
|
|
20
|
-
const TERMINAL_RUN_STATUSES: ReadonlySet<PlanRunStatus> = new Set(['completed', 'failed', 'aborted'])
|
|
21
|
-
|
|
22
|
-
function cycleScheduleToSpec(schedule: CycleSchedule): PlanScheduleSpec {
|
|
23
|
-
const startAt = schedule.startAt ? new Date(schedule.startAt) : undefined
|
|
24
|
-
const isFutureStart = startAt && startAt.getTime() > Date.now()
|
|
25
|
-
|
|
26
|
-
if (isFutureStart) {
|
|
27
|
-
return { type: 'absolute', at: startAt.toISOString(), missedPolicy: 'run-immediately' }
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const cron = frequencyToCron(schedule.frequency, schedule.customIntervalMs)
|
|
31
|
-
if (cron) {
|
|
32
|
-
return { type: 'cron', cron, missedPolicy: 'run-immediately' }
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
if (schedule.frequency === 'biweekly') {
|
|
36
|
-
return { type: 'monitoring', intervalMs: 14 * 24 * 60 * 60 * 1000, missedPolicy: 'run-immediately' }
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
if (schedule.frequency === 'custom' && schedule.customIntervalMs) {
|
|
40
|
-
return { type: 'monitoring', intervalMs: schedule.customIntervalMs, missedPolicy: 'run-immediately' }
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
return { type: 'cron', cron: '0 0 * * *', missedPolicy: 'run-immediately' }
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function frequencyToCron(frequency: CycleSchedule['frequency'], _customIntervalMs?: number): string | null {
|
|
47
|
-
switch (frequency) {
|
|
48
|
-
case 'daily':
|
|
49
|
-
return '0 0 * * *'
|
|
50
|
-
case 'weekly':
|
|
51
|
-
return '0 0 * * 1'
|
|
52
|
-
case 'monthly':
|
|
53
|
-
return '0 0 1 * *'
|
|
54
|
-
case 'quarterly':
|
|
55
|
-
return '0 0 1 */3 *'
|
|
56
|
-
case 'biweekly':
|
|
57
|
-
case 'custom':
|
|
58
|
-
return null
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
class PlanCycleService {
|
|
63
|
-
cycleScheduleToSpec = cycleScheduleToSpec
|
|
64
|
-
|
|
65
|
-
async createCycle(params: {
|
|
66
|
-
organizationId: RecordIdInput
|
|
67
|
-
threadId: RecordIdInput
|
|
68
|
-
templateId: RecordIdInput
|
|
69
|
-
name: string
|
|
70
|
-
schedule: CycleSchedule
|
|
71
|
-
carryForwardPolicy?: CarryForwardPolicy
|
|
72
|
-
leadAgentId: string
|
|
73
|
-
}): Promise<PlanCycleRecord> {
|
|
74
|
-
const now = new Date()
|
|
75
|
-
|
|
76
|
-
const cycle = await databaseService.create(
|
|
77
|
-
TABLES.PLAN_CYCLE,
|
|
78
|
-
{
|
|
79
|
-
organizationId: ensureRecordId(params.organizationId, TABLES.ORGANIZATION),
|
|
80
|
-
threadId: ensureRecordId(params.threadId, TABLES.THREAD),
|
|
81
|
-
templateId: ensureRecordId(params.templateId, TABLES.PLAN_TEMPLATE),
|
|
82
|
-
name: params.name,
|
|
83
|
-
schedule: params.schedule,
|
|
84
|
-
carryForwardPolicy: params.carryForwardPolicy ?? 'incomplete-only',
|
|
85
|
-
status: 'active',
|
|
86
|
-
currentIteration: 0,
|
|
87
|
-
createdAt: now,
|
|
88
|
-
},
|
|
89
|
-
PlanCycleRecordSchema,
|
|
90
|
-
)
|
|
91
|
-
|
|
92
|
-
const createResult = await planTemplateService.instantiate({
|
|
93
|
-
templateId: params.templateId,
|
|
94
|
-
organizationId: params.organizationId,
|
|
95
|
-
threadId: params.threadId,
|
|
96
|
-
leadAgentId: params.leadAgentId,
|
|
97
|
-
})
|
|
98
|
-
|
|
99
|
-
const createdRunId = createResult.plan?.runId
|
|
100
|
-
|
|
101
|
-
const scheduleSpec = cycleScheduleToSpec(params.schedule)
|
|
102
|
-
const scheduleRecord = await planSchedulerService.createSchedule({
|
|
103
|
-
organizationId: params.organizationId,
|
|
104
|
-
threadId: params.threadId,
|
|
105
|
-
...(createdRunId ? { runId: ensureRecordId(createdRunId, TABLES.PLAN_RUN) } : {}),
|
|
106
|
-
scheduleSpec,
|
|
107
|
-
})
|
|
108
|
-
|
|
109
|
-
const updated = await databaseService.update(
|
|
110
|
-
TABLES.PLAN_CYCLE,
|
|
111
|
-
ensureRecordId(cycle.id, TABLES.PLAN_CYCLE),
|
|
112
|
-
{
|
|
113
|
-
scheduleId: ensureRecordId(scheduleRecord.id, TABLES.PLAN_SCHEDULE),
|
|
114
|
-
...(createdRunId ? { currentRunId: ensureRecordId(createdRunId, TABLES.PLAN_RUN) } : {}),
|
|
115
|
-
currentIteration: 1,
|
|
116
|
-
},
|
|
117
|
-
PlanCycleRecordSchema,
|
|
118
|
-
)
|
|
119
|
-
|
|
120
|
-
return updated ?? cycle
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
async advanceCycle(cycleId: RecordIdInput): Promise<void> {
|
|
124
|
-
const cycle = await this.getCycle(cycleId)
|
|
125
|
-
if (!cycle) {
|
|
126
|
-
throw new Error(`Cycle not found: ${recordIdToString(cycleId, TABLES.PLAN_CYCLE)}`)
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
if (cycle.status !== 'active') {
|
|
130
|
-
return
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
if (cycle.currentRunId) {
|
|
134
|
-
const runRecord = await databaseService.findOne(
|
|
135
|
-
TABLES.PLAN_RUN,
|
|
136
|
-
{ id: ensureRecordId(cycle.currentRunId, TABLES.PLAN_RUN) },
|
|
137
|
-
PlanRunSchema,
|
|
138
|
-
)
|
|
139
|
-
if (runRecord && !TERMINAL_RUN_STATUSES.has(runRecord.status)) {
|
|
140
|
-
return
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
const template = await planTemplateService.getTemplate(cycle.templateId)
|
|
145
|
-
if (!template) {
|
|
146
|
-
throw new Error(`Template not found for cycle: ${recordIdToString(cycle.templateId, TABLES.PLAN_TEMPLATE)}`)
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
const previousRunArtifacts = cycle.currentRunId
|
|
150
|
-
? await databaseService.findMany(
|
|
151
|
-
TABLES.PLAN_ARTIFACT,
|
|
152
|
-
{ runId: ensureRecordId(cycle.currentRunId, TABLES.PLAN_RUN) },
|
|
153
|
-
PlanArtifactSchema,
|
|
154
|
-
{ orderBy: 'createdAt', orderDir: 'ASC' },
|
|
155
|
-
)
|
|
156
|
-
: []
|
|
157
|
-
|
|
158
|
-
const draft = this.buildCarryForwardDraft({ template, previousRunArtifacts, policy: cycle.carryForwardPolicy })
|
|
159
|
-
|
|
160
|
-
const result = await planTemplateService.instantiate({
|
|
161
|
-
templateId: cycle.templateId,
|
|
162
|
-
organizationId: cycle.organizationId,
|
|
163
|
-
threadId: cycle.threadId,
|
|
164
|
-
leadAgentId: 'system',
|
|
165
|
-
overrides: draft,
|
|
166
|
-
})
|
|
167
|
-
|
|
168
|
-
const newRunId = result.plan?.runId
|
|
169
|
-
|
|
170
|
-
await databaseService.update(
|
|
171
|
-
TABLES.PLAN_CYCLE,
|
|
172
|
-
ensureRecordId(cycleId, TABLES.PLAN_CYCLE),
|
|
173
|
-
{
|
|
174
|
-
...(newRunId ? { currentRunId: ensureRecordId(newRunId, TABLES.PLAN_RUN) } : {}),
|
|
175
|
-
currentIteration: cycle.currentIteration + 1,
|
|
176
|
-
},
|
|
177
|
-
PlanCycleRecordSchema,
|
|
178
|
-
)
|
|
179
|
-
|
|
180
|
-
// TODO: re-enable when playbook records exist and IDs align
|
|
181
|
-
// The previous implementation passed a PLAN_TEMPLATE record ID as a PLAYBOOK ID,
|
|
182
|
-
// causing refineFromCycle to always fail silently.
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
buildCarryForwardDraft(params: {
|
|
186
|
-
template: PlanTemplateRecord
|
|
187
|
-
previousRunArtifacts: PlanArtifactRecord[]
|
|
188
|
-
policy: CarryForwardPolicy
|
|
189
|
-
}): PlanDraft {
|
|
190
|
-
const draft = { ...params.template.draft }
|
|
191
|
-
|
|
192
|
-
if (params.policy === 'none' || params.previousRunArtifacts.length === 0) {
|
|
193
|
-
return draft
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
let artifacts: PlanArtifactRecord[]
|
|
197
|
-
if (params.policy === 'incomplete-only') {
|
|
198
|
-
artifacts = params.previousRunArtifacts.filter((a) => a.kind === 'markdown' || a.kind === 'json')
|
|
199
|
-
} else {
|
|
200
|
-
// 'all-pending'
|
|
201
|
-
artifacts = [...params.previousRunArtifacts]
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
if (artifacts.length > 0) {
|
|
205
|
-
const carryContext = artifacts.map((a) => `[carry-forward] ${a.name}: ${a.pointer}`)
|
|
206
|
-
draft.objective = `${draft.objective}\n\nCarry-forward context:\n${carryContext.join('\n')}`
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
return draft
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
async cancelCycle(cycleId: RecordIdInput): Promise<void> {
|
|
213
|
-
const cycle = await this.getCycle(cycleId)
|
|
214
|
-
if (!cycle) {
|
|
215
|
-
throw new Error(`Cycle not found: ${recordIdToString(cycleId, TABLES.PLAN_CYCLE)}`)
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
if (cycle.scheduleId) {
|
|
219
|
-
await planSchedulerService.cancelSchedule(cycle.scheduleId)
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
await databaseService.update(
|
|
223
|
-
TABLES.PLAN_CYCLE,
|
|
224
|
-
ensureRecordId(cycleId, TABLES.PLAN_CYCLE),
|
|
225
|
-
{ status: 'cancelled' },
|
|
226
|
-
PlanCycleRecordSchema,
|
|
227
|
-
)
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
async pauseCycle(cycleId: RecordIdInput): Promise<void> {
|
|
231
|
-
const cycle = await this.getCycle(cycleId)
|
|
232
|
-
if (!cycle) {
|
|
233
|
-
throw new Error(`Cycle not found: ${recordIdToString(cycleId, TABLES.PLAN_CYCLE)}`)
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
if (cycle.scheduleId) {
|
|
237
|
-
await planSchedulerService.pauseSchedule(cycle.scheduleId)
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
await databaseService.update(
|
|
241
|
-
TABLES.PLAN_CYCLE,
|
|
242
|
-
ensureRecordId(cycleId, TABLES.PLAN_CYCLE),
|
|
243
|
-
{ status: 'paused' },
|
|
244
|
-
PlanCycleRecordSchema,
|
|
245
|
-
)
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
async resumeCycle(cycleId: RecordIdInput): Promise<void> {
|
|
249
|
-
const cycle = await this.getCycle(cycleId)
|
|
250
|
-
if (!cycle) {
|
|
251
|
-
throw new Error(`Cycle not found: ${recordIdToString(cycleId, TABLES.PLAN_CYCLE)}`)
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
if (cycle.scheduleId) {
|
|
255
|
-
await planSchedulerService.resumeSchedule(cycle.scheduleId)
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
await databaseService.update(
|
|
259
|
-
TABLES.PLAN_CYCLE,
|
|
260
|
-
ensureRecordId(cycleId, TABLES.PLAN_CYCLE),
|
|
261
|
-
{ status: 'active' },
|
|
262
|
-
PlanCycleRecordSchema,
|
|
263
|
-
)
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
async listCycles(threadId: RecordIdInput): Promise<PlanCycleRecord[]> {
|
|
267
|
-
return databaseService.findMany(
|
|
268
|
-
TABLES.PLAN_CYCLE,
|
|
269
|
-
{ threadId: ensureRecordId(threadId, TABLES.THREAD) },
|
|
270
|
-
PlanCycleRecordSchema,
|
|
271
|
-
{ orderBy: 'createdAt', orderDir: 'ASC' },
|
|
272
|
-
)
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
async getCycle(cycleId: RecordIdInput): Promise<PlanCycleRecord | null> {
|
|
276
|
-
return databaseService.findOne(
|
|
277
|
-
TABLES.PLAN_CYCLE,
|
|
278
|
-
{ id: ensureRecordId(cycleId, TABLES.PLAN_CYCLE) },
|
|
279
|
-
PlanCycleRecordSchema,
|
|
280
|
-
)
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
export const planCycleService = new PlanCycleService()
|