tachibot-mcp 2.0.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/.env.example +260 -0
- package/CHANGELOG.md +54 -0
- package/CODE_OF_CONDUCT.md +56 -0
- package/CONTRIBUTING.md +54 -0
- package/Dockerfile +36 -0
- package/LICENSE +644 -0
- package/README.md +201 -0
- package/SECURITY.md +95 -0
- package/dist/personality/komaai-expressions.js +12 -0
- package/dist/profiles/balanced.json +33 -0
- package/dist/profiles/code_focus.json +33 -0
- package/dist/profiles/full.json +33 -0
- package/dist/profiles/minimal.json +33 -0
- package/dist/profiles/research_power.json +33 -0
- package/dist/scripts/build-profiles.js +46 -0
- package/dist/src/application/services/focus/FocusModeRegistry.js +46 -0
- package/dist/src/application/services/focus/FocusTool.service.js +109 -0
- package/dist/src/application/services/focus/ModeRegistry.js +46 -0
- package/dist/src/application/services/focus/modes/focus-deep.mode.js +27 -0
- package/dist/src/application/services/focus/modes/status.mode.js +50 -0
- package/dist/src/application/services/focus/modes/tachibot-status.mode.js +50 -0
- package/dist/src/collaborative-orchestrator.js +391 -0
- package/dist/src/config/model-constants.js +188 -0
- package/dist/src/config/model-defaults.js +57 -0
- package/dist/src/config/model-preferences.js +382 -0
- package/dist/src/config/timeout-config.js +130 -0
- package/dist/src/config.js +173 -0
- package/dist/src/domain/interfaces/IFocusMode.js +5 -0
- package/dist/src/domain/interfaces/IProvider.js +6 -0
- package/dist/src/domain/interfaces/ITool.js +5 -0
- package/dist/src/focus-deep.js +245 -0
- package/dist/src/infrastructure/ascii/art/robots.ascii.js +16 -0
- package/dist/src/mcp-client.js +90 -0
- package/dist/src/memory/index.js +17 -0
- package/dist/src/memory/memory-config.js +135 -0
- package/dist/src/memory/memory-interface.js +174 -0
- package/dist/src/memory/memory-manager.js +383 -0
- package/dist/src/memory/providers/devlog-provider.js +385 -0
- package/dist/src/memory/providers/hybrid-provider.js +399 -0
- package/dist/src/memory/providers/local-provider.js +388 -0
- package/dist/src/memory/providers/mem0-provider.js +337 -0
- package/dist/src/modes/architect.js +477 -0
- package/dist/src/modes/auditor.js +362 -0
- package/dist/src/modes/challenger.js +841 -0
- package/dist/src/modes/code-reviewer.js +382 -0
- package/dist/src/modes/commit-guardian.js +424 -0
- package/dist/src/modes/documentation-writer.js +572 -0
- package/dist/src/modes/scout.js +587 -0
- package/dist/src/modes/shared/helpers/challenger-helpers.js +454 -0
- package/dist/src/modes/shared/helpers/index.js +17 -0
- package/dist/src/modes/shared/helpers/scout-helpers.js +270 -0
- package/dist/src/modes/shared/helpers/verifier-helpers.js +332 -0
- package/dist/src/modes/test-architect.js +767 -0
- package/dist/src/modes/verifier.js +378 -0
- package/dist/src/monitoring/performance-monitor.js +435 -0
- package/dist/src/optimization/batch-executor.js +121 -0
- package/dist/src/optimization/context-pruner.js +196 -0
- package/dist/src/optimization/cost-monitor.js +338 -0
- package/dist/src/optimization/index.js +65 -0
- package/dist/src/optimization/model-router.js +264 -0
- package/dist/src/optimization/result-cache.js +114 -0
- package/dist/src/optimization/token-optimizer.js +257 -0
- package/dist/src/optimization/token-tracker.js +118 -0
- package/dist/src/orchestrator-instructions.js +128 -0
- package/dist/src/orchestrator-lite.js +139 -0
- package/dist/src/orchestrator.js +191 -0
- package/dist/src/orchestrators/collaborative/interfaces/IToolExecutionEngine.js +1 -0
- package/dist/src/orchestrators/collaborative/interfaces/IToolExecutionStrategy.js +5 -0
- package/dist/src/orchestrators/collaborative/interfaces/IVisualizationRenderer.js +1 -0
- package/dist/src/orchestrators/collaborative/registries/ModelProviderRegistry.js +95 -0
- package/dist/src/orchestrators/collaborative/registries/ToolAdapterRegistry.js +64 -0
- package/dist/src/orchestrators/collaborative/services/tool-execution/ToolExecutionService.js +502 -0
- package/dist/src/orchestrators/collaborative/services/visualization/VisualizationService.js +206 -0
- package/dist/src/orchestrators/collaborative/types/session-types.js +5 -0
- package/dist/src/profiles/balanced.js +37 -0
- package/dist/src/profiles/code_focus.js +37 -0
- package/dist/src/profiles/debug_intensive.js +59 -0
- package/dist/src/profiles/full.js +37 -0
- package/dist/src/profiles/minimal.js +37 -0
- package/dist/src/profiles/research_code.js +59 -0
- package/dist/src/profiles/research_power.js +37 -0
- package/dist/src/profiles/types.js +5 -0
- package/dist/src/profiles/workflow_builder.js +53 -0
- package/dist/src/prompt-engineer-lite.js +78 -0
- package/dist/src/prompt-engineer.js +399 -0
- package/dist/src/reasoning-chain.js +508 -0
- package/dist/src/sequential-thinking.js +291 -0
- package/dist/src/server-diagnostic.js +74 -0
- package/dist/src/server-raw.js +158 -0
- package/dist/src/server-simple.js +58 -0
- package/dist/src/server.js +514 -0
- package/dist/src/session/session-logger.js +617 -0
- package/dist/src/session/session-manager.js +571 -0
- package/dist/src/session/session-tools.js +400 -0
- package/dist/src/tools/advanced-modes.js +200 -0
- package/dist/src/tools/claude-integration.js +356 -0
- package/dist/src/tools/consolidated/ai-router.js +174 -0
- package/dist/src/tools/consolidated/ai-tool.js +48 -0
- package/dist/src/tools/consolidated/brainstorm-tool.js +87 -0
- package/dist/src/tools/consolidated/environment-detector.js +80 -0
- package/dist/src/tools/consolidated/index.js +50 -0
- package/dist/src/tools/consolidated/search-tool.js +110 -0
- package/dist/src/tools/consolidated/workflow-tool.js +238 -0
- package/dist/src/tools/gemini-tools.js +329 -0
- package/dist/src/tools/grok-enhanced.js +376 -0
- package/dist/src/tools/grok-tools.js +299 -0
- package/dist/src/tools/lmstudio-tools.js +223 -0
- package/dist/src/tools/openai-tools.js +498 -0
- package/dist/src/tools/openrouter-tools.js +317 -0
- package/dist/src/tools/optimized-wrapper.js +204 -0
- package/dist/src/tools/perplexity-tools.js +294 -0
- package/dist/src/tools/pingpong-tool.js +343 -0
- package/dist/src/tools/qwen-wrapper.js +74 -0
- package/dist/src/tools/tool-router.js +444 -0
- package/dist/src/tools/unified-ai-provider.js +260 -0
- package/dist/src/tools/workflow-runner.js +425 -0
- package/dist/src/tools/workflow-validator-tool.js +107 -0
- package/dist/src/types.js +23 -0
- package/dist/src/utils/input-validator.js +130 -0
- package/dist/src/utils/model-router.js +91 -0
- package/dist/src/utils/progress-stream.js +255 -0
- package/dist/src/utils/provider-router.js +88 -0
- package/dist/src/utils/smart-api-client.js +146 -0
- package/dist/src/utils/table-builder.js +218 -0
- package/dist/src/utils/timestamp-formatter.js +134 -0
- package/dist/src/utils/tool-compressor.js +257 -0
- package/dist/src/utils/tool-config.js +201 -0
- package/dist/src/validators/dependency-graph-validator.js +147 -0
- package/dist/src/validators/interpolation-validator.js +222 -0
- package/dist/src/validators/output-usage-validator.js +151 -0
- package/dist/src/validators/syntax-validator.js +102 -0
- package/dist/src/validators/tool-registry-validator.js +123 -0
- package/dist/src/validators/tool-types.js +97 -0
- package/dist/src/validators/types.js +8 -0
- package/dist/src/validators/workflow-validator.js +134 -0
- package/dist/src/visualizer-lite.js +42 -0
- package/dist/src/visualizer.js +179 -0
- package/dist/src/workflows/circuit-breaker.js +199 -0
- package/dist/src/workflows/custom-workflows.js +451 -0
- package/dist/src/workflows/engine/AutoSynthesizer.js +97 -0
- package/dist/src/workflows/engine/StepParameterResolver.js +74 -0
- package/dist/src/workflows/engine/VariableInterpolator.js +123 -0
- package/dist/src/workflows/engine/WorkflowDiscovery.js +125 -0
- package/dist/src/workflows/engine/WorkflowExecutionEngine.js +485 -0
- package/dist/src/workflows/engine/WorkflowExecutor.js +113 -0
- package/dist/src/workflows/engine/WorkflowFileManager.js +244 -0
- package/dist/src/workflows/engine/WorkflowHelpers.js +114 -0
- package/dist/src/workflows/engine/WorkflowOutputFormatter.js +83 -0
- package/dist/src/workflows/engine/events/WorkflowEventBus.js +132 -0
- package/dist/src/workflows/engine/events/interfaces/IEventBus.js +5 -0
- package/dist/src/workflows/engine/handlers/ErrorRecoveryHandler.js +162 -0
- package/dist/src/workflows/engine/handlers/PromptEnhancementHandler.js +115 -0
- package/dist/src/workflows/engine/handlers/SessionPersistenceHandler.js +167 -0
- package/dist/src/workflows/engine/handlers/StepExecutionHandler.js +231 -0
- package/dist/src/workflows/engine/handlers/ToolInvocationHandler.js +46 -0
- package/dist/src/workflows/engine/interfaces/IAutoSynthesizer.js +5 -0
- package/dist/src/workflows/engine/interfaces/IStepParameterResolver.js +5 -0
- package/dist/src/workflows/engine/interfaces/IVariableInterpolator.js +5 -0
- package/dist/src/workflows/engine/interfaces/IWorkflowDiscovery.js +4 -0
- package/dist/src/workflows/engine/interfaces/IWorkflowFileManager.js +5 -0
- package/dist/src/workflows/engine/interfaces/IWorkflowOutputFormatter.js +5 -0
- package/dist/src/workflows/engine/state/WorkflowStateMachine.js +194 -0
- package/dist/src/workflows/engine/state/interfaces/IStateMachine.js +17 -0
- package/dist/src/workflows/fallback-strategies.js +373 -0
- package/dist/src/workflows/message-queue.js +455 -0
- package/dist/src/workflows/model-router.js +189 -0
- package/dist/src/workflows/orchestrator-examples.js +174 -0
- package/dist/src/workflows/orchestrator-integration.js +200 -0
- package/dist/src/workflows/self-healing.js +524 -0
- package/dist/src/workflows/tool-mapper.js +407 -0
- package/dist/src/workflows/tool-orchestrator.js +796 -0
- package/dist/src/workflows/workflow-engine.js +573 -0
- package/dist/src/workflows/workflow-parser.js +283 -0
- package/dist/src/workflows/workflow-types.js +95 -0
- package/dist/src/workflows.js +568 -0
- package/dist/test-workflow-file-output.js +93 -0
- package/docs/API_KEYS.md +570 -0
- package/docs/CLAUDE_CODE_SETUP.md +181 -0
- package/docs/CLAUDE_DESKTOP_MANUAL.md +127 -0
- package/docs/CONFIGURATION.md +745 -0
- package/docs/FOCUS_MODES.md +240 -0
- package/docs/INSTALLATION_BOTH.md +145 -0
- package/docs/TERMS.md +352 -0
- package/docs/TOOLS_REFERENCE.md +1622 -0
- package/docs/TOOL_PARAMETERS.md +496 -0
- package/docs/TOOL_PROFILES.md +236 -0
- package/docs/WORKFLOWS.md +987 -0
- package/docs/WORKFLOW_OUTPUT.md +198 -0
- package/docs/WORKFLOW_PROGRESS_TRACKING.md +305 -0
- package/docs/workflows/design-brainstorm.md +335 -0
- package/package.json +97 -0
- package/profiles/balanced.json +37 -0
- package/profiles/code_focus.json +37 -0
- package/profiles/debug_intensive.json +34 -0
- package/profiles/full.json +37 -0
- package/profiles/minimal.json +37 -0
- package/profiles/research_power.json +37 -0
- package/profiles/workflow_builder.json +37 -0
- package/smithery.yaml +66 -0
- package/start.sh +8 -0
- package/tools.config.json +81 -0
- package/tsconfig.json +18 -0
- package/workflows/accessibility-code-audit.yaml +92 -0
- package/workflows/code-architecture-review.yaml +202 -0
- package/workflows/code-review.yaml +142 -0
- package/workflows/core/iterative-problem-solver.yaml +283 -0
- package/workflows/creative-brainstorm-yaml.yaml +215 -0
- package/workflows/pingpong.yaml +141 -0
- package/workflows/system/README.md +412 -0
- package/workflows/system/challenger.yaml +175 -0
- package/workflows/system/scout.yaml +164 -0
- package/workflows/system/verifier.yaml +133 -0
- package/workflows/ultra-creative-brainstorm.yaml +318 -0
- package/workflows/ux-research-flow.yaml +92 -0
|
@@ -0,0 +1,485 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workflow Execution Engine
|
|
3
|
+
* Contains executeWorkflow implementation (615 lines extracted)
|
|
4
|
+
*/
|
|
5
|
+
import * as path from 'path';
|
|
6
|
+
import { promises as fsPromises } from 'fs';
|
|
7
|
+
import { generateWorkflowId } from '../../utils/timestamp-formatter.js';
|
|
8
|
+
import { loadConfig } from '../../config.js';
|
|
9
|
+
import { modelRouter } from '../../optimization/model-router.js';
|
|
10
|
+
import { tokenOptimizer } from '../../optimization/token-optimizer.js';
|
|
11
|
+
export { executeWorkflowImpl };
|
|
12
|
+
async function executeWorkflowImpl(parent, workflowName, input, options) {
|
|
13
|
+
const workflow = parent.workflows.get(workflowName);
|
|
14
|
+
if (!workflow) {
|
|
15
|
+
throw new Error(`Workflow '${workflowName}' not found`);
|
|
16
|
+
}
|
|
17
|
+
// Generate unique workflow ID: YYYY-MM-DD-DayName-HH-MM-shortid
|
|
18
|
+
// Example: 2025-11-23-Sunday-22-44-a1b2c3d4
|
|
19
|
+
const workflowId = generateWorkflowId();
|
|
20
|
+
// Setup output directory for file-based workflows
|
|
21
|
+
// Uses WORKFLOW_OUTPUT_DIR env var (default: ./workflow-output)
|
|
22
|
+
const config = loadConfig();
|
|
23
|
+
const baseOutputDir = config.workflow.outputDir;
|
|
24
|
+
const outputDir = path.join(process.cwd(), baseOutputDir, workflowName, workflowId);
|
|
25
|
+
// Initialize execution tracking
|
|
26
|
+
const execution = {
|
|
27
|
+
workflowName,
|
|
28
|
+
workflowId,
|
|
29
|
+
outputDir,
|
|
30
|
+
startTime: new Date(),
|
|
31
|
+
status: "running",
|
|
32
|
+
cost: 0,
|
|
33
|
+
outputs: [],
|
|
34
|
+
};
|
|
35
|
+
parent.executionHistory.push(execution);
|
|
36
|
+
console.error(`\n📝 Workflow manifest: ${path.join(outputDir, 'manifest.json')}`);
|
|
37
|
+
console.error(` To monitor progress: tail -f ${path.join(outputDir, 'manifest.json')}\n`);
|
|
38
|
+
// Merge variables
|
|
39
|
+
const variables = {
|
|
40
|
+
...workflow.variables,
|
|
41
|
+
...options?.variables,
|
|
42
|
+
input,
|
|
43
|
+
query: input, // Also provide 'query' alias for backwards compatibility with workflows that use ${query}
|
|
44
|
+
};
|
|
45
|
+
console.error(`🔍 Workflow variables initialized:`, JSON.stringify(variables, null, 2));
|
|
46
|
+
// Calculate step numbers for sequential/parallel execution display
|
|
47
|
+
const stepNumbers = parent.calculateStepNumbers(workflow);
|
|
48
|
+
const stepNumberEntries = Array.from(stepNumbers.entries());
|
|
49
|
+
console.error(`📊 Step numbering calculated:`, stepNumberEntries.map(([name, num]) => `${num}: ${name}`).join(', '));
|
|
50
|
+
// Execute steps
|
|
51
|
+
const stepOutputs = {};
|
|
52
|
+
let previousOutput = input;
|
|
53
|
+
// Initialize output directory if ANY step has saveToFile
|
|
54
|
+
const needsFileOutput = workflow.steps.some((s) => s.saveToFile);
|
|
55
|
+
if (needsFileOutput) {
|
|
56
|
+
try {
|
|
57
|
+
await fsPromises.mkdir(outputDir, { recursive: true });
|
|
58
|
+
// Create initial manifest
|
|
59
|
+
const manifest = {
|
|
60
|
+
workflowId,
|
|
61
|
+
workflowName,
|
|
62
|
+
startTime: new Date().toISOString(),
|
|
63
|
+
endTime: null,
|
|
64
|
+
status: 'running',
|
|
65
|
+
query: input,
|
|
66
|
+
steps: []
|
|
67
|
+
};
|
|
68
|
+
await fsPromises.writeFile(path.join(outputDir, 'manifest.json'), JSON.stringify(manifest, null, 2));
|
|
69
|
+
console.error(`📁 Workflow output directory: ${outputDir}`);
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
console.error('⚠️ Failed to initialize workflow directory:', error);
|
|
73
|
+
// Continue without file output
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
try {
|
|
77
|
+
for (let i = 0; i < workflow.steps.length; i++) {
|
|
78
|
+
const step = workflow.steps[i];
|
|
79
|
+
// Check condition
|
|
80
|
+
if (step.condition?.if) {
|
|
81
|
+
const shouldRun = parent.evaluateCondition(step.condition.if, variables, stepOutputs);
|
|
82
|
+
if (!shouldRun && step.condition.skip) {
|
|
83
|
+
console.error(`⏭️ Skipping step: ${step.name}`);
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
// 🔥 LIVE PROGRESS - Show what's starting
|
|
88
|
+
const stepNumber = i + 1;
|
|
89
|
+
const totalSteps = workflow.steps.length;
|
|
90
|
+
console.error(`\n${"⏳".repeat(40)}`);
|
|
91
|
+
console.error(`🚀 STARTING STEP ${stepNumber}/${totalSteps}: ${step.name} (${step.tool})`);
|
|
92
|
+
console.error(`${"⏳".repeat(40)}\n`);
|
|
93
|
+
// Load saved step files if requested (hybrid: summary + lazy loader)
|
|
94
|
+
if (step.loadFiles && step.loadFiles.length > 0 && outputDir) {
|
|
95
|
+
for (const stepId of step.loadFiles) {
|
|
96
|
+
const existingRef = stepOutputs[stepId];
|
|
97
|
+
if (existingRef) {
|
|
98
|
+
// Already have FileReference - reuse it directly
|
|
99
|
+
variables[stepId] = existingRef;
|
|
100
|
+
console.error(`📂 Loaded existing FileReference for '${stepId}' (${existingRef.summary.length} char summary)`);
|
|
101
|
+
// Also set output variable if defined
|
|
102
|
+
const previousStep = workflow.steps.find((s) => s.name === stepId);
|
|
103
|
+
if (previousStep?.output?.variable) {
|
|
104
|
+
variables[previousStep.output.variable] = existingRef;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
// Fallback: Try to load from file (for external files)
|
|
109
|
+
const filename = `${stepId}.md`;
|
|
110
|
+
const filepath = path.join(outputDir, filename);
|
|
111
|
+
try {
|
|
112
|
+
const fileContent = await fsPromises.readFile(filepath, 'utf-8');
|
|
113
|
+
const outputMatch = fileContent.match(/## Output\s*\n\n([\s\S]*)/);
|
|
114
|
+
const content = outputMatch ? outputMatch[1].trim() : fileContent;
|
|
115
|
+
// Create FileReference from loaded file
|
|
116
|
+
const fileRef = await parent.createFileReference(stepId, content, workflowId, workflowName, true, outputDir, stepNumbers.get(stepId), // Pass step number for filename
|
|
117
|
+
undefined // Model unknown for loaded files
|
|
118
|
+
);
|
|
119
|
+
variables[stepId] = fileRef;
|
|
120
|
+
stepOutputs[stepId] = fileRef;
|
|
121
|
+
console.error(`📂 Loaded external file for '${stepId}' (${fileRef.sizeBytes} bytes)`);
|
|
122
|
+
// Also set output variable
|
|
123
|
+
const previousStep = workflow.steps.find((s) => s.name === stepId);
|
|
124
|
+
if (previousStep?.output?.variable) {
|
|
125
|
+
variables[previousStep.output.variable] = fileRef;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
catch (error) {
|
|
129
|
+
console.error(`⚠️ Could not load file for step '${stepId}':`, error);
|
|
130
|
+
// Continue without this file
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
// Prepare input
|
|
136
|
+
console.error(`\n🔍 [${step.name}] Preparing input...`);
|
|
137
|
+
console.error(`🔍 Available variables: [${Object.keys(variables).join(', ')}]`);
|
|
138
|
+
console.error(`🔍 Available stepOutputs: [${Object.keys(stepOutputs).join(', ')}]`);
|
|
139
|
+
// Extract variables that this step wants to use
|
|
140
|
+
const inputStr = JSON.stringify(step.input);
|
|
141
|
+
const usedVars = [...inputStr.matchAll(/\$\{([^}]+)\}/g)].map(m => m[1]);
|
|
142
|
+
if (usedVars.length > 0) {
|
|
143
|
+
console.error(`🔍 Variables needed by this step: [${usedVars.join(', ')}]`);
|
|
144
|
+
// Check if all needed variables exist
|
|
145
|
+
for (const varName of usedVars) {
|
|
146
|
+
const exists = variables.hasOwnProperty(varName) || stepOutputs.hasOwnProperty(varName);
|
|
147
|
+
if (!exists) {
|
|
148
|
+
console.error(`❌ MISSING: Variable '${varName}' is not available!`);
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
const value = variables[varName] || stepOutputs[varName];
|
|
152
|
+
console.error(`✓ FOUND: '${varName}' (${typeof value}, ${String(value).length} chars)`);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
let stepInput = input;
|
|
157
|
+
if (step.input) {
|
|
158
|
+
if (typeof step.input === "string") {
|
|
159
|
+
stepInput = await parent.interpolateVariables(step.input, variables, stepOutputs);
|
|
160
|
+
}
|
|
161
|
+
else {
|
|
162
|
+
// Handle any object structure - interpolate all string values
|
|
163
|
+
stepInput = {};
|
|
164
|
+
const debugLines = [
|
|
165
|
+
`🔍 Interpolating step input for ${step.name}:`,
|
|
166
|
+
` Available variables: ${Object.keys(variables).join(', ')}`,
|
|
167
|
+
` Available step outputs: ${Object.keys(stepOutputs).join(', ')}`,
|
|
168
|
+
];
|
|
169
|
+
for (const [key, value] of Object.entries(step.input)) {
|
|
170
|
+
debugLines.push(` Processing ${step.name}.${key}: type=${typeof value}, value="${String(value).substring(0, 100)}"`);
|
|
171
|
+
if (typeof value === 'string') {
|
|
172
|
+
const interpolated = await parent.interpolateVariables(value, variables, stepOutputs);
|
|
173
|
+
if (value !== interpolated) {
|
|
174
|
+
debugLines.push(`🔄 Variable substitution in ${step.name}.${key}:`);
|
|
175
|
+
debugLines.push(` Before: ${value.substring(0, 100)}...`);
|
|
176
|
+
debugLines.push(` After: ${interpolated.substring(0, 100)}...`);
|
|
177
|
+
}
|
|
178
|
+
else {
|
|
179
|
+
debugLines.push(`⚠️ No substitution for ${step.name}.${key} - value: "${value.substring(0, 50)}..."`);
|
|
180
|
+
}
|
|
181
|
+
stepInput[key] = interpolated;
|
|
182
|
+
}
|
|
183
|
+
else {
|
|
184
|
+
stepInput[key] = value;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
// Log to file and console
|
|
188
|
+
const debugMsg = debugLines.join('\n');
|
|
189
|
+
console.error(debugMsg);
|
|
190
|
+
await fsPromises.appendFile('/tmp/workflow-debug.log', debugMsg + '\n\n').catch(() => { });
|
|
191
|
+
// Special handling for previousStep reference
|
|
192
|
+
if (stepInput.previousStep && typeof stepInput.previousStep === 'string') {
|
|
193
|
+
const prevStepRef = stepOutputs[stepInput.previousStep];
|
|
194
|
+
stepInput.previousStep = prevStepRef ? prevStepRef.summary : previousOutput;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
// RUNTIME PARAMETER RESOLUTION
|
|
199
|
+
// Resolve ${step_output} references and convert string numbers to actual numbers
|
|
200
|
+
const resolvedParams = parent.resolveStepParameters(step, variables, stepOutputs);
|
|
201
|
+
// Select optimal model if using smart routing
|
|
202
|
+
let model = resolvedParams.model;
|
|
203
|
+
if (workflow.settings?.optimization?.smartRouting && !model) {
|
|
204
|
+
const context = modelRouter.buildContext(typeof stepInput === "string" ? stepInput : stepInput?.prompt || "");
|
|
205
|
+
const selection = modelRouter.selectModel(context);
|
|
206
|
+
model = selection.primary;
|
|
207
|
+
console.error(`🧠 Smart routing selected: ${model} for ${step.name}`);
|
|
208
|
+
}
|
|
209
|
+
// Apply optimizations (only for string inputs)
|
|
210
|
+
if (workflow.settings?.optimization?.enabled && typeof stepInput === "string") {
|
|
211
|
+
const optimized = await tokenOptimizer.optimize({
|
|
212
|
+
prompt: stepInput,
|
|
213
|
+
model: model || "gpt-5-mini",
|
|
214
|
+
maxTokens: resolvedParams.maxTokens,
|
|
215
|
+
});
|
|
216
|
+
if (optimized.fromCache) {
|
|
217
|
+
// NOTE: When fromCache=true, optimized.prompt contains the cached RESPONSE (not the input)
|
|
218
|
+
// This is intentional - see token-optimizer.ts:175 where cached.response overwrites request.prompt
|
|
219
|
+
console.error(`✅ Using cached result for ${step.name}`);
|
|
220
|
+
console.error(`📦 Cached output length: ${optimized.prompt.length} chars`);
|
|
221
|
+
const cachedResult = optimized.prompt;
|
|
222
|
+
// Convert cached result to FileReference
|
|
223
|
+
const cachedFileRef = await parent.createFileReference(step.name, cachedResult, workflowId, workflowName, step.saveToFile || false, outputDir, stepNumbers.get(step.name), // Pass step number for filename
|
|
224
|
+
model || resolvedParams.model // Pass model name
|
|
225
|
+
);
|
|
226
|
+
stepOutputs[step.name] = cachedFileRef;
|
|
227
|
+
// Store in variables if output.variable is specified
|
|
228
|
+
if (step.output?.variable) {
|
|
229
|
+
variables[step.output.variable] = cachedFileRef;
|
|
230
|
+
console.error(`✅ Stored cached FileReference in variables['${step.output.variable}']`);
|
|
231
|
+
console.error(`✅ Available variables now: [${Object.keys(variables).join(', ')}]`);
|
|
232
|
+
}
|
|
233
|
+
previousOutput = cachedFileRef.summary;
|
|
234
|
+
execution.outputs.push({
|
|
235
|
+
step: step.name,
|
|
236
|
+
input: '[cached]',
|
|
237
|
+
output: cachedFileRef.summary,
|
|
238
|
+
filePath: cachedFileRef.filePath || undefined
|
|
239
|
+
});
|
|
240
|
+
continue; // Skip tool execution
|
|
241
|
+
}
|
|
242
|
+
// When not cached, optimized.prompt contains the compressed INPUT
|
|
243
|
+
stepInput = optimized.prompt;
|
|
244
|
+
}
|
|
245
|
+
// Execute step (with retry logic)
|
|
246
|
+
let attempts = 0;
|
|
247
|
+
const maxAttempts = step.retry?.attempts || 1;
|
|
248
|
+
let result = "";
|
|
249
|
+
let actualModelUsed = model || resolvedParams.model || "unknown";
|
|
250
|
+
while (attempts < maxAttempts) {
|
|
251
|
+
try {
|
|
252
|
+
console.error(`🔧 Executing step: ${step.name} (${step.tool})`);
|
|
253
|
+
if (options?.dryRun) {
|
|
254
|
+
result = `[DRY RUN] Would execute ${step.tool} with model ${model}`;
|
|
255
|
+
}
|
|
256
|
+
else {
|
|
257
|
+
// Emit before_invoke event for PromptEnhancementHandler (Phase 3: Enhanced context)
|
|
258
|
+
await parent.eventBus.publish('workflow.tool.before_invoke', {
|
|
259
|
+
stepName: step.name,
|
|
260
|
+
tool: step.tool,
|
|
261
|
+
input: stepInput,
|
|
262
|
+
context: {
|
|
263
|
+
step,
|
|
264
|
+
variables,
|
|
265
|
+
accumulatedResults: execution.outputs,
|
|
266
|
+
// NEW: Phase 3 context for smart prompt enhancement
|
|
267
|
+
stepIndex: i,
|
|
268
|
+
totalSteps: workflow.steps.length,
|
|
269
|
+
workflowName: workflowName
|
|
270
|
+
}
|
|
271
|
+
});
|
|
272
|
+
// Call the actual tool with RESOLVED parameters
|
|
273
|
+
console.error(`🔧 About to call ${step.tool} with stepInput:`, JSON.stringify(stepInput, null, 2).substring(0, 500));
|
|
274
|
+
const toolResult = await parent.callTool(step.tool, stepInput, {
|
|
275
|
+
model: model || resolvedParams.model,
|
|
276
|
+
maxTokens: resolvedParams.maxTokens,
|
|
277
|
+
temperature: resolvedParams.temperature,
|
|
278
|
+
skipValidation: true, // Skip validation for internal workflow calls
|
|
279
|
+
});
|
|
280
|
+
result = toolResult.result;
|
|
281
|
+
actualModelUsed = toolResult.modelUsed;
|
|
282
|
+
console.error(`✅ Tool executed with model: ${actualModelUsed}`);
|
|
283
|
+
}
|
|
284
|
+
break; // Success
|
|
285
|
+
}
|
|
286
|
+
catch (error) {
|
|
287
|
+
attempts++;
|
|
288
|
+
if (attempts >= maxAttempts) {
|
|
289
|
+
if (step.condition?.failOnError) {
|
|
290
|
+
throw error;
|
|
291
|
+
}
|
|
292
|
+
console.error(`❌ Step ${step.name} failed after ${maxAttempts} attempts`);
|
|
293
|
+
result = "";
|
|
294
|
+
}
|
|
295
|
+
else {
|
|
296
|
+
const backoff = (step.retry?.backoff || 1000) * attempts;
|
|
297
|
+
console.warn(`⚠️ Retrying ${step.name} in ${backoff}ms...`);
|
|
298
|
+
await new Promise((resolve) => setTimeout(resolve, backoff));
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
// Store output with validation
|
|
303
|
+
console.error(`📦 Step "${step.name}" completed - Output type: ${typeof result}`);
|
|
304
|
+
// VALIDATION: Ensure result is not undefined/null
|
|
305
|
+
if (result === undefined || result === null) {
|
|
306
|
+
const errorMsg = `Step '${step.name}' returned ${result}. This indicates tool '${step.tool}' execution failed silently.`;
|
|
307
|
+
console.error(`❌ ${errorMsg}`);
|
|
308
|
+
throw new Error(errorMsg);
|
|
309
|
+
}
|
|
310
|
+
// VALIDATION: Warn if result is empty
|
|
311
|
+
if (result.trim().length === 0) {
|
|
312
|
+
console.error(`⚠️ WARNING: Step '${step.name}' returned empty string. Tool may have failed.`);
|
|
313
|
+
}
|
|
314
|
+
console.error(`📦 Output length: ${result.length} chars, Preview: ${result.substring(0, 150)}...`);
|
|
315
|
+
// Convert result to string if it's an object
|
|
316
|
+
if (typeof result !== 'string') {
|
|
317
|
+
result = JSON.stringify(result, null, 2);
|
|
318
|
+
console.error(`📦 Converted object result to JSON string (${result.length} chars)`);
|
|
319
|
+
}
|
|
320
|
+
// Create FileReference for result (replaces full content storage)
|
|
321
|
+
const fileRef = await parent.createFileReference(step.name, result, workflowId, workflowName, step.saveToFile || false, outputDir, stepNumbers.get(step.name), // Pass step number for filename
|
|
322
|
+
actualModelUsed // Pass ACTUAL model used (not requested model)
|
|
323
|
+
);
|
|
324
|
+
// Store FileReference (replaces full content)
|
|
325
|
+
stepOutputs[step.name] = fileRef;
|
|
326
|
+
if (step.output?.variable) {
|
|
327
|
+
variables[step.output.variable] = fileRef;
|
|
328
|
+
console.error(`✅ Stored FileReference in variables['${step.output.variable}']`);
|
|
329
|
+
console.error(`✅ Available variables now: [${Object.keys(variables).join(', ')}]`);
|
|
330
|
+
}
|
|
331
|
+
// Update previous output with summary for chaining
|
|
332
|
+
previousOutput = fileRef.summary;
|
|
333
|
+
// Store summary in execution history (not full content)
|
|
334
|
+
execution.outputs.push({
|
|
335
|
+
step: step.name,
|
|
336
|
+
input: parent.extractInputSummary(stepInput),
|
|
337
|
+
output: fileRef.summary,
|
|
338
|
+
filePath: fileRef.filePath || undefined
|
|
339
|
+
});
|
|
340
|
+
console.error(`📦 Created FileReference: ${fileRef.sizeBytes} bytes → ${fileRef.summary.length} char summary`);
|
|
341
|
+
// 🔥 LIVE OUTPUT - Show summary for each step
|
|
342
|
+
console.error(`\n${"=".repeat(80)}`);
|
|
343
|
+
console.error(`✅ STEP ${stepNumber}/${totalSteps} COMPLETE: ${step.name}`);
|
|
344
|
+
console.error(`${"=".repeat(80)}`);
|
|
345
|
+
console.error(`\n${fileRef.summary}\n`);
|
|
346
|
+
if (fileRef.filePath) {
|
|
347
|
+
console.error(`📄 Full output saved to: ${fileRef.filePath}`);
|
|
348
|
+
}
|
|
349
|
+
console.error(`${"=".repeat(80)}\n`);
|
|
350
|
+
// Progress tracking is handled by manifest.json updates
|
|
351
|
+
const logOutput = fileRef.filePath
|
|
352
|
+
? `${fileRef.summary}\n\n📄 Full output: ${fileRef.filePath}`
|
|
353
|
+
: fileRef.summary;
|
|
354
|
+
// Update manifest with step completion (manifest.json tracks all progress)
|
|
355
|
+
console.error(`✅ Step ${stepNumber}/${totalSteps} completed: ${step.name}`);
|
|
356
|
+
// (Session logging removed - manifest.json provides full tracking)
|
|
357
|
+
const _metadata = {
|
|
358
|
+
stepNumber,
|
|
359
|
+
stepName: step.name,
|
|
360
|
+
totalSteps,
|
|
361
|
+
workflowName,
|
|
362
|
+
filePath: fileRef.filePath,
|
|
363
|
+
sizeBytes: fileRef.sizeBytes
|
|
364
|
+
};
|
|
365
|
+
// Check if we should run next step in parallel
|
|
366
|
+
if (step.parallel && i < workflow.steps.length - 1) {
|
|
367
|
+
// In a real implementation, you'd use Promise.all or similar
|
|
368
|
+
console.error(`🔀 Next step will run in parallel`);
|
|
369
|
+
}
|
|
370
|
+
// Progressive checkpointing during workflow execution
|
|
371
|
+
const autoSynthesis = workflow.settings?.autoSynthesis;
|
|
372
|
+
if (autoSynthesis?.enabled && outputDir) {
|
|
373
|
+
const checkpointInterval = autoSynthesis.checkpointInterval ?? 10000;
|
|
374
|
+
const accumulatedResults = Object.values(stepOutputs);
|
|
375
|
+
const totalTokens = parent.estimateTotalTokens(accumulatedResults);
|
|
376
|
+
const logLevel = autoSynthesis.logLevel ?? 'info';
|
|
377
|
+
if (totalTokens > 0 && totalTokens % checkpointInterval < 5000) {
|
|
378
|
+
await parent.createCheckpoint(workflowName, outputDir, variables, i);
|
|
379
|
+
if (logLevel !== 'silent' && logLevel !== 'error') {
|
|
380
|
+
console.error(`💾 Checkpoint created at step ${i + 1}/${workflow.steps.length}`);
|
|
381
|
+
console.error(` Total tokens: ${totalTokens}`);
|
|
382
|
+
console.error(` Variables saved: ${Object.keys(variables).length}`);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
// Auto-synthesis: Check if we should generate a summary AFTER all steps complete
|
|
388
|
+
const autoSynthesis = workflow.settings?.autoSynthesis;
|
|
389
|
+
if (autoSynthesis?.enabled) {
|
|
390
|
+
// Collect all results
|
|
391
|
+
const accumulatedResults = Object.values(stepOutputs);
|
|
392
|
+
// Check if we should trigger synthesis
|
|
393
|
+
if (parent.shouldAutoSynthesize(workflow, accumulatedResults, workflow.steps.length - 1)) {
|
|
394
|
+
const totalTokens = parent.estimateTotalTokens(accumulatedResults);
|
|
395
|
+
const logLevel = autoSynthesis.logLevel ?? 'info';
|
|
396
|
+
if (logLevel !== 'silent') {
|
|
397
|
+
console.error(`\n🤖 Auto-synthesis triggered (${totalTokens} tokens accumulated)`);
|
|
398
|
+
console.error(` Threshold: ${autoSynthesis.tokenThreshold ?? 20000} tokens`);
|
|
399
|
+
console.error(` Tool: ${autoSynthesis.synthesisTool ?? 'gemini_analyze_text'}`);
|
|
400
|
+
console.error(`📊 Generating executive summary...`);
|
|
401
|
+
console.error(` Steps to synthesize: ${workflow.steps.length}`);
|
|
402
|
+
console.error(` Variables available: ${Object.keys(variables).length}`);
|
|
403
|
+
}
|
|
404
|
+
const synthesisStep = parent.createSynthesisStep(workflow, variables, outputDir);
|
|
405
|
+
try {
|
|
406
|
+
// Execute synthesis step with retry logic
|
|
407
|
+
const maxRetries = autoSynthesis.maxRetries ?? 3;
|
|
408
|
+
let synthesisResult = '';
|
|
409
|
+
let attempt = 0;
|
|
410
|
+
while (attempt < maxRetries) {
|
|
411
|
+
try {
|
|
412
|
+
const toolResult = await parent.callTool(synthesisStep.tool, synthesisStep.input ?? {}, {
|
|
413
|
+
maxTokens: typeof synthesisStep.maxTokens === 'number'
|
|
414
|
+
? synthesisStep.maxTokens
|
|
415
|
+
: undefined,
|
|
416
|
+
skipValidation: true,
|
|
417
|
+
});
|
|
418
|
+
synthesisResult = toolResult.result;
|
|
419
|
+
console.error(`✅ Auto-synthesis executed with model: ${toolResult.modelUsed}`);
|
|
420
|
+
break; // Success
|
|
421
|
+
}
|
|
422
|
+
catch (error) {
|
|
423
|
+
attempt++;
|
|
424
|
+
if (attempt >= maxRetries) {
|
|
425
|
+
console.error(`❌ Auto-synthesis failed after ${maxRetries} attempts`);
|
|
426
|
+
throw error;
|
|
427
|
+
}
|
|
428
|
+
const backoff = 1000 * Math.pow(2, attempt - 1); // Exponential backoff
|
|
429
|
+
console.warn(`⚠️ Retrying synthesis in ${backoff}ms...`);
|
|
430
|
+
await new Promise(resolve => setTimeout(resolve, backoff));
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
// Convert synthesis result to FileReference
|
|
434
|
+
const synthesisFileRef = await parent.createFileReference(synthesisStep.name, synthesisResult, workflowId, workflowName, synthesisStep.saveToFile || false, outputDir, stepNumbers.get(synthesisStep.name), // Pass step number for filename
|
|
435
|
+
undefined // Model unknown for synthesis
|
|
436
|
+
);
|
|
437
|
+
// Store synthesis result
|
|
438
|
+
if (synthesisStep.output?.variable) {
|
|
439
|
+
variables[synthesisStep.output.variable] = synthesisFileRef;
|
|
440
|
+
stepOutputs[synthesisStep.name] = synthesisFileRef;
|
|
441
|
+
if (logLevel !== 'silent') {
|
|
442
|
+
console.error(`✅ Auto-synthesis complete! Stored FileReference in variables['${synthesisStep.output.variable}']`);
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
execution.outputs.push({
|
|
446
|
+
step: synthesisStep.name,
|
|
447
|
+
input: '[auto-synthesis]',
|
|
448
|
+
output: synthesisFileRef.summary,
|
|
449
|
+
filePath: synthesisFileRef.filePath || undefined
|
|
450
|
+
});
|
|
451
|
+
// Show synthesis result
|
|
452
|
+
if (logLevel !== 'silent') {
|
|
453
|
+
console.error(`\n${"=".repeat(80)}`);
|
|
454
|
+
console.error(`✅ AUTO-SYNTHESIS COMPLETE`);
|
|
455
|
+
console.error(`${"=".repeat(80)}`);
|
|
456
|
+
console.error(`\n${synthesisResult}\n`);
|
|
457
|
+
console.error(`${"=".repeat(80)}\n`);
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
catch (error) {
|
|
461
|
+
console.error(`⚠️ Auto-synthesis failed, continuing with full outputs:`, error);
|
|
462
|
+
// Don't throw - synthesis failure should not stop workflow
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
// Mark as completed
|
|
467
|
+
execution.status = "completed";
|
|
468
|
+
execution.endTime = new Date();
|
|
469
|
+
// Delete checkpoint on successful completion
|
|
470
|
+
if (workflow.settings?.autoSynthesis?.enabled && outputDir) {
|
|
471
|
+
await parent.deleteCheckpoint(outputDir);
|
|
472
|
+
}
|
|
473
|
+
// Workflow complete - manifest.json contains full execution details
|
|
474
|
+
console.error(`\n✅ Workflow complete! Manifest: ${path.join(outputDir, 'manifest.json')}\n`);
|
|
475
|
+
// Format output - prioritize runtime options over workflow YAML settings
|
|
476
|
+
return parent.formatOutput(execution, workflow.output?.format || "summary", options?.truncateSteps ?? workflow.output?.truncateSteps ?? true, options?.maxStepTokens ?? workflow.output?.maxStepTokens ?? 2500);
|
|
477
|
+
}
|
|
478
|
+
catch (error) {
|
|
479
|
+
execution.status = "failed";
|
|
480
|
+
execution.endTime = new Date();
|
|
481
|
+
// Manifest will reflect failed status
|
|
482
|
+
console.error(`\n❌ Workflow failed. Check manifest: ${path.join(outputDir, 'manifest.json')}\n`);
|
|
483
|
+
throw error;
|
|
484
|
+
}
|
|
485
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workflow Executor
|
|
3
|
+
* Handles the main workflow execution logic
|
|
4
|
+
* Extracted from CustomWorkflowEngine to reduce file size
|
|
5
|
+
*/
|
|
6
|
+
import * as path from 'path';
|
|
7
|
+
import { promises as fsPromises } from 'fs';
|
|
8
|
+
import { generateWorkflowId } from '../../utils/timestamp-formatter.js';
|
|
9
|
+
import { loadConfig } from '../../config.js';
|
|
10
|
+
import { WorkflowHelpers } from './WorkflowHelpers.js';
|
|
11
|
+
export class WorkflowExecutor {
|
|
12
|
+
constructor(sessionLogger, eventBus, variableInterpolator, stepParameterResolver, fileManager, outputFormatter, autoSynthesizer, callToolFn) {
|
|
13
|
+
this.sessionLogger = sessionLogger;
|
|
14
|
+
this.eventBus = eventBus;
|
|
15
|
+
this.variableInterpolator = variableInterpolator;
|
|
16
|
+
this.stepParameterResolver = stepParameterResolver;
|
|
17
|
+
this.fileManager = fileManager;
|
|
18
|
+
this.outputFormatter = outputFormatter;
|
|
19
|
+
this.autoSynthesizer = autoSynthesizer;
|
|
20
|
+
this.callToolFn = callToolFn;
|
|
21
|
+
}
|
|
22
|
+
async execute(workflow, input, options) {
|
|
23
|
+
// Generate unique workflow ID
|
|
24
|
+
const workflowId = generateWorkflowId();
|
|
25
|
+
// Setup output directory
|
|
26
|
+
const config = loadConfig();
|
|
27
|
+
const baseOutputDir = config.workflow.outputDir;
|
|
28
|
+
const outputDir = path.join(process.cwd(), baseOutputDir, workflow.name, workflowId);
|
|
29
|
+
// Initialize execution tracking
|
|
30
|
+
const execution = {
|
|
31
|
+
workflowName: workflow.name,
|
|
32
|
+
workflowId,
|
|
33
|
+
outputDir,
|
|
34
|
+
startTime: new Date(),
|
|
35
|
+
status: 'running',
|
|
36
|
+
cost: 0,
|
|
37
|
+
outputs: []
|
|
38
|
+
};
|
|
39
|
+
// Start session logging
|
|
40
|
+
const sessionId = await this.sessionLogger.startSession(`workflow-${workflow.name}`, input, workflow.name);
|
|
41
|
+
console.error(`\n📝 Session log: .workflow-sessions/${sessionId}.md`);
|
|
42
|
+
console.error(` To monitor progress: tail -f .workflow-sessions/${sessionId}.md\n`);
|
|
43
|
+
// Merge variables
|
|
44
|
+
const variables = {
|
|
45
|
+
...workflow.variables,
|
|
46
|
+
...options?.variables,
|
|
47
|
+
input,
|
|
48
|
+
query: input
|
|
49
|
+
};
|
|
50
|
+
console.error(`🔍 Workflow variables initialized:`, JSON.stringify(variables, null, 2));
|
|
51
|
+
// Calculate step numbers
|
|
52
|
+
const stepNumbers = WorkflowHelpers.calculateStepNumbers(workflow);
|
|
53
|
+
console.error(`📊 Step numbering calculated:`, Array.from(stepNumbers.entries())
|
|
54
|
+
.map(([name, num]) => `${num}: ${name}`)
|
|
55
|
+
.join(', '));
|
|
56
|
+
// Execute steps
|
|
57
|
+
const stepOutputs = {};
|
|
58
|
+
let previousOutput = input;
|
|
59
|
+
// Initialize output directory if needed
|
|
60
|
+
const needsFileOutput = workflow.steps.some((s) => s.saveToFile);
|
|
61
|
+
if (needsFileOutput) {
|
|
62
|
+
try {
|
|
63
|
+
await fsPromises.mkdir(outputDir, { recursive: true });
|
|
64
|
+
const manifest = {
|
|
65
|
+
workflowId,
|
|
66
|
+
workflowName: workflow.name,
|
|
67
|
+
startTime: new Date().toISOString(),
|
|
68
|
+
endTime: null,
|
|
69
|
+
status: 'running',
|
|
70
|
+
query: input,
|
|
71
|
+
steps: []
|
|
72
|
+
};
|
|
73
|
+
await fsPromises.writeFile(path.join(outputDir, 'manifest.json'), JSON.stringify(manifest, null, 2));
|
|
74
|
+
console.error(`📁 Workflow output directory: ${outputDir}`);
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
console.error('⚠️ Failed to initialize workflow directory:', error);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
try {
|
|
81
|
+
// Execute all steps
|
|
82
|
+
for (let i = 0; i < workflow.steps.length; i++) {
|
|
83
|
+
const step = workflow.steps[i];
|
|
84
|
+
// Delegate to step execution (will be extracted further)
|
|
85
|
+
const result = await this.executeStep(step, i, workflow, variables, stepOutputs, previousOutput, execution, options);
|
|
86
|
+
if (result) {
|
|
87
|
+
previousOutput = result.output;
|
|
88
|
+
if (result.fileRef) {
|
|
89
|
+
stepOutputs[step.name] = result.fileRef;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
execution.status = 'completed';
|
|
94
|
+
execution.endTime = new Date();
|
|
95
|
+
// End session
|
|
96
|
+
const sessionFile = await this.sessionLogger.endSession(true);
|
|
97
|
+
console.error(`\n✅ Workflow complete! Full session saved to: ${sessionFile}\n`);
|
|
98
|
+
// Format output
|
|
99
|
+
return this.outputFormatter.format(execution, workflow.output?.format || 'summary', options?.truncateSteps ?? workflow.output?.truncateSteps ?? true, options?.maxStepTokens ?? workflow.output?.maxStepTokens ?? 2500);
|
|
100
|
+
}
|
|
101
|
+
catch (error) {
|
|
102
|
+
execution.status = 'failed';
|
|
103
|
+
execution.endTime = new Date();
|
|
104
|
+
await this.sessionLogger.endSession(true);
|
|
105
|
+
throw error;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
async executeStep(step, index, workflow, variables, stepOutputs, previousOutput, execution, options) {
|
|
109
|
+
// This will be extracted further in next iteration
|
|
110
|
+
// For now, just return null to indicate "not yet implemented"
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
}
|