@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
|
@@ -140,6 +140,15 @@ export function parsePersistedWorkstreamState(value: unknown): WorkstreamState |
|
|
|
140
140
|
return parseWorkstreamState(value)
|
|
141
141
|
}
|
|
142
142
|
|
|
143
|
+
export function stripExecutionPlanFieldsFromWorkstreamState(
|
|
144
|
+
state: WorkstreamState | null | undefined,
|
|
145
|
+
hasExecutionPlan: boolean,
|
|
146
|
+
): WorkstreamState | null | undefined {
|
|
147
|
+
if (!state || !hasExecutionPlan) return state
|
|
148
|
+
|
|
149
|
+
return { ...state, currentPlan: null, tasks: [], artifacts: [] }
|
|
150
|
+
}
|
|
151
|
+
|
|
143
152
|
export async function waitForWorkstreamCompactionIfNeeded(workstreamId: RecordIdRef): Promise<WorkstreamRecord> {
|
|
144
153
|
return await waitForCompactionIfNeeded({
|
|
145
154
|
entityId: recordIdToString(workstreamId, TABLES.WORKSTREAM),
|
|
@@ -220,6 +229,7 @@ export interface WorkstreamTurnParams {
|
|
|
220
229
|
inputMessage: ChatMessage
|
|
221
230
|
persistInputMessage?: boolean
|
|
222
231
|
abortSignal?: AbortSignal
|
|
232
|
+
streamId?: string
|
|
223
233
|
}
|
|
224
234
|
|
|
225
235
|
export interface WorkstreamApprovalContinuationParams {
|
|
@@ -230,6 +240,7 @@ export interface WorkstreamApprovalContinuationParams {
|
|
|
230
240
|
userName?: string | null
|
|
231
241
|
approvalMessages: ChatMessage[]
|
|
232
242
|
abortSignal?: AbortSignal
|
|
243
|
+
streamId?: string
|
|
233
244
|
}
|
|
234
245
|
|
|
235
246
|
export type WorkstreamRunCoreParams = {
|
|
@@ -239,9 +250,11 @@ export type WorkstreamRunCoreParams = {
|
|
|
239
250
|
userRef: RecordIdRef
|
|
240
251
|
userName?: string | null
|
|
241
252
|
abortSignal?: AbortSignal
|
|
253
|
+
streamId?: string
|
|
242
254
|
} & (
|
|
243
255
|
| { kind: 'userTurn'; inputMessage: ChatMessage; persistInputMessage?: boolean }
|
|
244
256
|
| { kind: 'approvalContinuation'; approvalMessages: ChatMessage[] }
|
|
257
|
+
| { kind: 'nativeToolApprovalTurn'; approvalMessages: ChatMessage[] }
|
|
245
258
|
)
|
|
246
259
|
|
|
247
260
|
export interface PreparedWorkstreamTurn {
|
|
@@ -307,7 +320,8 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
307
320
|
|
|
308
321
|
let inputMessage: ChatMessage | undefined
|
|
309
322
|
const shouldPersistInputMessage = params.kind === 'userTurn' ? params.persistInputMessage !== false : false
|
|
310
|
-
const shouldProcessPostRunSideEffects =
|
|
323
|
+
const shouldProcessPostRunSideEffects =
|
|
324
|
+
params.kind === 'approvalContinuation' || params.kind === 'nativeToolApprovalTurn' || shouldPersistInputMessage
|
|
311
325
|
if (params.kind === 'userTurn') {
|
|
312
326
|
inputMessage = hydrateMessageFileUrls(withMessageCreatedAt(params.inputMessage))
|
|
313
327
|
if (inputMessage.role !== 'user') {
|
|
@@ -326,7 +340,7 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
326
340
|
throw new WorkstreamTurnError('A chat run is already active.', 409)
|
|
327
341
|
}
|
|
328
342
|
|
|
329
|
-
if (params.kind === 'approvalContinuation') {
|
|
343
|
+
if (params.kind === 'approvalContinuation' || params.kind === 'nativeToolApprovalTurn') {
|
|
330
344
|
const approvedAssistantMessage = [...params.approvalMessages]
|
|
331
345
|
.reverse()
|
|
332
346
|
.find((m) => m.role === 'assistant' && hasApprovalRespondedParts(m))
|
|
@@ -539,11 +553,38 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
539
553
|
disabled: onboardingActive,
|
|
540
554
|
loadPlan: async () => await executionPlanService.getActivePlanForWorkstream(workstreamRef),
|
|
541
555
|
})
|
|
556
|
+
const getExecutionPlan = async () => await executionPlanInstructionSectionCache.getPlan()
|
|
542
557
|
const getExecutionPlanInstructionSections = async (): Promise<string[] | undefined> =>
|
|
543
558
|
await executionPlanInstructionSectionCache.getSections()
|
|
544
559
|
const invalidateExecutionPlanInstructionSections = () => {
|
|
545
560
|
executionPlanInstructionSectionCache.invalidate()
|
|
546
561
|
}
|
|
562
|
+
const getWorkstreamStateSection = async (): Promise<string | undefined> => {
|
|
563
|
+
const executionPlan = await getExecutionPlan()
|
|
564
|
+
return contextCompactionRuntime.formatWorkstreamStateForPrompt(
|
|
565
|
+
stripExecutionPlanFieldsFromWorkstreamState(workstreamState, Boolean(executionPlan)),
|
|
566
|
+
)
|
|
567
|
+
}
|
|
568
|
+
const respondedBy = recordIdToString(userRef, TABLES.USER)
|
|
569
|
+
if (params.kind === 'approvalContinuation') {
|
|
570
|
+
const appliedApproval = await executionPlanService.applyApprovalResponseFromMessages({
|
|
571
|
+
workstreamId: workstreamRef,
|
|
572
|
+
approvalMessages: params.approvalMessages,
|
|
573
|
+
respondedBy,
|
|
574
|
+
})
|
|
575
|
+
if (appliedApproval) {
|
|
576
|
+
invalidateExecutionPlanInstructionSections()
|
|
577
|
+
}
|
|
578
|
+
} else if (userMessage) {
|
|
579
|
+
const appliedHumanInput = await executionPlanService.applyHumanInputFromUserMessage({
|
|
580
|
+
workstreamId: workstreamRef,
|
|
581
|
+
message: userMessage,
|
|
582
|
+
respondedBy,
|
|
583
|
+
})
|
|
584
|
+
if (appliedHumanInput) {
|
|
585
|
+
invalidateExecutionPlanInstructionSections()
|
|
586
|
+
}
|
|
587
|
+
}
|
|
547
588
|
const preSeededMemoriesByAgent = new Map<string, string | undefined>()
|
|
548
589
|
const getPreSeededMemoriesSection = async (agentId: string): Promise<string | undefined> => {
|
|
549
590
|
if (preSeededMemoriesByAgent.has(agentId)) {
|
|
@@ -601,6 +642,9 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
601
642
|
const serverRunId = Bun.randomUUIDv7()
|
|
602
643
|
const runAbort = createServerRunAbortController(params.abortSignal)
|
|
603
644
|
await workstreamService.setActiveRunId(workstreamRef, serverRunId)
|
|
645
|
+
if (params.streamId) {
|
|
646
|
+
await workstreamService.setActiveStreamId(workstreamRef, params.streamId)
|
|
647
|
+
}
|
|
604
648
|
chatRunRegistry.register(serverRunId, runAbort.controller)
|
|
605
649
|
|
|
606
650
|
try {
|
|
@@ -692,7 +736,7 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
692
736
|
preSeededMemoriesSection: await getPreSeededMemoriesSection(resolvedAgentId),
|
|
693
737
|
retrievedKnowledgeSection,
|
|
694
738
|
workstreamMemoryBlock: memoryBlock,
|
|
695
|
-
workstreamStateSection:
|
|
739
|
+
workstreamStateSection: await getWorkstreamStateSection(),
|
|
696
740
|
learnedSkillsSection: await getLearnedSkillsSection(resolvedAgentId),
|
|
697
741
|
additionalInstructionSections: mergeInstructionSections(
|
|
698
742
|
executionPlanInstructionSections,
|
|
@@ -839,6 +883,12 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
839
883
|
)
|
|
840
884
|
}
|
|
841
885
|
|
|
886
|
+
// Execution-plan approval continuations mutate plan state and persist the approval message,
|
|
887
|
+
// but they do not begin a new visible agent turn.
|
|
888
|
+
if (params.kind === 'approvalContinuation') {
|
|
889
|
+
return
|
|
890
|
+
}
|
|
891
|
+
|
|
842
892
|
const consultSpecialistTool = createTool({
|
|
843
893
|
description: 'Consult one specialist teammate for domain-specific guidance before replying to the user.',
|
|
844
894
|
inputSchema: ConsultSpecialistArgsSchema,
|
|
@@ -883,7 +933,7 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
883
933
|
preSeededMemoriesSection: await getPreSeededMemoriesSection(agentId),
|
|
884
934
|
retrievedKnowledgeSection,
|
|
885
935
|
workstreamMemoryBlock: specialistMemoryBlock,
|
|
886
|
-
workstreamStateSection:
|
|
936
|
+
workstreamStateSection: await getWorkstreamStateSection(),
|
|
887
937
|
learnedSkillsSection: await getLearnedSkillsSection(agentId),
|
|
888
938
|
additionalInstructionSections: mergeInstructionSections(
|
|
889
939
|
specialistExecutionPlanInstructionSections,
|
|
@@ -995,17 +1045,6 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
995
1045
|
throw new WorkstreamTurnError('Direct workstreams require an assigned agent.', 400)
|
|
996
1046
|
}
|
|
997
1047
|
await runVisibleAgent({ agentId: workstream.agentId, mode: 'direct' })
|
|
998
|
-
} else if (params.kind === 'userTurn') {
|
|
999
|
-
await runVisibleAgent({
|
|
1000
|
-
agentId: visibleWorkstreamAgentId ?? 'chief',
|
|
1001
|
-
mode: 'workstreamMode',
|
|
1002
|
-
skills: coreWorkstreamProfile?.skills ? [...coreWorkstreamProfile.skills] : undefined,
|
|
1003
|
-
additionalInstructionSections: mergeInstructionSections(coreInstructionSections, hookInstructionSections),
|
|
1004
|
-
extraTools: {
|
|
1005
|
-
[CONSULT_SPECIALIST_TOOL_NAME]: consultSpecialistTool,
|
|
1006
|
-
...(teamThinkTool ? { [CONSULT_TEAM_TOOL_NAME]: teamThinkTool } : {}),
|
|
1007
|
-
},
|
|
1008
|
-
})
|
|
1009
1048
|
} else {
|
|
1010
1049
|
await runVisibleAgent({
|
|
1011
1050
|
agentId: visibleWorkstreamAgentId ?? 'chief',
|
|
@@ -1043,6 +1082,9 @@ export async function prepareWorkstreamRunCore(params: WorkstreamRunCoreParams):
|
|
|
1043
1082
|
unregisterRun: (runId) => chatRunRegistry.unregister(runId),
|
|
1044
1083
|
clearActiveRunId: (runId) => workstreamService.clearActiveRunIdIfMatches(workstreamRef, runId),
|
|
1045
1084
|
disposeAbort: () => runAbort.dispose(),
|
|
1085
|
+
activeStreamId: params.streamId,
|
|
1086
|
+
clearActiveStreamId: (streamId) =>
|
|
1087
|
+
workstreamService.clearActiveStreamIdIfMatches(workstreamRef, streamId),
|
|
1046
1088
|
})
|
|
1047
1089
|
|
|
1048
1090
|
if (allAssistantMessages.length > 0 && shouldProcessPostRunSideEffects) {
|
|
@@ -21,6 +21,18 @@ export async function createWorkstreamApprovalContinuationStream(params: Workstr
|
|
|
21
21
|
})
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
+
export async function createWorkstreamNativeToolApprovalStream(params: WorkstreamApprovalContinuationParams) {
|
|
25
|
+
const prepared = await prepareWorkstreamRunCore({ ...params, kind: 'nativeToolApprovalTurn' })
|
|
26
|
+
|
|
27
|
+
return createUIMessageStream<ChatMessage>({
|
|
28
|
+
originalMessages: prepared.originalMessages,
|
|
29
|
+
onError: (error) => (error instanceof Error ? error.message : 'Native tool approval stream failed.'),
|
|
30
|
+
execute: async ({ writer }) => {
|
|
31
|
+
await prepared.run(writer)
|
|
32
|
+
},
|
|
33
|
+
})
|
|
34
|
+
}
|
|
35
|
+
|
|
24
36
|
export async function createWorkstreamTurnStream(params: WorkstreamTurnParams) {
|
|
25
37
|
const prepared = await prepareWorkstreamRunCore({ ...params, kind: 'userTurn' })
|
|
26
38
|
|
|
@@ -504,6 +504,32 @@ class WorkstreamService extends BaseService<typeof WorkstreamSchema> {
|
|
|
504
504
|
await this.setActiveRunId(workstreamId, null)
|
|
505
505
|
}
|
|
506
506
|
|
|
507
|
+
async setActiveStreamId(workstreamId: RecordIdRef, streamId: string): Promise<void> {
|
|
508
|
+
const workstreamRef = ensureRecordId(workstreamId, TABLES.WORKSTREAM)
|
|
509
|
+
await databaseService.query<unknown>(surql`
|
|
510
|
+
UPDATE ONLY ${workstreamRef}
|
|
511
|
+
SET activeStreamId = ${streamId}
|
|
512
|
+
`)
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
async getActiveStreamId(workstreamId: RecordIdRef): Promise<string | null> {
|
|
516
|
+
const workstream = await this.getById(workstreamId)
|
|
517
|
+
const activeStreamId = workstream.activeStreamId
|
|
518
|
+
if (typeof activeStreamId !== 'string') return null
|
|
519
|
+
const normalized = activeStreamId.trim()
|
|
520
|
+
return normalized.length > 0 ? normalized : null
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
async clearActiveStreamIdIfMatches(workstreamId: RecordIdRef, streamId: string): Promise<void> {
|
|
524
|
+
const activeStreamId = await this.getActiveStreamId(workstreamId)
|
|
525
|
+
if (activeStreamId !== streamId) return
|
|
526
|
+
const workstreamRef = ensureRecordId(workstreamId, TABLES.WORKSTREAM)
|
|
527
|
+
await databaseService.query<unknown>(surql`
|
|
528
|
+
UPDATE ONLY ${workstreamRef}
|
|
529
|
+
SET activeStreamId = NONE
|
|
530
|
+
`)
|
|
531
|
+
}
|
|
532
|
+
|
|
507
533
|
async stopActiveRun(workstreamId: RecordIdRef): Promise<boolean> {
|
|
508
534
|
const activeRunId = await this.getActiveRunId(workstreamId)
|
|
509
535
|
if (!activeRunId) return false
|
|
@@ -42,6 +42,7 @@ export const WorkstreamSchema = z.object({
|
|
|
42
42
|
memoryBlock: z.string().nullish(),
|
|
43
43
|
memoryBlockSummary: z.string().nullish(),
|
|
44
44
|
activeRunId: z.string().nullish(),
|
|
45
|
+
activeStreamId: z.string().nullish(),
|
|
45
46
|
compactionSummary: z.string().nullish(),
|
|
46
47
|
lastCompactedMessageId: z.string().nullish(),
|
|
47
48
|
nameGenerated: z.boolean().optional().default(false),
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
import type { CreateHelperToolLoopAgentOptions } from '../runtime/agent-types'
|
|
9
9
|
import { resolveHelperAgentOptions } from './helper-agent-options'
|
|
10
10
|
|
|
11
|
-
const WORKSTREAM_TITLE_MAX_TOKENS =
|
|
11
|
+
const WORKSTREAM_TITLE_MAX_TOKENS = 512
|
|
12
12
|
|
|
13
13
|
export const WORKSTREAM_TITLE_GENERATOR_PROMPT = `<agent-instructions>
|
|
14
14
|
You are a **Title Generator** that creates concise chat titles.
|
|
@@ -18,7 +18,7 @@ Generate a chat title based only on the user's message.
|
|
|
18
18
|
</task>
|
|
19
19
|
|
|
20
20
|
<constraints>
|
|
21
|
-
- Maximum 4
|
|
21
|
+
- Maximum 3-4 words
|
|
22
22
|
- Capture the core workstream or intent
|
|
23
23
|
- Use natural, readable language
|
|
24
24
|
- No punctuation at the end
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
CreateExecutionPlanArgsSchema,
|
|
3
3
|
GetActiveExecutionPlanArgsSchema,
|
|
4
|
-
|
|
4
|
+
ResumeExecutionPlanRunArgsSchema,
|
|
5
5
|
ReplaceExecutionPlanArgsSchema,
|
|
6
|
-
|
|
6
|
+
SubmitExecutionNodeResultArgsSchema,
|
|
7
7
|
} from '@lota-sdk/shared/schemas/tools'
|
|
8
8
|
import type { ExecutionPlanToolResultData } from '@lota-sdk/shared/schemas/tools'
|
|
9
9
|
import { tool } from 'ai'
|
|
@@ -34,7 +34,7 @@ export function createCreateExecutionPlanTool(params: {
|
|
|
34
34
|
}) {
|
|
35
35
|
return tool({
|
|
36
36
|
description:
|
|
37
|
-
'Create a
|
|
37
|
+
'Create a contract-driven execution plan for this workstream. Nodes must define deliverables, success criteria, completion checks, and executor policy.',
|
|
38
38
|
inputSchema: CreateExecutionPlanArgsSchema,
|
|
39
39
|
execute: async (input) => {
|
|
40
40
|
const result = await executionPlanService.createPlan({
|
|
@@ -56,7 +56,8 @@ export function createReplaceExecutionPlanTool(params: {
|
|
|
56
56
|
onPlanChanged?: () => void
|
|
57
57
|
}) {
|
|
58
58
|
return tool({
|
|
59
|
-
description:
|
|
59
|
+
description:
|
|
60
|
+
'Replace the active execution run with a new contract-driven plan when the graph, contracts, or approach materially change.',
|
|
60
61
|
inputSchema: ReplaceExecutionPlanArgsSchema,
|
|
61
62
|
execute: async (input) => {
|
|
62
63
|
const result = await executionPlanService.replacePlan({
|
|
@@ -71,48 +72,17 @@ export function createReplaceExecutionPlanTool(params: {
|
|
|
71
72
|
})
|
|
72
73
|
}
|
|
73
74
|
|
|
74
|
-
export function
|
|
75
|
+
export function createSubmitExecutionNodeResultTool(params: {
|
|
75
76
|
workstreamId: RecordIdRef
|
|
76
77
|
agentId: string
|
|
77
78
|
onPlanChanged?: () => void
|
|
78
79
|
}) {
|
|
79
80
|
return tool({
|
|
80
81
|
description:
|
|
81
|
-
'
|
|
82
|
-
inputSchema:
|
|
83
|
-
execute: async
|
|
84
|
-
const
|
|
85
|
-
workstreamId: params.workstreamId,
|
|
86
|
-
includeEvents: true,
|
|
87
|
-
})
|
|
88
|
-
|
|
89
|
-
if (activePlan.plan && activePlan.plan.planId === input.planId) {
|
|
90
|
-
const optimisticPlan = structuredClone(activePlan.plan)
|
|
91
|
-
const targetTask = optimisticPlan.tasks.find((task) => task.id === input.taskId)
|
|
92
|
-
|
|
93
|
-
if (targetTask) {
|
|
94
|
-
targetTask.status = input.status
|
|
95
|
-
if (input.resultStatus !== undefined) targetTask.resultStatus = input.resultStatus
|
|
96
|
-
if (input.outputSummary !== undefined) targetTask.outputSummary = input.outputSummary || undefined
|
|
97
|
-
if (input.blockedReason !== undefined) targetTask.blockedReason = input.blockedReason || undefined
|
|
98
|
-
if (input.externalRef !== undefined) targetTask.externalRef = input.externalRef || undefined
|
|
99
|
-
if (input.status === 'in-progress') {
|
|
100
|
-
optimisticPlan.status = 'executing'
|
|
101
|
-
optimisticPlan.currentTaskId = targetTask.id
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
yield {
|
|
105
|
-
action: 'task-status-updated',
|
|
106
|
-
message: `Updating task "${targetTask.title}".`,
|
|
107
|
-
changedTaskId: targetTask.id,
|
|
108
|
-
plan: optimisticPlan,
|
|
109
|
-
hasPlan: true,
|
|
110
|
-
status: optimisticPlan.status,
|
|
111
|
-
} satisfies ExecutionPlanToolResultData
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
const result = await executionPlanService.setTaskStatus({
|
|
82
|
+
'Submit the result for the currently running execution node. The executor validates outputs, artifacts, and completion checks before advancing the run.',
|
|
83
|
+
inputSchema: SubmitExecutionNodeResultArgsSchema,
|
|
84
|
+
execute: async (input) => {
|
|
85
|
+
const result = await executionPlanService.submitNodeResult({
|
|
116
86
|
workstreamId: params.workstreamId,
|
|
117
87
|
emittedBy: params.agentId,
|
|
118
88
|
input,
|
|
@@ -123,7 +93,7 @@ export function createSetExecutionTaskStatusTool(params: {
|
|
|
123
93
|
toModelOutput: ({ output }) => {
|
|
124
94
|
const result = getLatestExecutionPlanToolResult(output)
|
|
125
95
|
const summary = result?.message?.trim()
|
|
126
|
-
return { type: 'text', value: summary && summary.length > 0 ? summary : 'Execution
|
|
96
|
+
return { type: 'text', value: summary && summary.length > 0 ? summary : 'Execution node result submitted.' }
|
|
127
97
|
},
|
|
128
98
|
})
|
|
129
99
|
}
|
|
@@ -131,27 +101,31 @@ export function createSetExecutionTaskStatusTool(params: {
|
|
|
131
101
|
export function createGetActiveExecutionPlanTool(params: { workstreamId: RecordIdRef }) {
|
|
132
102
|
return tool({
|
|
133
103
|
description:
|
|
134
|
-
'Load the active execution
|
|
104
|
+
'Load the active execution run for this workstream, including graph state, node contracts, recent events, approvals, artifacts, and checkpoints when requested.',
|
|
135
105
|
inputSchema: GetActiveExecutionPlanArgsSchema,
|
|
136
106
|
execute: async (input) =>
|
|
137
107
|
await executionPlanService.getActivePlanToolResult({
|
|
138
108
|
workstreamId: params.workstreamId,
|
|
139
109
|
includeEvents: input.includeEvents,
|
|
110
|
+
includeArtifacts: input.includeArtifacts,
|
|
111
|
+
includeApprovals: input.includeApprovals,
|
|
112
|
+
includeCheckpoints: input.includeCheckpoints,
|
|
113
|
+
includeValidationIssues: input.includeValidationIssues,
|
|
140
114
|
}),
|
|
141
115
|
})
|
|
142
116
|
}
|
|
143
117
|
|
|
144
|
-
export function
|
|
118
|
+
export function createResumeExecutionPlanRunTool(params: {
|
|
145
119
|
workstreamId: RecordIdRef
|
|
146
120
|
agentId: string
|
|
147
121
|
onPlanChanged?: () => void
|
|
148
122
|
}) {
|
|
149
123
|
return tool({
|
|
150
124
|
description:
|
|
151
|
-
'
|
|
152
|
-
inputSchema:
|
|
125
|
+
'Resume the active execution run from its latest checkpoint after interruption. The executor reconciles state before re-activating nodes.',
|
|
126
|
+
inputSchema: ResumeExecutionPlanRunArgsSchema,
|
|
153
127
|
execute: async (input) => {
|
|
154
|
-
const result = await executionPlanService.
|
|
128
|
+
const result = await executionPlanService.resumeRun({
|
|
155
129
|
workstreamId: params.workstreamId,
|
|
156
130
|
emittedBy: params.agentId,
|
|
157
131
|
input,
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { tool } from 'ai'
|
|
2
|
+
import { z } from 'zod'
|
|
3
|
+
|
|
4
|
+
export function createLogHelloWorldTool() {
|
|
5
|
+
return tool({
|
|
6
|
+
description: 'Logs "Hello World" to the server console. Requires user approval before running.',
|
|
7
|
+
inputSchema: z.object({
|
|
8
|
+
message: z.string().optional().describe('Optional custom message to log alongside Hello World'),
|
|
9
|
+
}),
|
|
10
|
+
needsApproval: true,
|
|
11
|
+
execute: async ({ message }: { message?: string }) => {
|
|
12
|
+
const output = message ? `Hello World: ${message}` : 'Hello World'
|
|
13
|
+
console.log('[logHelloWorld]', output)
|
|
14
|
+
return { logged: output, timestamp: new Date().toISOString() }
|
|
15
|
+
},
|
|
16
|
+
})
|
|
17
|
+
}
|
|
@@ -7,7 +7,7 @@ import { ensureRecordId, recordIdToString } from '../db/record-id'
|
|
|
7
7
|
import type { RecordIdRef } 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
|
import type { SkillExtractionJob } from '../queues/skill-extraction.queue'
|
|
12
12
|
import { createHelperModelRuntime } from '../runtime/helper-model'
|
|
13
13
|
import { getRuntimeAdapters, withConfiguredWorkspaceMemoryLock } from '../runtime/runtime-extensions'
|
|
@@ -54,7 +54,7 @@ interface SkillExtractionRunResult {
|
|
|
54
54
|
extractedSkills: number
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
-
const embeddings =
|
|
57
|
+
const embeddings = getDefaultEmbeddings()
|
|
58
58
|
|
|
59
59
|
const helperModelRuntime = createHelperModelRuntime()
|
|
60
60
|
|