feat(rah-light): remove agent delegation system
- Delete src/services/agents/delegation.ts (AgentDelegationService) - Delete app/api/rah/delegations/ directory (all routes) - Delete src/components/agents/DelegationIndicator.tsx - Update workflowExecutor.ts to work without delegation streaming - Update executeWorkflow.ts tool to execute workflows directly - Update quickAdd.ts to execute directly without delegation tracking - Add stub AgentDelegation types to UI components for compatibility - Add stub voice hooks to RAHChat.tsx (will be removed in story 2) Files deleted: - src/services/agents/delegation.ts - app/api/rah/delegations/route.ts - app/api/rah/delegations/stream/route.ts - app/api/rah/delegations/[sessionId]/route.ts - app/api/rah/delegations/[sessionId]/summary/route.ts - src/components/agents/DelegationIndicator.tsx Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.5
parent
08012a8e5a
commit
6b7bb5a5f9
@@ -1,243 +0,0 @@
|
||||
import { getSQLiteClient } from '@/services/database/sqlite-client';
|
||||
import { eventBroadcaster } from '@/services/events';
|
||||
|
||||
export type DelegationStatus = 'queued' | 'in_progress' | 'completed' | 'failed';
|
||||
export type DelegationAgentType = 'workflow' | 'wise-rah';
|
||||
|
||||
export interface AgentDelegation {
|
||||
id: number;
|
||||
sessionId: string;
|
||||
task: string;
|
||||
context: string[];
|
||||
expectedOutcome?: string | null;
|
||||
status: DelegationStatus;
|
||||
summary?: string | null;
|
||||
agentType: DelegationAgentType;
|
||||
supabaseToken?: string | null;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
function rowToDelegation(row: any): AgentDelegation {
|
||||
return {
|
||||
id: row.id,
|
||||
sessionId: row.session_id,
|
||||
task: row.task,
|
||||
context: (() => {
|
||||
try {
|
||||
return row.context ? JSON.parse(row.context) : [];
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
})(),
|
||||
expectedOutcome: row.expected_outcome,
|
||||
status: row.status as DelegationStatus,
|
||||
summary: row.summary,
|
||||
agentType: (row.agent_type || 'mini') as DelegationAgentType,
|
||||
supabaseToken: row.supabase_token ?? null,
|
||||
// SQLite CURRENT_TIMESTAMP is UTC, append 'Z' to parse correctly as UTC
|
||||
createdAt: row.created_at ? row.created_at.replace(' ', 'T') + 'Z' : row.created_at,
|
||||
updatedAt: row.updated_at ? row.updated_at.replace(' ', 'T') + 'Z' : row.updated_at,
|
||||
};
|
||||
}
|
||||
|
||||
function ensureTable() {
|
||||
const db = getSQLiteClient();
|
||||
db.query(`
|
||||
CREATE TABLE IF NOT EXISTS agent_delegations (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
session_id TEXT UNIQUE NOT NULL,
|
||||
task TEXT NOT NULL,
|
||||
context TEXT,
|
||||
expected_outcome TEXT,
|
||||
status TEXT NOT NULL DEFAULT 'queued',
|
||||
summary TEXT,
|
||||
agent_type TEXT NOT NULL DEFAULT 'mini',
|
||||
supabase_token TEXT,
|
||||
created_at TEXT DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TEXT DEFAULT CURRENT_TIMESTAMP
|
||||
)
|
||||
`);
|
||||
|
||||
// Add agent_type column if it doesn't exist (migration)
|
||||
try {
|
||||
const stmt = db.prepare('SELECT 1 FROM agent_delegations LIMIT 0');
|
||||
const tableExists = stmt !== null;
|
||||
|
||||
if (tableExists) {
|
||||
// Try to add the column, ignore if it already exists
|
||||
try {
|
||||
db.prepare(`ALTER TABLE agent_delegations ADD COLUMN agent_type TEXT NOT NULL DEFAULT 'mini'`).run();
|
||||
console.log('✅ Added agent_type column to agent_delegations table');
|
||||
} catch (alterError: any) {
|
||||
// Column already exists, ignore
|
||||
if (!alterError.message?.includes('duplicate column')) {
|
||||
console.warn('Migration warning:', alterError.message);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
db.prepare(`ALTER TABLE agent_delegations ADD COLUMN supabase_token TEXT`).run();
|
||||
console.log('✅ Added supabase_token column to agent_delegations table');
|
||||
} catch (alterError: any) {
|
||||
if (!alterError.message?.includes('duplicate column')) {
|
||||
console.warn('Migration warning:', alterError.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error: any) {
|
||||
// Table doesn't exist yet or other error - it will be created with the columns
|
||||
console.log('Table creation will include agent_type and supabase_token columns');
|
||||
}
|
||||
}
|
||||
|
||||
export class AgentDelegationService {
|
||||
static createDelegation(input: {
|
||||
task: string;
|
||||
context?: string[];
|
||||
expectedOutcome?: string | null;
|
||||
agentType?: DelegationAgentType;
|
||||
supabaseToken?: string | null;
|
||||
}): AgentDelegation {
|
||||
ensureTable();
|
||||
const db = getSQLiteClient();
|
||||
const sessionId = `delegation_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
|
||||
const contextJson = JSON.stringify(input.context ?? []);
|
||||
const agentType = input.agentType || 'mini';
|
||||
db.prepare(
|
||||
`INSERT INTO agent_delegations (session_id, task, context, expected_outcome, status, agent_type, supabase_token)
|
||||
VALUES (?, ?, ?, ?, 'queued', ?, ?)`
|
||||
).run(
|
||||
sessionId,
|
||||
input.task,
|
||||
contextJson,
|
||||
input.expectedOutcome ?? null,
|
||||
agentType,
|
||||
input.supabaseToken ?? null
|
||||
);
|
||||
|
||||
const row = db
|
||||
.prepare('SELECT * FROM agent_delegations WHERE session_id = ?')
|
||||
.get(sessionId);
|
||||
|
||||
const delegation = rowToDelegation(row);
|
||||
eventBroadcaster.broadcast({ type: 'AGENT_DELEGATION_CREATED', data: { delegation } });
|
||||
return delegation;
|
||||
}
|
||||
|
||||
static markInProgress(sessionId: string): AgentDelegation | null {
|
||||
ensureTable();
|
||||
const db = getSQLiteClient();
|
||||
db.prepare(
|
||||
`UPDATE agent_delegations
|
||||
SET status = 'in_progress', updated_at = CURRENT_TIMESTAMP
|
||||
WHERE session_id = ? AND status = 'queued'`
|
||||
).run(sessionId);
|
||||
const row = db.prepare('SELECT * FROM agent_delegations WHERE session_id = ?').get(sessionId);
|
||||
if (!row) return null;
|
||||
const delegation = rowToDelegation(row);
|
||||
eventBroadcaster.broadcast({ type: 'AGENT_DELEGATION_UPDATED', data: { delegation } });
|
||||
return delegation;
|
||||
}
|
||||
|
||||
static touchDelegation(sessionId: string): void {
|
||||
// Update the timestamp to prevent cleanup from killing active delegations
|
||||
ensureTable();
|
||||
const db = getSQLiteClient();
|
||||
db.prepare(
|
||||
`UPDATE agent_delegations SET updated_at = CURRENT_TIMESTAMP WHERE session_id = ? AND status = 'in_progress'`
|
||||
).run(sessionId);
|
||||
}
|
||||
|
||||
static completeDelegation(sessionId: string, summary: string, status: DelegationStatus = 'completed'): AgentDelegation | null {
|
||||
ensureTable();
|
||||
const db = getSQLiteClient();
|
||||
db.prepare(
|
||||
`UPDATE agent_delegations
|
||||
SET status = ?, summary = ?, updated_at = CURRENT_TIMESTAMP, supabase_token = NULL
|
||||
WHERE session_id = ?`
|
||||
).run(status, summary, sessionId);
|
||||
const row = db.prepare('SELECT * FROM agent_delegations WHERE session_id = ?').get(sessionId);
|
||||
if (!row) return null;
|
||||
const delegation = rowToDelegation(row);
|
||||
eventBroadcaster.broadcast({ type: 'AGENT_DELEGATION_UPDATED', data: { delegation } });
|
||||
return delegation;
|
||||
}
|
||||
|
||||
static getBySessionId(sessionId: string): AgentDelegation | null {
|
||||
ensureTable();
|
||||
const db = getSQLiteClient();
|
||||
const row = db.prepare('SELECT * FROM agent_delegations WHERE session_id = ?').get(sessionId);
|
||||
return row ? rowToDelegation(row) : null;
|
||||
}
|
||||
|
||||
static getDelegation(sessionId: string): AgentDelegation | null {
|
||||
return this.getBySessionId(sessionId);
|
||||
}
|
||||
|
||||
static listActive({ includeCompleted = true, limit = 100 }: { includeCompleted?: boolean; limit?: number } = {}): AgentDelegation[] {
|
||||
ensureTable();
|
||||
const db = getSQLiteClient();
|
||||
// Load all delegations - user closes them manually from UI
|
||||
const rows = includeCompleted
|
||||
? db.prepare(
|
||||
`SELECT * FROM agent_delegations
|
||||
ORDER BY updated_at DESC
|
||||
LIMIT ?`
|
||||
).all(limit)
|
||||
: db.prepare(
|
||||
`SELECT * FROM agent_delegations
|
||||
WHERE status IN ('queued','in_progress')
|
||||
ORDER BY updated_at DESC
|
||||
LIMIT ?`
|
||||
).all(limit);
|
||||
return rows.map(rowToDelegation);
|
||||
}
|
||||
|
||||
static listRecent(limit = 20): AgentDelegation[] {
|
||||
ensureTable();
|
||||
const db = getSQLiteClient();
|
||||
const rows = db.prepare(
|
||||
`SELECT * FROM agent_delegations
|
||||
ORDER BY created_at DESC
|
||||
LIMIT ?`
|
||||
).all(limit);
|
||||
return rows.map(rowToDelegation);
|
||||
}
|
||||
|
||||
static deleteDelegation(sessionId: string): boolean {
|
||||
ensureTable();
|
||||
const db = getSQLiteClient();
|
||||
const result = db.prepare('DELETE FROM agent_delegations WHERE session_id = ?').run(sessionId);
|
||||
return result.changes > 0;
|
||||
}
|
||||
|
||||
static cleanupStaleDelegations(timeoutMinutes = 15): number {
|
||||
ensureTable();
|
||||
const db = getSQLiteClient();
|
||||
const cutoffTime = new Date(Date.now() - timeoutMinutes * 60 * 1000).toISOString();
|
||||
|
||||
const result = db.prepare(`
|
||||
UPDATE agent_delegations
|
||||
SET status = 'failed',
|
||||
summary = 'Task timed out (exceeded ${timeoutMinutes} minutes)',
|
||||
updated_at = CURRENT_TIMESTAMP
|
||||
WHERE status = 'in_progress'
|
||||
AND updated_at < ?
|
||||
`).run(cutoffTime);
|
||||
|
||||
const affectedCount = result.changes || 0;
|
||||
|
||||
if (affectedCount > 0) {
|
||||
const rows = db.prepare(
|
||||
`SELECT * FROM agent_delegations WHERE status = 'failed' AND summary LIKE 'Task timed out%'`
|
||||
).all();
|
||||
rows.forEach(row => {
|
||||
const delegation = rowToDelegation(row);
|
||||
eventBroadcaster.broadcast({ type: 'AGENT_DELEGATION_UPDATED', data: { delegation } });
|
||||
});
|
||||
}
|
||||
|
||||
return affectedCount;
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
import { AgentDelegationService } from './delegation';
|
||||
import { summarizeToolExecution } from './toolResultUtils';
|
||||
import { youtubeExtractTool } from '@/tools/other/youtubeExtract';
|
||||
import { websiteExtractTool } from '@/tools/other/websiteExtract';
|
||||
import { paperExtractTool } from '@/tools/other/paperExtract';
|
||||
import { formatNodeForChat } from '@/tools/infrastructure/nodeFormatter';
|
||||
import { summarizeTranscript } from './transcriptSummarizer';
|
||||
import { eventBroadcaster } from '@/services/events';
|
||||
|
||||
export type QuickAddMode = 'link' | 'note' | 'chat';
|
||||
|
||||
@@ -340,41 +340,42 @@ async function handleChatTranscriptQuickAdd(rawInput: string, task: string): Pro
|
||||
});
|
||||
}
|
||||
|
||||
export async function enqueueQuickAdd({ rawInput, mode, description }: QuickAddInput) {
|
||||
export interface QuickAddResult {
|
||||
success: boolean;
|
||||
summary?: string;
|
||||
error?: string;
|
||||
nodeId?: number;
|
||||
}
|
||||
|
||||
export async function enqueueQuickAdd({ rawInput, mode, description }: QuickAddInput): Promise<QuickAddResult> {
|
||||
const inputType = detectInputType(rawInput, mode);
|
||||
const context: string[] = (inputType === 'note' || inputType === 'chat') ? [] : [rawInput];
|
||||
const task = buildTaskPrompt(inputType, rawInput);
|
||||
const expectedOutcome = buildExpectedOutcome(inputType);
|
||||
|
||||
const delegation = AgentDelegationService.createDelegation({
|
||||
task,
|
||||
context,
|
||||
expectedOutcome,
|
||||
});
|
||||
|
||||
setImmediate(async () => {
|
||||
try {
|
||||
AgentDelegationService.markInProgress(delegation.sessionId);
|
||||
try {
|
||||
let summary: string;
|
||||
if (inputType === 'note') {
|
||||
summary = await handleNoteQuickAdd(rawInput, task, description);
|
||||
} else if (inputType === 'chat') {
|
||||
summary = await handleChatTranscriptQuickAdd(rawInput, task);
|
||||
} else {
|
||||
summary = await handleExtractionQuickAdd(inputType as ExtractionQuickAddType, rawInput, task);
|
||||
}
|
||||
|
||||
let summary: string;
|
||||
if (inputType === 'note') {
|
||||
summary = await handleNoteQuickAdd(rawInput, task, description);
|
||||
} else if (inputType === 'chat') {
|
||||
summary = await handleChatTranscriptQuickAdd(rawInput, task);
|
||||
} else {
|
||||
summary = await handleExtractionQuickAdd(inputType as ExtractionQuickAddType, rawInput, task);
|
||||
}
|
||||
// Extract node ID from summary if present
|
||||
const nodeIdMatch = summary.match(/\[NODE:(\d+)/);
|
||||
const nodeId = nodeIdMatch ? parseInt(nodeIdMatch[1], 10) : undefined;
|
||||
|
||||
AgentDelegationService.completeDelegation(delegation.sessionId, summary, 'completed');
|
||||
} catch (error) {
|
||||
const message = error instanceof Error ? error.message : 'unknown error';
|
||||
AgentDelegationService.completeDelegation(
|
||||
delegation.sessionId,
|
||||
`Quick Add failed: ${message}`,
|
||||
'failed'
|
||||
);
|
||||
// Broadcast node created event
|
||||
if (nodeId) {
|
||||
eventBroadcaster.broadcast({
|
||||
type: 'NODE_CREATED',
|
||||
data: { node: { id: nodeId, title: 'Quick Add' } }
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return delegation;
|
||||
return { success: true, summary, nodeId };
|
||||
} catch (error) {
|
||||
const message = error instanceof Error ? error.message : 'unknown error';
|
||||
return { success: false, error: `Quick Add failed: ${message}` };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { streamText, ModelMessage } from 'ai';
|
||||
import { createOpenAI } from '@ai-sdk/openai';
|
||||
import type { LanguageModelV2ToolResultOutput } from '@ai-sdk/provider';
|
||||
import { AgentDelegationService } from '@/services/agents/delegation';
|
||||
import { WORKFLOW_EXECUTOR_SYSTEM_PROMPT } from '@/config/prompts/workflow-executor';
|
||||
import { getToolsByNames } from '@/tools/infrastructure/registry';
|
||||
import { WorkflowRegistry } from '@/services/workflows/registry';
|
||||
@@ -10,7 +9,6 @@ import { calculateCost } from '@/services/analytics/pricing';
|
||||
import { UsageData } from '@/types/analytics';
|
||||
import { summarizeToolExecution } from '@/services/agents/toolResultUtils';
|
||||
import { edgeService } from '@/services/database/edges';
|
||||
import { delegationStreamBroadcaster } from '@/app/api/rah/delegations/stream/route';
|
||||
import { RequestContext } from '@/services/context/requestContext';
|
||||
|
||||
export interface WorkflowExecutionInput {
|
||||
@@ -38,8 +36,7 @@ export class WorkflowExecutor {
|
||||
throw new Error('OPENAI_API_KEY is not set for workflow execution.');
|
||||
}
|
||||
|
||||
AgentDelegationService.markInProgress(sessionId);
|
||||
console.log('✅ [WorkflowExecutor] Delegation marked in progress');
|
||||
console.log('✅ [WorkflowExecutor] Starting workflow execution');
|
||||
|
||||
// Get workflow definition if available
|
||||
const workflow = workflowKey ? await WorkflowRegistry.getWorkflowByKey(workflowKey) : null;
|
||||
@@ -165,28 +162,9 @@ export class WorkflowExecutor {
|
||||
|
||||
const ensureString = (value: unknown) => (typeof value === 'string' ? value.trim() : '');
|
||||
|
||||
const sanitizeForBroadcast = (value: unknown) => {
|
||||
if (value === undefined) return undefined;
|
||||
try {
|
||||
return JSON.parse(JSON.stringify(value));
|
||||
} catch (error) {
|
||||
console.warn('[WorkflowExecutor] Failed to serialize delegation payload', error);
|
||||
if (typeof value === 'string') return value;
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
|
||||
const emitDelegationEvent = (payload: Record<string, unknown>) => {
|
||||
delegationStreamBroadcaster.broadcast(sessionId, payload);
|
||||
};
|
||||
|
||||
// Logging helpers (no-op for lite version without delegation streaming)
|
||||
const emitToolStart = (toolCallId: string, toolName: string, input: unknown) => {
|
||||
emitDelegationEvent({
|
||||
type: 'tool-input-start',
|
||||
toolCallId,
|
||||
toolName,
|
||||
input: sanitizeForBroadcast(input),
|
||||
});
|
||||
console.log(`🔧 [WorkflowExecutor] Tool start: ${toolName}`);
|
||||
};
|
||||
|
||||
const emitToolCompletion = (
|
||||
@@ -197,15 +175,7 @@ export class WorkflowExecutor {
|
||||
status: 'complete' | 'error' = 'complete',
|
||||
errorMessage?: string
|
||||
) => {
|
||||
emitDelegationEvent({
|
||||
type: 'tool-output-available',
|
||||
toolCallId,
|
||||
toolName,
|
||||
result: sanitizeForBroadcast(rawResult),
|
||||
summary,
|
||||
status,
|
||||
error: errorMessage,
|
||||
});
|
||||
console.log(`✅ [WorkflowExecutor] Tool complete: ${toolName} - ${status}`);
|
||||
};
|
||||
|
||||
const buildToolOutput = (toolName: string, summary: string, rawResult: any): LanguageModelV2ToolResultOutput => {
|
||||
@@ -280,9 +250,6 @@ export class WorkflowExecutor {
|
||||
for (let i = 0; i < maxIterations; i++) {
|
||||
console.log(`🔄 [WorkflowExecutor] Iteration ${i + 1}/${maxIterations}`);
|
||||
|
||||
// Touch delegation every iteration to prevent cleanup from killing it
|
||||
AgentDelegationService.touchDelegation(sessionId);
|
||||
|
||||
const streamResult = await streamText({
|
||||
model: openaiProvider('gpt-5-mini'),
|
||||
messages,
|
||||
@@ -308,12 +275,9 @@ export class WorkflowExecutor {
|
||||
|
||||
console.log(`📊 [WorkflowExecutor] Step ${i + 1} finishReason:`, response.finishReason);
|
||||
|
||||
// Stream text response to delegation chat
|
||||
// Log text response
|
||||
if (response.text && response.text.trim()) {
|
||||
emitDelegationEvent({
|
||||
type: 'text-delta',
|
||||
delta: response.text,
|
||||
});
|
||||
console.log(`📝 [WorkflowExecutor] Response text: ${response.text.substring(0, 100)}...`);
|
||||
}
|
||||
|
||||
if (response.finishReason !== 'tool-calls') {
|
||||
@@ -325,9 +289,9 @@ export class WorkflowExecutor {
|
||||
const toolCalls = response.toolCalls || [];
|
||||
console.log(`🔧 [WorkflowExecutor] Executing ${toolCalls.length} tool calls`);
|
||||
|
||||
// Broadcast new assistant message for next iteration
|
||||
// Log tool calls
|
||||
if (toolCalls.length > 0) {
|
||||
emitDelegationEvent({ type: 'assistant-message' });
|
||||
console.log(`🔧 [WorkflowExecutor] Processing ${toolCalls.length} tool calls`);
|
||||
}
|
||||
|
||||
messages.push({
|
||||
@@ -441,25 +405,12 @@ export class WorkflowExecutor {
|
||||
console.log('📏 [WorkflowExecutor] Summary length:', summary.length);
|
||||
|
||||
if (!summary) {
|
||||
emitDelegationEvent({
|
||||
type: 'assistant-message',
|
||||
});
|
||||
emitDelegationEvent({
|
||||
type: 'text-delta',
|
||||
delta: 'Workflow executor attempted to summarise but the response was empty. Check tool logs above for context.',
|
||||
});
|
||||
console.warn('[WorkflowExecutor] Empty summary received');
|
||||
throw new Error('Workflow executor returned empty summary');
|
||||
}
|
||||
|
||||
console.log('[WorkflowExecutor] summary:', summary);
|
||||
|
||||
// Emit final summary to the stream so it appears in the UI
|
||||
emitDelegationEvent({ type: 'assistant-message' });
|
||||
emitDelegationEvent({
|
||||
type: 'text-delta',
|
||||
delta: summary,
|
||||
});
|
||||
|
||||
// Calculate cost and log to chats table
|
||||
if (usage) {
|
||||
const inputTokens = (usage as any).promptTokens || usage.inputTokens || 0;
|
||||
@@ -487,16 +438,13 @@ export class WorkflowExecutor {
|
||||
workflowNodeId,
|
||||
};
|
||||
|
||||
const delegation = AgentDelegationService.getDelegation(sessionId);
|
||||
const delegationId = delegation?.id;
|
||||
|
||||
await ChatLoggingMiddleware.logChatInteraction(
|
||||
task,
|
||||
summary,
|
||||
{
|
||||
helperName: 'workflow-agent',
|
||||
agentType: 'planner',
|
||||
delegationId: delegationId ?? null,
|
||||
delegationId: null,
|
||||
sessionId,
|
||||
usageData,
|
||||
traceId,
|
||||
@@ -511,23 +459,12 @@ export class WorkflowExecutor {
|
||||
console.log(`💰 [WorkflowExecutor] Cost: $${costResult.totalCostUsd.toFixed(6)} (${totalTokens} tokens)`);
|
||||
}
|
||||
|
||||
console.log('✅ [WorkflowExecutor] Completing delegation with summary');
|
||||
return AgentDelegationService.completeDelegation(sessionId, summary);
|
||||
console.log('✅ [WorkflowExecutor] Workflow execution complete');
|
||||
return { sessionId, summary, status: 'completed' as const };
|
||||
} catch (error) {
|
||||
console.error('❌ [WorkflowExecutor] Error during execution:', error);
|
||||
console.error('❌ [WorkflowExecutor] Error stack:', error instanceof Error ? error.stack : 'No stack');
|
||||
const message = error instanceof Error ? error.message : 'Unknown delegation error';
|
||||
|
||||
// Broadcast error to delegation stream
|
||||
delegationStreamBroadcaster.broadcast(sessionId, {
|
||||
type: 'assistant-message',
|
||||
});
|
||||
delegationStreamBroadcaster.broadcast(sessionId, {
|
||||
type: 'text-delta',
|
||||
delta: `Workflow executor failed: ${message}`,
|
||||
});
|
||||
|
||||
AgentDelegationService.completeDelegation(sessionId, `Workflow executor failed: ${message}`, 'failed');
|
||||
const message = error instanceof Error ? error.message : 'Unknown workflow error';
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user