@sylphx/flow 1.8.0 → 1.8.2
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/CHANGELOG.md +72 -0
- package/assets/output-styles/silent.md +145 -8
- package/assets/rules/core.md +19 -2
- package/package.json +2 -12
- package/src/commands/flow/execute.ts +470 -0
- package/src/commands/flow/index.ts +11 -0
- package/src/commands/flow/prompt.ts +35 -0
- package/src/commands/flow/setup.ts +312 -0
- package/src/commands/flow/targets.ts +18 -0
- package/src/commands/flow/types.ts +47 -0
- package/src/commands/flow-command.ts +18 -967
- package/src/commands/flow-orchestrator.ts +14 -5
- package/src/commands/hook-command.ts +1 -1
- package/src/commands/init-core.ts +12 -3
- package/src/commands/run-command.ts +1 -1
- package/src/config/rules.ts +1 -1
- package/src/core/error-handling.ts +1 -1
- package/src/core/loop-controller.ts +1 -1
- package/src/core/state-detector.ts +1 -1
- package/src/core/target-manager.ts +1 -1
- package/src/index.ts +1 -1
- package/src/shared/files/index.ts +1 -1
- package/src/shared/processing/index.ts +1 -1
- package/src/targets/claude-code.ts +3 -3
- package/src/targets/opencode.ts +3 -3
- package/src/utils/agent-enhancer.ts +2 -2
- package/src/utils/{mcp-config.ts → config/mcp-config.ts} +4 -4
- package/src/utils/{paths.ts → config/paths.ts} +1 -1
- package/src/utils/{settings.ts → config/settings.ts} +1 -1
- package/src/utils/{target-config.ts → config/target-config.ts} +5 -5
- package/src/utils/{target-utils.ts → config/target-utils.ts} +3 -3
- package/src/utils/display/banner.ts +25 -0
- package/src/utils/display/status.ts +55 -0
- package/src/utils/{file-operations.ts → files/file-operations.ts} +2 -2
- package/src/utils/files/jsonc.ts +36 -0
- package/src/utils/{sync-utils.ts → files/sync-utils.ts} +3 -3
- package/src/utils/index.ts +42 -61
- package/src/utils/version.ts +47 -0
- package/src/components/benchmark-monitor.tsx +0 -331
- package/src/components/reindex-progress.tsx +0 -261
- package/src/composables/functional/index.ts +0 -14
- package/src/composables/functional/useEnvironment.ts +0 -171
- package/src/composables/functional/useFileSystem.ts +0 -139
- package/src/composables/index.ts +0 -4
- package/src/composables/useEnv.ts +0 -13
- package/src/composables/useRuntimeConfig.ts +0 -27
- package/src/core/ai-sdk.ts +0 -603
- package/src/core/app-factory.ts +0 -381
- package/src/core/builtin-agents.ts +0 -9
- package/src/core/command-system.ts +0 -550
- package/src/core/config-system.ts +0 -550
- package/src/core/connection-pool.ts +0 -390
- package/src/core/di-container.ts +0 -155
- package/src/core/headless-display.ts +0 -96
- package/src/core/interfaces/index.ts +0 -22
- package/src/core/interfaces/repository.interface.ts +0 -91
- package/src/core/interfaces/service.interface.ts +0 -133
- package/src/core/interfaces.ts +0 -96
- package/src/core/result.ts +0 -351
- package/src/core/service-config.ts +0 -252
- package/src/core/session-service.ts +0 -121
- package/src/core/storage-factory.ts +0 -115
- package/src/core/stream-handler.ts +0 -288
- package/src/core/type-utils.ts +0 -427
- package/src/core/unified-storage.ts +0 -456
- package/src/core/validation/limit.ts +0 -46
- package/src/core/validation/query.ts +0 -20
- package/src/db/auto-migrate.ts +0 -322
- package/src/db/base-database-client.ts +0 -144
- package/src/db/cache-db.ts +0 -218
- package/src/db/cache-schema.ts +0 -75
- package/src/db/database.ts +0 -70
- package/src/db/index.ts +0 -252
- package/src/db/memory-db.ts +0 -153
- package/src/db/memory-schema.ts +0 -29
- package/src/db/schema.ts +0 -289
- package/src/db/session-repository.ts +0 -733
- package/src/domains/index.ts +0 -6
- package/src/domains/utilities/index.ts +0 -6
- package/src/domains/utilities/time/index.ts +0 -5
- package/src/domains/utilities/time/tools.ts +0 -291
- package/src/services/agent-service.ts +0 -273
- package/src/services/evaluation-service.ts +0 -271
- package/src/services/functional/evaluation-logic.ts +0 -296
- package/src/services/functional/file-processor.ts +0 -273
- package/src/services/functional/index.ts +0 -12
- package/src/services/memory.service.ts +0 -476
- package/src/types/api/batch.ts +0 -108
- package/src/types/api/errors.ts +0 -118
- package/src/types/api/index.ts +0 -55
- package/src/types/api/requests.ts +0 -76
- package/src/types/api/responses.ts +0 -180
- package/src/types/api/websockets.ts +0 -85
- package/src/types/benchmark.ts +0 -49
- package/src/types/database.types.ts +0 -510
- package/src/types/memory-types.ts +0 -63
- package/src/utils/advanced-tokenizer.ts +0 -191
- package/src/utils/ai-model-fetcher.ts +0 -19
- package/src/utils/async-file-operations.ts +0 -516
- package/src/utils/audio-player.ts +0 -345
- package/src/utils/codebase-helpers.ts +0 -211
- package/src/utils/console-ui.ts +0 -79
- package/src/utils/database-errors.ts +0 -140
- package/src/utils/debug-logger.ts +0 -49
- package/src/utils/file-scanner.ts +0 -259
- package/src/utils/help.ts +0 -20
- package/src/utils/immutable-cache.ts +0 -106
- package/src/utils/jsonc.ts +0 -158
- package/src/utils/memory-tui.ts +0 -414
- package/src/utils/models-dev.ts +0 -91
- package/src/utils/parallel-operations.ts +0 -487
- package/src/utils/process-manager.ts +0 -155
- package/src/utils/prompts.ts +0 -120
- package/src/utils/search-tool-builder.ts +0 -214
- package/src/utils/session-manager.ts +0 -168
- package/src/utils/session-title.ts +0 -87
- package/src/utils/simplified-errors.ts +0 -410
- package/src/utils/template-engine.ts +0 -94
- package/src/utils/test-audio.ts +0 -71
- package/src/utils/todo-context.ts +0 -46
- package/src/utils/token-counter.ts +0 -288
- /package/src/utils/{cli-output.ts → display/cli-output.ts} +0 -0
- /package/src/utils/{logger.ts → display/logger.ts} +0 -0
- /package/src/utils/{notifications.ts → display/notifications.ts} +0 -0
- /package/src/utils/{secret-utils.ts → security/secret-utils.ts} +0 -0
- /package/src/utils/{security.ts → security/security.ts} +0 -0
package/src/core/ai-sdk.ts
DELETED
|
@@ -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 };
|