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.
Files changed (214) hide show
  1. package/.env.example +260 -0
  2. package/CHANGELOG.md +54 -0
  3. package/CODE_OF_CONDUCT.md +56 -0
  4. package/CONTRIBUTING.md +54 -0
  5. package/Dockerfile +36 -0
  6. package/LICENSE +644 -0
  7. package/README.md +201 -0
  8. package/SECURITY.md +95 -0
  9. package/dist/personality/komaai-expressions.js +12 -0
  10. package/dist/profiles/balanced.json +33 -0
  11. package/dist/profiles/code_focus.json +33 -0
  12. package/dist/profiles/full.json +33 -0
  13. package/dist/profiles/minimal.json +33 -0
  14. package/dist/profiles/research_power.json +33 -0
  15. package/dist/scripts/build-profiles.js +46 -0
  16. package/dist/src/application/services/focus/FocusModeRegistry.js +46 -0
  17. package/dist/src/application/services/focus/FocusTool.service.js +109 -0
  18. package/dist/src/application/services/focus/ModeRegistry.js +46 -0
  19. package/dist/src/application/services/focus/modes/focus-deep.mode.js +27 -0
  20. package/dist/src/application/services/focus/modes/status.mode.js +50 -0
  21. package/dist/src/application/services/focus/modes/tachibot-status.mode.js +50 -0
  22. package/dist/src/collaborative-orchestrator.js +391 -0
  23. package/dist/src/config/model-constants.js +188 -0
  24. package/dist/src/config/model-defaults.js +57 -0
  25. package/dist/src/config/model-preferences.js +382 -0
  26. package/dist/src/config/timeout-config.js +130 -0
  27. package/dist/src/config.js +173 -0
  28. package/dist/src/domain/interfaces/IFocusMode.js +5 -0
  29. package/dist/src/domain/interfaces/IProvider.js +6 -0
  30. package/dist/src/domain/interfaces/ITool.js +5 -0
  31. package/dist/src/focus-deep.js +245 -0
  32. package/dist/src/infrastructure/ascii/art/robots.ascii.js +16 -0
  33. package/dist/src/mcp-client.js +90 -0
  34. package/dist/src/memory/index.js +17 -0
  35. package/dist/src/memory/memory-config.js +135 -0
  36. package/dist/src/memory/memory-interface.js +174 -0
  37. package/dist/src/memory/memory-manager.js +383 -0
  38. package/dist/src/memory/providers/devlog-provider.js +385 -0
  39. package/dist/src/memory/providers/hybrid-provider.js +399 -0
  40. package/dist/src/memory/providers/local-provider.js +388 -0
  41. package/dist/src/memory/providers/mem0-provider.js +337 -0
  42. package/dist/src/modes/architect.js +477 -0
  43. package/dist/src/modes/auditor.js +362 -0
  44. package/dist/src/modes/challenger.js +841 -0
  45. package/dist/src/modes/code-reviewer.js +382 -0
  46. package/dist/src/modes/commit-guardian.js +424 -0
  47. package/dist/src/modes/documentation-writer.js +572 -0
  48. package/dist/src/modes/scout.js +587 -0
  49. package/dist/src/modes/shared/helpers/challenger-helpers.js +454 -0
  50. package/dist/src/modes/shared/helpers/index.js +17 -0
  51. package/dist/src/modes/shared/helpers/scout-helpers.js +270 -0
  52. package/dist/src/modes/shared/helpers/verifier-helpers.js +332 -0
  53. package/dist/src/modes/test-architect.js +767 -0
  54. package/dist/src/modes/verifier.js +378 -0
  55. package/dist/src/monitoring/performance-monitor.js +435 -0
  56. package/dist/src/optimization/batch-executor.js +121 -0
  57. package/dist/src/optimization/context-pruner.js +196 -0
  58. package/dist/src/optimization/cost-monitor.js +338 -0
  59. package/dist/src/optimization/index.js +65 -0
  60. package/dist/src/optimization/model-router.js +264 -0
  61. package/dist/src/optimization/result-cache.js +114 -0
  62. package/dist/src/optimization/token-optimizer.js +257 -0
  63. package/dist/src/optimization/token-tracker.js +118 -0
  64. package/dist/src/orchestrator-instructions.js +128 -0
  65. package/dist/src/orchestrator-lite.js +139 -0
  66. package/dist/src/orchestrator.js +191 -0
  67. package/dist/src/orchestrators/collaborative/interfaces/IToolExecutionEngine.js +1 -0
  68. package/dist/src/orchestrators/collaborative/interfaces/IToolExecutionStrategy.js +5 -0
  69. package/dist/src/orchestrators/collaborative/interfaces/IVisualizationRenderer.js +1 -0
  70. package/dist/src/orchestrators/collaborative/registries/ModelProviderRegistry.js +95 -0
  71. package/dist/src/orchestrators/collaborative/registries/ToolAdapterRegistry.js +64 -0
  72. package/dist/src/orchestrators/collaborative/services/tool-execution/ToolExecutionService.js +502 -0
  73. package/dist/src/orchestrators/collaborative/services/visualization/VisualizationService.js +206 -0
  74. package/dist/src/orchestrators/collaborative/types/session-types.js +5 -0
  75. package/dist/src/profiles/balanced.js +37 -0
  76. package/dist/src/profiles/code_focus.js +37 -0
  77. package/dist/src/profiles/debug_intensive.js +59 -0
  78. package/dist/src/profiles/full.js +37 -0
  79. package/dist/src/profiles/minimal.js +37 -0
  80. package/dist/src/profiles/research_code.js +59 -0
  81. package/dist/src/profiles/research_power.js +37 -0
  82. package/dist/src/profiles/types.js +5 -0
  83. package/dist/src/profiles/workflow_builder.js +53 -0
  84. package/dist/src/prompt-engineer-lite.js +78 -0
  85. package/dist/src/prompt-engineer.js +399 -0
  86. package/dist/src/reasoning-chain.js +508 -0
  87. package/dist/src/sequential-thinking.js +291 -0
  88. package/dist/src/server-diagnostic.js +74 -0
  89. package/dist/src/server-raw.js +158 -0
  90. package/dist/src/server-simple.js +58 -0
  91. package/dist/src/server.js +514 -0
  92. package/dist/src/session/session-logger.js +617 -0
  93. package/dist/src/session/session-manager.js +571 -0
  94. package/dist/src/session/session-tools.js +400 -0
  95. package/dist/src/tools/advanced-modes.js +200 -0
  96. package/dist/src/tools/claude-integration.js +356 -0
  97. package/dist/src/tools/consolidated/ai-router.js +174 -0
  98. package/dist/src/tools/consolidated/ai-tool.js +48 -0
  99. package/dist/src/tools/consolidated/brainstorm-tool.js +87 -0
  100. package/dist/src/tools/consolidated/environment-detector.js +80 -0
  101. package/dist/src/tools/consolidated/index.js +50 -0
  102. package/dist/src/tools/consolidated/search-tool.js +110 -0
  103. package/dist/src/tools/consolidated/workflow-tool.js +238 -0
  104. package/dist/src/tools/gemini-tools.js +329 -0
  105. package/dist/src/tools/grok-enhanced.js +376 -0
  106. package/dist/src/tools/grok-tools.js +299 -0
  107. package/dist/src/tools/lmstudio-tools.js +223 -0
  108. package/dist/src/tools/openai-tools.js +498 -0
  109. package/dist/src/tools/openrouter-tools.js +317 -0
  110. package/dist/src/tools/optimized-wrapper.js +204 -0
  111. package/dist/src/tools/perplexity-tools.js +294 -0
  112. package/dist/src/tools/pingpong-tool.js +343 -0
  113. package/dist/src/tools/qwen-wrapper.js +74 -0
  114. package/dist/src/tools/tool-router.js +444 -0
  115. package/dist/src/tools/unified-ai-provider.js +260 -0
  116. package/dist/src/tools/workflow-runner.js +425 -0
  117. package/dist/src/tools/workflow-validator-tool.js +107 -0
  118. package/dist/src/types.js +23 -0
  119. package/dist/src/utils/input-validator.js +130 -0
  120. package/dist/src/utils/model-router.js +91 -0
  121. package/dist/src/utils/progress-stream.js +255 -0
  122. package/dist/src/utils/provider-router.js +88 -0
  123. package/dist/src/utils/smart-api-client.js +146 -0
  124. package/dist/src/utils/table-builder.js +218 -0
  125. package/dist/src/utils/timestamp-formatter.js +134 -0
  126. package/dist/src/utils/tool-compressor.js +257 -0
  127. package/dist/src/utils/tool-config.js +201 -0
  128. package/dist/src/validators/dependency-graph-validator.js +147 -0
  129. package/dist/src/validators/interpolation-validator.js +222 -0
  130. package/dist/src/validators/output-usage-validator.js +151 -0
  131. package/dist/src/validators/syntax-validator.js +102 -0
  132. package/dist/src/validators/tool-registry-validator.js +123 -0
  133. package/dist/src/validators/tool-types.js +97 -0
  134. package/dist/src/validators/types.js +8 -0
  135. package/dist/src/validators/workflow-validator.js +134 -0
  136. package/dist/src/visualizer-lite.js +42 -0
  137. package/dist/src/visualizer.js +179 -0
  138. package/dist/src/workflows/circuit-breaker.js +199 -0
  139. package/dist/src/workflows/custom-workflows.js +451 -0
  140. package/dist/src/workflows/engine/AutoSynthesizer.js +97 -0
  141. package/dist/src/workflows/engine/StepParameterResolver.js +74 -0
  142. package/dist/src/workflows/engine/VariableInterpolator.js +123 -0
  143. package/dist/src/workflows/engine/WorkflowDiscovery.js +125 -0
  144. package/dist/src/workflows/engine/WorkflowExecutionEngine.js +485 -0
  145. package/dist/src/workflows/engine/WorkflowExecutor.js +113 -0
  146. package/dist/src/workflows/engine/WorkflowFileManager.js +244 -0
  147. package/dist/src/workflows/engine/WorkflowHelpers.js +114 -0
  148. package/dist/src/workflows/engine/WorkflowOutputFormatter.js +83 -0
  149. package/dist/src/workflows/engine/events/WorkflowEventBus.js +132 -0
  150. package/dist/src/workflows/engine/events/interfaces/IEventBus.js +5 -0
  151. package/dist/src/workflows/engine/handlers/ErrorRecoveryHandler.js +162 -0
  152. package/dist/src/workflows/engine/handlers/PromptEnhancementHandler.js +115 -0
  153. package/dist/src/workflows/engine/handlers/SessionPersistenceHandler.js +167 -0
  154. package/dist/src/workflows/engine/handlers/StepExecutionHandler.js +231 -0
  155. package/dist/src/workflows/engine/handlers/ToolInvocationHandler.js +46 -0
  156. package/dist/src/workflows/engine/interfaces/IAutoSynthesizer.js +5 -0
  157. package/dist/src/workflows/engine/interfaces/IStepParameterResolver.js +5 -0
  158. package/dist/src/workflows/engine/interfaces/IVariableInterpolator.js +5 -0
  159. package/dist/src/workflows/engine/interfaces/IWorkflowDiscovery.js +4 -0
  160. package/dist/src/workflows/engine/interfaces/IWorkflowFileManager.js +5 -0
  161. package/dist/src/workflows/engine/interfaces/IWorkflowOutputFormatter.js +5 -0
  162. package/dist/src/workflows/engine/state/WorkflowStateMachine.js +194 -0
  163. package/dist/src/workflows/engine/state/interfaces/IStateMachine.js +17 -0
  164. package/dist/src/workflows/fallback-strategies.js +373 -0
  165. package/dist/src/workflows/message-queue.js +455 -0
  166. package/dist/src/workflows/model-router.js +189 -0
  167. package/dist/src/workflows/orchestrator-examples.js +174 -0
  168. package/dist/src/workflows/orchestrator-integration.js +200 -0
  169. package/dist/src/workflows/self-healing.js +524 -0
  170. package/dist/src/workflows/tool-mapper.js +407 -0
  171. package/dist/src/workflows/tool-orchestrator.js +796 -0
  172. package/dist/src/workflows/workflow-engine.js +573 -0
  173. package/dist/src/workflows/workflow-parser.js +283 -0
  174. package/dist/src/workflows/workflow-types.js +95 -0
  175. package/dist/src/workflows.js +568 -0
  176. package/dist/test-workflow-file-output.js +93 -0
  177. package/docs/API_KEYS.md +570 -0
  178. package/docs/CLAUDE_CODE_SETUP.md +181 -0
  179. package/docs/CLAUDE_DESKTOP_MANUAL.md +127 -0
  180. package/docs/CONFIGURATION.md +745 -0
  181. package/docs/FOCUS_MODES.md +240 -0
  182. package/docs/INSTALLATION_BOTH.md +145 -0
  183. package/docs/TERMS.md +352 -0
  184. package/docs/TOOLS_REFERENCE.md +1622 -0
  185. package/docs/TOOL_PARAMETERS.md +496 -0
  186. package/docs/TOOL_PROFILES.md +236 -0
  187. package/docs/WORKFLOWS.md +987 -0
  188. package/docs/WORKFLOW_OUTPUT.md +198 -0
  189. package/docs/WORKFLOW_PROGRESS_TRACKING.md +305 -0
  190. package/docs/workflows/design-brainstorm.md +335 -0
  191. package/package.json +97 -0
  192. package/profiles/balanced.json +37 -0
  193. package/profiles/code_focus.json +37 -0
  194. package/profiles/debug_intensive.json +34 -0
  195. package/profiles/full.json +37 -0
  196. package/profiles/minimal.json +37 -0
  197. package/profiles/research_power.json +37 -0
  198. package/profiles/workflow_builder.json +37 -0
  199. package/smithery.yaml +66 -0
  200. package/start.sh +8 -0
  201. package/tools.config.json +81 -0
  202. package/tsconfig.json +18 -0
  203. package/workflows/accessibility-code-audit.yaml +92 -0
  204. package/workflows/code-architecture-review.yaml +202 -0
  205. package/workflows/code-review.yaml +142 -0
  206. package/workflows/core/iterative-problem-solver.yaml +283 -0
  207. package/workflows/creative-brainstorm-yaml.yaml +215 -0
  208. package/workflows/pingpong.yaml +141 -0
  209. package/workflows/system/README.md +412 -0
  210. package/workflows/system/challenger.yaml +175 -0
  211. package/workflows/system/scout.yaml +164 -0
  212. package/workflows/system/verifier.yaml +133 -0
  213. package/workflows/ultra-creative-brainstorm.yaml +318 -0
  214. package/workflows/ux-research-flow.yaml +92 -0
@@ -0,0 +1,162 @@
1
+ /**
2
+ * Error Recovery Handler
3
+ * Implements circuit breaker pattern for failing steps
4
+ * Tracks failure rates and applies recovery strategies
5
+ */
6
+ import { EventEmitter } from 'events';
7
+ import { WorkflowEvents } from '../events/WorkflowEventBus.js';
8
+ var CircuitState;
9
+ (function (CircuitState) {
10
+ CircuitState["CLOSED"] = "CLOSED";
11
+ CircuitState["OPEN"] = "OPEN";
12
+ CircuitState["HALF_OPEN"] = "HALF_OPEN"; // Testing if service recovered
13
+ })(CircuitState || (CircuitState = {}));
14
+ export class ErrorRecoveryHandler extends EventEmitter {
15
+ constructor(eventBus) {
16
+ super();
17
+ this.failureRecords = new Map();
18
+ this.circuitStates = new Map();
19
+ // Circuit breaker thresholds
20
+ this.failureThreshold = 3; // Open circuit after 3 failures
21
+ this.recoveryTimeout = 30000; // 30 seconds
22
+ this.halfOpenAttempts = 1; // Allow 1 attempt in half-open state
23
+ this.eventBus = eventBus;
24
+ // Subscribe to failure events
25
+ this.eventBus.subscribe(WorkflowEvents.STEP_FAILED, this.handleStepFailure.bind(this));
26
+ this.eventBus.subscribe(WorkflowEvents.STEP_COMPLETED, this.handleStepSuccess.bind(this));
27
+ this.eventBus.subscribe(WorkflowEvents.TOOL_FAILURE, this.handleToolFailure.bind(this));
28
+ }
29
+ async handleStepFailure(event) {
30
+ const { stepName, error } = event;
31
+ // Update failure record
32
+ const record = this.failureRecords.get(stepName) || {
33
+ stepName,
34
+ failures: 0,
35
+ lastFailureTime: 0,
36
+ consecutiveFailures: 0
37
+ };
38
+ record.failures++;
39
+ record.consecutiveFailures++;
40
+ record.lastFailureTime = Date.now();
41
+ this.failureRecords.set(stepName, record);
42
+ console.error(`[ErrorRecovery] Step ${stepName} failed (${record.consecutiveFailures} consecutive failures)`);
43
+ // Check if circuit should open
44
+ if (record.consecutiveFailures >= this.failureThreshold) {
45
+ await this.openCircuit(stepName);
46
+ }
47
+ // Emit recovery suggestions
48
+ const suggestions = this.getRecoverySuggestions(stepName, error);
49
+ await this.eventBus.publish('workflow.error.recovery_suggestions', {
50
+ stepName,
51
+ suggestions
52
+ });
53
+ }
54
+ async handleStepSuccess(event) {
55
+ const { stepName } = event;
56
+ // Reset consecutive failures
57
+ const record = this.failureRecords.get(stepName);
58
+ if (record) {
59
+ record.consecutiveFailures = 0;
60
+ this.failureRecords.set(stepName, record);
61
+ }
62
+ // Close circuit if it was open
63
+ const state = this.circuitStates.get(stepName);
64
+ if (state === CircuitState.HALF_OPEN) {
65
+ await this.closeCircuit(stepName);
66
+ }
67
+ }
68
+ async handleToolFailure(event) {
69
+ const { stepName, tool, error } = event;
70
+ console.error(`[ErrorRecovery] Tool ${tool} failed for step ${stepName}`);
71
+ // Check if we should attempt recovery
72
+ const shouldRecover = await this.shouldAttemptRecovery(stepName);
73
+ if (shouldRecover) {
74
+ await this.eventBus.publish(WorkflowEvents.ERROR_RECOVERED, {
75
+ stepName,
76
+ tool,
77
+ recoveryAction: 'retry'
78
+ });
79
+ }
80
+ }
81
+ async openCircuit(stepName) {
82
+ this.circuitStates.set(stepName, CircuitState.OPEN);
83
+ console.error(`[ErrorRecovery] Circuit OPEN for step ${stepName} - rejecting requests`);
84
+ this.emit('circuit:opened', { stepName });
85
+ // Schedule half-open transition
86
+ setTimeout(() => {
87
+ this.transitionToHalfOpen(stepName);
88
+ }, this.recoveryTimeout);
89
+ }
90
+ async closeCircuit(stepName) {
91
+ this.circuitStates.set(stepName, CircuitState.CLOSED);
92
+ console.error(`[ErrorRecovery] Circuit CLOSED for step ${stepName} - normal operation`);
93
+ this.emit('circuit:closed', { stepName });
94
+ }
95
+ transitionToHalfOpen(stepName) {
96
+ const state = this.circuitStates.get(stepName);
97
+ if (state === CircuitState.OPEN) {
98
+ this.circuitStates.set(stepName, CircuitState.HALF_OPEN);
99
+ console.error(`[ErrorRecovery] Circuit HALF_OPEN for step ${stepName} - testing recovery`);
100
+ this.emit('circuit:half_open', { stepName });
101
+ }
102
+ }
103
+ async shouldAttemptRecovery(stepName) {
104
+ const state = this.circuitStates.get(stepName) || CircuitState.CLOSED;
105
+ // Don't attempt recovery if circuit is open
106
+ if (state === CircuitState.OPEN) {
107
+ return false;
108
+ }
109
+ // In half-open state, allow limited attempts
110
+ if (state === CircuitState.HALF_OPEN) {
111
+ return true;
112
+ }
113
+ // In closed state, always attempt recovery
114
+ return true;
115
+ }
116
+ getRecoverySuggestions(stepName, error) {
117
+ const suggestions = [];
118
+ const errorMessage = error.message.toLowerCase();
119
+ // Rate limiting
120
+ if (errorMessage.includes('rate limit') || errorMessage.includes('429')) {
121
+ suggestions.push('Wait for rate limit to reset');
122
+ suggestions.push('Consider using a different model or provider');
123
+ }
124
+ // Timeout errors
125
+ if (errorMessage.includes('timeout') || errorMessage.includes('timed out')) {
126
+ suggestions.push('Increase maxTokens or timeout settings');
127
+ suggestions.push('Simplify the prompt or split into smaller steps');
128
+ }
129
+ // Model errors
130
+ if (errorMessage.includes('model') || errorMessage.includes('404')) {
131
+ suggestions.push('Check if model name is correct');
132
+ suggestions.push('Try a different model or fallback option');
133
+ }
134
+ // Generic suggestions
135
+ if (suggestions.length === 0) {
136
+ suggestions.push('Review step configuration');
137
+ suggestions.push('Check input data validity');
138
+ suggestions.push('Enable retry with backoff');
139
+ }
140
+ return suggestions;
141
+ }
142
+ /**
143
+ * Get circuit state for a step
144
+ */
145
+ getCircuitState(stepName) {
146
+ return this.circuitStates.get(stepName) || CircuitState.CLOSED;
147
+ }
148
+ /**
149
+ * Get failure statistics for a step
150
+ */
151
+ getFailureStats(stepName) {
152
+ return this.failureRecords.get(stepName) || null;
153
+ }
154
+ /**
155
+ * Reset circuit breaker for a step
156
+ */
157
+ resetCircuit(stepName) {
158
+ this.circuitStates.delete(stepName);
159
+ this.failureRecords.delete(stepName);
160
+ console.error(`[ErrorRecovery] Circuit reset for step ${stepName}`);
161
+ }
162
+ }
@@ -0,0 +1,115 @@
1
+ /**
2
+ * Prompt Enhancement Handler
3
+ * Wraps PromptEngineer to enhance step prompts before tool invocation
4
+ * Subscribes to workflow.tool.before_invoke events
5
+ */
6
+ import { WorkflowEvents } from '../events/WorkflowEventBus.js';
7
+ import { PromptEngineer } from '../../../prompt-engineer.js';
8
+ export class PromptEnhancementHandler {
9
+ constructor(eventBus) {
10
+ this.enhancedSteps = new Set(); // Track enhanced steps to prevent double-enhancement
11
+ this.eventBus = eventBus;
12
+ this.promptEngineer = new PromptEngineer();
13
+ // Subscribe to before_invoke events
14
+ this.eventBus.subscribe(WorkflowEvents.TOOL_BEFORE_INVOKE, this.handleBeforeInvoke.bind(this));
15
+ }
16
+ async handleBeforeInvoke(event) {
17
+ const { tool, input, context } = event;
18
+ const { step } = context;
19
+ // Check if already enhanced (prevent double-enhancement)
20
+ if (this.enhancedSteps.has(step.name)) {
21
+ return;
22
+ }
23
+ // Check if step has promptTechnique specified
24
+ const technique = step.promptTechnique;
25
+ if (!technique) {
26
+ // No enhancement needed
27
+ return;
28
+ }
29
+ // Mark as enhanced
30
+ this.enhancedSteps.add(step.name);
31
+ try {
32
+ // Extract the query/prompt from input
33
+ const query = this.extractQuery(input);
34
+ if (!query) {
35
+ console.error(`[PromptEnhancement] No query found in input for step ${step.name}`);
36
+ return;
37
+ }
38
+ // Get accumulated results for context (legacy)
39
+ const previousResults = event.context.accumulatedResults || [];
40
+ // Build EnhancementContext (NEW - Phase 3)
41
+ const enhancementContext = {
42
+ stepNumber: context.stepIndex !== undefined ? context.stepIndex + 1 : undefined,
43
+ totalSteps: context.totalSteps,
44
+ workflowName: context.workflowName,
45
+ previousSteps: previousResults.map((result, idx) => ({
46
+ name: result.tool || `step-${idx}`, // ToolResult has 'tool' not 'step'
47
+ output: result.output,
48
+ technique: undefined // Not tracked yet
49
+ })),
50
+ workflowVariables: context.variables,
51
+ targetModel: tool
52
+ };
53
+ // Apply prompt engineering technique with enhanced context
54
+ const enhancedPrompt = this.promptEngineer.applyTechnique(tool, technique, query, previousResults, // Legacy - still pass for backwards compat
55
+ enhancementContext // NEW - rich context
56
+ );
57
+ // Update the input with enhanced prompt
58
+ this.updateInputWithEnhancedPrompt(input, enhancedPrompt);
59
+ // Log enhancement
60
+ const description = this.promptEngineer.getTechniqueDescription(technique);
61
+ console.error(`[PromptEnhancement] Applied "${description}" to ${step.name} (${tool})`);
62
+ // Publish enhancement event
63
+ await this.eventBus.publish('workflow.prompt.enhanced', {
64
+ stepName: step.name,
65
+ technique,
66
+ originalQuery: query.substring(0, 100),
67
+ enhancedLength: enhancedPrompt.length
68
+ });
69
+ }
70
+ catch (error) {
71
+ console.error(`[PromptEnhancement] Error enhancing prompt for ${step.name}:`, error);
72
+ // Non-fatal - continue with original prompt
73
+ }
74
+ }
75
+ /**
76
+ * Extract query/prompt from various input formats
77
+ */
78
+ extractQuery(input) {
79
+ if (typeof input === 'string') {
80
+ return input;
81
+ }
82
+ // Try common field names
83
+ const record = input;
84
+ const query = record.query ||
85
+ record.prompt ||
86
+ record.thought ||
87
+ record.problem ||
88
+ record.topic ||
89
+ record.content;
90
+ return typeof query === 'string' ? query : null;
91
+ }
92
+ /**
93
+ * Update input object with enhanced prompt
94
+ */
95
+ updateInputWithEnhancedPrompt(input, enhancedPrompt) {
96
+ if (typeof input === 'string') {
97
+ // Can't modify string input - this shouldn't happen as we'd extract query first
98
+ return;
99
+ }
100
+ const record = input;
101
+ // Update the field that contained the query
102
+ if (record.query !== undefined)
103
+ record.query = enhancedPrompt;
104
+ else if (record.prompt !== undefined)
105
+ record.prompt = enhancedPrompt;
106
+ else if (record.thought !== undefined)
107
+ record.thought = enhancedPrompt;
108
+ else if (record.problem !== undefined)
109
+ record.problem = enhancedPrompt;
110
+ else if (record.topic !== undefined)
111
+ record.topic = enhancedPrompt;
112
+ else if (record.content !== undefined)
113
+ record.content = enhancedPrompt;
114
+ }
115
+ }
@@ -0,0 +1,167 @@
1
+ /**
2
+ * Session Persistence Handler
3
+ * Handles workflow session checkpointing and restoration
4
+ * Subscribes to state change events for automatic persistence
5
+ */
6
+ import * as fs from 'fs';
7
+ import * as path from 'path';
8
+ import { WorkflowEvents } from '../events/WorkflowEventBus.js';
9
+ export class SessionPersistenceHandler {
10
+ constructor(eventBus, options = {}) {
11
+ this.eventBus = eventBus;
12
+ this.checkpointDir = options.checkpointDir || './.workflow-checkpoints';
13
+ this.autoSaveEnabled = options.autoSave !== false; // Default: true
14
+ // Ensure checkpoint directory exists
15
+ this.ensureCheckpointDir();
16
+ // Subscribe to events for auto-save
17
+ if (this.autoSaveEnabled) {
18
+ this.setupAutoSave();
19
+ }
20
+ }
21
+ ensureCheckpointDir() {
22
+ if (!fs.existsSync(this.checkpointDir)) {
23
+ fs.mkdirSync(this.checkpointDir, { recursive: true });
24
+ console.error(`[SessionPersistence] Created checkpoint directory: ${this.checkpointDir}`);
25
+ }
26
+ }
27
+ setupAutoSave() {
28
+ // Save checkpoint after each step completes
29
+ this.eventBus.subscribe(WorkflowEvents.STEP_COMPLETED, this.handleStepCompleted.bind(this));
30
+ // Save checkpoint when workflow completes
31
+ this.eventBus.subscribe(WorkflowEvents.WORKFLOW_COMPLETED, this.handleWorkflowCompleted.bind(this));
32
+ // Save checkpoint before failure
33
+ this.eventBus.subscribe(WorkflowEvents.WORKFLOW_FAILED, this.handleWorkflowFailed.bind(this));
34
+ }
35
+ async handleStepCompleted(event) {
36
+ // Auto-save checkpoint after each step
37
+ console.error(`[SessionPersistence] Auto-saving checkpoint after step: ${event.stepName}`);
38
+ // Publish checkpoint event
39
+ await this.eventBus.publish(WorkflowEvents.SESSION_CHECKPOINT, {
40
+ stepName: event.stepName,
41
+ reason: 'step_completed'
42
+ });
43
+ }
44
+ async handleWorkflowCompleted(event) {
45
+ console.error(`[SessionPersistence] Workflow ${event.workflowName} completed - saving final checkpoint`);
46
+ await this.eventBus.publish(WorkflowEvents.SESSION_CHECKPOINT, {
47
+ workflowId: event.workflowId,
48
+ reason: 'workflow_completed'
49
+ });
50
+ }
51
+ async handleWorkflowFailed(event) {
52
+ console.error(`[SessionPersistence] Workflow ${event.workflowName} failed - saving error checkpoint`);
53
+ await this.eventBus.publish(WorkflowEvents.SESSION_CHECKPOINT, {
54
+ workflowId: event.workflowId,
55
+ reason: 'workflow_failed',
56
+ error: event.error.message
57
+ });
58
+ }
59
+ /**
60
+ * Save workflow checkpoint to disk
61
+ */
62
+ async saveCheckpoint(checkpoint) {
63
+ const filename = this.getCheckpointFilename(checkpoint.workflowId);
64
+ const filepath = path.join(this.checkpointDir, filename);
65
+ try {
66
+ const data = JSON.stringify(checkpoint, null, 2);
67
+ await fs.promises.writeFile(filepath, data, 'utf-8');
68
+ console.error(`[SessionPersistence] Checkpoint saved: ${filepath}`);
69
+ return filepath;
70
+ }
71
+ catch (error) {
72
+ console.error(`[SessionPersistence] Error saving checkpoint:`, error);
73
+ throw error;
74
+ }
75
+ }
76
+ /**
77
+ * Load workflow checkpoint from disk
78
+ */
79
+ async loadCheckpoint(workflowId) {
80
+ const filename = this.getCheckpointFilename(workflowId);
81
+ const filepath = path.join(this.checkpointDir, filename);
82
+ try {
83
+ if (!fs.existsSync(filepath)) {
84
+ return null;
85
+ }
86
+ const data = await fs.promises.readFile(filepath, 'utf-8');
87
+ const checkpoint = JSON.parse(data);
88
+ console.error(`[SessionPersistence] Checkpoint loaded: ${filepath}`);
89
+ // Publish restore event
90
+ await this.eventBus.publish(WorkflowEvents.SESSION_RESTORED, {
91
+ workflowId: checkpoint.workflowId,
92
+ workflowName: checkpoint.workflowName,
93
+ state: checkpoint.state
94
+ });
95
+ return checkpoint;
96
+ }
97
+ catch (error) {
98
+ console.error(`[SessionPersistence] Error loading checkpoint:`, error);
99
+ return null;
100
+ }
101
+ }
102
+ /**
103
+ * List all checkpoints for a workflow
104
+ */
105
+ listCheckpoints(workflowName) {
106
+ try {
107
+ const files = fs.readdirSync(this.checkpointDir);
108
+ if (workflowName) {
109
+ return files.filter((f) => f.includes(workflowName) && f.endsWith('.json'));
110
+ }
111
+ return files.filter((f) => f.endsWith('.json'));
112
+ }
113
+ catch (error) {
114
+ console.error(`[SessionPersistence] Error listing checkpoints:`, error);
115
+ return [];
116
+ }
117
+ }
118
+ /**
119
+ * Delete checkpoint file
120
+ */
121
+ async deleteCheckpoint(workflowId) {
122
+ const filename = this.getCheckpointFilename(workflowId);
123
+ const filepath = path.join(this.checkpointDir, filename);
124
+ try {
125
+ if (fs.existsSync(filepath)) {
126
+ await fs.promises.unlink(filepath);
127
+ console.error(`[SessionPersistence] Checkpoint deleted: ${filepath}`);
128
+ return true;
129
+ }
130
+ return false;
131
+ }
132
+ catch (error) {
133
+ console.error(`[SessionPersistence] Error deleting checkpoint:`, error);
134
+ return false;
135
+ }
136
+ }
137
+ /**
138
+ * Get checkpoint filename for workflow ID
139
+ */
140
+ getCheckpointFilename(workflowId) {
141
+ return `checkpoint-${workflowId}.json`;
142
+ }
143
+ /**
144
+ * Clean up old checkpoints (older than specified days)
145
+ */
146
+ async cleanupOldCheckpoints(daysOld = 7) {
147
+ const cutoffTime = Date.now() - daysOld * 24 * 60 * 60 * 1000;
148
+ let deletedCount = 0;
149
+ try {
150
+ const files = this.listCheckpoints();
151
+ for (const file of files) {
152
+ const filepath = path.join(this.checkpointDir, file);
153
+ const stats = fs.statSync(filepath);
154
+ if (stats.mtimeMs < cutoffTime) {
155
+ await fs.promises.unlink(filepath);
156
+ deletedCount++;
157
+ }
158
+ }
159
+ console.error(`[SessionPersistence] Cleaned up ${deletedCount} old checkpoints (>${daysOld} days)`);
160
+ return deletedCount;
161
+ }
162
+ catch (error) {
163
+ console.error(`[SessionPersistence] Error cleaning up checkpoints:`, error);
164
+ return deletedCount;
165
+ }
166
+ }
167
+ }
@@ -0,0 +1,231 @@
1
+ /**
2
+ * Step Execution Handler
3
+ * Subscribes to step.ready events and executes workflow steps
4
+ *
5
+ * Phase 1: Event infrastructure and state management
6
+ * Phase 2: Will integrate with actual tool execution
7
+ */
8
+ import { WorkflowEvents } from '../events/WorkflowEventBus.js';
9
+ import { WorkflowState } from '../state/interfaces/IStateMachine.js';
10
+ export class StepExecutionHandler {
11
+ constructor(eventBus, stateMachine) {
12
+ this.activeExecutions = new Map();
13
+ this.eventBus = eventBus;
14
+ this.stateMachine = stateMachine;
15
+ // Subscribe to step ready events
16
+ this.eventBus.subscribe(WorkflowEvents.STEP_READY, this.handleStepReady.bind(this));
17
+ }
18
+ async handleStepReady(context) {
19
+ const { step, workflowId, stepIndex, totalSteps } = context;
20
+ try {
21
+ // Transition to executing state
22
+ this.stateMachine.transition(WorkflowState.STEP_EXECUTING, {
23
+ workflowId,
24
+ workflowName: context.workflowName,
25
+ currentStep: step.name,
26
+ stepIndex,
27
+ totalSteps
28
+ });
29
+ // Check if step should be skipped
30
+ if (await this.shouldSkipStep(step, context)) {
31
+ await this.eventBus.publish(WorkflowEvents.STEP_SKIPPED, {
32
+ stepName: step.name,
33
+ reason: 'condition not met'
34
+ });
35
+ // Transition back to running
36
+ this.stateMachine.transition(WorkflowState.RUNNING, {
37
+ workflowId,
38
+ workflowName: context.workflowName
39
+ });
40
+ return;
41
+ }
42
+ // Execute step with retry logic
43
+ const result = await this.executeStepWithRetry(context);
44
+ // Publish completion event
45
+ await this.eventBus.publish(WorkflowEvents.STEP_COMPLETED, result);
46
+ // Transition back to running
47
+ this.stateMachine.transition(WorkflowState.RUNNING, {
48
+ workflowId,
49
+ workflowName: context.workflowName
50
+ });
51
+ }
52
+ catch (error) {
53
+ // Handle step failure
54
+ await this.handleStepFailure(context, error);
55
+ }
56
+ }
57
+ async executeStepWithRetry(context) {
58
+ const { step, workflowId } = context;
59
+ const maxAttempts = step.retry?.attempts || 1;
60
+ const backoff = step.retry?.backoff || 1000;
61
+ let lastError;
62
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
63
+ try {
64
+ if (attempt > 1) {
65
+ // Transition to retrying state
66
+ this.stateMachine.transition(WorkflowState.RETRYING, {
67
+ workflowId,
68
+ workflowName: context.workflowName,
69
+ currentStep: step.name,
70
+ metadata: {
71
+ retryAttempt: attempt,
72
+ maxRetries: maxAttempts
73
+ }
74
+ });
75
+ // Wait with exponential backoff
76
+ await this.delay(backoff * Math.pow(2, attempt - 2));
77
+ }
78
+ // Execute the step
79
+ const result = await this.executeStep(context);
80
+ return result;
81
+ }
82
+ catch (error) {
83
+ lastError = error;
84
+ console.error(`[StepExecutionHandler] Attempt ${attempt}/${maxAttempts} failed:`, error);
85
+ // If this was the last attempt, throw
86
+ if (attempt === maxAttempts) {
87
+ throw lastError;
88
+ }
89
+ }
90
+ }
91
+ // Should never reach here, but TypeScript requires it
92
+ throw lastError || new Error('Step execution failed');
93
+ }
94
+ async executeStep(context) {
95
+ const { step } = context;
96
+ const startTime = Date.now();
97
+ // Create abort controller for this execution
98
+ const abortController = new AbortController();
99
+ this.activeExecutions.set(step.name, abortController);
100
+ try {
101
+ // Publish before invoke event (for PromptEnhancementHandler)
102
+ await this.eventBus.publish(WorkflowEvents.TOOL_BEFORE_INVOKE, {
103
+ stepName: step.name,
104
+ tool: step.tool,
105
+ input: step.input,
106
+ context
107
+ });
108
+ // TODO Phase 2: Integrate with actual tool execution via tool-mapper.ts
109
+ // For now, just simulate execution
110
+ const mockResult = {
111
+ output: `[Phase 1] Simulated execution of ${step.tool}`,
112
+ tokensUsed: 0,
113
+ cost: 0
114
+ };
115
+ // Publish tool success event
116
+ await this.eventBus.publish(WorkflowEvents.TOOL_SUCCESS, {
117
+ stepName: step.name,
118
+ tool: step.tool,
119
+ result: mockResult
120
+ });
121
+ const duration = Date.now() - startTime;
122
+ return {
123
+ stepName: step.name,
124
+ success: true,
125
+ output: mockResult.output,
126
+ duration,
127
+ tokensUsed: mockResult.tokensUsed,
128
+ cost: mockResult.cost
129
+ };
130
+ }
131
+ catch (error) {
132
+ const duration = Date.now() - startTime;
133
+ // Publish tool failure event
134
+ await this.eventBus.publish(WorkflowEvents.TOOL_FAILURE, {
135
+ stepName: step.name,
136
+ tool: step.tool,
137
+ error
138
+ });
139
+ throw error;
140
+ }
141
+ finally {
142
+ this.activeExecutions.delete(step.name);
143
+ }
144
+ }
145
+ async shouldSkipStep(step, context) {
146
+ // Check condition.skip flag
147
+ if (step.condition?.skip) {
148
+ return true;
149
+ }
150
+ // Check condition.if expression
151
+ if (step.condition?.if) {
152
+ // Simple condition evaluation (can be enhanced)
153
+ const conditionMet = this.evaluateCondition(step.condition.if, context.variables);
154
+ return !conditionMet;
155
+ }
156
+ return false;
157
+ }
158
+ evaluateCondition(condition, variables) {
159
+ // Simple equality check: "variable == value"
160
+ const match = condition.match(/^(\w+)\s*==\s*(.+)$/);
161
+ if (match) {
162
+ const [, variable, expectedValue] = match;
163
+ const actualValue = variables[variable];
164
+ return String(actualValue).trim() === expectedValue.trim().replace(/['"]/, '');
165
+ }
166
+ // Default: check if variable exists and is truthy
167
+ return !!variables[condition];
168
+ }
169
+ async handleStepFailure(context, error) {
170
+ const { step, workflowId } = context;
171
+ // Check if step allows failure
172
+ const failOnError = step.condition?.failOnError !== false;
173
+ if (failOnError) {
174
+ // Publish step failed event
175
+ await this.eventBus.publish(WorkflowEvents.STEP_FAILED, {
176
+ stepName: step.name,
177
+ error,
178
+ fatal: true
179
+ });
180
+ // Transition to failed state
181
+ this.stateMachine.transition(WorkflowState.FAILED, {
182
+ workflowId,
183
+ workflowName: context.workflowName,
184
+ currentStep: step.name,
185
+ error
186
+ });
187
+ // Publish workflow failed event
188
+ await this.eventBus.publish(WorkflowEvents.WORKFLOW_FAILED, {
189
+ workflowId,
190
+ workflowName: context.workflowName,
191
+ error,
192
+ failedAt: step.name
193
+ });
194
+ }
195
+ else {
196
+ // Non-fatal failure - continue workflow
197
+ await this.eventBus.publish(WorkflowEvents.STEP_FAILED, {
198
+ stepName: step.name,
199
+ error,
200
+ fatal: false
201
+ });
202
+ // Transition back to running
203
+ this.stateMachine.transition(WorkflowState.RUNNING, {
204
+ workflowId,
205
+ workflowName: context.workflowName
206
+ });
207
+ }
208
+ }
209
+ delay(ms) {
210
+ return new Promise((resolve) => setTimeout(resolve, ms));
211
+ }
212
+ /**
213
+ * Abort a specific step execution
214
+ */
215
+ abortStep(stepName) {
216
+ const controller = this.activeExecutions.get(stepName);
217
+ if (controller) {
218
+ controller.abort();
219
+ this.activeExecutions.delete(stepName);
220
+ }
221
+ }
222
+ /**
223
+ * Abort all active step executions
224
+ */
225
+ abortAll() {
226
+ for (const controller of this.activeExecutions.values()) {
227
+ controller.abort();
228
+ }
229
+ this.activeExecutions.clear();
230
+ }
231
+ }