@lota-sdk/core 0.1.8 → 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 +2 -1
- 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 +14 -6
- package/src/runtime/workstream-chat-helpers.ts +5 -5
- package/src/services/context-compaction.service.ts +6 -2
- 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 +70 -196
- package/src/services/workstream-turn.ts +12 -0
- package/src/services/workstream.service.ts +24 -182
- package/src/services/workstream.types.ts +2 -65
- 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
- package/src/services/workstream-change-tracker.service.ts +0 -313
- package/src/system-agents/workstream-tracker.agent.ts +0 -58
|
@@ -20,23 +20,12 @@ import {
|
|
|
20
20
|
parseMemoryBlock,
|
|
21
21
|
serializeMemoryBlock,
|
|
22
22
|
} from '../runtime/memory-block'
|
|
23
|
-
import { toOptionalTrimmedString } from '../runtime/workstream-chat-helpers'
|
|
24
|
-
import { WorkstreamStateSchema } from '../runtime/workstream-state'
|
|
25
|
-
import type { WorkstreamState } from '../runtime/workstream-state'
|
|
26
23
|
import { toIsoDateTimeString } from '../utils/date-time'
|
|
27
24
|
import { chatRunRegistry } from './chat-run-registry.service'
|
|
28
25
|
import { contextCompactionService } from './context-compaction.service'
|
|
29
26
|
import { workstreamMessageService } from './workstream-message.service'
|
|
30
27
|
import { WorkstreamSchema, WorkstreamStatusSchema } from './workstream.types'
|
|
31
|
-
import type {
|
|
32
|
-
NormalizedWorkstream,
|
|
33
|
-
PublicWorkstreamApprovalState,
|
|
34
|
-
PublicWorkstreamDetail,
|
|
35
|
-
PublicWorkstreamStateFocus,
|
|
36
|
-
PublicWorkstreamStatePayload,
|
|
37
|
-
PublicWorkstreamStateProgress,
|
|
38
|
-
WorkstreamRecord,
|
|
39
|
-
} from './workstream.types'
|
|
28
|
+
import type { NormalizedWorkstream, WorkstreamRecord } from './workstream.types'
|
|
40
29
|
|
|
41
30
|
// Uses SurrealQL directly to keep pagination/order logic close to queries.
|
|
42
31
|
|
|
@@ -146,155 +135,6 @@ function buildCoreWorkstreamId({
|
|
|
146
135
|
return new RecordId(TABLES.WORKSTREAM, `core_${typeValue}_user_${userValue}_organization_${orgValue}`)
|
|
147
136
|
}
|
|
148
137
|
|
|
149
|
-
function toOptionalIsoDateTimeString(value: number | null | undefined): string | null {
|
|
150
|
-
if (typeof value !== 'number' || !Number.isFinite(value) || value <= 0) return null
|
|
151
|
-
return toIsoDateTimeString(new Date(value))
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
function getCompactedSummaryFocus(chatSummary: string | null): string | null {
|
|
155
|
-
if (!chatSummary) return null
|
|
156
|
-
|
|
157
|
-
const lines = chatSummary
|
|
158
|
-
.split('\n')
|
|
159
|
-
.map((line) => line.trim())
|
|
160
|
-
.filter(Boolean)
|
|
161
|
-
|
|
162
|
-
for (const line of lines) {
|
|
163
|
-
if (line.endsWith(':')) continue
|
|
164
|
-
if (line.startsWith('- ')) {
|
|
165
|
-
return toOptionalTrimmedString(line.slice(2))
|
|
166
|
-
}
|
|
167
|
-
return toOptionalTrimmedString(line)
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
return null
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
function parsePersistedWorkstreamState(value: unknown): WorkstreamState | null {
|
|
174
|
-
const parsed = WorkstreamStateSchema.safeParse(value)
|
|
175
|
-
return parsed.success ? parsed.data : null
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
function buildEmptyWorkstreamStateProgress(): PublicWorkstreamStateProgress {
|
|
179
|
-
return {
|
|
180
|
-
hasState: false,
|
|
181
|
-
lastUpdated: null,
|
|
182
|
-
completionRatio: null,
|
|
183
|
-
tasks: { total: 0, open: 0, inProgress: 0, done: 0, blocked: 0 },
|
|
184
|
-
constraints: { total: 0, approved: 0, candidate: 0 },
|
|
185
|
-
keyDecisions: 0,
|
|
186
|
-
openQuestions: 0,
|
|
187
|
-
risks: 0,
|
|
188
|
-
artifacts: 0,
|
|
189
|
-
agentContributions: 0,
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
function buildWorkstreamStateProgress(state: WorkstreamState | null): PublicWorkstreamStateProgress {
|
|
194
|
-
if (!state) {
|
|
195
|
-
return buildEmptyWorkstreamStateProgress()
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
const tasks = {
|
|
199
|
-
total: state.tasks.length,
|
|
200
|
-
open: state.tasks.filter((task) => task.status === 'open').length,
|
|
201
|
-
inProgress: state.tasks.filter((task) => task.status === 'in-progress').length,
|
|
202
|
-
done: state.tasks.filter((task) => task.status === 'done').length,
|
|
203
|
-
blocked: state.tasks.filter((task) => task.status === 'blocked').length,
|
|
204
|
-
}
|
|
205
|
-
const constraintsApproved = state.activeConstraints.filter((constraint) => constraint.approved).length
|
|
206
|
-
|
|
207
|
-
return {
|
|
208
|
-
hasState:
|
|
209
|
-
state.currentPlan !== null ||
|
|
210
|
-
state.activeConstraints.length > 0 ||
|
|
211
|
-
state.keyDecisions.length > 0 ||
|
|
212
|
-
state.tasks.length > 0 ||
|
|
213
|
-
state.openQuestions.length > 0 ||
|
|
214
|
-
state.risks.length > 0 ||
|
|
215
|
-
state.artifacts.length > 0 ||
|
|
216
|
-
state.agentContributions.length > 0 ||
|
|
217
|
-
toOptionalTrimmedString(state.approvedBy) !== null ||
|
|
218
|
-
typeof state.approvedAt === 'number' ||
|
|
219
|
-
toOptionalTrimmedString(state.approvalMessageId) !== null ||
|
|
220
|
-
toOptionalTrimmedString(state.approvalNote) !== null,
|
|
221
|
-
lastUpdated: toOptionalIsoDateTimeString(state.lastUpdated),
|
|
222
|
-
completionRatio: tasks.total > 0 ? Number((tasks.done / tasks.total).toFixed(4)) : null,
|
|
223
|
-
tasks,
|
|
224
|
-
constraints: {
|
|
225
|
-
total: state.activeConstraints.length,
|
|
226
|
-
approved: constraintsApproved,
|
|
227
|
-
candidate: state.activeConstraints.length - constraintsApproved,
|
|
228
|
-
},
|
|
229
|
-
keyDecisions: state.keyDecisions.length,
|
|
230
|
-
openQuestions: state.openQuestions.length,
|
|
231
|
-
risks: state.risks.length,
|
|
232
|
-
artifacts: state.artifacts.length,
|
|
233
|
-
agentContributions: state.agentContributions.length,
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
function buildWorkstreamApprovalState(state: WorkstreamState | null): PublicWorkstreamApprovalState | null {
|
|
238
|
-
if (!state) return null
|
|
239
|
-
|
|
240
|
-
const approvedBy = toOptionalTrimmedString(state.approvedBy)
|
|
241
|
-
const approvedAt = toOptionalIsoDateTimeString(state.approvedAt)
|
|
242
|
-
const approvalMessageId = toOptionalTrimmedString(state.approvalMessageId)
|
|
243
|
-
const approvalNote = toOptionalTrimmedString(state.approvalNote)
|
|
244
|
-
|
|
245
|
-
if (!approvedBy && !approvedAt && !approvalMessageId && !approvalNote) {
|
|
246
|
-
return null
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
return { approvedBy, approvedAt, approvalMessageId, approvalNote }
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
function buildWorkstreamStateFocus(
|
|
253
|
-
state: WorkstreamState | null,
|
|
254
|
-
chatSummary: string | null,
|
|
255
|
-
): PublicWorkstreamStateFocus | null {
|
|
256
|
-
if (!state) {
|
|
257
|
-
const compactedSummaryFocus = getCompactedSummaryFocus(chatSummary)
|
|
258
|
-
return compactedSummaryFocus ? { kind: 'chat-summary', text: compactedSummaryFocus } : null
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
const currentPlan = toOptionalTrimmedString(state.currentPlan?.text)
|
|
262
|
-
if (currentPlan) {
|
|
263
|
-
return { kind: 'plan', text: currentPlan }
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
const latestTask =
|
|
267
|
-
[...state.tasks].reverse().find((task) => task.status === 'blocked') ??
|
|
268
|
-
[...state.tasks].reverse().find((task) => task.status === 'in-progress') ??
|
|
269
|
-
[...state.tasks].reverse().find((task) => task.status === 'open')
|
|
270
|
-
const taskTitle = toOptionalTrimmedString(latestTask?.title)
|
|
271
|
-
if (taskTitle) {
|
|
272
|
-
return { kind: 'task', text: taskTitle }
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
const latestQuestion = toOptionalTrimmedString(state.openQuestions.at(-1)?.text)
|
|
276
|
-
if (latestQuestion) {
|
|
277
|
-
return { kind: 'question', text: latestQuestion }
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
const latestDecision = toOptionalTrimmedString(state.keyDecisions.at(-1)?.decision)
|
|
281
|
-
if (latestDecision) {
|
|
282
|
-
return { kind: 'decision', text: latestDecision }
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
const latestAgentNote = toOptionalTrimmedString(state.agentContributions.at(-1)?.summary)
|
|
286
|
-
if (latestAgentNote) {
|
|
287
|
-
return { kind: 'agent-note', text: latestAgentNote }
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
const compactedSummaryFocus = getCompactedSummaryFocus(chatSummary)
|
|
291
|
-
if (compactedSummaryFocus) {
|
|
292
|
-
return { kind: 'chat-summary', text: compactedSummaryFocus }
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
return null
|
|
296
|
-
}
|
|
297
|
-
|
|
298
138
|
class WorkstreamService extends BaseService<typeof WorkstreamSchema> {
|
|
299
139
|
constructor() {
|
|
300
140
|
super(TABLES.WORKSTREAM, WorkstreamSchema)
|
|
@@ -664,12 +504,30 @@ class WorkstreamService extends BaseService<typeof WorkstreamSchema> {
|
|
|
664
504
|
await this.setActiveRunId(workstreamId, null)
|
|
665
505
|
}
|
|
666
506
|
|
|
667
|
-
async
|
|
668
|
-
workstreamId
|
|
669
|
-
|
|
670
|
-
|
|
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
|
|
671
526
|
const workstreamRef = ensureRecordId(workstreamId, TABLES.WORKSTREAM)
|
|
672
|
-
await
|
|
527
|
+
await databaseService.query<unknown>(surql`
|
|
528
|
+
UPDATE ONLY ${workstreamRef}
|
|
529
|
+
SET activeStreamId = NONE
|
|
530
|
+
`)
|
|
673
531
|
}
|
|
674
532
|
|
|
675
533
|
async stopActiveRun(workstreamId: RecordIdRef): Promise<boolean> {
|
|
@@ -868,22 +726,6 @@ class WorkstreamService extends BaseService<typeof WorkstreamSchema> {
|
|
|
868
726
|
}
|
|
869
727
|
}
|
|
870
728
|
|
|
871
|
-
toPublicWorkstreamDetail(workstream: WorkstreamRecord): PublicWorkstreamDetail {
|
|
872
|
-
const publicWorkstream = this.toPublicWorkstream(workstream)
|
|
873
|
-
const snapshot = parsePersistedWorkstreamState(workstream.state)
|
|
874
|
-
const chatSummary = toOptionalTrimmedString(workstream.chatSummary)
|
|
875
|
-
const progress = buildWorkstreamStateProgress(snapshot)
|
|
876
|
-
const workstreamState: PublicWorkstreamStatePayload = {
|
|
877
|
-
focus: buildWorkstreamStateFocus(snapshot, chatSummary),
|
|
878
|
-
chatSummary,
|
|
879
|
-
approval: buildWorkstreamApprovalState(snapshot),
|
|
880
|
-
progress,
|
|
881
|
-
snapshot,
|
|
882
|
-
}
|
|
883
|
-
|
|
884
|
-
return { ...publicWorkstream, workstreamState }
|
|
885
|
-
}
|
|
886
|
-
|
|
887
729
|
async incrementTurnCount(workstreamId: RecordIdRef): Promise<number> {
|
|
888
730
|
const workstreamRef = ensureRecordId(workstreamId, TABLES.WORKSTREAM)
|
|
889
731
|
const result = await databaseService.query<{ turnCount: number }>(surql`
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod'
|
|
2
2
|
|
|
3
|
-
import type { WorkstreamState } from '../runtime/workstream-state'
|
|
4
|
-
|
|
5
3
|
export interface Citation {
|
|
6
4
|
title?: string
|
|
7
5
|
url?: string
|
|
@@ -33,68 +31,6 @@ export interface NormalizedWorkstream {
|
|
|
33
31
|
organizationId: string
|
|
34
32
|
}
|
|
35
33
|
|
|
36
|
-
export interface PublicWorkstreamStateFocus {
|
|
37
|
-
kind: 'plan' | 'task' | 'question' | 'decision' | 'agent-note' | 'chat-summary'
|
|
38
|
-
text: string
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export interface PublicWorkstreamTaskProgress {
|
|
42
|
-
total: number
|
|
43
|
-
open: number
|
|
44
|
-
inProgress: number
|
|
45
|
-
done: number
|
|
46
|
-
blocked: number
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export interface PublicWorkstreamConstraintProgress {
|
|
50
|
-
total: number
|
|
51
|
-
approved: number
|
|
52
|
-
candidate: number
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
export interface PublicWorkstreamStateProgress {
|
|
56
|
-
hasState: boolean
|
|
57
|
-
lastUpdated: string | null
|
|
58
|
-
completionRatio: number | null
|
|
59
|
-
tasks: PublicWorkstreamTaskProgress
|
|
60
|
-
constraints: PublicWorkstreamConstraintProgress
|
|
61
|
-
keyDecisions: number
|
|
62
|
-
openQuestions: number
|
|
63
|
-
risks: number
|
|
64
|
-
artifacts: number
|
|
65
|
-
agentContributions: number
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
export interface PublicWorkstreamApprovalState {
|
|
69
|
-
approvedBy: string | null
|
|
70
|
-
approvedAt: string | null
|
|
71
|
-
approvalMessageId: string | null
|
|
72
|
-
approvalNote: string | null
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
export interface PublicWorkstreamStatePayload {
|
|
76
|
-
focus: PublicWorkstreamStateFocus | null
|
|
77
|
-
chatSummary: string | null
|
|
78
|
-
approval: PublicWorkstreamApprovalState | null
|
|
79
|
-
progress: PublicWorkstreamStateProgress
|
|
80
|
-
snapshot: WorkstreamState | null
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
export interface PublicWorkstreamDetail {
|
|
84
|
-
id: string
|
|
85
|
-
title: string
|
|
86
|
-
status: 'regular' | 'archived'
|
|
87
|
-
mode: 'direct' | 'group'
|
|
88
|
-
core: boolean
|
|
89
|
-
coreType?: string
|
|
90
|
-
isRunning: boolean
|
|
91
|
-
isCompacting: boolean
|
|
92
|
-
agentId?: string | null
|
|
93
|
-
createdAt: string
|
|
94
|
-
updatedAt: string
|
|
95
|
-
workstreamState: PublicWorkstreamStatePayload
|
|
96
|
-
}
|
|
97
|
-
|
|
98
34
|
export const WorkstreamSchema = z.object({
|
|
99
35
|
id: z.any(), // RecordId
|
|
100
36
|
mode: WorkstreamModeSchema.optional().default('group'),
|
|
@@ -106,7 +42,8 @@ export const WorkstreamSchema = z.object({
|
|
|
106
42
|
memoryBlock: z.string().nullish(),
|
|
107
43
|
memoryBlockSummary: z.string().nullish(),
|
|
108
44
|
activeRunId: z.string().nullish(),
|
|
109
|
-
|
|
45
|
+
activeStreamId: z.string().nullish(),
|
|
46
|
+
compactionSummary: z.string().nullish(),
|
|
110
47
|
lastCompactedMessageId: z.string().nullish(),
|
|
111
48
|
nameGenerated: z.boolean().optional().default(false),
|
|
112
49
|
isCompacting: z.boolean().optional(),
|
|
@@ -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
|
|