@sylphx/flow 1.7.0 → 1.8.1

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 (131) hide show
  1. package/CHANGELOG.md +78 -0
  2. package/assets/agents/coder.md +72 -119
  3. package/assets/agents/orchestrator.md +26 -90
  4. package/assets/agents/reviewer.md +76 -47
  5. package/assets/agents/writer.md +82 -63
  6. package/assets/output-styles/silent.md +141 -8
  7. package/assets/rules/code-standards.md +9 -33
  8. package/assets/rules/core.md +67 -59
  9. package/package.json +2 -12
  10. package/src/commands/flow/execute.ts +470 -0
  11. package/src/commands/flow/index.ts +11 -0
  12. package/src/commands/flow/prompt.ts +35 -0
  13. package/src/commands/flow/setup.ts +312 -0
  14. package/src/commands/flow/targets.ts +18 -0
  15. package/src/commands/flow/types.ts +47 -0
  16. package/src/commands/flow-command.ts +18 -967
  17. package/src/commands/flow-orchestrator.ts +14 -5
  18. package/src/commands/hook-command.ts +1 -1
  19. package/src/commands/init-core.ts +12 -3
  20. package/src/commands/run-command.ts +1 -1
  21. package/src/config/rules.ts +1 -1
  22. package/src/core/error-handling.ts +1 -1
  23. package/src/core/loop-controller.ts +1 -1
  24. package/src/core/state-detector.ts +1 -1
  25. package/src/core/target-manager.ts +1 -1
  26. package/src/index.ts +1 -1
  27. package/src/shared/files/index.ts +1 -1
  28. package/src/shared/processing/index.ts +1 -1
  29. package/src/targets/claude-code.ts +3 -3
  30. package/src/targets/opencode.ts +3 -3
  31. package/src/utils/agent-enhancer.ts +2 -2
  32. package/src/utils/{mcp-config.ts → config/mcp-config.ts} +4 -4
  33. package/src/utils/{paths.ts → config/paths.ts} +1 -1
  34. package/src/utils/{settings.ts → config/settings.ts} +1 -1
  35. package/src/utils/{target-config.ts → config/target-config.ts} +5 -5
  36. package/src/utils/{target-utils.ts → config/target-utils.ts} +3 -3
  37. package/src/utils/display/banner.ts +25 -0
  38. package/src/utils/display/status.ts +55 -0
  39. package/src/utils/{file-operations.ts → files/file-operations.ts} +2 -2
  40. package/src/utils/files/jsonc.ts +36 -0
  41. package/src/utils/{sync-utils.ts → files/sync-utils.ts} +3 -3
  42. package/src/utils/index.ts +42 -61
  43. package/src/utils/version.ts +47 -0
  44. package/src/components/benchmark-monitor.tsx +0 -331
  45. package/src/components/reindex-progress.tsx +0 -261
  46. package/src/composables/functional/index.ts +0 -14
  47. package/src/composables/functional/useEnvironment.ts +0 -171
  48. package/src/composables/functional/useFileSystem.ts +0 -139
  49. package/src/composables/index.ts +0 -4
  50. package/src/composables/useEnv.ts +0 -13
  51. package/src/composables/useRuntimeConfig.ts +0 -27
  52. package/src/core/ai-sdk.ts +0 -603
  53. package/src/core/app-factory.ts +0 -381
  54. package/src/core/builtin-agents.ts +0 -9
  55. package/src/core/command-system.ts +0 -550
  56. package/src/core/config-system.ts +0 -550
  57. package/src/core/connection-pool.ts +0 -390
  58. package/src/core/di-container.ts +0 -155
  59. package/src/core/headless-display.ts +0 -96
  60. package/src/core/interfaces/index.ts +0 -22
  61. package/src/core/interfaces/repository.interface.ts +0 -91
  62. package/src/core/interfaces/service.interface.ts +0 -133
  63. package/src/core/interfaces.ts +0 -96
  64. package/src/core/result.ts +0 -351
  65. package/src/core/service-config.ts +0 -252
  66. package/src/core/session-service.ts +0 -121
  67. package/src/core/storage-factory.ts +0 -115
  68. package/src/core/stream-handler.ts +0 -288
  69. package/src/core/type-utils.ts +0 -427
  70. package/src/core/unified-storage.ts +0 -456
  71. package/src/core/validation/limit.ts +0 -46
  72. package/src/core/validation/query.ts +0 -20
  73. package/src/db/auto-migrate.ts +0 -322
  74. package/src/db/base-database-client.ts +0 -144
  75. package/src/db/cache-db.ts +0 -218
  76. package/src/db/cache-schema.ts +0 -75
  77. package/src/db/database.ts +0 -70
  78. package/src/db/index.ts +0 -252
  79. package/src/db/memory-db.ts +0 -153
  80. package/src/db/memory-schema.ts +0 -29
  81. package/src/db/schema.ts +0 -289
  82. package/src/db/session-repository.ts +0 -733
  83. package/src/domains/index.ts +0 -6
  84. package/src/domains/utilities/index.ts +0 -6
  85. package/src/domains/utilities/time/index.ts +0 -5
  86. package/src/domains/utilities/time/tools.ts +0 -291
  87. package/src/services/agent-service.ts +0 -273
  88. package/src/services/evaluation-service.ts +0 -271
  89. package/src/services/functional/evaluation-logic.ts +0 -296
  90. package/src/services/functional/file-processor.ts +0 -273
  91. package/src/services/functional/index.ts +0 -12
  92. package/src/services/memory.service.ts +0 -476
  93. package/src/types/api/batch.ts +0 -108
  94. package/src/types/api/errors.ts +0 -118
  95. package/src/types/api/index.ts +0 -55
  96. package/src/types/api/requests.ts +0 -76
  97. package/src/types/api/responses.ts +0 -180
  98. package/src/types/api/websockets.ts +0 -85
  99. package/src/types/benchmark.ts +0 -49
  100. package/src/types/database.types.ts +0 -510
  101. package/src/types/memory-types.ts +0 -63
  102. package/src/utils/advanced-tokenizer.ts +0 -191
  103. package/src/utils/ai-model-fetcher.ts +0 -19
  104. package/src/utils/async-file-operations.ts +0 -516
  105. package/src/utils/audio-player.ts +0 -345
  106. package/src/utils/codebase-helpers.ts +0 -211
  107. package/src/utils/console-ui.ts +0 -79
  108. package/src/utils/database-errors.ts +0 -140
  109. package/src/utils/debug-logger.ts +0 -49
  110. package/src/utils/file-scanner.ts +0 -259
  111. package/src/utils/help.ts +0 -20
  112. package/src/utils/immutable-cache.ts +0 -106
  113. package/src/utils/jsonc.ts +0 -158
  114. package/src/utils/memory-tui.ts +0 -414
  115. package/src/utils/models-dev.ts +0 -91
  116. package/src/utils/parallel-operations.ts +0 -487
  117. package/src/utils/process-manager.ts +0 -155
  118. package/src/utils/prompts.ts +0 -120
  119. package/src/utils/search-tool-builder.ts +0 -214
  120. package/src/utils/session-manager.ts +0 -168
  121. package/src/utils/session-title.ts +0 -87
  122. package/src/utils/simplified-errors.ts +0 -410
  123. package/src/utils/template-engine.ts +0 -94
  124. package/src/utils/test-audio.ts +0 -71
  125. package/src/utils/todo-context.ts +0 -46
  126. package/src/utils/token-counter.ts +0 -288
  127. /package/src/utils/{cli-output.ts → display/cli-output.ts} +0 -0
  128. /package/src/utils/{logger.ts → display/logger.ts} +0 -0
  129. /package/src/utils/{notifications.ts → display/notifications.ts} +0 -0
  130. /package/src/utils/{secret-utils.ts → security/secret-utils.ts} +0 -0
  131. /package/src/utils/{security.ts → security/security.ts} +0 -0
@@ -1,603 +0,0 @@
1
- /**
2
- * Sylphx Flow AI SDK
3
- * Unified AI streaming interface with tool support
4
- * Content parts based design - own type system with proper conversion
5
- */
6
-
7
- import { streamText, type AssistantContent, type ModelMessage } from 'ai';
8
- import type { LanguageModelV2, LanguageModelV2ToolResultOutput } from '@ai-sdk/provider';
9
- import * as os from 'node:os';
10
- import { getAISDKTools } from '../tools/index.js';
11
- import { hasUserInputHandler } from '../tools/interaction.js';
12
- import { getCurrentSystemPrompt } from './agent-manager.js';
13
- import { getEnabledRulesContent } from './rule-manager.js';
14
- import { buildTodoContext } from '../utils/todo-context.js';
15
-
16
- // Legacy system prompt - kept for backwards compatibility and fallback
17
- const LEGACY_SYSTEM_PROMPT = `You are a helpful coding assistant.
18
-
19
- You help users with:
20
- - Programming tasks and code review
21
- - Debugging and troubleshooting
22
- - File operations and system tasks
23
- - Software development best practices
24
-
25
- Guidelines:
26
- - Write clean, functional, well-documented code
27
- - Use tools proactively when needed to complete tasks
28
- - Explain complex concepts clearly
29
- - Follow language-specific best practices
30
- - Test and verify your work when possible`;
31
-
32
- /**
33
- * Base system prompt - introduces Sylphx
34
- */
35
- const BASE_SYSTEM_PROMPT = `You are Sylphx, an AI development assistant.`;
36
-
37
- /**
38
- * Get the system prompt to use (combines base + rules + agent)
39
- */
40
- export function getSystemPrompt(): string {
41
- const parts: string[] = [];
42
-
43
- // 1. Base prompt (introduces Sylphx)
44
- parts.push(BASE_SYSTEM_PROMPT);
45
-
46
- // 2. Enabled rules (shared across all agents)
47
- try {
48
- const rulesContent = getEnabledRulesContent();
49
- if (rulesContent) {
50
- parts.push(rulesContent);
51
- }
52
- } catch {
53
- // Rule manager not initialized or no rules enabled
54
- }
55
-
56
- // 3. Agent-specific prompt
57
- try {
58
- const agentPrompt = getCurrentSystemPrompt();
59
- parts.push(agentPrompt);
60
- } catch {
61
- // Fallback to legacy if agent manager not initialized
62
- parts.push(LEGACY_SYSTEM_PROMPT);
63
- }
64
-
65
- return parts.join('\n\n');
66
- }
67
-
68
- // Export for backwards compatibility
69
- export const SYSTEM_PROMPT = LEGACY_SYSTEM_PROMPT;
70
-
71
- /**
72
- * Stream chunk types (our own)
73
- */
74
- export type TextStartChunk = {
75
- type: 'text-start';
76
- };
77
-
78
- export type TextDeltaChunk = {
79
- type: 'text-delta';
80
- textDelta: string;
81
- };
82
-
83
- export type TextEndChunk = {
84
- type: 'text-end';
85
- };
86
-
87
- export type ReasoningStartChunk = {
88
- type: 'reasoning-start';
89
- };
90
-
91
- export type ReasoningDeltaChunk = {
92
- type: 'reasoning-delta';
93
- textDelta: string;
94
- };
95
-
96
- export type ReasoningEndChunk = {
97
- type: 'reasoning-end';
98
- };
99
-
100
- export type ToolCallChunk = {
101
- type: 'tool-call';
102
- toolCallId: string;
103
- toolName: string;
104
- args: unknown;
105
- };
106
-
107
- export type ToolInputStartChunk = {
108
- type: 'tool-input-start';
109
- toolCallId: string;
110
- toolName: string;
111
- };
112
-
113
- export type ToolInputDeltaChunk = {
114
- type: 'tool-input-delta';
115
- toolCallId: string;
116
- argsTextDelta: string;
117
- };
118
-
119
- export type ToolInputEndChunk = {
120
- type: 'tool-input-end';
121
- toolCallId: string;
122
- };
123
-
124
- export type ToolResultChunk = {
125
- type: 'tool-result';
126
- toolCallId: string;
127
- toolName: string;
128
- result: unknown;
129
- };
130
-
131
- export type ToolErrorChunk = {
132
- type: 'tool-error';
133
- toolCallId: string;
134
- toolName: string;
135
- error: string;
136
- };
137
-
138
- export type StreamErrorChunk = {
139
- type: 'error';
140
- error: string;
141
- };
142
-
143
- export type AbortChunk = {
144
- type: 'abort';
145
- };
146
-
147
- export type FinishChunk = {
148
- type: 'finish';
149
- finishReason: string;
150
- usage: {
151
- promptTokens: number;
152
- completionTokens: number;
153
- totalTokens: number;
154
- };
155
- };
156
-
157
- export type StreamChunk =
158
- | TextStartChunk
159
- | TextDeltaChunk
160
- | TextEndChunk
161
- | ReasoningStartChunk
162
- | ReasoningDeltaChunk
163
- | ReasoningEndChunk
164
- | ToolCallChunk
165
- | ToolInputStartChunk
166
- | ToolInputDeltaChunk
167
- | ToolInputEndChunk
168
- | ToolResultChunk
169
- | ToolErrorChunk
170
- | StreamErrorChunk
171
- | AbortChunk
172
- | FinishChunk;
173
-
174
- /**
175
- * Step info (our own)
176
- */
177
- export interface StepInfo {
178
- finishReason: string;
179
- usage: {
180
- promptTokens: number;
181
- completionTokens: number;
182
- totalTokens: number;
183
- };
184
- content: AssistantContent[];
185
- }
186
-
187
- /**
188
- * Create AI stream options
189
- */
190
- export interface CreateAIStreamOptions {
191
- model: LanguageModelV2;
192
- messages: ModelMessage[];
193
- systemPrompt?: string;
194
- /**
195
- * Optional abort signal to cancel the stream
196
- */
197
- abortSignal?: AbortSignal;
198
- onStepFinish?: (step: StepInfo) => void;
199
- /**
200
- * Called before each step to prepare messages
201
- * Can be used to inject context (e.g., todo list, system status)
202
- * @param messages - Current message history
203
- * @param stepNumber - Current step number
204
- * @returns Modified messages array
205
- */
206
- onPrepareMessages?: (messages: ModelMessage[], stepNumber: number) => ModelMessage[];
207
- /**
208
- * Called to transform tool result output before saving to history
209
- * Can be used to inject metadata (e.g., system status, timestamp)
210
- * @param output - Tool result output
211
- * @param toolName - Name of the tool
212
- * @returns Modified output
213
- */
214
- onTransformToolResult?: (
215
- output: LanguageModelV2ToolResultOutput,
216
- toolName: string
217
- ) => LanguageModelV2ToolResultOutput;
218
- }
219
-
220
- /**
221
- * System status object (captured at message creation time)
222
- *
223
- * Design: Separation of capture vs construction
224
- * ==============================================
225
- *
226
- * Why we have TWO functions (getSystemStatus + buildSystemStatusFromMetadata):
227
- *
228
- * 1. getSystemStatus() - Captures CURRENT system state
229
- * - Called when creating a NEW message
230
- * - Returns object { timestamp, cpu, memory }
231
- * - Stored in SessionMessage.metadata
232
- * - NEVER called again for historical messages
233
- *
234
- * 2. buildSystemStatusFromMetadata() - Constructs string from STORED values
235
- * - Called when building ModelMessage from SessionMessage
236
- * - Uses HISTORICAL values from metadata (never current values)
237
- * - Ensures prompt cache works (historical messages never change)
238
- *
239
- * ⚠️ CRITICAL for prompt cache:
240
- * - Historical messages must be IMMUTABLE
241
- * - If we use current values, messages change every request → cache miss
242
- * - Using stored metadata values → messages stay same → cache hit ✅
243
- *
244
- * Example timeline:
245
- * T1: User sends "hi"
246
- * → getSystemStatus() returns { cpu: "45%", memory: "4.2GB" }
247
- * → Store in message.metadata
248
- * T2: User sends "bye" (10 minutes later)
249
- * → getSystemStatus() returns { cpu: "67%", memory: "5.1GB" } for NEW message
250
- * → buildSystemStatusFromMetadata(T1.metadata) still returns "45%, 4.2GB" for T1 ✅
251
- * → Prompt cache recognizes T1 message as unchanged → cache hit!
252
- */
253
- export interface SystemStatus {
254
- timestamp: string; // ISO format
255
- cpu: string; // e.g., "45.3% (8 cores)"
256
- memory: string; // e.g., "4.2GB/16.0GB"
257
- }
258
-
259
- /**
260
- * Get CURRENT system status (called only when creating NEW messages)
261
- *
262
- * ⚠️ IMPORTANT: Never call this for historical messages!
263
- * Use buildSystemStatusFromMetadata() instead to preserve prompt cache.
264
- */
265
- function getSystemStatus(): SystemStatus {
266
- const timestamp = new Date().toISOString();
267
-
268
- // Get memory usage
269
- const totalMem = os.totalmem();
270
- const freeMem = os.freemem();
271
- const usedMem = totalMem - freeMem;
272
- const memUsageGB = (usedMem / 1024 / 1024 / 1024).toFixed(1);
273
- const totalMemGB = (totalMem / 1024 / 1024 / 1024).toFixed(1);
274
-
275
- // Get CPU usage (average load)
276
- const cpus = os.cpus();
277
- const cpuCount = cpus.length;
278
-
279
- // Calculate average CPU usage from all cores
280
- let totalIdle = 0;
281
- let totalTick = 0;
282
-
283
- cpus.forEach((cpu) => {
284
- for (const type in cpu.times) {
285
- totalTick += cpu.times[type as keyof typeof cpu.times];
286
- }
287
- totalIdle += cpu.times.idle;
288
- });
289
-
290
- const cpuUsage = (100 - (100 * totalIdle) / totalTick).toFixed(1);
291
-
292
- return {
293
- timestamp,
294
- cpu: `${cpuUsage}% (${cpuCount} cores)`,
295
- memory: `${memUsageGB}GB/${totalMemGB}GB`,
296
- };
297
- }
298
-
299
- /**
300
- * Build system status string from STORED metadata (not current values)
301
- *
302
- * ⚠️ CRITICAL: This function MUST use the metadata parameter values,
303
- * NEVER call getSystemStatus() or use current system values!
304
- *
305
- * Why: Prompt cache requires historical messages to be immutable.
306
- * Using stored metadata ensures the same message always produces the same output.
307
- *
308
- * Called by:
309
- * - useChat when building ModelMessage from SessionMessage (historical messages)
310
- * - Tool result injection (for current step's system status)
311
- *
312
- * @param metadata - Stored SystemStatus from SessionMessage.metadata
313
- * @returns Formatted system status string for LLM
314
- */
315
- function buildSystemStatusFromMetadata(metadata: SystemStatus): string {
316
- return `<system_status>
317
- Time: ${metadata.timestamp}
318
- CPU: ${metadata.cpu}
319
- Memory: ${metadata.memory}
320
- </system_status>`;
321
- }
322
-
323
- /**
324
- * Inject system status into tool result output
325
- * Convert all types to content type and prepend system status as text part
326
- */
327
- function injectSystemStatusToOutput(output: LanguageModelV2ToolResultOutput, systemStatus: SystemStatus): Extract<
328
- LanguageModelV2ToolResultOutput,
329
- { type: 'content' }
330
- > {
331
- if (!output || typeof output !== 'object') {
332
- return output;
333
- }
334
-
335
- // Convert to content type if not already
336
- const content: Extract<
337
- LanguageModelV2ToolResultOutput,
338
- { type: 'content' }
339
- > = {
340
- type: 'content',
341
- value: [],
342
- }
343
-
344
- if (output.type === 'content') {
345
- // Already content type
346
- content.value = output.value;
347
- } else if (output.type === 'text' || output.type === 'error-text') {
348
- content.value.push({
349
- type: 'text',
350
- text: output.value,
351
- });
352
- } else if (output.type === 'json' || output.type === 'error-json') {
353
- // Convert JSON to content (stringify)
354
- content.value.push({
355
- type: 'text',
356
- text: JSON.stringify(output.value, null, 2),
357
- });
358
- }
359
-
360
- // Prepend system status as text part
361
- const systemStatusString = buildSystemStatusFromMetadata(systemStatus);
362
-
363
- content.value.unshift({
364
- type: 'text',
365
- text: systemStatusString,
366
- })
367
- return content;
368
- }
369
-
370
- /**
371
- * Normalize content to modern array format
372
- * Converts legacy string content to Array<TextPart | ImagePart | FilePart | ... >
373
- */
374
- function normalizeMessage(message: ModelMessage): ModelMessage {
375
- const content = message.content;
376
- if (typeof content === 'string') {
377
- // Legacy string format → convert to TextPart array
378
- message.content = [
379
- {
380
- type: 'text',
381
- text: content,
382
- },
383
- ];
384
- }
385
-
386
- // Already array format (or other object)
387
- return message;
388
- }
389
-
390
- /**
391
- * Create AI stream with Sylphx tools pre-configured
392
- * Uses manual loop to control message history with timestamps
393
- */
394
- export async function* createAIStream(
395
- options: CreateAIStreamOptions
396
- ): AsyncIterable<StreamChunk> {
397
- const {
398
- systemPrompt = getSystemPrompt(),
399
- model,
400
- messages: initialMessages,
401
- abortSignal,
402
- onStepFinish,
403
- onPrepareMessages,
404
- onTransformToolResult,
405
- } = options;
406
-
407
- // Normalize all messages to array format
408
- let messageHistory = initialMessages.map(normalizeMessage);
409
-
410
- let stepNumber = 0;
411
- const MAX_STEPS = 1000;
412
-
413
- while (stepNumber < MAX_STEPS) {
414
- // Emit step-start event
415
- yield {
416
- type: 'step-start' as any,
417
- stepNumber,
418
- };
419
-
420
-
421
- // Prepare messages for this step (caller can inject context)
422
- const preparedMessages = onPrepareMessages
423
- ? onPrepareMessages(messageHistory, stepNumber)
424
- : messageHistory;
425
-
426
- // Call AI SDK with single step
427
- const { fullStream, response, finishReason, usage, content } = streamText({
428
- model,
429
- messages: preparedMessages,
430
- system: systemPrompt,
431
- tools: getAISDKTools({ interactive: hasUserInputHandler() }),
432
- // Only pass abortSignal if provided (exactOptionalPropertyTypes compliance)
433
- ...(abortSignal ? { abortSignal } : {}),
434
- // Don't handle errors here - let them propagate to the caller
435
- // onError callback is for non-fatal errors, fatal ones should throw
436
- });
437
-
438
- // Stream all chunks to user
439
- for await (const chunk of fullStream) {
440
- // DEBUG: Log every chunk
441
-
442
- switch (chunk.type) {
443
- case 'text-start':
444
- yield { type: 'text-start' };
445
- break;
446
-
447
- case 'text-delta':
448
- yield { type: 'text-delta', textDelta: chunk.text };
449
- break;
450
-
451
- case 'text-end':
452
- yield { type: 'text-end' };
453
- break;
454
-
455
- case 'reasoning-start':
456
- yield { type: 'reasoning-start' };
457
- break;
458
-
459
- case 'reasoning-delta':
460
- yield { type: 'reasoning-delta', textDelta: chunk.text };
461
- break;
462
-
463
- case 'reasoning-end':
464
- yield { type: 'reasoning-end' };
465
- break;
466
-
467
- case 'tool-call':
468
- yield {
469
- type: 'tool-call',
470
- toolCallId: chunk.toolCallId,
471
- toolName: chunk.toolName,
472
- args: chunk.input,
473
- };
474
- break;
475
-
476
- case 'tool-input-start':
477
- yield {
478
- type: 'tool-input-start',
479
- toolCallId: chunk.id,
480
- toolName: chunk.toolName,
481
- };
482
- break;
483
-
484
- case 'tool-input-delta':
485
- yield {
486
- type: 'tool-input-delta',
487
- toolCallId: chunk.id,
488
- argsTextDelta: chunk.delta,
489
- };
490
- break;
491
-
492
- case 'tool-input-end':
493
- yield {
494
- type: 'tool-input-end',
495
- toolCallId: chunk.id,
496
- };
497
- break;
498
-
499
- case 'tool-result':
500
- yield {
501
- type: 'tool-result',
502
- toolCallId: chunk.toolCallId,
503
- toolName: chunk.toolName,
504
- result: chunk.output,
505
- };
506
- break;
507
-
508
- case 'finish':
509
- yield {
510
- type: 'finish',
511
- finishReason: chunk.finishReason,
512
- usage: {
513
- promptTokens: chunk.totalUsage.inputTokens ?? 0,
514
- completionTokens: chunk.totalUsage.outputTokens ?? 0,
515
- totalTokens: chunk.totalUsage.totalTokens ?? 0,
516
- },
517
- };
518
- break;
519
-
520
- case 'error':
521
- yield {
522
- type: 'error',
523
- error: chunk.error instanceof Error ? chunk.error.message : String(chunk.error),
524
- };
525
- break;
526
-
527
- case 'tool-error':
528
- yield {
529
- type: 'tool-error',
530
- toolCallId: chunk.toolCallId,
531
- toolName: chunk.toolName,
532
- error: chunk.error instanceof Error ? chunk.error.message : String(chunk.error),
533
- };
534
- break;
535
-
536
- case 'abort':
537
- yield {
538
- type: 'abort',
539
- };
540
- break;
541
-
542
- default:
543
- break;
544
- }
545
- }
546
-
547
- // Call onStepFinish callback if provided
548
- if (onStepFinish) {
549
- const stepInfo: StepInfo = {
550
- finishReason: await finishReason,
551
- usage: {
552
- promptTokens: (await usage).inputTokens ?? 0,
553
- completionTokens: (await usage).outputTokens ?? 0,
554
- totalTokens: (await usage).totalTokens ?? 0,
555
- },
556
- content: await content,
557
- };
558
- onStepFinish(stepInfo);
559
- }
560
-
561
- // Save LLM response messages to history
562
- const responseMessages = (await response).messages;
563
-
564
- for (const msg of responseMessages) {
565
- // Transform tool result output if callback provided
566
- if (msg.role === 'tool' && onTransformToolResult) {
567
- messageHistory.push({
568
- ...msg,
569
- content: msg.content.map((part) => ({
570
- ...part,
571
- output: onTransformToolResult(part.output, part.toolName),
572
- })),
573
- });
574
- } else {
575
- messageHistory.push(msg);
576
- }
577
- }
578
-
579
- const currentFinishReason = await finishReason;
580
-
581
-
582
- // Emit step-end event
583
- yield {
584
- type: 'step-end' as any,
585
- stepNumber,
586
- finishReason: currentFinishReason,
587
- };
588
-
589
- // Check if we should continue the loop
590
- if (currentFinishReason !== 'tool-calls') {
591
- // No more tool calls, exit loop
592
- break;
593
- }
594
-
595
- stepNumber++;
596
- }
597
-
598
- }
599
-
600
- /**
601
- * Export helper functions
602
- */
603
- export { getAISDKTools, getSystemStatus, buildSystemStatusFromMetadata, injectSystemStatusToOutput, buildTodoContext, normalizeMessage };