@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.
Files changed (38) hide show
  1. package/infrastructure/schema/00_workstream.surql +2 -1
  2. package/infrastructure/schema/02_execution_plan.surql +202 -52
  3. package/package.json +4 -2
  4. package/src/bifrost/bifrost.ts +94 -25
  5. package/src/config/model-constants.ts +8 -6
  6. package/src/db/memory-store.ts +3 -71
  7. package/src/db/service.ts +42 -2
  8. package/src/db/tables.ts +9 -2
  9. package/src/embeddings/provider.ts +92 -21
  10. package/src/index.ts +6 -0
  11. package/src/redis/stream-context.ts +44 -0
  12. package/src/runtime/approval-continuation.ts +59 -0
  13. package/src/runtime/chat-request-routing.ts +5 -1
  14. package/src/runtime/execution-plan.ts +21 -14
  15. package/src/runtime/turn-lifecycle.ts +14 -6
  16. package/src/runtime/workstream-chat-helpers.ts +5 -5
  17. package/src/services/context-compaction.service.ts +6 -2
  18. package/src/services/document-chunk.service.ts +2 -2
  19. package/src/services/execution-plan.service.ts +579 -786
  20. package/src/services/learned-skill.service.ts +2 -2
  21. package/src/services/plan-approval.service.ts +83 -0
  22. package/src/services/plan-artifact.service.ts +45 -0
  23. package/src/services/plan-builder.service.ts +61 -0
  24. package/src/services/plan-checkpoint.service.ts +53 -0
  25. package/src/services/plan-compiler.service.ts +81 -0
  26. package/src/services/plan-executor.service.ts +1623 -0
  27. package/src/services/plan-run.service.ts +422 -0
  28. package/src/services/plan-validator.service.ts +760 -0
  29. package/src/services/workstream-turn-preparation.ts +70 -196
  30. package/src/services/workstream-turn.ts +12 -0
  31. package/src/services/workstream.service.ts +24 -182
  32. package/src/services/workstream.types.ts +2 -65
  33. package/src/system-agents/title-generator.agent.ts +2 -2
  34. package/src/tools/execution-plan.tool.ts +20 -46
  35. package/src/tools/log-hello-world.tool.ts +17 -0
  36. package/src/workers/skill-extraction.runner.ts +2 -2
  37. package/src/services/workstream-change-tracker.service.ts +0 -313
  38. package/src/system-agents/workstream-tracker.agent.ts +0 -58
@@ -1,313 +0,0 @@
1
- import { z } from 'zod'
2
-
3
- import { aiLogger } from '../config/logger'
4
- import type { RecordIdRef } from '../db/record-id'
5
- import { mergeStateDelta } from '../runtime/context-compaction'
6
- import { createHelperModelRuntime, extractJsonObjectCandidates } from '../runtime/helper-model'
7
- import { toOptionalTrimmedString } from '../runtime/workstream-chat-helpers'
8
- import {
9
- StructuredWorkstreamStateDeltaSchema,
10
- createEmptyStructuredWorkstreamStateDelta,
11
- createEmptyWorkstreamState,
12
- parseStructuredWorkstreamStateDelta,
13
- } from '../runtime/workstream-state'
14
- import type { WorkstreamState, WorkstreamStateDelta } from '../runtime/workstream-state'
15
- import { createWorkstreamTrackerAgent } from '../system-agents/workstream-tracker.agent'
16
- import { compactWhitespace, isRecord, truncateText } from '../utils/string'
17
- import { workstreamService } from './workstream.service'
18
-
19
- const helperModelRuntime = createHelperModelRuntime()
20
-
21
- const TrackerOutputSchema = z.object({
22
- summary: z.string().trim().min(1).max(1_200),
23
- stateDelta: StructuredWorkstreamStateDeltaSchema,
24
- })
25
-
26
- type TrackerMessage = { label: string; text: string }
27
-
28
- const TRACKER_JSON_WRAPPER_KEYS = ['output', 'result', 'data'] as const
29
- const TRACKER_SUMMARY_KEYS = ['summary', 'chatSummary', 'summaryText', 'sidebarSummary', 'message'] as const
30
- const TRACKER_STATE_DELTA_KEYS = ['stateDelta', 'delta', 'workstreamStateDelta', 'trackerStateDelta', 'state'] as const
31
- const TRACKER_FALLBACK_SECTION_PREFIX =
32
- /^(?:#{1,6}\s*)?(?:\*\*)?(?:state delta|current plan|new decisions|resolved questions|new questions|new constraints|new risks|task updates|artifacts|agent note|conflicts|approved by|approved at|approval message id|approval note)(?:\*\*)?\s*:?\s*/i
33
-
34
- function renderMessages(messages: TrackerMessage[]): string {
35
- if (messages.length === 0) return '- None'
36
- return messages
37
- .map((message, index) => `### Message ${index + 1}\n- Actor: ${message.label}\n- Content: ${message.text}`)
38
- .join('\n\n')
39
- }
40
-
41
- function getRecordStringField(record: Record<string, unknown>, keys: readonly string[]): string | null {
42
- for (const key of keys) {
43
- const field = toOptionalTrimmedString(typeof record[key] === 'string' ? record[key] : null)
44
- if (field) return field
45
- }
46
-
47
- return null
48
- }
49
-
50
- function getTrackerJsonObjects(value: unknown): Record<string, unknown>[] {
51
- if (!isRecord(value)) return []
52
-
53
- const candidates: Record<string, unknown>[] = [value]
54
- for (const key of TRACKER_JSON_WRAPPER_KEYS) {
55
- const nested = value[key]
56
- if (isRecord(nested)) {
57
- candidates.push(nested)
58
- }
59
- }
60
-
61
- return candidates
62
- }
63
-
64
- function parseTrackerJsonFallback(text: string): z.infer<typeof TrackerOutputSchema> | null {
65
- for (const candidateText of extractJsonObjectCandidates(text)) {
66
- let parsedJson: unknown
67
-
68
- try {
69
- parsedJson = JSON.parse(candidateText) as unknown
70
- } catch {
71
- continue
72
- }
73
-
74
- for (const candidate of getTrackerJsonObjects(parsedJson)) {
75
- const summary = getRecordStringField(candidate, TRACKER_SUMMARY_KEYS)
76
- if (!summary) continue
77
-
78
- const stateDeltaValue = TRACKER_STATE_DELTA_KEYS.map((key) => candidate[key]).find((value) => value !== undefined)
79
- const parsedStateDelta = StructuredWorkstreamStateDeltaSchema.safeParse(stateDeltaValue)
80
-
81
- return {
82
- summary: truncateText(summary, 1_200),
83
- stateDelta: parsedStateDelta.success ? parsedStateDelta.data : createEmptyStructuredWorkstreamStateDelta(),
84
- }
85
- }
86
- }
87
-
88
- return null
89
- }
90
-
91
- function extractTrackerFallbackSummary(text: string): string | null {
92
- const normalized = text.replace(/\r/g, '').trim()
93
- if (!normalized) return null
94
-
95
- const summaryLineMatch = normalized.match(/(?:^|\n)(?:#{1,6}\s*)?(?:\*\*)?summary(?:\*\*)?\s*:?\s*(.+?)(?=\n|$)/i)
96
- const summaryLine = summaryLineMatch?.[1]?.trim()
97
- if (summaryLine && !summaryLine.startsWith('{') && !summaryLine.startsWith('```')) {
98
- return truncateText(summaryLine, 1_200)
99
- }
100
-
101
- const summarySectionMatch = normalized.match(
102
- /(?:^|\n)(?:#{1,6}\s*)?(?:\*\*)?summary(?:\*\*)?\s*:?\s*\n([\s\S]*?)(?=\n(?:#{1,6}\s*)?(?:\*\*)?(?:state delta|summary)(?:\*\*)?\s*:?\s*\n|$)/i,
103
- )
104
- const summarySource = summarySectionMatch?.[1]?.trim() || normalized
105
- const paragraphs = summarySource
106
- .split(/\n\s*\n/)
107
- .map((paragraph) => paragraph.trim())
108
- .filter((paragraph) => paragraph.length > 0)
109
-
110
- for (const paragraph of paragraphs) {
111
- const candidate = paragraph.replace(/^(?:#{1,6}\s*)?(?:\*\*)?summary(?:\*\*)?\s*:?\s*/i, '').trim()
112
- if (
113
- !candidate ||
114
- candidate.startsWith('{') ||
115
- candidate.startsWith('```') ||
116
- TRACKER_FALLBACK_SECTION_PREFIX.test(candidate)
117
- ) {
118
- continue
119
- }
120
-
121
- return truncateText(candidate, 1_200)
122
- }
123
-
124
- return null
125
- }
126
-
127
- export function parseTrackerTextFallback(text: string): z.infer<typeof TrackerOutputSchema> | null {
128
- const jsonFallback = parseTrackerJsonFallback(text)
129
- if (jsonFallback) {
130
- return jsonFallback
131
- }
132
-
133
- const summary = extractTrackerFallbackSummary(text)
134
- if (!summary) return null
135
-
136
- return { summary, stateDelta: createEmptyStructuredWorkstreamStateDelta() }
137
- }
138
-
139
- function extractHeuristicSummaryCandidate(text: string | null | undefined): string | null {
140
- const normalized = compactWhitespace(toOptionalTrimmedString(text) ?? '')
141
- if (!normalized) return null
142
-
143
- const match = /^(.+?[.!?])(?:\s|$)/.exec(normalized)
144
- const candidate = match ? String(match[1]) : normalized
145
- return truncateText(candidate, 1_200)
146
- }
147
-
148
- export function buildHeuristicTrackerSummary(params: {
149
- previousSummary: string | null
150
- userMessageText: string | null
151
- assistantMessages: TrackerMessage[]
152
- }): string | null {
153
- for (const message of params.assistantMessages) {
154
- const summary = extractHeuristicSummaryCandidate(message.text)
155
- if (summary) {
156
- return summary
157
- }
158
- }
159
-
160
- const userSummary = extractHeuristicSummaryCandidate(params.userMessageText)
161
- if (userSummary) {
162
- return truncateText(`User request: ${userSummary}`, 1_200)
163
- }
164
-
165
- return extractHeuristicSummaryCandidate(params.previousSummary)
166
- }
167
-
168
- function formatTrackerPrompt(params: {
169
- title: string
170
- mode: 'direct' | 'group'
171
- coreType?: string
172
- visibleAgentId?: string
173
- hasActiveExecutionPlan: boolean
174
- previousSummary: string | null
175
- existingState: WorkstreamState
176
- userMessageText: string | null
177
- assistantMessages: TrackerMessage[]
178
- }): string {
179
- return [
180
- '# Workstream Turn',
181
- '',
182
- `- Title: ${params.title}`,
183
- `- Mode: ${params.mode}`,
184
- `- Visible agent: ${params.visibleAgentId ?? 'none'}`,
185
- `- Active execution plan: ${params.hasActiveExecutionPlan ? 'yes' : 'no'}`,
186
- ...(params.coreType ? [`- Core type: ${params.coreType}`] : []),
187
- '',
188
- '## Previous Summary',
189
- params.previousSummary ?? 'None',
190
- '',
191
- '## Existing State',
192
- JSON.stringify(params.existingState),
193
- '',
194
- '## User Message',
195
- params.userMessageText ?? 'None',
196
- '',
197
- '## Assistant Messages',
198
- renderMessages(params.assistantMessages),
199
- ...(params.hasActiveExecutionPlan
200
- ? [
201
- '',
202
- '## Tracker Constraint',
203
- 'An active execution plan exists. Do not update currentPlan or taskUpdates. Track only decisions, questions, risks, artifacts, approvals, and agent notes.',
204
- ]
205
- : []),
206
- ].join('\n')
207
- }
208
-
209
- export function applyTrackedStateDelta(params: {
210
- existingState: WorkstreamState
211
- delta: WorkstreamStateDelta
212
- hasActiveExecutionPlan: boolean
213
- now: () => number
214
- }): WorkstreamState {
215
- const mergedState = mergeStateDelta(params.existingState, params.delta, params.now)
216
- if (!params.hasActiveExecutionPlan) {
217
- return mergedState
218
- }
219
-
220
- return { ...mergedState, currentPlan: null, tasks: [] }
221
- }
222
-
223
- export async function updateWorkstreamChangeTracker(params: {
224
- workstreamId: RecordIdRef
225
- title: string
226
- mode: 'direct' | 'group'
227
- coreType?: string
228
- visibleAgentId?: string
229
- hasActiveExecutionPlan: boolean
230
- previousSummary: string | null
231
- existingState: WorkstreamState | null
232
- userMessageText: string | null
233
- assistantMessages: TrackerMessage[]
234
- }): Promise<boolean> {
235
- const assistantMessages = params.assistantMessages
236
- .map((message) => ({
237
- label: toOptionalTrimmedString(message.label) ?? 'Assistant',
238
- text: truncateText(message.text.trim(), 2_400),
239
- }))
240
- .filter((message) => message.text.length > 0)
241
- .slice(0, 6)
242
-
243
- if (!toOptionalTrimmedString(params.userMessageText) && assistantMessages.length === 0) {
244
- return false
245
- }
246
-
247
- const existingState = params.existingState ?? createEmptyWorkstreamState()
248
-
249
- try {
250
- const output = await helperModelRuntime.generateHelperStructured({
251
- tag: 'workstream-change-tracker',
252
- createAgent: createWorkstreamTrackerAgent,
253
- schema: TrackerOutputSchema,
254
- maxOutputTokens: 1_400,
255
- textFallbackParser: parseTrackerTextFallback,
256
- messages: [
257
- {
258
- role: 'user',
259
- content: formatTrackerPrompt({
260
- title: params.title,
261
- mode: params.mode,
262
- ...(params.coreType ? { coreType: params.coreType } : {}),
263
- ...(params.visibleAgentId ? { visibleAgentId: params.visibleAgentId } : {}),
264
- hasActiveExecutionPlan: params.hasActiveExecutionPlan,
265
- previousSummary: params.previousSummary,
266
- existingState,
267
- userMessageText: toOptionalTrimmedString(params.userMessageText),
268
- assistantMessages,
269
- }),
270
- },
271
- ],
272
- })
273
-
274
- const sparseStateDelta = parseStructuredWorkstreamStateDelta(output.stateDelta)
275
- const nextState = applyTrackedStateDelta({
276
- existingState,
277
- delta: sparseStateDelta,
278
- hasActiveExecutionPlan: params.hasActiveExecutionPlan,
279
- now: () => Date.now(),
280
- })
281
- await workstreamService.persistChangeTracker(params.workstreamId, { chatSummary: output.summary, state: nextState })
282
- return true
283
- } catch (error) {
284
- const fallbackSummary = buildHeuristicTrackerSummary({
285
- previousSummary: params.previousSummary,
286
- userMessageText: toOptionalTrimmedString(params.userMessageText),
287
- assistantMessages,
288
- })
289
-
290
- if (!fallbackSummary) {
291
- aiLogger.warn`Workstream change tracker update failed: ${error}`
292
- return false
293
- }
294
-
295
- try {
296
- const nextState = applyTrackedStateDelta({
297
- existingState,
298
- delta: {},
299
- hasActiveExecutionPlan: params.hasActiveExecutionPlan,
300
- now: () => Date.now(),
301
- })
302
- await workstreamService.persistChangeTracker(params.workstreamId, {
303
- chatSummary: fallbackSummary,
304
- state: nextState,
305
- })
306
- aiLogger.info`Workstream change tracker used heuristic fallback after helper failure`
307
- return true
308
- } catch (persistError) {
309
- aiLogger.warn`Workstream change tracker update failed: ${error}; fallback_persist=${persistError}`
310
- return false
311
- }
312
- }
313
- }
@@ -1,58 +0,0 @@
1
- import { ToolLoopAgent } from 'ai'
2
-
3
- import { bifrostOpenRouterResponseHealingModel } from '../bifrost/bifrost'
4
- import {
5
- OPENROUTER_LOW_REASONING_PROVIDER_OPTIONS,
6
- OPENROUTER_STRUCTURED_REASONING_MODEL_ID,
7
- } from '../config/model-constants'
8
- import type { CreateHelperToolLoopAgentOptions } from '../runtime/agent-types'
9
- import { resolveHelperAgentOptions } from './helper-agent-options'
10
-
11
- const WORKSTREAM_TRACKER_PROMPT = `<agent-instructions>
12
- You are the **Workstream Tracker**.
13
-
14
- <task>
15
- Convert one completed workstream turn into:
16
- - a concise change-tracker summary for the right sidebar
17
- - a structured workstream state delta containing only the updates implied by this turn
18
- </task>
19
-
20
- <rules>
21
- - Use only the provided turn evidence and prior state.
22
- - Keep the summary short, concrete, and focused on what changed or what is blocked now.
23
- - Prefer durable tracker items over prose: tasks, decisions, questions, risks, artifacts, and agent notes.
24
- - Do not duplicate existing state unless this turn meaningfully changed it.
25
- - Never invent analytics, repository facts, customer signals, or business decisions that were not stated.
26
- </rules>
27
-
28
- <state-guidance>
29
- - Use taskUpdates for concrete next steps, ongoing work, completed work, or blocked work.
30
- - When the turn includes concrete tool work or operational execution, capture that work as taskUpdates even if it was
31
- completed in the same turn.
32
- - For website inspection or plugin refresh flows, prefer task titles that describe the executed step clearly, such as
33
- refreshing website intelligence, overwriting artifacts, or reviewing evidence gaps.
34
- - If an owner is not explicit, default the task owner to the visible lead agent for the workstream.
35
- - Use newDecisions only when a clear decision or tradeoff was made.
36
- - Use newQuestions only for unresolved questions that matter for progress.
37
- - Use newRisks only for concrete execution or business risks surfaced in this turn.
38
- - Use agentNote for the main contribution from the lead agent when it is worth retaining.
39
- - Keep the delta compact. A noisy tracker is worse than a sparse tracker.
40
- </state-guidance>
41
-
42
- <output>
43
- The caller enforces a structured schema.
44
- - Return every stateDelta field.
45
- - Use empty arrays for unchanged list fields.
46
- - Use null for unchanged nullable fields.
47
- - For currentPlan, use \`{"action":"unchanged","text":null}\` when nothing changed, \`{"action":"clear","text":null}\` to clear it, and \`{"action":"set","text":"..."}\` to replace it.
48
- </output>
49
- </agent-instructions>`
50
-
51
- export function createWorkstreamTrackerAgent(options: CreateHelperToolLoopAgentOptions) {
52
- return new ToolLoopAgent({
53
- id: 'workstream-tracker',
54
- model: bifrostOpenRouterResponseHealingModel(OPENROUTER_STRUCTURED_REASONING_MODEL_ID),
55
- providerOptions: OPENROUTER_LOW_REASONING_PROVIDER_OPTIONS,
56
- ...resolveHelperAgentOptions(options, { instructions: WORKSTREAM_TRACKER_PROMPT }),
57
- })
58
- }