@haoyiyin/workflow 0.2.10 → 0.3.0
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/dist/src/agents/contracts/implementer.d.ts +29 -0
- package/dist/src/agents/contracts/implementer.d.ts.map +1 -0
- package/dist/src/agents/contracts/implementer.js +94 -0
- package/dist/src/agents/contracts/implementer.js.map +1 -0
- package/dist/src/agents/contracts/index.d.ts +11 -0
- package/dist/src/agents/contracts/index.d.ts.map +1 -0
- package/dist/src/agents/contracts/index.js +11 -0
- package/dist/src/agents/contracts/index.js.map +1 -0
- package/dist/src/agents/contracts/planner.d.ts +25 -0
- package/dist/src/agents/contracts/planner.d.ts.map +1 -0
- package/dist/src/agents/contracts/planner.js +107 -0
- package/dist/src/agents/contracts/planner.js.map +1 -0
- package/dist/src/agents/contracts/router.d.ts +24 -0
- package/dist/src/agents/contracts/router.d.ts.map +1 -0
- package/dist/src/agents/contracts/router.js +137 -0
- package/dist/src/agents/contracts/router.js.map +1 -0
- package/dist/src/agents/contracts/verifier.d.ts +27 -0
- package/dist/src/agents/contracts/verifier.d.ts.map +1 -0
- package/dist/src/agents/contracts/verifier.js +115 -0
- package/dist/src/agents/contracts/verifier.js.map +1 -0
- package/dist/src/agents/dispatcher.d.ts +94 -51
- package/dist/src/agents/dispatcher.d.ts.map +1 -1
- package/dist/src/agents/dispatcher.js +207 -164
- package/dist/src/agents/dispatcher.js.map +1 -1
- package/dist/src/persistence/index.d.ts +4 -2
- package/dist/src/persistence/index.d.ts.map +1 -1
- package/dist/src/persistence/index.js +4 -1
- package/dist/src/persistence/index.js.map +1 -1
- package/dist/src/persistence/plan-md.d.ts +3 -2
- package/dist/src/persistence/plan-md.d.ts.map +1 -1
- package/dist/src/persistence/plan-md.js +47 -15
- package/dist/src/persistence/plan-md.js.map +1 -1
- package/dist/src/persistence/state-md.d.ts +2 -0
- package/dist/src/persistence/state-md.d.ts.map +1 -1
- package/dist/src/persistence/state-md.js +40 -22
- package/dist/src/persistence/state-md.js.map +1 -1
- package/dist/src/persistence/types.d.ts +35 -39
- package/dist/src/persistence/types.d.ts.map +1 -1
- package/dist/src/router/namespace/core/intent-router.d.ts +24 -0
- package/dist/src/router/namespace/core/intent-router.d.ts.map +1 -0
- package/dist/src/router/namespace/core/intent-router.js +190 -0
- package/dist/src/router/namespace/core/intent-router.js.map +1 -0
- package/dist/src/router/namespace/core/lifecycle-router.d.ts +28 -0
- package/dist/src/router/namespace/core/lifecycle-router.d.ts.map +1 -0
- package/dist/src/router/namespace/core/lifecycle-router.js +132 -0
- package/dist/src/router/namespace/core/lifecycle-router.js.map +1 -0
- package/dist/src/router/namespace/core/state-router.d.ts +32 -0
- package/dist/src/router/namespace/core/state-router.d.ts.map +1 -0
- package/dist/src/router/namespace/core/state-router.js +157 -0
- package/dist/src/router/namespace/core/state-router.js.map +1 -0
- package/dist/src/router/namespace/domain/code-router.d.ts +26 -0
- package/dist/src/router/namespace/domain/code-router.d.ts.map +1 -0
- package/dist/src/router/namespace/domain/code-router.js +171 -0
- package/dist/src/router/namespace/domain/code-router.js.map +1 -0
- package/dist/src/router/namespace/domain/debug-router.d.ts +25 -0
- package/dist/src/router/namespace/domain/debug-router.d.ts.map +1 -0
- package/dist/src/router/namespace/domain/debug-router.js +139 -0
- package/dist/src/router/namespace/domain/debug-router.js.map +1 -0
- package/dist/src/router/namespace/domain/plan-router.d.ts +29 -0
- package/dist/src/router/namespace/domain/plan-router.d.ts.map +1 -0
- package/dist/src/router/namespace/domain/plan-router.js +160 -0
- package/dist/src/router/namespace/domain/plan-router.js.map +1 -0
- package/dist/src/router/namespace/domain/review-router.d.ts +24 -0
- package/dist/src/router/namespace/domain/review-router.d.ts.map +1 -0
- package/dist/src/router/namespace/domain/review-router.js +116 -0
- package/dist/src/router/namespace/domain/review-router.js.map +1 -0
- package/dist/src/router/namespace/index.d.ts +19 -0
- package/dist/src/router/namespace/index.d.ts.map +1 -0
- package/dist/src/router/namespace/index.js +22 -0
- package/dist/src/router/namespace/index.js.map +1 -0
- package/dist/src/router/namespace/registry.d.ts +67 -0
- package/dist/src/router/namespace/registry.d.ts.map +1 -0
- package/dist/src/router/namespace/registry.js +197 -0
- package/dist/src/router/namespace/registry.js.map +1 -0
- package/dist/src/router/namespace/types.d.ts +124 -0
- package/dist/src/router/namespace/types.d.ts.map +1 -0
- package/dist/src/router/namespace/types.js +20 -0
- package/dist/src/router/namespace/types.js.map +1 -0
- package/dist/src/router/namespace/utility/fallback-router.d.ts +28 -0
- package/dist/src/router/namespace/utility/fallback-router.d.ts.map +1 -0
- package/dist/src/router/namespace/utility/fallback-router.js +88 -0
- package/dist/src/router/namespace/utility/fallback-router.js.map +1 -0
- package/dist/src/router/namespace/utility/quick-task-router.d.ts +28 -0
- package/dist/src/router/namespace/utility/quick-task-router.d.ts.map +1 -0
- package/dist/src/router/namespace/utility/quick-task-router.js +99 -0
- package/dist/src/router/namespace/utility/quick-task-router.js.map +1 -0
- package/dist/src/router/namespace/utility/research-router.d.ts +24 -0
- package/dist/src/router/namespace/utility/research-router.d.ts.map +1 -0
- package/dist/src/router/namespace/utility/research-router.js +84 -0
- package/dist/src/router/namespace/utility/research-router.js.map +1 -0
- package/dist/src/skills/agents-md/index.js +2 -2
- package/dist/src/skills/agents-md/index.js.map +1 -1
- package/dist/src/skills/execute-plan/index.d.ts +45 -65
- package/dist/src/skills/execute-plan/index.d.ts.map +1 -1
- package/dist/src/skills/execute-plan/index.js +325 -551
- package/dist/src/skills/execute-plan/index.js.map +1 -1
- package/dist/src/skills/index.d.ts +1 -0
- package/dist/src/skills/index.d.ts.map +1 -1
- package/dist/src/skills/index.js +1 -0
- package/dist/src/skills/index.js.map +1 -1
- package/dist/src/skills/quick-task/index.d.ts +4 -4
- package/dist/src/skills/quick-task/index.js +1 -1
- package/dist/src/skills/quick-task/index.js.map +1 -1
- package/dist/src/skills/review-diff/index.d.ts +6 -6
- package/dist/src/skills/review-diff/index.js +1 -1
- package/dist/src/skills/review-diff/index.js.map +1 -1
- package/dist/src/skills/router/index.d.ts +101 -0
- package/dist/src/skills/router/index.d.ts.map +1 -0
- package/dist/src/skills/router/index.js +450 -0
- package/dist/src/skills/router/index.js.map +1 -0
- package/dist/src/skills/router/types.d.ts +79 -0
- package/dist/src/skills/router/types.d.ts.map +1 -0
- package/dist/src/skills/router/types.js +8 -0
- package/dist/src/skills/router/types.js.map +1 -0
- package/dist/src/skills/systematic-debugging/index.js +1 -1
- package/dist/src/skills/systematic-debugging/index.js.map +1 -1
- package/dist/src/skills/tdd/index.d.ts +14 -14
- package/dist/src/skills/tdd/index.js +1 -1
- package/dist/src/skills/tdd/index.js.map +1 -1
- package/dist/src/skills/to-plan/index-enhanced.d.ts +4 -4
- package/dist/src/skills/to-plan/index-enhanced.d.ts.map +1 -1
- package/dist/src/skills/to-plan/index-enhanced.js +3 -5
- package/dist/src/skills/to-plan/index-enhanced.js.map +1 -1
- package/dist/src/skills/to-plan/index.d.ts +24 -91
- package/dist/src/skills/to-plan/index.d.ts.map +1 -1
- package/dist/src/skills/to-plan/index.js +214 -409
- package/dist/src/skills/to-plan/index.js.map +1 -1
- package/package.json +3 -5
- package/src/agents/contracts/implementer.ts +122 -0
- package/src/agents/contracts/index.ts +27 -0
- package/src/agents/contracts/planner.ts +129 -0
- package/src/agents/contracts/router.ts +168 -0
- package/src/agents/contracts/verifier.ts +137 -0
- package/src/agents/dispatcher.ts +387 -362
- package/src/persistence/index.ts +10 -4
- package/src/persistence/plan-md.ts +52 -18
- package/src/persistence/state-md.ts +45 -23
- package/src/persistence/types.ts +37 -40
- package/src/router/namespace/README.md +127 -0
- package/src/router/namespace/core/intent-router.ts +221 -0
- package/src/router/namespace/core/lifecycle-router.ts +156 -0
- package/src/router/namespace/core/state-router.ts +192 -0
- package/src/router/namespace/domain/code-router.ts +202 -0
- package/src/router/namespace/domain/debug-router.ts +167 -0
- package/src/router/namespace/domain/plan-router.ts +196 -0
- package/src/router/namespace/domain/review-router.ts +142 -0
- package/src/router/namespace/index.ts +84 -0
- package/src/router/namespace/registry.ts +242 -0
- package/src/router/namespace/types.ts +182 -0
- package/src/router/namespace/utility/fallback-router.ts +107 -0
- package/src/router/namespace/utility/quick-task-router.ts +121 -0
- package/src/router/namespace/utility/research-router.ts +105 -0
- package/src/skills/agents-md/index.ts +2 -2
- package/src/skills/execute-plan/index.ts +419 -673
- package/src/skills/index.ts +1 -0
- package/src/skills/quick-task/index.ts +1 -1
- package/src/skills/review-diff/index.ts +1 -1
- package/src/skills/router/SKILL.md +181 -0
- package/src/skills/router/index.ts +577 -0
- package/src/skills/router/types.ts +90 -0
- package/src/skills/systematic-debugging/index.ts +1 -1
- package/src/skills/tdd/index.ts +1 -1
- package/src/skills/to-plan/index-enhanced.ts +3 -5
- package/src/skills/to-plan/index.ts +231 -502
|
@@ -0,0 +1,577 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Router Skill - 5-Layer Routing Logic
|
|
3
|
+
*
|
|
4
|
+
* Layer 1: Parse & Classify - Extract intent and entities from user request
|
|
5
|
+
* Layer 2: State-Aware Scoring - Factor in project phase and execution state
|
|
6
|
+
* Layer 3: Skill Matching - Score each skill against request and state
|
|
7
|
+
* Layer 4: Confidence Calibration - Adjust scores based on history and context
|
|
8
|
+
* Layer 5: Selection & Output - Return structured routing decision
|
|
9
|
+
*
|
|
10
|
+
* This skill reads from STATE.md and PLAN.md to make context-aware decisions.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { z } from 'zod'
|
|
14
|
+
import { Skill } from '../skill.js'
|
|
15
|
+
import type { SkillContext } from '../types.js'
|
|
16
|
+
import { StateMdManager } from '../../persistence/state-md.js'
|
|
17
|
+
import { PlanMdManager } from '../../persistence/plan-md.js'
|
|
18
|
+
import { createDispatcher } from '../../agents/dispatcher.js'
|
|
19
|
+
import type { SubagentConfig, SubagentContract, SubagentResult } from '../../agents/types.js'
|
|
20
|
+
import type { WorkflowState } from '../../persistence/types.js'
|
|
21
|
+
import type { RouterInput, RouterOutput, ProjectState, ProjectPhase, RoutingAlternative } from './types.js'
|
|
22
|
+
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
// Schemas
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
|
|
27
|
+
const ConversationHistoryItemSchema = z.object({
|
|
28
|
+
role: z.enum(['user', 'assistant']),
|
|
29
|
+
content: z.string(),
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
const RouterInputSchema = z.object({
|
|
33
|
+
userRequest: z.string().min(1, 'User request is required'),
|
|
34
|
+
conversationHistory: z.array(ConversationHistoryItemSchema).optional(),
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
const RouterOutputSchema = z.object({
|
|
38
|
+
selectedSkill: z.string(),
|
|
39
|
+
confidence: z.number().min(0).max(1),
|
|
40
|
+
reasoning: z.string(),
|
|
41
|
+
projectState: z.object({
|
|
42
|
+
phase: z.enum(['discovery', 'planning', 'execution', 'verification', 'completed', 'idle']),
|
|
43
|
+
hasPlan: z.boolean(),
|
|
44
|
+
isExecuting: z.boolean(),
|
|
45
|
+
completedTasks: z.number(),
|
|
46
|
+
totalTasks: z.number(),
|
|
47
|
+
}),
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
type RouterInputType = z.infer<typeof RouterInputSchema>
|
|
51
|
+
type RouterOutputType = z.infer<typeof RouterOutputSchema>
|
|
52
|
+
|
|
53
|
+
// ---------------------------------------------------------------------------
|
|
54
|
+
// 5-Layer Router Types
|
|
55
|
+
// ---------------------------------------------------------------------------
|
|
56
|
+
|
|
57
|
+
interface Layer1Classification {
|
|
58
|
+
intent: string
|
|
59
|
+
entities: string[]
|
|
60
|
+
complexity: 'low' | 'medium' | 'high'
|
|
61
|
+
urgency: 'low' | 'medium' | 'high'
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
interface Layer2StateFactors {
|
|
65
|
+
phaseWeight: number
|
|
66
|
+
executionWeight: number
|
|
67
|
+
completionWeight: number
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
interface Layer3SkillScore {
|
|
71
|
+
skillName: string
|
|
72
|
+
baseScore: number
|
|
73
|
+
stateAdjustedScore: number
|
|
74
|
+
matches: string[]
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
interface Layer4Calibration {
|
|
78
|
+
confidenceAdjustment: number
|
|
79
|
+
historyBonus: number
|
|
80
|
+
contextPenalty: number
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
interface Layer5Decision {
|
|
84
|
+
selectedSkill: string
|
|
85
|
+
finalConfidence: number
|
|
86
|
+
reasoning: string
|
|
87
|
+
alternatives: RoutingAlternative[]
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// ---------------------------------------------------------------------------
|
|
91
|
+
// Available Skills by Phase
|
|
92
|
+
// ---------------------------------------------------------------------------
|
|
93
|
+
|
|
94
|
+
const SKILLS_BY_PHASE: Record<ProjectPhase, string[]> = {
|
|
95
|
+
discovery: ['to-plan', 'research', 'agents-md'],
|
|
96
|
+
planning: ['to-plan', 'research', 'review-diff'],
|
|
97
|
+
execution: ['execute-plan', 'quick-task', 'tdd', 'systematic-debugging'],
|
|
98
|
+
verification: ['review-diff', 'systematic-debugging', 'agents-md'],
|
|
99
|
+
completed: ['review-diff', 'agents-md', 'research'],
|
|
100
|
+
idle: ['to-plan', 'research', 'quick-task', 'agents-md'],
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// ---------------------------------------------------------------------------
|
|
104
|
+
// Prompt Builders for Subagent
|
|
105
|
+
// ---------------------------------------------------------------------------
|
|
106
|
+
|
|
107
|
+
function buildLayer1Prompt(userRequest: string, history: Array<{ role: 'user' | 'assistant'; content: string }> | undefined): string {
|
|
108
|
+
const historyBlock = history && history.length > 0
|
|
109
|
+
? `## Conversation History\n${history.map(h => `${h.role}: ${h.content}`).join('\n')}\n`
|
|
110
|
+
: ''
|
|
111
|
+
|
|
112
|
+
return [
|
|
113
|
+
'## Layer 1: Parse & Classify',
|
|
114
|
+
'',
|
|
115
|
+
'Analyze the user request and extract structured information.',
|
|
116
|
+
'',
|
|
117
|
+
'**User Request**:',
|
|
118
|
+
userRequest,
|
|
119
|
+
'',
|
|
120
|
+
historyBlock,
|
|
121
|
+
'## Output Format (JSON)',
|
|
122
|
+
'',
|
|
123
|
+
'```json',
|
|
124
|
+
'{',
|
|
125
|
+
' "intent": "primary intent category (plan, execute, debug, review, research, quick-task)",',
|
|
126
|
+
' "entities": ["file paths mentioned", "concepts", "technologies"],',
|
|
127
|
+
' "complexity": "low|medium|high",',
|
|
128
|
+
' "urgency": "low|medium|high"',
|
|
129
|
+
'}',
|
|
130
|
+
'```',
|
|
131
|
+
].join('\n')
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function buildLayer3Prompt(
|
|
135
|
+
classification: Layer1Classification,
|
|
136
|
+
projectState: ProjectState,
|
|
137
|
+
availableSkills: string[],
|
|
138
|
+
): string {
|
|
139
|
+
return [
|
|
140
|
+
'## Layer 3: Skill Matching',
|
|
141
|
+
'',
|
|
142
|
+
'Score each available skill based on how well it matches the request and project state.',
|
|
143
|
+
'',
|
|
144
|
+
'**Request Classification**:',
|
|
145
|
+
`- Intent: ${classification.intent}`,
|
|
146
|
+
`- Entities: ${classification.entities.join(', ') || 'none'}`,
|
|
147
|
+
`- Complexity: ${classification.complexity}`,
|
|
148
|
+
`- Urgency: ${classification.urgency}`,
|
|
149
|
+
'',
|
|
150
|
+
'**Project State**:',
|
|
151
|
+
`- Phase: ${projectState.phase}`,
|
|
152
|
+
`- Has Plan: ${projectState.hasPlan}`,
|
|
153
|
+
`- Is Executing: ${projectState.isExecuting}`,
|
|
154
|
+
`- Progress: ${projectState.completedTasks}/${projectState.totalTasks} tasks`,
|
|
155
|
+
'',
|
|
156
|
+
'**Available Skills**:',
|
|
157
|
+
availableSkills.map(s => `- ${s}`).join('\n'),
|
|
158
|
+
'',
|
|
159
|
+
'## Output Format (JSON)',
|
|
160
|
+
'',
|
|
161
|
+
'```json',
|
|
162
|
+
'{',
|
|
163
|
+
' "scores": [',
|
|
164
|
+
' { "skillName": "skill-name", "baseScore": 0.85, "matches": ["reason1", "reason2"] }',
|
|
165
|
+
' ]',
|
|
166
|
+
'}',
|
|
167
|
+
'```',
|
|
168
|
+
].join('\n')
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function buildLayer5Prompt(
|
|
172
|
+
calibratedScores: Layer3SkillScore[],
|
|
173
|
+
projectState: ProjectState,
|
|
174
|
+
): string {
|
|
175
|
+
return [
|
|
176
|
+
'## Layer 5: Selection & Output',
|
|
177
|
+
'',
|
|
178
|
+
'Make the final routing decision based on calibrated scores and project state.',
|
|
179
|
+
'',
|
|
180
|
+
'**Calibrated Scores**:',
|
|
181
|
+
calibratedScores.map(s => `- ${s.skillName}: ${s.stateAdjustedScore.toFixed(2)}`).join('\n'),
|
|
182
|
+
'',
|
|
183
|
+
'**Project State**:',
|
|
184
|
+
`- Phase: ${projectState.phase}`,
|
|
185
|
+
`- Has Plan: ${projectState.hasPlan}`,
|
|
186
|
+
`- Is Executing: ${projectState.isExecuting}`,
|
|
187
|
+
'',
|
|
188
|
+
'## Output Format (JSON)',
|
|
189
|
+
'',
|
|
190
|
+
'```json',
|
|
191
|
+
'{',
|
|
192
|
+
' "selectedSkill": "skill-name",',
|
|
193
|
+
' "finalConfidence": 0.92,',
|
|
194
|
+
' "reasoning": "Detailed explanation of why this skill was selected",',
|
|
195
|
+
' "alternatives": [',
|
|
196
|
+
' { "skill": "other-skill", "confidence": 0.65, "reasoning": "Why it was considered" }',
|
|
197
|
+
' ]',
|
|
198
|
+
'}',
|
|
199
|
+
'```',
|
|
200
|
+
].join('\n')
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// ---------------------------------------------------------------------------
|
|
204
|
+
// Parsing Helpers
|
|
205
|
+
// ---------------------------------------------------------------------------
|
|
206
|
+
|
|
207
|
+
function parseLayer1Output(output: string): Layer1Classification {
|
|
208
|
+
const jsonMatch = output.match(/```json\s*([\s\S]*?)\s*```/)
|
|
209
|
+
if (jsonMatch) {
|
|
210
|
+
try {
|
|
211
|
+
const parsed = JSON.parse(jsonMatch[1].trim())
|
|
212
|
+
return {
|
|
213
|
+
intent: parsed.intent || 'general',
|
|
214
|
+
entities: Array.isArray(parsed.entities) ? parsed.entities : [],
|
|
215
|
+
complexity: ['low', 'medium', 'high'].includes(parsed.complexity) ? parsed.complexity : 'medium',
|
|
216
|
+
urgency: ['low', 'medium', 'high'].includes(parsed.urgency) ? parsed.urgency : 'medium',
|
|
217
|
+
}
|
|
218
|
+
} catch {
|
|
219
|
+
// Fall through to defaults
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
return {
|
|
223
|
+
intent: 'general',
|
|
224
|
+
entities: [],
|
|
225
|
+
complexity: 'medium',
|
|
226
|
+
urgency: 'medium',
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function parseLayer3Output(output: string): Layer3SkillScore[] {
|
|
231
|
+
const jsonMatch = output.match(/```json\s*([\s\S]*?)\s*```/)
|
|
232
|
+
if (jsonMatch) {
|
|
233
|
+
try {
|
|
234
|
+
const parsed = JSON.parse(jsonMatch[1].trim())
|
|
235
|
+
if (Array.isArray(parsed.scores)) {
|
|
236
|
+
return parsed.scores.map((s: { skillName?: string; baseScore?: number; matches?: string[] }) => ({
|
|
237
|
+
skillName: s.skillName || 'unknown',
|
|
238
|
+
baseScore: s.baseScore || 0,
|
|
239
|
+
stateAdjustedScore: s.baseScore || 0,
|
|
240
|
+
matches: Array.isArray(s.matches) ? s.matches : [],
|
|
241
|
+
}))
|
|
242
|
+
}
|
|
243
|
+
} catch {
|
|
244
|
+
// Fall through
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
return []
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
function parseLayer5Output(output: string): Layer5Decision {
|
|
251
|
+
const jsonMatch = output.match(/```json\s*([\s\S]*?)\s*```/)
|
|
252
|
+
if (jsonMatch) {
|
|
253
|
+
try {
|
|
254
|
+
const parsed = JSON.parse(jsonMatch[1].trim())
|
|
255
|
+
return {
|
|
256
|
+
selectedSkill: parsed.selectedSkill || 'quick-task',
|
|
257
|
+
finalConfidence: parsed.finalConfidence || 0.5,
|
|
258
|
+
reasoning: parsed.reasoning || 'No reasoning provided',
|
|
259
|
+
alternatives: Array.isArray(parsed.alternatives) ? parsed.alternatives : [],
|
|
260
|
+
}
|
|
261
|
+
} catch {
|
|
262
|
+
// Fall through to defaults
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
return {
|
|
266
|
+
selectedSkill: 'quick-task',
|
|
267
|
+
finalConfidence: 0.5,
|
|
268
|
+
reasoning: 'Failed to parse decision, defaulting to quick-task',
|
|
269
|
+
alternatives: [],
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// ---------------------------------------------------------------------------
|
|
274
|
+
// State Mapping Helpers
|
|
275
|
+
// ---------------------------------------------------------------------------
|
|
276
|
+
|
|
277
|
+
function mapWorkflowPhaseToProjectPhase(phase: WorkflowState['phase']): ProjectPhase {
|
|
278
|
+
switch (phase) {
|
|
279
|
+
case 'planning':
|
|
280
|
+
return 'planning'
|
|
281
|
+
case 'executing':
|
|
282
|
+
return 'execution'
|
|
283
|
+
case 'verifying':
|
|
284
|
+
return 'verification'
|
|
285
|
+
case 'completed':
|
|
286
|
+
return 'completed'
|
|
287
|
+
default:
|
|
288
|
+
return 'idle'
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function buildProjectState(
|
|
293
|
+
workflowState: WorkflowState,
|
|
294
|
+
hasPlan: boolean,
|
|
295
|
+
): ProjectState {
|
|
296
|
+
return {
|
|
297
|
+
phase: mapWorkflowPhaseToProjectPhase(workflowState.phase),
|
|
298
|
+
hasPlan,
|
|
299
|
+
isExecuting: workflowState.phase === 'executing',
|
|
300
|
+
completedTasks: workflowState.completedTasks?.length || 0,
|
|
301
|
+
totalTasks: workflowState.totalTasks || 0,
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// ---------------------------------------------------------------------------
|
|
306
|
+
// Layer 2: State-Aware Scoring (Pure Function)
|
|
307
|
+
// ---------------------------------------------------------------------------
|
|
308
|
+
|
|
309
|
+
function calculateStateFactors(
|
|
310
|
+
classification: Layer1Classification,
|
|
311
|
+
projectState: ProjectState,
|
|
312
|
+
): Layer2StateFactors {
|
|
313
|
+
let phaseWeight = 1.0
|
|
314
|
+
let executionWeight = 1.0
|
|
315
|
+
let completionWeight = 1.0
|
|
316
|
+
|
|
317
|
+
// Adjust based on phase
|
|
318
|
+
switch (projectState.phase) {
|
|
319
|
+
case 'planning':
|
|
320
|
+
phaseWeight = classification.intent === 'plan' ? 1.5 : 0.8
|
|
321
|
+
break
|
|
322
|
+
case 'execution':
|
|
323
|
+
phaseWeight = classification.intent === 'execute' ? 1.5 : 0.9
|
|
324
|
+
break
|
|
325
|
+
case 'verification':
|
|
326
|
+
phaseWeight = classification.intent === 'review' ? 1.5 : 0.8
|
|
327
|
+
break
|
|
328
|
+
case 'completed':
|
|
329
|
+
phaseWeight = classification.intent === 'research' ? 1.2 : 1.0
|
|
330
|
+
break
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// Adjust based on execution state
|
|
334
|
+
if (projectState.isExecuting && classification.intent === 'execute') {
|
|
335
|
+
executionWeight = 1.3
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// Adjust based on completion progress
|
|
339
|
+
const progressRatio = projectState.totalTasks > 0
|
|
340
|
+
? projectState.completedTasks / projectState.totalTasks
|
|
341
|
+
: 0
|
|
342
|
+
completionWeight = 0.8 + (0.4 * progressRatio)
|
|
343
|
+
|
|
344
|
+
return {
|
|
345
|
+
phaseWeight,
|
|
346
|
+
executionWeight,
|
|
347
|
+
completionWeight,
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// ---------------------------------------------------------------------------
|
|
352
|
+
// Layer 4: Confidence Calibration (Pure Function)
|
|
353
|
+
// ---------------------------------------------------------------------------
|
|
354
|
+
|
|
355
|
+
function calibrateConfidence(
|
|
356
|
+
scores: Layer3SkillScore[],
|
|
357
|
+
stateFactors: Layer2StateFactors,
|
|
358
|
+
classification: Layer1Classification,
|
|
359
|
+
): Layer3SkillScore[] {
|
|
360
|
+
const totalWeight = stateFactors.phaseWeight * stateFactors.executionWeight * stateFactors.completionWeight
|
|
361
|
+
|
|
362
|
+
return scores.map((score) => {
|
|
363
|
+
// Apply state-based adjustment
|
|
364
|
+
let adjustedScore = score.baseScore * totalWeight
|
|
365
|
+
|
|
366
|
+
// Boost for high urgency
|
|
367
|
+
if (classification.urgency === 'high' && score.skillName === 'quick-task') {
|
|
368
|
+
adjustedScore *= 1.2
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// Cap at 1.0
|
|
372
|
+
adjustedScore = Math.min(adjustedScore, 1.0)
|
|
373
|
+
|
|
374
|
+
return {
|
|
375
|
+
...score,
|
|
376
|
+
stateAdjustedScore: adjustedScore,
|
|
377
|
+
}
|
|
378
|
+
})
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
// ---------------------------------------------------------------------------
|
|
382
|
+
// Router Skill Class
|
|
383
|
+
// ---------------------------------------------------------------------------
|
|
384
|
+
|
|
385
|
+
export class RouterSkill extends Skill<RouterInputType, RouterOutputType> {
|
|
386
|
+
private stateManager: StateMdManager
|
|
387
|
+
private planManager: PlanMdManager
|
|
388
|
+
|
|
389
|
+
constructor(statePath = '.pi/state/STATE.md', planPath = '.pi/state/PLAN.md') {
|
|
390
|
+
super({
|
|
391
|
+
name: 'router',
|
|
392
|
+
description: '5-layer routing skill that selects the appropriate skill based on user request and project state',
|
|
393
|
+
requires: [],
|
|
394
|
+
inputSchema: RouterInputSchema,
|
|
395
|
+
outputSchema: RouterOutputSchema,
|
|
396
|
+
})
|
|
397
|
+
this.stateManager = new StateMdManager(statePath)
|
|
398
|
+
this.planManager = new PlanMdManager(planPath)
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
/**
|
|
402
|
+
* Get available skills for the current project phase
|
|
403
|
+
*/
|
|
404
|
+
getAvailableSkillsForPhase(phase: ProjectPhase): string[] {
|
|
405
|
+
return [...(SKILLS_BY_PHASE[phase] || SKILLS_BY_PHASE.idle)]
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
async execute(input: RouterInputType, context: SkillContext): Promise<RouterOutputType> {
|
|
409
|
+
const { logger, config } = context
|
|
410
|
+
const dispatcher = createDispatcher({ logger })
|
|
411
|
+
const model = config.defaultModel || 'glm-5'
|
|
412
|
+
|
|
413
|
+
logger.info('[router] Starting 5-layer routing process')
|
|
414
|
+
|
|
415
|
+
// -----------------------------------------------------------------------
|
|
416
|
+
// Read Project State (STATE.md and PLAN.md)
|
|
417
|
+
// -----------------------------------------------------------------------
|
|
418
|
+
|
|
419
|
+
let workflowState: WorkflowState
|
|
420
|
+
let hasPlan = false
|
|
421
|
+
|
|
422
|
+
try {
|
|
423
|
+
workflowState = await this.stateManager.readCurrentState()
|
|
424
|
+
logger.info(`[router] Read workflow state: phase=${workflowState.phase}`)
|
|
425
|
+
} catch (error) {
|
|
426
|
+
logger.warn('[router] No existing workflow state, using defaults')
|
|
427
|
+
workflowState = {
|
|
428
|
+
goal: '',
|
|
429
|
+
phase: 'planning',
|
|
430
|
+
startedAt: new Date().toISOString(),
|
|
431
|
+
lastUpdated: new Date().toISOString(),
|
|
432
|
+
completedTasks: [],
|
|
433
|
+
failedTasks: [],
|
|
434
|
+
totalTasks: 0,
|
|
435
|
+
tokenUsage: { used: 0, limit: 1000000, percentage: 0 },
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
try {
|
|
440
|
+
await this.planManager.readPlan()
|
|
441
|
+
hasPlan = true
|
|
442
|
+
logger.info('[router] Plan file exists')
|
|
443
|
+
} catch {
|
|
444
|
+
logger.info('[router] No plan file found')
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
const projectState = buildProjectState(workflowState, hasPlan)
|
|
448
|
+
const availableSkills = this.getAvailableSkillsForPhase(projectState.phase)
|
|
449
|
+
|
|
450
|
+
logger.info(`[router] Available skills for ${projectState.phase}: ${availableSkills.join(', ')}`)
|
|
451
|
+
|
|
452
|
+
// -----------------------------------------------------------------------
|
|
453
|
+
// Layer 1: Parse & Classify (Dispatch subagent)
|
|
454
|
+
// -----------------------------------------------------------------------
|
|
455
|
+
|
|
456
|
+
logger.info('[router] Layer 1: Parsing and classifying request')
|
|
457
|
+
|
|
458
|
+
const layer1Config: SubagentConfig = {
|
|
459
|
+
role: 'explorer',
|
|
460
|
+
model,
|
|
461
|
+
tokenBudget: 8000,
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
const layer1Contract: SubagentContract = {
|
|
465
|
+
permissions: {
|
|
466
|
+
readFiles: false,
|
|
467
|
+
searchCode: false,
|
|
468
|
+
runCommands: false,
|
|
469
|
+
writeFiles: false,
|
|
470
|
+
gitOperations: false,
|
|
471
|
+
},
|
|
472
|
+
prompt: buildLayer1Prompt(input.userRequest, (input.conversationHistory ?? []) as Array<{ role: 'user' | 'assistant'; content: string }>),
|
|
473
|
+
owns: [],
|
|
474
|
+
reads: [],
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
const layer1Result = await dispatcher.dispatch(layer1Config, layer1Contract)
|
|
478
|
+
const classification = parseLayer1Output(layer1Result.output)
|
|
479
|
+
|
|
480
|
+
logger.info(`[router] Layer 1 complete: intent=${classification.intent}, complexity=${classification.complexity}`)
|
|
481
|
+
|
|
482
|
+
// -----------------------------------------------------------------------
|
|
483
|
+
// Layer 2: State-Aware Scoring (Pure function)
|
|
484
|
+
// -----------------------------------------------------------------------
|
|
485
|
+
|
|
486
|
+
logger.info('[router] Layer 2: Calculating state factors')
|
|
487
|
+
|
|
488
|
+
const stateFactors = calculateStateFactors(classification, projectState)
|
|
489
|
+
|
|
490
|
+
logger.info(`[router] State factors: phase=${stateFactors.phaseWeight.toFixed(2)}, execution=${stateFactors.executionWeight.toFixed(2)}`)
|
|
491
|
+
|
|
492
|
+
// -----------------------------------------------------------------------
|
|
493
|
+
// Layer 3: Skill Matching (Dispatch subagent)
|
|
494
|
+
// -----------------------------------------------------------------------
|
|
495
|
+
|
|
496
|
+
logger.info('[router] Layer 3: Matching skills')
|
|
497
|
+
|
|
498
|
+
const layer3Config: SubagentConfig = {
|
|
499
|
+
role: 'planner',
|
|
500
|
+
model,
|
|
501
|
+
tokenBudget: 10000,
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
const layer3Contract: SubagentContract = {
|
|
505
|
+
permissions: {
|
|
506
|
+
readFiles: false,
|
|
507
|
+
searchCode: false,
|
|
508
|
+
runCommands: false,
|
|
509
|
+
writeFiles: false,
|
|
510
|
+
gitOperations: false,
|
|
511
|
+
},
|
|
512
|
+
prompt: buildLayer3Prompt(classification, projectState, availableSkills),
|
|
513
|
+
owns: [],
|
|
514
|
+
reads: [],
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
const layer3Result = await dispatcher.dispatch(layer3Config, layer3Contract)
|
|
518
|
+
const skillScores = parseLayer3Output(layer3Result.output)
|
|
519
|
+
|
|
520
|
+
logger.info(`[router] Layer 3 complete: ${skillScores.length} skills scored`)
|
|
521
|
+
|
|
522
|
+
// -----------------------------------------------------------------------
|
|
523
|
+
// Layer 4: Confidence Calibration (Pure function)
|
|
524
|
+
// -----------------------------------------------------------------------
|
|
525
|
+
|
|
526
|
+
logger.info('[router] Layer 4: Calibrating confidence')
|
|
527
|
+
|
|
528
|
+
const calibratedScores = calibrateConfidence(skillScores, stateFactors, classification)
|
|
529
|
+
|
|
530
|
+
logger.info(`[router] Layer 4 complete: top score = ${Math.max(...calibratedScores.map(s => s.stateAdjustedScore)).toFixed(2)}`)
|
|
531
|
+
|
|
532
|
+
// -----------------------------------------------------------------------
|
|
533
|
+
// Layer 5: Selection & Output (Dispatch subagent)
|
|
534
|
+
// -----------------------------------------------------------------------
|
|
535
|
+
|
|
536
|
+
logger.info('[router] Layer 5: Making final selection')
|
|
537
|
+
|
|
538
|
+
const layer5Config: SubagentConfig = {
|
|
539
|
+
role: 'planner',
|
|
540
|
+
model,
|
|
541
|
+
tokenBudget: 8000,
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
const layer5Contract: SubagentContract = {
|
|
545
|
+
permissions: {
|
|
546
|
+
readFiles: false,
|
|
547
|
+
searchCode: false,
|
|
548
|
+
runCommands: false,
|
|
549
|
+
writeFiles: false,
|
|
550
|
+
gitOperations: false,
|
|
551
|
+
},
|
|
552
|
+
prompt: buildLayer5Prompt(calibratedScores, projectState),
|
|
553
|
+
owns: [],
|
|
554
|
+
reads: [],
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
const layer5Result = await dispatcher.dispatch(layer5Config, layer5Contract)
|
|
558
|
+
const decision = parseLayer5Output(layer5Result.output)
|
|
559
|
+
|
|
560
|
+
logger.info(`[router] Layer 5 complete: selected=${decision.selectedSkill}, confidence=${decision.finalConfidence.toFixed(2)}`)
|
|
561
|
+
|
|
562
|
+
// -----------------------------------------------------------------------
|
|
563
|
+
// Return Structured Output
|
|
564
|
+
// -----------------------------------------------------------------------
|
|
565
|
+
|
|
566
|
+
return {
|
|
567
|
+
selectedSkill: decision.selectedSkill,
|
|
568
|
+
confidence: decision.finalConfidence,
|
|
569
|
+
reasoning: decision.reasoning,
|
|
570
|
+
projectState,
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
// Singleton instance
|
|
576
|
+
export const routerSkill = new RouterSkill()
|
|
577
|
+
export default routerSkill
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Router Skill Types
|
|
3
|
+
*
|
|
4
|
+
* Type definitions for the router skill that determines which skill
|
|
5
|
+
* should handle a given user request based on conversation context.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Input to the router skill
|
|
10
|
+
*/
|
|
11
|
+
export interface RouterInput {
|
|
12
|
+
/** The user's current request/message */
|
|
13
|
+
userRequest: string;
|
|
14
|
+
/** Optional conversation history for context */
|
|
15
|
+
conversationHistory?: Array<{
|
|
16
|
+
role: 'user' | 'assistant';
|
|
17
|
+
content: string;
|
|
18
|
+
}>;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Project lifecycle phases
|
|
23
|
+
*/
|
|
24
|
+
export type ProjectPhase =
|
|
25
|
+
| 'discovery'
|
|
26
|
+
| 'planning'
|
|
27
|
+
| 'execution'
|
|
28
|
+
| 'verification'
|
|
29
|
+
| 'completed'
|
|
30
|
+
| 'idle';
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Current state of the project being worked on
|
|
34
|
+
*/
|
|
35
|
+
export interface ProjectState {
|
|
36
|
+
/** Current phase of the project lifecycle */
|
|
37
|
+
phase: ProjectPhase;
|
|
38
|
+
/** Whether a plan has been created */
|
|
39
|
+
hasPlan: boolean;
|
|
40
|
+
/** Whether execution is currently in progress */
|
|
41
|
+
isExecuting: boolean;
|
|
42
|
+
/** Number of completed tasks */
|
|
43
|
+
completedTasks: number;
|
|
44
|
+
/** Total number of tasks in the plan */
|
|
45
|
+
totalTasks: number;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Output from the router skill
|
|
50
|
+
*/
|
|
51
|
+
export interface RouterOutput {
|
|
52
|
+
/** The skill selected to handle the request */
|
|
53
|
+
selectedSkill: string;
|
|
54
|
+
/** Confidence score (0-1) in the selection */
|
|
55
|
+
confidence: number;
|
|
56
|
+
/** Explanation of why this skill was selected */
|
|
57
|
+
reasoning: string;
|
|
58
|
+
/** Current project state at time of routing */
|
|
59
|
+
projectState: ProjectState;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Alternative skill that could have been selected
|
|
64
|
+
*/
|
|
65
|
+
export interface RoutingAlternative {
|
|
66
|
+
/** Name of the alternative skill */
|
|
67
|
+
skill: string;
|
|
68
|
+
/** Confidence score for this alternative */
|
|
69
|
+
confidence: number;
|
|
70
|
+
/** Why this skill was considered */
|
|
71
|
+
reasoning: string;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Complete routing decision with full context
|
|
76
|
+
*/
|
|
77
|
+
export interface RoutingDecision {
|
|
78
|
+
/** The selected target skill */
|
|
79
|
+
targetSkill: string;
|
|
80
|
+
/** Confidence score (0-1) in the decision */
|
|
81
|
+
confidence: number;
|
|
82
|
+
/** Detailed reasoning for the selection */
|
|
83
|
+
reasoning: string;
|
|
84
|
+
/** Whether project state was used in the decision */
|
|
85
|
+
stateUsed: boolean;
|
|
86
|
+
/** Current lifecycle phase when decision was made */
|
|
87
|
+
lifecyclePhase: ProjectPhase;
|
|
88
|
+
/** Alternative skills that were considered */
|
|
89
|
+
alternatives: RoutingAlternative[];
|
|
90
|
+
}
|
|
@@ -235,7 +235,7 @@ export class SystematicDebuggingSkill extends Skill<DebugInput, DebugOutput> {
|
|
|
235
235
|
context: SkillContext,
|
|
236
236
|
): Promise<DebugOutput> {
|
|
237
237
|
const { config, logger } = context
|
|
238
|
-
const dispatcher = createDispatcher(logger)
|
|
238
|
+
const dispatcher = createDispatcher({ logger })
|
|
239
239
|
const guard = createMainAgentGuard({}, logger)
|
|
240
240
|
let totalTokens = 0
|
|
241
241
|
|
package/src/skills/tdd/index.ts
CHANGED
|
@@ -190,7 +190,7 @@ export class TDDSkill extends Skill<TDDInput, TDDOutput> {
|
|
|
190
190
|
context: SkillContext,
|
|
191
191
|
): Promise<TDDOutput> {
|
|
192
192
|
const { config, logger } = context
|
|
193
|
-
const dispatcher = createDispatcher(logger)
|
|
193
|
+
const dispatcher = createDispatcher({ logger })
|
|
194
194
|
const guard = createMainAgentGuard({}, logger)
|
|
195
195
|
|
|
196
196
|
const phases: PhaseResult[] = []
|