@lota-sdk/core 0.1.9 → 0.1.11
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/infrastructure/schema/00_workstream.surql +1 -0
- package/infrastructure/schema/02_execution_plan.surql +202 -52
- package/package.json +4 -2
- package/src/bifrost/bifrost.ts +94 -25
- package/src/config/model-constants.ts +8 -6
- package/src/db/memory-store.ts +3 -71
- package/src/db/service.ts +42 -2
- package/src/db/tables.ts +9 -2
- package/src/embeddings/provider.ts +92 -21
- package/src/index.ts +6 -0
- package/src/redis/stream-context.ts +44 -0
- package/src/runtime/approval-continuation.ts +59 -0
- package/src/runtime/chat-request-routing.ts +5 -1
- package/src/runtime/execution-plan.ts +21 -14
- package/src/runtime/turn-lifecycle.ts +12 -4
- package/src/services/document-chunk.service.ts +2 -2
- package/src/services/execution-plan.service.ts +579 -786
- package/src/services/learned-skill.service.ts +2 -2
- package/src/services/plan-approval.service.ts +83 -0
- package/src/services/plan-artifact.service.ts +45 -0
- package/src/services/plan-builder.service.ts +61 -0
- package/src/services/plan-checkpoint.service.ts +53 -0
- package/src/services/plan-compiler.service.ts +81 -0
- package/src/services/plan-executor.service.ts +1623 -0
- package/src/services/plan-run.service.ts +422 -0
- package/src/services/plan-validator.service.ts +760 -0
- package/src/services/workstream-turn-preparation.ts +57 -15
- package/src/services/workstream-turn.ts +12 -0
- package/src/services/workstream.service.ts +26 -0
- package/src/services/workstream.types.ts +1 -0
- package/src/system-agents/title-generator.agent.ts +2 -2
- package/src/tools/execution-plan.tool.ts +20 -46
- package/src/tools/log-hello-world.tool.ts +17 -0
- package/src/workers/skill-extraction.runner.ts +2 -2
|
@@ -7,9 +7,9 @@ import { serverLogger } from '../config/logger'
|
|
|
7
7
|
import { ensureRecordId } from '../db/record-id'
|
|
8
8
|
import { databaseService } from '../db/service'
|
|
9
9
|
import { TABLES } from '../db/tables'
|
|
10
|
-
import {
|
|
10
|
+
import { getDefaultEmbeddings } from '../embeddings/provider'
|
|
11
11
|
|
|
12
|
-
const embeddings =
|
|
12
|
+
const embeddings = getDefaultEmbeddings()
|
|
13
13
|
|
|
14
14
|
const PROMOTION_MIN_USES = 5
|
|
15
15
|
const PROMOTION_MIN_SUCCESS_RATE = 0.6
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { PlanApprovalSchema } from '@lota-sdk/shared/schemas/execution-plan'
|
|
2
|
+
import type { PlanApprovalRecord, PlanApprovalStatus } from '@lota-sdk/shared/schemas/execution-plan'
|
|
3
|
+
import { RecordId } from 'surrealdb'
|
|
4
|
+
|
|
5
|
+
import type { RecordIdInput } from '../db/record-id'
|
|
6
|
+
import { ensureRecordId } from '../db/record-id'
|
|
7
|
+
import { databaseService } from '../db/service'
|
|
8
|
+
import type { DatabaseTransaction } from '../db/service'
|
|
9
|
+
import { TABLES } from '../db/tables'
|
|
10
|
+
|
|
11
|
+
class PlanApprovalService {
|
|
12
|
+
async createPendingApproval(params: {
|
|
13
|
+
tx: DatabaseTransaction
|
|
14
|
+
runId: RecordIdInput
|
|
15
|
+
nodeRunId: RecordIdInput
|
|
16
|
+
nodeId: string
|
|
17
|
+
requestedBy: string
|
|
18
|
+
presented: Record<string, unknown>
|
|
19
|
+
}): Promise<PlanApprovalRecord> {
|
|
20
|
+
const approvalId = new RecordId(TABLES.PLAN_APPROVAL, Bun.randomUUIDv7())
|
|
21
|
+
const created = await params.tx
|
|
22
|
+
.create(approvalId)
|
|
23
|
+
.content({
|
|
24
|
+
runId: ensureRecordId(params.runId, TABLES.PLAN_RUN),
|
|
25
|
+
nodeRunId: ensureRecordId(params.nodeRunId, TABLES.PLAN_NODE_RUN),
|
|
26
|
+
nodeId: params.nodeId,
|
|
27
|
+
status: 'pending',
|
|
28
|
+
requestedBy: params.requestedBy,
|
|
29
|
+
presented: params.presented,
|
|
30
|
+
requiredEdits: [],
|
|
31
|
+
})
|
|
32
|
+
.output('after')
|
|
33
|
+
|
|
34
|
+
return PlanApprovalSchema.parse(created)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async getApprovalById(approvalId: RecordIdInput): Promise<PlanApprovalRecord | null> {
|
|
38
|
+
return await databaseService.findOne(
|
|
39
|
+
TABLES.PLAN_APPROVAL,
|
|
40
|
+
{ id: ensureRecordId(approvalId, TABLES.PLAN_APPROVAL) },
|
|
41
|
+
PlanApprovalSchema,
|
|
42
|
+
)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async getPendingApprovalForNodeRun(nodeRunId: RecordIdInput): Promise<PlanApprovalRecord | null> {
|
|
46
|
+
const approvals = await databaseService.findMany(
|
|
47
|
+
TABLES.PLAN_APPROVAL,
|
|
48
|
+
{ nodeRunId: ensureRecordId(nodeRunId, TABLES.PLAN_NODE_RUN), status: 'pending' },
|
|
49
|
+
PlanApprovalSchema,
|
|
50
|
+
{ orderBy: 'createdAt', orderDir: 'DESC', limit: 1 },
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
return approvals.at(0) ?? null
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async updateApprovalResponse(params: {
|
|
57
|
+
tx: DatabaseTransaction
|
|
58
|
+
approval: PlanApprovalRecord
|
|
59
|
+
status: PlanApprovalStatus
|
|
60
|
+
response: Record<string, unknown>
|
|
61
|
+
respondedBy: string
|
|
62
|
+
approvalMessageId?: string
|
|
63
|
+
comments?: string
|
|
64
|
+
requiredEdits?: string[]
|
|
65
|
+
}): Promise<PlanApprovalRecord> {
|
|
66
|
+
const updated = await params.tx
|
|
67
|
+
.update(ensureRecordId(params.approval.id, TABLES.PLAN_APPROVAL))
|
|
68
|
+
.merge({
|
|
69
|
+
status: params.status,
|
|
70
|
+
response: params.response,
|
|
71
|
+
respondedBy: params.respondedBy,
|
|
72
|
+
...(params.approvalMessageId ? { approvalMessageId: params.approvalMessageId } : {}),
|
|
73
|
+
...(params.comments ? { comments: params.comments } : {}),
|
|
74
|
+
...(params.requiredEdits ? { requiredEdits: params.requiredEdits } : {}),
|
|
75
|
+
respondedAt: new Date(),
|
|
76
|
+
})
|
|
77
|
+
.output('after')
|
|
78
|
+
|
|
79
|
+
return PlanApprovalSchema.parse(updated)
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export const planApprovalService = new PlanApprovalService()
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { PlanArtifactSchema } from '@lota-sdk/shared/schemas/execution-plan'
|
|
2
|
+
import type { PlanArtifactRecord } from '@lota-sdk/shared/schemas/execution-plan'
|
|
3
|
+
import type { PlanArtifactSubmission } from '@lota-sdk/shared/schemas/tools'
|
|
4
|
+
import { RecordId } from 'surrealdb'
|
|
5
|
+
|
|
6
|
+
import type { RecordIdInput } from '../db/record-id'
|
|
7
|
+
import { ensureRecordId } from '../db/record-id'
|
|
8
|
+
import type { DatabaseTransaction } from '../db/service'
|
|
9
|
+
import { TABLES } from '../db/tables'
|
|
10
|
+
|
|
11
|
+
class PlanArtifactService {
|
|
12
|
+
async persistArtifacts(params: {
|
|
13
|
+
tx: DatabaseTransaction
|
|
14
|
+
runId: RecordIdInput
|
|
15
|
+
attemptId: RecordIdInput
|
|
16
|
+
nodeId: string
|
|
17
|
+
artifacts: PlanArtifactSubmission[]
|
|
18
|
+
}): Promise<PlanArtifactRecord[]> {
|
|
19
|
+
const records: PlanArtifactRecord[] = []
|
|
20
|
+
|
|
21
|
+
for (const artifact of params.artifacts) {
|
|
22
|
+
const artifactId = new RecordId(TABLES.PLAN_ARTIFACT, Bun.randomUUIDv7())
|
|
23
|
+
const created = await params.tx
|
|
24
|
+
.create(artifactId)
|
|
25
|
+
.content({
|
|
26
|
+
runId: ensureRecordId(params.runId, TABLES.PLAN_RUN),
|
|
27
|
+
attemptId: ensureRecordId(params.attemptId, TABLES.PLAN_NODE_ATTEMPT),
|
|
28
|
+
nodeId: params.nodeId,
|
|
29
|
+
name: artifact.name,
|
|
30
|
+
kind: artifact.kind,
|
|
31
|
+
pointer: artifact.pointer,
|
|
32
|
+
...(artifact.schemaRef ? { schemaRef: artifact.schemaRef } : {}),
|
|
33
|
+
...(artifact.description ? { description: artifact.description } : {}),
|
|
34
|
+
...(artifact.payload ? { payload: artifact.payload } : {}),
|
|
35
|
+
})
|
|
36
|
+
.output('after')
|
|
37
|
+
|
|
38
|
+
records.push(PlanArtifactSchema.parse(created))
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return records
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export const planArtifactService = new PlanArtifactService()
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { PlanDraft } from '@lota-sdk/shared/schemas/execution-plan'
|
|
2
|
+
|
|
3
|
+
function buildImplicitLinearEdges(draft: PlanDraft) {
|
|
4
|
+
if (draft.edges.length > 0 || draft.nodes.length <= 1) {
|
|
5
|
+
return draft.edges
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
return draft.nodes
|
|
9
|
+
.slice(0, -1)
|
|
10
|
+
.map((node, index) => ({
|
|
11
|
+
id: `edge_${node.id}_to_${draft.nodes[index + 1]?.id ?? index + 1}`,
|
|
12
|
+
source: node.id,
|
|
13
|
+
target: draft.nodes[index + 1].id,
|
|
14
|
+
map: {},
|
|
15
|
+
}))
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
class PlanBuilderService {
|
|
19
|
+
roleAssignment(draft: PlanDraft): PlanDraft {
|
|
20
|
+
return draft
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
structureDesign(draft: PlanDraft): PlanDraft {
|
|
24
|
+
return {
|
|
25
|
+
...draft,
|
|
26
|
+
edges: buildImplicitLinearEdges(draft),
|
|
27
|
+
entryNodeIds: draft.entryNodeIds && draft.entryNodeIds.length > 0 ? draft.entryNodeIds : [draft.nodes[0].id],
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
semanticCompletion(draft: PlanDraft): PlanDraft {
|
|
32
|
+
return {
|
|
33
|
+
...draft,
|
|
34
|
+
nodes: draft.nodes.map((node) => ({
|
|
35
|
+
...node,
|
|
36
|
+
deliverables: [...node.deliverables],
|
|
37
|
+
successCriteria: [...node.successCriteria],
|
|
38
|
+
completionChecks: [...node.completionChecks],
|
|
39
|
+
failurePolicy: [...node.failurePolicy],
|
|
40
|
+
retryPolicy: { ...node.retryPolicy, retryOn: [...node.retryPolicy.retryOn] },
|
|
41
|
+
toolPolicy: { allow: [...node.toolPolicy.allow], deny: [...node.toolPolicy.deny] },
|
|
42
|
+
contextPolicy: {
|
|
43
|
+
retrievalScopes: [...node.contextPolicy.retrievalScopes],
|
|
44
|
+
attachmentPolicy: node.contextPolicy.attachmentPolicy,
|
|
45
|
+
webPolicy: node.contextPolicy.webPolicy,
|
|
46
|
+
},
|
|
47
|
+
})),
|
|
48
|
+
edges: draft.edges.map((edge) => ({ ...edge, map: { ...edge.map } })),
|
|
49
|
+
schemas: structuredClone(draft.schemas),
|
|
50
|
+
entryNodeIds: [...(draft.entryNodeIds ?? [])],
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
prepareDraft(draft: PlanDraft): PlanDraft {
|
|
55
|
+
const withRoles = this.roleAssignment(draft)
|
|
56
|
+
const withStructure = this.structureDesign(withRoles)
|
|
57
|
+
return this.semanticCompletion(withStructure)
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export const planBuilderService = new PlanBuilderService()
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { PlanCheckpointSchema } from '@lota-sdk/shared/schemas/execution-plan'
|
|
2
|
+
import type { PlanCheckpointRecord, PlanRunStatus } from '@lota-sdk/shared/schemas/execution-plan'
|
|
3
|
+
import { RecordId } from 'surrealdb'
|
|
4
|
+
|
|
5
|
+
import type { RecordIdInput } from '../db/record-id'
|
|
6
|
+
import { ensureRecordId } from '../db/record-id'
|
|
7
|
+
import { databaseService } from '../db/service'
|
|
8
|
+
import type { DatabaseTransaction } from '../db/service'
|
|
9
|
+
import { TABLES } from '../db/tables'
|
|
10
|
+
|
|
11
|
+
class PlanCheckpointService {
|
|
12
|
+
async createCheckpoint(params: {
|
|
13
|
+
tx: DatabaseTransaction
|
|
14
|
+
runId: RecordIdInput
|
|
15
|
+
sequence: number
|
|
16
|
+
runStatus: PlanRunStatus
|
|
17
|
+
readyNodeIds: string[]
|
|
18
|
+
activeNodeIds: string[]
|
|
19
|
+
artifactIds: RecordIdInput[]
|
|
20
|
+
lastCompletedNodeIds: string[]
|
|
21
|
+
snapshot: Record<string, unknown>
|
|
22
|
+
}): Promise<PlanCheckpointRecord> {
|
|
23
|
+
const checkpointId = new RecordId(TABLES.PLAN_CHECKPOINT, Bun.randomUUIDv7())
|
|
24
|
+
const created = await params.tx
|
|
25
|
+
.create(checkpointId)
|
|
26
|
+
.content({
|
|
27
|
+
runId: ensureRecordId(params.runId, TABLES.PLAN_RUN),
|
|
28
|
+
sequence: params.sequence,
|
|
29
|
+
runStatus: params.runStatus,
|
|
30
|
+
readyNodeIds: [...params.readyNodeIds],
|
|
31
|
+
activeNodeIds: [...params.activeNodeIds],
|
|
32
|
+
artifactIds: params.artifactIds.map((artifactId) => ensureRecordId(artifactId, TABLES.PLAN_ARTIFACT)),
|
|
33
|
+
lastCompletedNodeIds: [...params.lastCompletedNodeIds],
|
|
34
|
+
snapshot: params.snapshot,
|
|
35
|
+
})
|
|
36
|
+
.output('after')
|
|
37
|
+
|
|
38
|
+
return PlanCheckpointSchema.parse(created)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async loadLatestForRun(runId: RecordIdInput): Promise<PlanCheckpointRecord | null> {
|
|
42
|
+
const checkpoints = await databaseService.findMany(
|
|
43
|
+
TABLES.PLAN_CHECKPOINT,
|
|
44
|
+
{ runId: ensureRecordId(runId, TABLES.PLAN_RUN) },
|
|
45
|
+
PlanCheckpointSchema,
|
|
46
|
+
{ orderBy: 'sequence', orderDir: 'DESC', limit: 1 },
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
return checkpoints.at(0) ?? null
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export const planCheckpointService = new PlanCheckpointService()
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import type { PlanDraft, PlanNodeSpec, PlanNodeSpecRecord } from '@lota-sdk/shared/schemas/execution-plan'
|
|
2
|
+
|
|
3
|
+
import { planValidatorService } from './plan-validator.service'
|
|
4
|
+
|
|
5
|
+
export interface CompiledPlanNode {
|
|
6
|
+
node: PlanNodeSpec
|
|
7
|
+
position: number
|
|
8
|
+
upstreamNodeIds: string[]
|
|
9
|
+
downstreamNodeIds: string[]
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface CompiledPlanDraft {
|
|
13
|
+
draft: PlanDraft
|
|
14
|
+
nodes: CompiledPlanNode[]
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
class PlanCompilerService {
|
|
18
|
+
compile(draft: PlanDraft): CompiledPlanDraft {
|
|
19
|
+
const validation = planValidatorService.validateDraft(draft)
|
|
20
|
+
if (validation.blocking.length > 0) {
|
|
21
|
+
throw new Error(
|
|
22
|
+
`Plan draft failed validation: ${validation.blocking.map((issue) => `${issue.code}: ${issue.message}`).join(' | ')}`,
|
|
23
|
+
)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const upstreamByNodeId = new Map<string, string[]>()
|
|
27
|
+
const downstreamByNodeId = new Map<string, string[]>()
|
|
28
|
+
|
|
29
|
+
for (const node of draft.nodes) {
|
|
30
|
+
upstreamByNodeId.set(node.id, [])
|
|
31
|
+
downstreamByNodeId.set(node.id, [])
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
for (const edge of draft.edges) {
|
|
35
|
+
upstreamByNodeId.get(edge.target)?.push(edge.source)
|
|
36
|
+
downstreamByNodeId.get(edge.source)?.push(edge.target)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return {
|
|
40
|
+
draft,
|
|
41
|
+
nodes: draft.nodes.map((node, index) => ({
|
|
42
|
+
node,
|
|
43
|
+
position: index,
|
|
44
|
+
upstreamNodeIds: [...(upstreamByNodeId.get(node.id) ?? [])],
|
|
45
|
+
downstreamNodeIds: [...(downstreamByNodeId.get(node.id) ?? [])],
|
|
46
|
+
})),
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
toNodeSpecRecords(
|
|
51
|
+
compiled: CompiledPlanDraft,
|
|
52
|
+
): Array<Omit<PlanNodeSpecRecord, 'id' | 'planSpecId' | 'createdAt' | 'updatedAt'>> {
|
|
53
|
+
return compiled.nodes.map(({ node, position, upstreamNodeIds, downstreamNodeIds }) => ({
|
|
54
|
+
nodeId: node.id,
|
|
55
|
+
position,
|
|
56
|
+
type: node.type,
|
|
57
|
+
label: node.label,
|
|
58
|
+
owner: node.owner,
|
|
59
|
+
objective: node.objective,
|
|
60
|
+
instructions: node.instructions,
|
|
61
|
+
...(node.inputSchemaRef ? { inputSchemaRef: node.inputSchemaRef } : {}),
|
|
62
|
+
...(node.outputSchemaRef ? { outputSchemaRef: node.outputSchemaRef } : {}),
|
|
63
|
+
deliverables: [...node.deliverables],
|
|
64
|
+
successCriteria: [...node.successCriteria],
|
|
65
|
+
completionChecks: [...node.completionChecks],
|
|
66
|
+
retryPolicy: { ...node.retryPolicy, retryOn: [...node.retryPolicy.retryOn] },
|
|
67
|
+
failurePolicy: [...node.failurePolicy],
|
|
68
|
+
...(node.timeoutMs ? { timeoutMs: node.timeoutMs } : {}),
|
|
69
|
+
toolPolicy: { allow: [...node.toolPolicy.allow], deny: [...node.toolPolicy.deny] },
|
|
70
|
+
contextPolicy: {
|
|
71
|
+
retrievalScopes: [...node.contextPolicy.retrievalScopes],
|
|
72
|
+
attachmentPolicy: node.contextPolicy.attachmentPolicy,
|
|
73
|
+
webPolicy: node.contextPolicy.webPolicy,
|
|
74
|
+
},
|
|
75
|
+
upstreamNodeIds,
|
|
76
|
+
downstreamNodeIds,
|
|
77
|
+
}))
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export const planCompilerService = new PlanCompilerService()
|