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,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool Invocation Handler
|
|
3
|
+
* Handles actual tool execution via tool-mapper
|
|
4
|
+
* Subscribes to workflow.tool.invoke events
|
|
5
|
+
*/
|
|
6
|
+
import { WorkflowEvents } from '../events/WorkflowEventBus.js';
|
|
7
|
+
export class ToolInvocationHandler {
|
|
8
|
+
constructor(eventBus) {
|
|
9
|
+
this.eventBus = eventBus;
|
|
10
|
+
// Subscribe to tool invoke events
|
|
11
|
+
this.eventBus.subscribe(WorkflowEvents.TOOL_INVOKED, this.handleToolInvoke.bind(this));
|
|
12
|
+
}
|
|
13
|
+
async handleToolInvoke(event) {
|
|
14
|
+
const { stepName, tool, input, options } = event;
|
|
15
|
+
const startTime = Date.now();
|
|
16
|
+
try {
|
|
17
|
+
// Dynamic import to avoid circular dependencies
|
|
18
|
+
const { executeWorkflowTool } = await import('../../tool-mapper.js');
|
|
19
|
+
console.error(`[ToolInvocation] Executing ${tool} for step ${stepName}`);
|
|
20
|
+
// Execute the tool
|
|
21
|
+
const toolResult = await executeWorkflowTool(tool, input, options);
|
|
22
|
+
const duration = Date.now() - startTime;
|
|
23
|
+
// Publish success event
|
|
24
|
+
await this.eventBus.publish(WorkflowEvents.TOOL_SUCCESS, {
|
|
25
|
+
stepName,
|
|
26
|
+
success: true,
|
|
27
|
+
result: toolResult.result,
|
|
28
|
+
modelUsed: toolResult.modelUsed,
|
|
29
|
+
duration,
|
|
30
|
+
tokensUsed: 0, // TODO: Extract from toolResult if available
|
|
31
|
+
cost: 0 // TODO: Calculate based on model and tokens
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
catch (error) {
|
|
35
|
+
const duration = Date.now() - startTime;
|
|
36
|
+
console.error(`[ToolInvocation] Error executing ${tool} for step ${stepName}:`, error);
|
|
37
|
+
// Publish failure event
|
|
38
|
+
await this.eventBus.publish(WorkflowEvents.TOOL_FAILURE, {
|
|
39
|
+
stepName,
|
|
40
|
+
success: false,
|
|
41
|
+
error: error,
|
|
42
|
+
duration
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workflow State Machine
|
|
3
|
+
* Manages workflow state transitions with event emission
|
|
4
|
+
* Follows CircuitBreaker pattern (extends EventEmitter)
|
|
5
|
+
*/
|
|
6
|
+
import { EventEmitter } from 'events';
|
|
7
|
+
import { WorkflowState } from './interfaces/IStateMachine.js';
|
|
8
|
+
export class WorkflowStateMachine extends EventEmitter {
|
|
9
|
+
constructor() {
|
|
10
|
+
super();
|
|
11
|
+
this.currentState = WorkflowState.INITIALIZED;
|
|
12
|
+
this.stateHistory = [];
|
|
13
|
+
// Valid state transitions (adjacency list)
|
|
14
|
+
this.validTransitions = new Map([
|
|
15
|
+
[WorkflowState.INITIALIZED, [
|
|
16
|
+
WorkflowState.VALIDATING,
|
|
17
|
+
WorkflowState.FAILED
|
|
18
|
+
]],
|
|
19
|
+
[WorkflowState.VALIDATING, [
|
|
20
|
+
WorkflowState.RUNNING,
|
|
21
|
+
WorkflowState.FAILED
|
|
22
|
+
]],
|
|
23
|
+
[WorkflowState.RUNNING, [
|
|
24
|
+
WorkflowState.STEP_EXECUTING,
|
|
25
|
+
WorkflowState.SYNTHESIZING,
|
|
26
|
+
WorkflowState.COMPLETED,
|
|
27
|
+
WorkflowState.FAILED,
|
|
28
|
+
WorkflowState.PAUSED
|
|
29
|
+
]],
|
|
30
|
+
[WorkflowState.STEP_EXECUTING, [
|
|
31
|
+
WorkflowState.WAITING,
|
|
32
|
+
WorkflowState.RUNNING,
|
|
33
|
+
WorkflowState.RETRYING,
|
|
34
|
+
WorkflowState.FAILED
|
|
35
|
+
]],
|
|
36
|
+
[WorkflowState.WAITING, [
|
|
37
|
+
WorkflowState.STEP_EXECUTING,
|
|
38
|
+
WorkflowState.RUNNING,
|
|
39
|
+
WorkflowState.FAILED
|
|
40
|
+
]],
|
|
41
|
+
[WorkflowState.RETRYING, [
|
|
42
|
+
WorkflowState.STEP_EXECUTING,
|
|
43
|
+
WorkflowState.FAILED
|
|
44
|
+
]],
|
|
45
|
+
[WorkflowState.SYNTHESIZING, [
|
|
46
|
+
WorkflowState.STEP_EXECUTING,
|
|
47
|
+
WorkflowState.COMPLETED,
|
|
48
|
+
WorkflowState.FAILED
|
|
49
|
+
]],
|
|
50
|
+
[WorkflowState.PAUSED, [
|
|
51
|
+
WorkflowState.RUNNING,
|
|
52
|
+
WorkflowState.FAILED
|
|
53
|
+
]],
|
|
54
|
+
[WorkflowState.COMPLETED, []],
|
|
55
|
+
[WorkflowState.FAILED, []]
|
|
56
|
+
]);
|
|
57
|
+
this.recordState(WorkflowState.INITIALIZED, {
|
|
58
|
+
workflowId: '',
|
|
59
|
+
workflowName: ''
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
getCurrentState() {
|
|
63
|
+
return this.currentState;
|
|
64
|
+
}
|
|
65
|
+
canTransition(to) {
|
|
66
|
+
const allowedStates = this.validTransitions.get(this.currentState) || [];
|
|
67
|
+
return allowedStates.includes(to);
|
|
68
|
+
}
|
|
69
|
+
transition(to, context) {
|
|
70
|
+
const from = this.currentState;
|
|
71
|
+
// Validate transition
|
|
72
|
+
if (!this.canTransition(to)) {
|
|
73
|
+
throw new Error(`Invalid state transition: ${from} -> ${to}. ` +
|
|
74
|
+
`Allowed transitions from ${from}: ${this.validTransitions.get(from)?.join(', ') || 'none'}`);
|
|
75
|
+
}
|
|
76
|
+
// Update state
|
|
77
|
+
this.currentState = to;
|
|
78
|
+
this.recordState(to, context);
|
|
79
|
+
// Emit state change event
|
|
80
|
+
this.emit('state:changed', {
|
|
81
|
+
from,
|
|
82
|
+
to,
|
|
83
|
+
context,
|
|
84
|
+
timestamp: new Date()
|
|
85
|
+
});
|
|
86
|
+
// Emit specific workflow lifecycle events
|
|
87
|
+
this.emitLifecycleEvents(to, context);
|
|
88
|
+
}
|
|
89
|
+
emitLifecycleEvents(state, context) {
|
|
90
|
+
switch (state) {
|
|
91
|
+
case WorkflowState.VALIDATING:
|
|
92
|
+
this.emit('workflow:validating', context);
|
|
93
|
+
break;
|
|
94
|
+
case WorkflowState.RUNNING:
|
|
95
|
+
this.emit('workflow:started', context);
|
|
96
|
+
break;
|
|
97
|
+
case WorkflowState.STEP_EXECUTING:
|
|
98
|
+
this.emit('step:started', {
|
|
99
|
+
stepName: context.currentStep,
|
|
100
|
+
stepIndex: context.stepIndex,
|
|
101
|
+
totalSteps: context.totalSteps,
|
|
102
|
+
workflowId: context.workflowId
|
|
103
|
+
});
|
|
104
|
+
break;
|
|
105
|
+
case WorkflowState.WAITING:
|
|
106
|
+
this.emit('step:waiting', {
|
|
107
|
+
stepName: context.currentStep,
|
|
108
|
+
reason: context.metadata?.waitReason || 'dependency'
|
|
109
|
+
});
|
|
110
|
+
break;
|
|
111
|
+
case WorkflowState.RETRYING:
|
|
112
|
+
this.emit('step:retrying', {
|
|
113
|
+
stepName: context.currentStep,
|
|
114
|
+
attempt: context.metadata?.retryAttempt || 1,
|
|
115
|
+
maxAttempts: context.metadata?.maxRetries || 3,
|
|
116
|
+
error: context.error
|
|
117
|
+
});
|
|
118
|
+
break;
|
|
119
|
+
case WorkflowState.SYNTHESIZING:
|
|
120
|
+
this.emit('workflow:synthesizing', {
|
|
121
|
+
workflowId: context.workflowId,
|
|
122
|
+
reason: context.metadata?.synthesisReason || 'auto'
|
|
123
|
+
});
|
|
124
|
+
break;
|
|
125
|
+
case WorkflowState.COMPLETED:
|
|
126
|
+
this.emit('workflow:completed', {
|
|
127
|
+
workflowId: context.workflowId,
|
|
128
|
+
workflowName: context.workflowName,
|
|
129
|
+
totalSteps: context.totalSteps,
|
|
130
|
+
duration: this.calculateDuration()
|
|
131
|
+
});
|
|
132
|
+
break;
|
|
133
|
+
case WorkflowState.FAILED:
|
|
134
|
+
this.emit('workflow:failed', {
|
|
135
|
+
workflowId: context.workflowId,
|
|
136
|
+
workflowName: context.workflowName,
|
|
137
|
+
error: context.error,
|
|
138
|
+
failedAt: context.currentStep,
|
|
139
|
+
duration: this.calculateDuration()
|
|
140
|
+
});
|
|
141
|
+
break;
|
|
142
|
+
case WorkflowState.PAUSED:
|
|
143
|
+
this.emit('workflow:paused', {
|
|
144
|
+
workflowId: context.workflowId,
|
|
145
|
+
pausedAt: context.currentStep
|
|
146
|
+
});
|
|
147
|
+
break;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
getStateHistory() {
|
|
151
|
+
return [...this.stateHistory];
|
|
152
|
+
}
|
|
153
|
+
reset() {
|
|
154
|
+
this.currentState = WorkflowState.INITIALIZED;
|
|
155
|
+
this.stateHistory = [];
|
|
156
|
+
this.recordState(WorkflowState.INITIALIZED, {
|
|
157
|
+
workflowId: '',
|
|
158
|
+
workflowName: ''
|
|
159
|
+
});
|
|
160
|
+
this.emit('state:reset');
|
|
161
|
+
}
|
|
162
|
+
recordState(state, context) {
|
|
163
|
+
this.stateHistory.push({
|
|
164
|
+
state,
|
|
165
|
+
timestamp: new Date(),
|
|
166
|
+
context
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
calculateDuration() {
|
|
170
|
+
if (this.stateHistory.length < 2)
|
|
171
|
+
return 0;
|
|
172
|
+
const start = this.stateHistory[0].timestamp.getTime();
|
|
173
|
+
const end = this.stateHistory[this.stateHistory.length - 1].timestamp.getTime();
|
|
174
|
+
return end - start;
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Get human-readable state description
|
|
178
|
+
*/
|
|
179
|
+
getStateDescription() {
|
|
180
|
+
const descriptions = {
|
|
181
|
+
[WorkflowState.INITIALIZED]: 'Ready to start',
|
|
182
|
+
[WorkflowState.VALIDATING]: 'Validating workflow configuration',
|
|
183
|
+
[WorkflowState.RUNNING]: 'Running workflow',
|
|
184
|
+
[WorkflowState.STEP_EXECUTING]: 'Executing step',
|
|
185
|
+
[WorkflowState.WAITING]: 'Waiting for dependencies',
|
|
186
|
+
[WorkflowState.RETRYING]: 'Retrying failed step',
|
|
187
|
+
[WorkflowState.SYNTHESIZING]: 'Synthesizing results',
|
|
188
|
+
[WorkflowState.COMPLETED]: 'Completed successfully',
|
|
189
|
+
[WorkflowState.FAILED]: 'Failed with errors',
|
|
190
|
+
[WorkflowState.PAUSED]: 'Paused'
|
|
191
|
+
};
|
|
192
|
+
return descriptions[this.currentState];
|
|
193
|
+
}
|
|
194
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* State Machine Interface
|
|
3
|
+
* Defines contract for workflow state management
|
|
4
|
+
*/
|
|
5
|
+
export var WorkflowState;
|
|
6
|
+
(function (WorkflowState) {
|
|
7
|
+
WorkflowState["INITIALIZED"] = "INITIALIZED";
|
|
8
|
+
WorkflowState["VALIDATING"] = "VALIDATING";
|
|
9
|
+
WorkflowState["RUNNING"] = "RUNNING";
|
|
10
|
+
WorkflowState["STEP_EXECUTING"] = "STEP_EXECUTING";
|
|
11
|
+
WorkflowState["WAITING"] = "WAITING";
|
|
12
|
+
WorkflowState["RETRYING"] = "RETRYING";
|
|
13
|
+
WorkflowState["SYNTHESIZING"] = "SYNTHESIZING";
|
|
14
|
+
WorkflowState["COMPLETED"] = "COMPLETED";
|
|
15
|
+
WorkflowState["FAILED"] = "FAILED";
|
|
16
|
+
WorkflowState["PAUSED"] = "PAUSED";
|
|
17
|
+
})(WorkflowState || (WorkflowState = {}));
|
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
import { EventEmitter } from 'events';
|
|
2
|
+
/**
|
|
3
|
+
* Collection of predefined fallback strategies
|
|
4
|
+
*/
|
|
5
|
+
export class FallbackStrategies {
|
|
6
|
+
constructor() {
|
|
7
|
+
this.strategies = [];
|
|
8
|
+
this.initializeStrategies();
|
|
9
|
+
}
|
|
10
|
+
initializeStrategies() {
|
|
11
|
+
// 1. Use cached results fallback
|
|
12
|
+
this.strategies.push({
|
|
13
|
+
name: 'cached-results',
|
|
14
|
+
description: 'Return previously cached successful results',
|
|
15
|
+
priority: 1,
|
|
16
|
+
canHandle: (context) => {
|
|
17
|
+
return context.previousResults !== undefined &&
|
|
18
|
+
context.previousResults.length > 0;
|
|
19
|
+
},
|
|
20
|
+
execute: async (context) => {
|
|
21
|
+
return {
|
|
22
|
+
result: context.previousResults[context.previousResults.length - 1],
|
|
23
|
+
fallbackUsed: 'cached-results',
|
|
24
|
+
warning: 'Using cached results due to service failure'
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
// 2. Use simpler alternative tool
|
|
29
|
+
this.strategies.push({
|
|
30
|
+
name: 'simpler-tool',
|
|
31
|
+
description: 'Use a simpler, more reliable tool',
|
|
32
|
+
priority: 2,
|
|
33
|
+
canHandle: (context) => {
|
|
34
|
+
const complexTools = ['focus', 'architect', 'verifier'];
|
|
35
|
+
return complexTools.includes(context.stepId);
|
|
36
|
+
},
|
|
37
|
+
execute: async (context) => {
|
|
38
|
+
const simplifiedTools = {
|
|
39
|
+
'focus': 'think',
|
|
40
|
+
'architect': 'code_reviewer',
|
|
41
|
+
'verifier': 'auditor',
|
|
42
|
+
'perplexity_research': 'perplexity_ask',
|
|
43
|
+
'focus_deep_research': 'scout'
|
|
44
|
+
};
|
|
45
|
+
return {
|
|
46
|
+
alternativeTool: simplifiedTools[context.stepId] || 'think',
|
|
47
|
+
fallbackUsed: 'simpler-tool',
|
|
48
|
+
warning: `Using simpler alternative due to ${context.stepId} failure`
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
// 3. Use cheaper model fallback
|
|
53
|
+
this.strategies.push({
|
|
54
|
+
name: 'cheaper-model',
|
|
55
|
+
description: 'Fallback to GPT-5 Nano for cost efficiency',
|
|
56
|
+
priority: 3,
|
|
57
|
+
canHandle: (context) => {
|
|
58
|
+
const expensiveModels = ['gpt5'];
|
|
59
|
+
return expensiveModels.some(model => context.stepId.includes(model));
|
|
60
|
+
},
|
|
61
|
+
execute: async (context) => {
|
|
62
|
+
return {
|
|
63
|
+
alternativeTool: 'gpt-5-nano',
|
|
64
|
+
fallbackUsed: 'cheaper-model',
|
|
65
|
+
warning: 'Falling back to GPT-5 Nano for cost efficiency'
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
// 4. Retry with reduced scope
|
|
70
|
+
this.strategies.push({
|
|
71
|
+
name: 'reduced-scope',
|
|
72
|
+
description: 'Retry with reduced complexity or scope',
|
|
73
|
+
priority: 4,
|
|
74
|
+
canHandle: (context) => {
|
|
75
|
+
return context.attemptNumber < 3 &&
|
|
76
|
+
context.originalQuery !== undefined;
|
|
77
|
+
},
|
|
78
|
+
execute: async (context) => {
|
|
79
|
+
// Simplify the query by taking first sentence or reducing length
|
|
80
|
+
const simplifiedQuery = context.originalQuery
|
|
81
|
+
.split('.')[0]
|
|
82
|
+
.substring(0, 200);
|
|
83
|
+
return {
|
|
84
|
+
simplifiedQuery,
|
|
85
|
+
fallbackUsed: 'reduced-scope',
|
|
86
|
+
warning: 'Retrying with reduced query scope'
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
// 5. Basic error response
|
|
91
|
+
this.strategies.push({
|
|
92
|
+
name: 'basic-response',
|
|
93
|
+
description: 'Return basic error information',
|
|
94
|
+
priority: 99,
|
|
95
|
+
canHandle: () => true, // Always can handle as last resort
|
|
96
|
+
execute: async (context) => {
|
|
97
|
+
return {
|
|
98
|
+
error: true,
|
|
99
|
+
fallbackUsed: 'basic-response',
|
|
100
|
+
message: `Service ${context.stepId} is temporarily unavailable`,
|
|
101
|
+
reason: context.failureReason,
|
|
102
|
+
suggestion: 'Please try again later or use a different approach'
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Get the best fallback strategy for the given context
|
|
109
|
+
*/
|
|
110
|
+
getBestStrategy(context) {
|
|
111
|
+
// Sort by priority and find first that can handle
|
|
112
|
+
const availableStrategies = this.strategies
|
|
113
|
+
.filter(s => s.canHandle(context))
|
|
114
|
+
.sort((a, b) => a.priority - b.priority);
|
|
115
|
+
return availableStrategies[0] || null;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Execute fallback with the best available strategy
|
|
119
|
+
*/
|
|
120
|
+
async executeFallback(context) {
|
|
121
|
+
const strategy = this.getBestStrategy(context);
|
|
122
|
+
if (!strategy) {
|
|
123
|
+
throw new Error(`No fallback strategy available for ${context.stepId}`);
|
|
124
|
+
}
|
|
125
|
+
console.error(`[Fallback] Using strategy: ${strategy.name} for ${context.stepId}`);
|
|
126
|
+
return strategy.execute(context);
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Add custom fallback strategy
|
|
130
|
+
*/
|
|
131
|
+
addStrategy(strategy) {
|
|
132
|
+
this.strategies.push(strategy);
|
|
133
|
+
// Re-sort by priority
|
|
134
|
+
this.strategies.sort((a, b) => a.priority - b.priority);
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Remove a strategy by name
|
|
138
|
+
*/
|
|
139
|
+
removeStrategy(name) {
|
|
140
|
+
this.strategies = this.strategies.filter(s => s.name !== name);
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Get all registered strategies
|
|
144
|
+
*/
|
|
145
|
+
getAllStrategies() {
|
|
146
|
+
return [...this.strategies];
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Health monitoring for circuit breakers
|
|
151
|
+
*/
|
|
152
|
+
export class HealthMonitor extends EventEmitter {
|
|
153
|
+
constructor() {
|
|
154
|
+
super(...arguments);
|
|
155
|
+
this.healthChecks = new Map();
|
|
156
|
+
this.healthStatus = new Map();
|
|
157
|
+
this.alertThresholds = {
|
|
158
|
+
consecutiveFailures: 3,
|
|
159
|
+
failureRate: 0.5,
|
|
160
|
+
responseTime: 10000 // 10 seconds
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Start monitoring a service
|
|
165
|
+
*/
|
|
166
|
+
startMonitoring(serviceId, checkFn, intervalMs = 30000) {
|
|
167
|
+
// Clear existing check if any
|
|
168
|
+
this.stopMonitoring(serviceId);
|
|
169
|
+
// Initial check
|
|
170
|
+
this.performHealthCheck(serviceId, checkFn);
|
|
171
|
+
// Set up interval
|
|
172
|
+
const interval = setInterval(() => {
|
|
173
|
+
this.performHealthCheck(serviceId, checkFn);
|
|
174
|
+
}, intervalMs);
|
|
175
|
+
this.healthChecks.set(serviceId, interval);
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Stop monitoring a service
|
|
179
|
+
*/
|
|
180
|
+
stopMonitoring(serviceId) {
|
|
181
|
+
const interval = this.healthChecks.get(serviceId);
|
|
182
|
+
if (interval) {
|
|
183
|
+
clearInterval(interval);
|
|
184
|
+
this.healthChecks.delete(serviceId);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Perform a health check
|
|
189
|
+
*/
|
|
190
|
+
async performHealthCheck(serviceId, checkFn) {
|
|
191
|
+
const startTime = Date.now();
|
|
192
|
+
try {
|
|
193
|
+
const isHealthy = await checkFn();
|
|
194
|
+
const responseTime = Date.now() - startTime;
|
|
195
|
+
const wasHealthy = this.healthStatus.get(serviceId);
|
|
196
|
+
this.healthStatus.set(serviceId, isHealthy);
|
|
197
|
+
// Emit events based on health changes
|
|
198
|
+
if (wasHealthy === false && isHealthy) {
|
|
199
|
+
this.emit('service-recovered', { serviceId, responseTime });
|
|
200
|
+
}
|
|
201
|
+
else if (wasHealthy === true && !isHealthy) {
|
|
202
|
+
this.emit('service-degraded', { serviceId, responseTime });
|
|
203
|
+
}
|
|
204
|
+
// Check response time threshold
|
|
205
|
+
if (responseTime > this.alertThresholds.responseTime) {
|
|
206
|
+
this.emit('slow-response', { serviceId, responseTime });
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
catch (error) {
|
|
210
|
+
this.healthStatus.set(serviceId, false);
|
|
211
|
+
this.emit('health-check-failed', {
|
|
212
|
+
serviceId,
|
|
213
|
+
error: error.message
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Get current health status
|
|
219
|
+
*/
|
|
220
|
+
getHealthStatus() {
|
|
221
|
+
return new Map(this.healthStatus);
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Check if a service is healthy
|
|
225
|
+
*/
|
|
226
|
+
isHealthy(serviceId) {
|
|
227
|
+
return this.healthStatus.get(serviceId) || false;
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Get overall system health score (0-1)
|
|
231
|
+
*/
|
|
232
|
+
getSystemHealthScore() {
|
|
233
|
+
if (this.healthStatus.size === 0)
|
|
234
|
+
return 1;
|
|
235
|
+
let healthyCount = 0;
|
|
236
|
+
for (const isHealthy of this.healthStatus.values()) {
|
|
237
|
+
if (isHealthy)
|
|
238
|
+
healthyCount++;
|
|
239
|
+
}
|
|
240
|
+
return healthyCount / this.healthStatus.size;
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Clean up all monitoring
|
|
244
|
+
*/
|
|
245
|
+
cleanup() {
|
|
246
|
+
for (const serviceId of this.healthChecks.keys()) {
|
|
247
|
+
this.stopMonitoring(serviceId);
|
|
248
|
+
}
|
|
249
|
+
this.healthStatus.clear();
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Proactive alert system for circuit breakers
|
|
254
|
+
*/
|
|
255
|
+
export class AlertManager extends EventEmitter {
|
|
256
|
+
constructor() {
|
|
257
|
+
super();
|
|
258
|
+
this.alerts = new Map();
|
|
259
|
+
this.alertRules = new Map();
|
|
260
|
+
this.initializeDefaultRules();
|
|
261
|
+
}
|
|
262
|
+
initializeDefaultRules() {
|
|
263
|
+
// Critical failure rate
|
|
264
|
+
this.alertRules.set('critical-failure-rate', (stats) => {
|
|
265
|
+
return stats.failureRate > 0.8;
|
|
266
|
+
});
|
|
267
|
+
// Multiple services down
|
|
268
|
+
this.alertRules.set('multiple-services-down', (health) => {
|
|
269
|
+
return health.openBreakers > 3;
|
|
270
|
+
});
|
|
271
|
+
// Cascade failure risk
|
|
272
|
+
this.alertRules.set('cascade-risk', (stats) => {
|
|
273
|
+
return stats.halfOpenBreakers > 2 && stats.openBreakers > 1;
|
|
274
|
+
});
|
|
275
|
+
// Low system health
|
|
276
|
+
this.alertRules.set('low-system-health', (health) => {
|
|
277
|
+
return health.healthScore < 0.5;
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* Check alerts based on current state
|
|
282
|
+
*/
|
|
283
|
+
checkAlerts(data) {
|
|
284
|
+
for (const [ruleName, ruleFn] of this.alertRules.entries()) {
|
|
285
|
+
if (ruleFn(data)) {
|
|
286
|
+
this.triggerAlert(ruleName, data);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* Trigger an alert
|
|
292
|
+
*/
|
|
293
|
+
triggerAlert(alertType, data) {
|
|
294
|
+
const alert = {
|
|
295
|
+
type: alertType,
|
|
296
|
+
timestamp: Date.now(),
|
|
297
|
+
data,
|
|
298
|
+
severity: this.getAlertSeverity(alertType)
|
|
299
|
+
};
|
|
300
|
+
// Store alert
|
|
301
|
+
if (!this.alerts.has(alertType)) {
|
|
302
|
+
this.alerts.set(alertType, []);
|
|
303
|
+
}
|
|
304
|
+
this.alerts.get(alertType).push(alert);
|
|
305
|
+
// Emit alert event
|
|
306
|
+
this.emit('alert', alert);
|
|
307
|
+
// Log critical alerts
|
|
308
|
+
if (alert.severity === 'critical') {
|
|
309
|
+
console.error(`[CRITICAL ALERT] ${alertType}:`, data);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
/**
|
|
313
|
+
* Get alert severity
|
|
314
|
+
*/
|
|
315
|
+
getAlertSeverity(alertType) {
|
|
316
|
+
const criticalAlerts = ['multiple-services-down', 'cascade-risk'];
|
|
317
|
+
const warningAlerts = ['critical-failure-rate', 'low-system-health'];
|
|
318
|
+
if (criticalAlerts.includes(alertType))
|
|
319
|
+
return 'critical';
|
|
320
|
+
if (warningAlerts.includes(alertType))
|
|
321
|
+
return 'warning';
|
|
322
|
+
return 'info';
|
|
323
|
+
}
|
|
324
|
+
/**
|
|
325
|
+
* Get recent alerts
|
|
326
|
+
*/
|
|
327
|
+
getRecentAlerts(minutes = 5) {
|
|
328
|
+
const cutoff = Date.now() - (minutes * 60 * 1000);
|
|
329
|
+
const recent = [];
|
|
330
|
+
for (const alerts of this.alerts.values()) {
|
|
331
|
+
recent.push(...alerts.filter(a => a.timestamp > cutoff));
|
|
332
|
+
}
|
|
333
|
+
return recent.sort((a, b) => b.timestamp - a.timestamp);
|
|
334
|
+
}
|
|
335
|
+
/**
|
|
336
|
+
* Clear old alerts
|
|
337
|
+
*/
|
|
338
|
+
clearOldAlerts(hours = 24) {
|
|
339
|
+
const cutoff = Date.now() - (hours * 60 * 60 * 1000);
|
|
340
|
+
for (const [type, alerts] of this.alerts.entries()) {
|
|
341
|
+
const filtered = alerts.filter(a => a.timestamp > cutoff);
|
|
342
|
+
if (filtered.length > 0) {
|
|
343
|
+
this.alerts.set(type, filtered);
|
|
344
|
+
}
|
|
345
|
+
else {
|
|
346
|
+
this.alerts.delete(type);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
/**
|
|
351
|
+
* Add custom alert rule
|
|
352
|
+
*/
|
|
353
|
+
addRule(name, ruleFn) {
|
|
354
|
+
this.alertRules.set(name, ruleFn);
|
|
355
|
+
}
|
|
356
|
+
/**
|
|
357
|
+
* Remove alert rule
|
|
358
|
+
*/
|
|
359
|
+
removeRule(name) {
|
|
360
|
+
this.alertRules.delete(name);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
// Export a singleton instance for global use
|
|
364
|
+
export const fallbackStrategies = new FallbackStrategies();
|
|
365
|
+
export const healthMonitor = new HealthMonitor();
|
|
366
|
+
export const alertManager = new AlertManager();
|
|
367
|
+
// Set up connections between components
|
|
368
|
+
healthMonitor.on('service-degraded', (data) => {
|
|
369
|
+
alertManager.checkAlerts({ serviceDown: data.serviceId });
|
|
370
|
+
});
|
|
371
|
+
healthMonitor.on('slow-response', (data) => {
|
|
372
|
+
alertManager.checkAlerts({ slowService: data.serviceId, responseTime: data.responseTime });
|
|
373
|
+
});
|