@haoyiyin/workflow 0.2.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/README.md +168 -0
- package/SETUP.md +71 -0
- package/SKILL.md +68 -0
- package/USAGE.md +152 -0
- package/dist/bin/yi-workflow.d.ts +3 -0
- package/dist/bin/yi-workflow.d.ts.map +1 -0
- package/dist/bin/yi-workflow.js +39 -0
- package/dist/bin/yi-workflow.js.map +1 -0
- package/dist/src/agents/contracts.d.ts +120 -0
- package/dist/src/agents/contracts.d.ts.map +1 -0
- package/dist/src/agents/contracts.js +431 -0
- package/dist/src/agents/contracts.js.map +1 -0
- package/dist/src/agents/dispatcher-enhanced.d.ts +68 -0
- package/dist/src/agents/dispatcher-enhanced.d.ts.map +1 -0
- package/dist/src/agents/dispatcher-enhanced.js +290 -0
- package/dist/src/agents/dispatcher-enhanced.js.map +1 -0
- package/dist/src/agents/dispatcher.d.ts +107 -0
- package/dist/src/agents/dispatcher.d.ts.map +1 -0
- package/dist/src/agents/dispatcher.js +454 -0
- package/dist/src/agents/dispatcher.js.map +1 -0
- package/dist/src/agents/index.d.ts +11 -0
- package/dist/src/agents/index.d.ts.map +1 -0
- package/dist/src/agents/index.js +10 -0
- package/dist/src/agents/index.js.map +1 -0
- package/dist/src/agents/resilience.d.ts +86 -0
- package/dist/src/agents/resilience.d.ts.map +1 -0
- package/dist/src/agents/resilience.js +183 -0
- package/dist/src/agents/resilience.js.map +1 -0
- package/dist/src/agents/token-budget.d.ts +47 -0
- package/dist/src/agents/token-budget.d.ts.map +1 -0
- package/dist/src/agents/token-budget.js +63 -0
- package/dist/src/agents/token-budget.js.map +1 -0
- package/dist/src/agents/types.d.ts +59 -0
- package/dist/src/agents/types.d.ts.map +1 -0
- package/dist/src/agents/types.js +5 -0
- package/dist/src/agents/types.js.map +1 -0
- package/dist/src/guard/main-agent.d.ts +72 -0
- package/dist/src/guard/main-agent.d.ts.map +1 -0
- package/dist/src/guard/main-agent.js +184 -0
- package/dist/src/guard/main-agent.js.map +1 -0
- package/dist/src/hooks/builtin/index.d.ts +9 -0
- package/dist/src/hooks/builtin/index.d.ts.map +1 -0
- package/dist/src/hooks/builtin/index.js +9 -0
- package/dist/src/hooks/builtin/index.js.map +1 -0
- package/dist/src/hooks/builtin/on-error.d.ts +7 -0
- package/dist/src/hooks/builtin/on-error.d.ts.map +1 -0
- package/dist/src/hooks/builtin/on-error.js +15 -0
- package/dist/src/hooks/builtin/on-error.js.map +1 -0
- package/dist/src/hooks/builtin/post-execute.d.ts +7 -0
- package/dist/src/hooks/builtin/post-execute.d.ts.map +1 -0
- package/dist/src/hooks/builtin/post-execute.js +30 -0
- package/dist/src/hooks/builtin/post-execute.js.map +1 -0
- package/dist/src/hooks/builtin/post-plan.d.ts +7 -0
- package/dist/src/hooks/builtin/post-plan.d.ts.map +1 -0
- package/dist/src/hooks/builtin/post-plan.js +15 -0
- package/dist/src/hooks/builtin/post-plan.js.map +1 -0
- package/dist/src/hooks/builtin/pre-execute.d.ts +7 -0
- package/dist/src/hooks/builtin/pre-execute.d.ts.map +1 -0
- package/dist/src/hooks/builtin/pre-execute.js +22 -0
- package/dist/src/hooks/builtin/pre-execute.js.map +1 -0
- package/dist/src/hooks/builtin/pre-plan.d.ts +7 -0
- package/dist/src/hooks/builtin/pre-plan.d.ts.map +1 -0
- package/dist/src/hooks/builtin/pre-plan.js +19 -0
- package/dist/src/hooks/builtin/pre-plan.js.map +1 -0
- package/dist/src/hooks/index.d.ts +8 -0
- package/dist/src/hooks/index.d.ts.map +1 -0
- package/dist/src/hooks/index.js +4 -0
- package/dist/src/hooks/index.js.map +1 -0
- package/dist/src/hooks/loader.d.ts +16 -0
- package/dist/src/hooks/loader.d.ts.map +1 -0
- package/dist/src/hooks/loader.js +77 -0
- package/dist/src/hooks/loader.js.map +1 -0
- package/dist/src/hooks/manager.d.ts +20 -0
- package/dist/src/hooks/manager.d.ts.map +1 -0
- package/dist/src/hooks/manager.js +76 -0
- package/dist/src/hooks/manager.js.map +1 -0
- package/dist/src/hooks/types-enhanced.d.ts +30 -0
- package/dist/src/hooks/types-enhanced.d.ts.map +1 -0
- package/dist/src/hooks/types-enhanced.js +22 -0
- package/dist/src/hooks/types-enhanced.js.map +1 -0
- package/dist/src/hooks/types.d.ts +27 -0
- package/dist/src/hooks/types.d.ts.map +1 -0
- package/dist/src/hooks/types.js +2 -0
- package/dist/src/hooks/types.js.map +1 -0
- package/dist/src/index.d.ts +43 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +41 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/persistence/index.d.ts +7 -0
- package/dist/src/persistence/index.d.ts.map +1 -0
- package/dist/src/persistence/index.js +6 -0
- package/dist/src/persistence/index.js.map +1 -0
- package/dist/src/persistence/plan-md.d.ts +11 -0
- package/dist/src/persistence/plan-md.d.ts.map +1 -0
- package/dist/src/persistence/plan-md.js +125 -0
- package/dist/src/persistence/plan-md.js.map +1 -0
- package/dist/src/persistence/state-md.d.ts +17 -0
- package/dist/src/persistence/state-md.d.ts.map +1 -0
- package/dist/src/persistence/state-md.js +143 -0
- package/dist/src/persistence/state-md.js.map +1 -0
- package/dist/src/persistence/types.d.ts +85 -0
- package/dist/src/persistence/types.d.ts.map +1 -0
- package/dist/src/persistence/types.js +5 -0
- package/dist/src/persistence/types.js.map +1 -0
- package/dist/src/router/classifier.d.ts +108 -0
- package/dist/src/router/classifier.d.ts.map +1 -0
- package/dist/src/router/classifier.js +476 -0
- package/dist/src/router/classifier.js.map +1 -0
- package/dist/src/router/guard.d.ts +128 -0
- package/dist/src/router/guard.d.ts.map +1 -0
- package/dist/src/router/guard.js +370 -0
- package/dist/src/router/guard.js.map +1 -0
- package/dist/src/router/index.d.ts +10 -0
- package/dist/src/router/index.d.ts.map +1 -0
- package/dist/src/router/index.js +8 -0
- package/dist/src/router/index.js.map +1 -0
- package/dist/src/router/router.d.ts +58 -0
- package/dist/src/router/router.d.ts.map +1 -0
- package/dist/src/router/router.js +78 -0
- package/dist/src/router/router.js.map +1 -0
- package/dist/src/router/types.d.ts +100 -0
- package/dist/src/router/types.d.ts.map +1 -0
- package/dist/src/router/types.js +24 -0
- package/dist/src/router/types.js.map +1 -0
- package/dist/src/skills/agents-md/index.d.ts +9 -0
- package/dist/src/skills/agents-md/index.d.ts.map +1 -0
- package/dist/src/skills/agents-md/index.js +28 -0
- package/dist/src/skills/agents-md/index.js.map +1 -0
- package/dist/src/skills/execute-plan/index.d.ts +141 -0
- package/dist/src/skills/execute-plan/index.d.ts.map +1 -0
- package/dist/src/skills/execute-plan/index.js +784 -0
- package/dist/src/skills/execute-plan/index.js.map +1 -0
- package/dist/src/skills/index.d.ts +14 -0
- package/dist/src/skills/index.d.ts.map +1 -0
- package/dist/src/skills/index.js +10 -0
- package/dist/src/skills/index.js.map +1 -0
- package/dist/src/skills/quick-task/index.d.ts +75 -0
- package/dist/src/skills/quick-task/index.d.ts.map +1 -0
- package/dist/src/skills/quick-task/index.js +284 -0
- package/dist/src/skills/quick-task/index.js.map +1 -0
- package/dist/src/skills/registry.d.ts +15 -0
- package/dist/src/skills/registry.d.ts.map +1 -0
- package/dist/src/skills/registry.js +44 -0
- package/dist/src/skills/registry.js.map +1 -0
- package/dist/src/skills/review-diff/index.d.ts +96 -0
- package/dist/src/skills/review-diff/index.d.ts.map +1 -0
- package/dist/src/skills/review-diff/index.js +316 -0
- package/dist/src/skills/review-diff/index.js.map +1 -0
- package/dist/src/skills/skill.d.ts +24 -0
- package/dist/src/skills/skill.d.ts.map +1 -0
- package/dist/src/skills/skill.js +39 -0
- package/dist/src/skills/skill.js.map +1 -0
- package/dist/src/skills/systematic-debugging/index.d.ts +90 -0
- package/dist/src/skills/systematic-debugging/index.d.ts.map +1 -0
- package/dist/src/skills/systematic-debugging/index.js +305 -0
- package/dist/src/skills/systematic-debugging/index.js.map +1 -0
- package/dist/src/skills/tdd/index.d.ts +103 -0
- package/dist/src/skills/tdd/index.d.ts.map +1 -0
- package/dist/src/skills/tdd/index.js +338 -0
- package/dist/src/skills/tdd/index.js.map +1 -0
- package/dist/src/skills/to-plan/index-enhanced.d.ts +100 -0
- package/dist/src/skills/to-plan/index-enhanced.d.ts.map +1 -0
- package/dist/src/skills/to-plan/index-enhanced.js +452 -0
- package/dist/src/skills/to-plan/index-enhanced.js.map +1 -0
- package/dist/src/skills/to-plan/index.d.ts +131 -0
- package/dist/src/skills/to-plan/index.d.ts.map +1 -0
- package/dist/src/skills/to-plan/index.js +460 -0
- package/dist/src/skills/to-plan/index.js.map +1 -0
- package/dist/src/skills/types.d.ts +44 -0
- package/dist/src/skills/types.d.ts.map +1 -0
- package/dist/src/skills/types.js +2 -0
- package/dist/src/skills/types.js.map +1 -0
- package/dist/src/state/cleanup.d.ts +22 -0
- package/dist/src/state/cleanup.d.ts.map +1 -0
- package/dist/src/state/cleanup.js +87 -0
- package/dist/src/state/cleanup.js.map +1 -0
- package/dist/src/state/index.d.ts +9 -0
- package/dist/src/state/index.d.ts.map +1 -0
- package/dist/src/state/index.js +5 -0
- package/dist/src/state/index.js.map +1 -0
- package/dist/src/state/manager.d.ts +15 -0
- package/dist/src/state/manager.d.ts.map +1 -0
- package/dist/src/state/manager.js +73 -0
- package/dist/src/state/manager.js.map +1 -0
- package/dist/src/state/persistence.d.ts +13 -0
- package/dist/src/state/persistence.d.ts.map +1 -0
- package/dist/src/state/persistence.js +68 -0
- package/dist/src/state/persistence.js.map +1 -0
- package/dist/src/state/types.d.ts +26 -0
- package/dist/src/state/types.d.ts.map +1 -0
- package/dist/src/state/types.js +2 -0
- package/dist/src/state/types.js.map +1 -0
- package/dist/src/state/validator.d.ts +12 -0
- package/dist/src/state/validator.d.ts.map +1 -0
- package/dist/src/state/validator.js +56 -0
- package/dist/src/state/validator.js.map +1 -0
- package/dist/src/types.d.ts +83 -0
- package/dist/src/types.d.ts.map +1 -0
- package/dist/src/types.js +5 -0
- package/dist/src/types.js.map +1 -0
- package/dist/src/utils/compress.d.ts +37 -0
- package/dist/src/utils/compress.d.ts.map +1 -0
- package/dist/src/utils/compress.js +298 -0
- package/dist/src/utils/compress.js.map +1 -0
- package/dist/src/utils/git.d.ts +9 -0
- package/dist/src/utils/git.d.ts.map +1 -0
- package/dist/src/utils/git.js +81 -0
- package/dist/src/utils/git.js.map +1 -0
- package/dist/src/utils/index.d.ts +7 -0
- package/dist/src/utils/index.d.ts.map +1 -0
- package/dist/src/utils/index.js +7 -0
- package/dist/src/utils/index.js.map +1 -0
- package/dist/src/utils/logger.d.ts +6 -0
- package/dist/src/utils/logger.d.ts.map +1 -0
- package/dist/src/utils/logger.js +19 -0
- package/dist/src/utils/logger.js.map +1 -0
- package/dist/src/utils/paths.d.ts +12 -0
- package/dist/src/utils/paths.d.ts.map +1 -0
- package/dist/src/utils/paths.js +44 -0
- package/dist/src/utils/paths.js.map +1 -0
- package/package.json +76 -0
- package/scripts/postinstall.js +69 -0
- package/yi-workflow.js +17 -0
|
@@ -0,0 +1,476 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Request Classifier - determines what type of subagent should handle a request.
|
|
3
|
+
*
|
|
4
|
+
* Analyzes user request text and contextual signals to produce an
|
|
5
|
+
* IntentClassification, then builds a RoutingDecision with the appropriate
|
|
6
|
+
* SubagentConfig and SubagentContract.
|
|
7
|
+
*
|
|
8
|
+
* Classification follows a priority-ordered chain:
|
|
9
|
+
* 1. execute (plan file exists / mid-execution)
|
|
10
|
+
* 2. quick-task (trivial typo/whitespace, small scoped change)
|
|
11
|
+
* 3. review (code review request)
|
|
12
|
+
* 4. debug (root cause analysis)
|
|
13
|
+
* 5. tdd (test-driven development request)
|
|
14
|
+
* 6. plan (new feature / significant change)
|
|
15
|
+
* 7. general (catch-all)
|
|
16
|
+
*/
|
|
17
|
+
import { ROUTE_KEYWORDS } from './types.js';
|
|
18
|
+
// ---------------------------------------------------------------------------
|
|
19
|
+
// Constants
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
/** Token budget recommendations by intent type (in thousands). */
|
|
22
|
+
const TOKEN_BUDGETS = {
|
|
23
|
+
plan: 64,
|
|
24
|
+
'quick-task': 32,
|
|
25
|
+
execute: 64,
|
|
26
|
+
review: 32,
|
|
27
|
+
debug: 64,
|
|
28
|
+
tdd: 64,
|
|
29
|
+
general: 32,
|
|
30
|
+
};
|
|
31
|
+
/** Subagent roles mapped from intent types. */
|
|
32
|
+
const SUBAGENT_ROLES = {
|
|
33
|
+
plan: 'planner',
|
|
34
|
+
'quick-task': 'implementer',
|
|
35
|
+
execute: 'implementer',
|
|
36
|
+
review: 'reviewer',
|
|
37
|
+
debug: 'debugger',
|
|
38
|
+
tdd: 'implementer',
|
|
39
|
+
general: 'general',
|
|
40
|
+
};
|
|
41
|
+
/** Default permissions by intent type. */
|
|
42
|
+
const SUBAGENT_PERMISSIONS = {
|
|
43
|
+
plan: {
|
|
44
|
+
readFiles: true,
|
|
45
|
+
searchCode: true,
|
|
46
|
+
runCommands: false,
|
|
47
|
+
writeFiles: true,
|
|
48
|
+
gitOperations: false,
|
|
49
|
+
},
|
|
50
|
+
'quick-task': {
|
|
51
|
+
readFiles: true,
|
|
52
|
+
searchCode: true,
|
|
53
|
+
runCommands: true,
|
|
54
|
+
writeFiles: true,
|
|
55
|
+
gitOperations: false,
|
|
56
|
+
},
|
|
57
|
+
execute: {
|
|
58
|
+
readFiles: true,
|
|
59
|
+
searchCode: true,
|
|
60
|
+
runCommands: true,
|
|
61
|
+
writeFiles: true,
|
|
62
|
+
gitOperations: true,
|
|
63
|
+
},
|
|
64
|
+
review: {
|
|
65
|
+
readFiles: true,
|
|
66
|
+
searchCode: true,
|
|
67
|
+
runCommands: false,
|
|
68
|
+
writeFiles: false,
|
|
69
|
+
gitOperations: false,
|
|
70
|
+
},
|
|
71
|
+
debug: {
|
|
72
|
+
readFiles: true,
|
|
73
|
+
searchCode: true,
|
|
74
|
+
runCommands: true,
|
|
75
|
+
writeFiles: false,
|
|
76
|
+
gitOperations: false,
|
|
77
|
+
},
|
|
78
|
+
tdd: {
|
|
79
|
+
readFiles: true,
|
|
80
|
+
searchCode: true,
|
|
81
|
+
runCommands: true,
|
|
82
|
+
writeFiles: true,
|
|
83
|
+
gitOperations: false,
|
|
84
|
+
},
|
|
85
|
+
general: {
|
|
86
|
+
readFiles: true,
|
|
87
|
+
searchCode: true,
|
|
88
|
+
runCommands: false,
|
|
89
|
+
writeFiles: false,
|
|
90
|
+
gitOperations: false,
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
/** Whether a given intent type uses worktree isolation. */
|
|
94
|
+
const SUBAGENT_ISOLATION = {
|
|
95
|
+
execute: 'worktree',
|
|
96
|
+
plan: 'worktree',
|
|
97
|
+
};
|
|
98
|
+
// ---------------------------------------------------------------------------
|
|
99
|
+
// Decision builders
|
|
100
|
+
// ---------------------------------------------------------------------------
|
|
101
|
+
/**
|
|
102
|
+
* Build an IntentClassification from type and metadata.
|
|
103
|
+
*/
|
|
104
|
+
function buildClassification(type, confidence, reasoning) {
|
|
105
|
+
return {
|
|
106
|
+
type,
|
|
107
|
+
confidence,
|
|
108
|
+
reasoning,
|
|
109
|
+
suggestedAgent: SUBAGENT_ROLES[type],
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Build a RoutingDecision: subagent config + contract + async flag.
|
|
114
|
+
*/
|
|
115
|
+
function buildRouting(type, prompt, config, owns = [], reads = []) {
|
|
116
|
+
const subagentConfig = {
|
|
117
|
+
role: SUBAGENT_ROLES[type],
|
|
118
|
+
model: config.defaultModel,
|
|
119
|
+
isolation: SUBAGENT_ISOLATION[type],
|
|
120
|
+
tokenBudget: TOKEN_BUDGETS[type] * 1000,
|
|
121
|
+
};
|
|
122
|
+
const contract = {
|
|
123
|
+
permissions: { ...SUBAGENT_PERMISSIONS[type] },
|
|
124
|
+
prompt,
|
|
125
|
+
owns,
|
|
126
|
+
reads,
|
|
127
|
+
};
|
|
128
|
+
return {
|
|
129
|
+
subagent: subagentConfig,
|
|
130
|
+
contract,
|
|
131
|
+
async: type !== 'quick-task',
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
// ---------------------------------------------------------------------------
|
|
135
|
+
// Pattern matchers
|
|
136
|
+
// ---------------------------------------------------------------------------
|
|
137
|
+
/**
|
|
138
|
+
* Check if request matches a trivial edit (typo, whitespace, single-line fix).
|
|
139
|
+
* The ONE exception to full delegation — quick-task can handle directly.
|
|
140
|
+
*/
|
|
141
|
+
function matchTrivialEdit(request) {
|
|
142
|
+
const normalized = request.toLowerCase().trim();
|
|
143
|
+
const patterns = [
|
|
144
|
+
/^fix\s+(a\s+)?typo\b/i,
|
|
145
|
+
/^fix\s+(a\s+)?spell(ing)?\s+(error|mistake)\b/i,
|
|
146
|
+
/^fix\s+whitespace\b/i,
|
|
147
|
+
/^remove\s+trailing\s+whitespace\b/i,
|
|
148
|
+
/^fix\s+lint(ing)?\s+(error|warning)\b/i,
|
|
149
|
+
/^format\s+(this|a|the)\s+(file|code)\b/i,
|
|
150
|
+
/^(add|remove)\s+(a\s+)?missing\s+semicolon/i,
|
|
151
|
+
/^change\s+(a\s+)?single\s+(character|char|letter)\b/i,
|
|
152
|
+
/^rename\s+(a\s+)?single\s+variable\b/i,
|
|
153
|
+
];
|
|
154
|
+
for (const p of patterns) {
|
|
155
|
+
if (p.test(normalized)) {
|
|
156
|
+
return {
|
|
157
|
+
type: 'quick-task',
|
|
158
|
+
confidence: 0.85,
|
|
159
|
+
reasoning: 'Trivial single-line fix — route to quick-task',
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
// Short request referencing a specific line number
|
|
164
|
+
if (normalized.length < 60 && /\bline\s+\d+\b/.test(normalized)) {
|
|
165
|
+
return {
|
|
166
|
+
type: 'quick-task',
|
|
167
|
+
confidence: 0.7,
|
|
168
|
+
reasoning: 'Very short request references a specific line number',
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
return null;
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Check if request is a code review.
|
|
175
|
+
*/
|
|
176
|
+
function matchReview(request) {
|
|
177
|
+
const keywords = ROUTE_KEYWORDS.review;
|
|
178
|
+
const normalized = request.toLowerCase().trim();
|
|
179
|
+
for (const kw of keywords) {
|
|
180
|
+
if (normalized.includes(kw)) {
|
|
181
|
+
return {
|
|
182
|
+
type: 'review',
|
|
183
|
+
confidence: 0.9,
|
|
184
|
+
reasoning: `Request matches review keyword: "${kw}"`,
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
return null;
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Check if request is about debugging / root cause analysis.
|
|
192
|
+
*/
|
|
193
|
+
function matchDebug(request) {
|
|
194
|
+
const keywords = ROUTE_KEYWORDS.debug;
|
|
195
|
+
const normalized = request.toLowerCase().trim();
|
|
196
|
+
for (const kw of keywords) {
|
|
197
|
+
if (normalized.includes(kw)) {
|
|
198
|
+
return {
|
|
199
|
+
type: 'debug',
|
|
200
|
+
confidence: 0.85,
|
|
201
|
+
reasoning: `Request matches debug keyword: "${kw}"`,
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
// Broader debug patterns
|
|
206
|
+
const broadPatterns = [
|
|
207
|
+
/\bwhy\s+(is|does|am\s*i|are)\b.*\b(error|fail|break|crash|wrong|bug)\b/i,
|
|
208
|
+
/\b(stack\s*trace|backtrace)\b/i,
|
|
209
|
+
/\binvestigate\s+(the|a|this|an)\s+(issue|error|bug|crash|problem)\b/i,
|
|
210
|
+
];
|
|
211
|
+
for (const p of broadPatterns) {
|
|
212
|
+
if (p.test(normalized)) {
|
|
213
|
+
return {
|
|
214
|
+
type: 'debug',
|
|
215
|
+
confidence: 0.75,
|
|
216
|
+
reasoning: 'Request matches broad debug pattern',
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
return null;
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Check if request is about test-driven development.
|
|
224
|
+
*/
|
|
225
|
+
function matchTdd(request) {
|
|
226
|
+
const keywords = ROUTE_KEYWORDS.tdd;
|
|
227
|
+
const normalized = request.toLowerCase().trim();
|
|
228
|
+
for (const kw of keywords) {
|
|
229
|
+
if (normalized.includes(kw)) {
|
|
230
|
+
return {
|
|
231
|
+
type: 'tdd',
|
|
232
|
+
confidence: 0.9,
|
|
233
|
+
reasoning: `Request matches TDD keyword: "${kw}"`,
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
return null;
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Check if request needs a full plan (complex, multi-step).
|
|
241
|
+
*/
|
|
242
|
+
function matchPlan(request) {
|
|
243
|
+
const keywords = ROUTE_KEYWORDS.plan;
|
|
244
|
+
const normalized = request.toLowerCase().trim();
|
|
245
|
+
for (const kw of keywords) {
|
|
246
|
+
if (normalized.includes(kw)) {
|
|
247
|
+
return {
|
|
248
|
+
type: 'plan',
|
|
249
|
+
confidence: 0.85,
|
|
250
|
+
reasoning: `Request matches planning keyword: "${kw}"`,
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
// Additional complexity indicators
|
|
255
|
+
const planIndicators = [
|
|
256
|
+
/\b(complex|major|big|large|significant)\s+(change|refactor|update|feature)\b/i,
|
|
257
|
+
/\b(multi[-\s]?(file|step|phase)|several\s+files?)\b/i,
|
|
258
|
+
/\b(end[-\s]?to[-\s]?end|full[-\s]?stack)\b/i,
|
|
259
|
+
/\b(from\s+scratch|restructure|reorgani[sz]e|rearchitect)\b/i,
|
|
260
|
+
];
|
|
261
|
+
for (const p of planIndicators) {
|
|
262
|
+
if (p.test(normalized)) {
|
|
263
|
+
return {
|
|
264
|
+
type: 'plan',
|
|
265
|
+
confidence: 0.75,
|
|
266
|
+
reasoning: 'Request contains complexity indicators — planning likely needed',
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
// Long requests (>500 chars) often need planning
|
|
271
|
+
if (normalized.length > 500) {
|
|
272
|
+
return {
|
|
273
|
+
type: 'plan',
|
|
274
|
+
confidence: 0.55,
|
|
275
|
+
reasoning: 'Long request suggests complex, multi-step work',
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
return null;
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* Check if request is a read-only / informational question.
|
|
282
|
+
*/
|
|
283
|
+
function matchGeneral(request) {
|
|
284
|
+
const keywords = ROUTE_KEYWORDS.general;
|
|
285
|
+
const normalized = request.toLowerCase().trim();
|
|
286
|
+
for (const kw of keywords) {
|
|
287
|
+
if (normalized.includes(kw)) {
|
|
288
|
+
return {
|
|
289
|
+
type: 'general',
|
|
290
|
+
confidence: 0.7,
|
|
291
|
+
reasoning: `Request matches general keyword: "${kw}"`,
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
return null;
|
|
296
|
+
}
|
|
297
|
+
// ---------------------------------------------------------------------------
|
|
298
|
+
// RequestClassifier
|
|
299
|
+
// ---------------------------------------------------------------------------
|
|
300
|
+
/**
|
|
301
|
+
* Analyzes user requests and produces routing decisions.
|
|
302
|
+
*
|
|
303
|
+
* Classification runs through a priority-ordered chain:
|
|
304
|
+
* 1. Context-based: plan exists → 'execute'
|
|
305
|
+
* 2. Pattern-based: trivial-edit → quick-task, review, debug, tdd, plan, general
|
|
306
|
+
* 3. Fallback: 'general'
|
|
307
|
+
*
|
|
308
|
+
* Usage:
|
|
309
|
+
* ```typescript
|
|
310
|
+
* const classifier = new RequestClassifier(config)
|
|
311
|
+
* const result = classifier.classify("Fix the typo in src/utils.ts", {
|
|
312
|
+
* planFileExists: false,
|
|
313
|
+
* isExecutingPlan: false,
|
|
314
|
+
* hasUncommittedChanges: true,
|
|
315
|
+
* mentionedFiles: ['src/utils.ts'],
|
|
316
|
+
* })
|
|
317
|
+
* // result.classification.type === 'quick-task'
|
|
318
|
+
* ```
|
|
319
|
+
*/
|
|
320
|
+
export class RequestClassifier {
|
|
321
|
+
config;
|
|
322
|
+
/** Priority-ordered classification rules. */
|
|
323
|
+
rules;
|
|
324
|
+
constructor(config) {
|
|
325
|
+
this.config = Object.freeze({ ...config });
|
|
326
|
+
this.rules = Object.freeze([
|
|
327
|
+
{ name: 'context-execute', match: (_req, ctx) => this.checkContextExecute(ctx) },
|
|
328
|
+
{ name: 'trivial-edit', match: (req, _ctx) => matchTrivialEdit(req) },
|
|
329
|
+
{ name: 'review', match: (req, _ctx) => matchReview(req) },
|
|
330
|
+
{ name: 'debug', match: (req, _ctx) => matchDebug(req) },
|
|
331
|
+
{ name: 'tdd', match: (req, _ctx) => matchTdd(req) },
|
|
332
|
+
{ name: 'plan', match: (req, _ctx) => matchPlan(req) },
|
|
333
|
+
{ name: 'general-keywords', match: (req, _ctx) => matchGeneral(req) },
|
|
334
|
+
]);
|
|
335
|
+
}
|
|
336
|
+
/**
|
|
337
|
+
* If a plan file exists or we are mid-execution, route to execute.
|
|
338
|
+
*/
|
|
339
|
+
checkContextExecute(ctx) {
|
|
340
|
+
if (ctx.isExecutingPlan) {
|
|
341
|
+
return {
|
|
342
|
+
type: 'execute',
|
|
343
|
+
confidence: 0.95,
|
|
344
|
+
reasoning: 'Currently executing a plan — continue with execute agent',
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
if (ctx.planFileExists) {
|
|
348
|
+
return {
|
|
349
|
+
type: 'execute',
|
|
350
|
+
confidence: 0.8,
|
|
351
|
+
reasoning: 'A plan file already exists for this task',
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
return null;
|
|
355
|
+
}
|
|
356
|
+
// -----------------------------------------------------------------------
|
|
357
|
+
// Public API
|
|
358
|
+
// -----------------------------------------------------------------------
|
|
359
|
+
/**
|
|
360
|
+
* Classify a user request and produce a full routing decision.
|
|
361
|
+
*
|
|
362
|
+
* @param request - Raw user request text
|
|
363
|
+
* @param context - Contextual signals to improve accuracy
|
|
364
|
+
* @returns ClassifierResult with classification metadata and routing decision
|
|
365
|
+
*/
|
|
366
|
+
classify(request, context) {
|
|
367
|
+
if (!request || request.trim().length === 0) {
|
|
368
|
+
const classification = buildClassification('general', 0.1, 'Empty request');
|
|
369
|
+
const routing = buildRouting('general', request, this.config);
|
|
370
|
+
return {
|
|
371
|
+
classification,
|
|
372
|
+
routing,
|
|
373
|
+
confidence: 0.1,
|
|
374
|
+
matchedRule: 'empty-request',
|
|
375
|
+
alternatives: [],
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
// Run through the priority-ordered rule chain
|
|
379
|
+
for (const rule of this.rules) {
|
|
380
|
+
try {
|
|
381
|
+
const match = rule.match(request, context);
|
|
382
|
+
if (match) {
|
|
383
|
+
const classification = buildClassification(match.type, match.confidence, match.reasoning);
|
|
384
|
+
const routing = buildRouting(match.type, request, this.config);
|
|
385
|
+
return {
|
|
386
|
+
classification,
|
|
387
|
+
routing,
|
|
388
|
+
confidence: match.confidence,
|
|
389
|
+
matchedRule: rule.name,
|
|
390
|
+
alternatives: this.computeAlternatives(match.type, match.confidence),
|
|
391
|
+
};
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
catch (error) {
|
|
395
|
+
console.error(`Classifier rule "${rule.name}" failed:`, error);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
// Fallback: general
|
|
399
|
+
const classification = buildClassification('general', 0.3, 'No specific pattern matched — routing to general agent');
|
|
400
|
+
const routing = buildRouting('general', request, this.config);
|
|
401
|
+
return {
|
|
402
|
+
classification,
|
|
403
|
+
routing,
|
|
404
|
+
confidence: 0.3,
|
|
405
|
+
matchedRule: 'fallback',
|
|
406
|
+
alternatives: [
|
|
407
|
+
{ type: 'plan', confidence: 0.3 },
|
|
408
|
+
{ type: 'quick-task', confidence: 0.2 },
|
|
409
|
+
{ type: 'debug', confidence: 0.2 },
|
|
410
|
+
],
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
/**
|
|
414
|
+
* Classify intent only (without building a full routing decision).
|
|
415
|
+
*/
|
|
416
|
+
classifyIntent(request, context) {
|
|
417
|
+
return this.classify(request, context).classification;
|
|
418
|
+
}
|
|
419
|
+
/**
|
|
420
|
+
* Produce just the routing decision.
|
|
421
|
+
*/
|
|
422
|
+
route(request, context) {
|
|
423
|
+
return this.classify(request, context).routing;
|
|
424
|
+
}
|
|
425
|
+
// -----------------------------------------------------------------------
|
|
426
|
+
// Configuration queries
|
|
427
|
+
// -----------------------------------------------------------------------
|
|
428
|
+
/** Get recommended token budget for an intent type. */
|
|
429
|
+
getTokenBudget(type) {
|
|
430
|
+
return TOKEN_BUDGETS[type] * 1000;
|
|
431
|
+
}
|
|
432
|
+
/** Get subagent role for an intent type. */
|
|
433
|
+
getSubagentRole(type) {
|
|
434
|
+
return SUBAGENT_ROLES[type];
|
|
435
|
+
}
|
|
436
|
+
/** Get default permissions for an intent type (returns a copy). */
|
|
437
|
+
getSubagentPermissions(type) {
|
|
438
|
+
return { ...SUBAGENT_PERMISSIONS[type] };
|
|
439
|
+
}
|
|
440
|
+
// -----------------------------------------------------------------------
|
|
441
|
+
// Reporting
|
|
442
|
+
// -----------------------------------------------------------------------
|
|
443
|
+
/**
|
|
444
|
+
* Generate a human-readable classification summary for logging.
|
|
445
|
+
*/
|
|
446
|
+
describeClassification(result) {
|
|
447
|
+
const { classification, confidence, matchedRule, routing } = result;
|
|
448
|
+
return [
|
|
449
|
+
`Intent: ${classification.type}`,
|
|
450
|
+
`Confidence: ${(confidence * 100).toFixed(0)}%`,
|
|
451
|
+
`Rule: ${matchedRule}`,
|
|
452
|
+
`Agent: ${classification.suggestedAgent}`,
|
|
453
|
+
`Reason: ${classification.reasoning}`,
|
|
454
|
+
`Async: ${routing.async}`,
|
|
455
|
+
routing.subagent.isolation ? `Isolation: ${routing.subagent.isolation}` : null,
|
|
456
|
+
]
|
|
457
|
+
.filter(Boolean)
|
|
458
|
+
.join(' | ');
|
|
459
|
+
}
|
|
460
|
+
// -----------------------------------------------------------------------
|
|
461
|
+
// Internal
|
|
462
|
+
// -----------------------------------------------------------------------
|
|
463
|
+
/**
|
|
464
|
+
* Compute alternative classifications based on the winning type.
|
|
465
|
+
*/
|
|
466
|
+
computeAlternatives(winningType, winningConfidence) {
|
|
467
|
+
const remaining = 1 - winningConfidence;
|
|
468
|
+
const allTypes = ['plan', 'quick-task', 'execute', 'review', 'debug', 'tdd', 'general'];
|
|
469
|
+
const others = allTypes.filter((t) => t !== winningType);
|
|
470
|
+
return others.slice(0, 3).map((type, i) => ({
|
|
471
|
+
type,
|
|
472
|
+
confidence: Math.round((remaining / (others.length - i)) * 100) / 100,
|
|
473
|
+
}));
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
//# sourceMappingURL=classifier.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"classifier.js","sourceRoot":"","sources":["../../../src/router/classifier.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Main Agent Guard - enforces that the main agent NEVER does substantive work.
|
|
3
|
+
*
|
|
4
|
+
* The guard is the enforcement mechanism for the "thin dispatcher" pattern.
|
|
5
|
+
* When embargo is active, the main agent is blocked from everything except
|
|
6
|
+
* meta-operations (reading plan files, state files, and dispatching subagents).
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* ```typescript
|
|
10
|
+
* const guard = MainAgentGuard.create({
|
|
11
|
+
* defaultModel: 'haiku',
|
|
12
|
+
* planningThreshold: 0.6,
|
|
13
|
+
* maxMainAgentTokens: 10000,
|
|
14
|
+
* autoRoute: true,
|
|
15
|
+
* })
|
|
16
|
+
*
|
|
17
|
+
* const check = guard.checkOperation('read-source', { path: 'src/main.ts' })
|
|
18
|
+
* if (!check.allowed) {
|
|
19
|
+
* throw new Error(check.reason)
|
|
20
|
+
* }
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
import type { RouterConfig } from './types.js';
|
|
24
|
+
/** Result of a guard check. */
|
|
25
|
+
export interface GuardCheckResult {
|
|
26
|
+
/** Whether the operation is permitted */
|
|
27
|
+
allowed: boolean;
|
|
28
|
+
/** Human-readable reason for the decision */
|
|
29
|
+
reason: string;
|
|
30
|
+
/** The specific rule that triggered (for debugging) */
|
|
31
|
+
rule?: string;
|
|
32
|
+
}
|
|
33
|
+
/** Context passed to guard checks for richer decision-making. */
|
|
34
|
+
export interface GuardContext {
|
|
35
|
+
/** The operation being attempted */
|
|
36
|
+
operation: string;
|
|
37
|
+
/** Optional file path involved */
|
|
38
|
+
path?: string;
|
|
39
|
+
/** Optional subagent ID if dispatching */
|
|
40
|
+
subagentId?: string;
|
|
41
|
+
}
|
|
42
|
+
/** Snapshot of guard state for monitoring/debugging. */
|
|
43
|
+
export interface GuardStatus {
|
|
44
|
+
embargoActive: boolean;
|
|
45
|
+
pendingSubagents: string[];
|
|
46
|
+
violations: GuardViolation[];
|
|
47
|
+
prohibitedOperations: string[];
|
|
48
|
+
allowedPaths: string[];
|
|
49
|
+
forbiddenPaths: string[];
|
|
50
|
+
}
|
|
51
|
+
/** Recorded violation for audit trail. */
|
|
52
|
+
export interface GuardViolation {
|
|
53
|
+
operation: string;
|
|
54
|
+
path?: string;
|
|
55
|
+
timestamp: Date;
|
|
56
|
+
reason: string;
|
|
57
|
+
}
|
|
58
|
+
/** Operation categories recognized by the guard. */
|
|
59
|
+
export type GuardOperation = 'read-source' | 'search-codebase' | 'write-files' | 'edit-files' | 'run-build' | 'run-tests' | 'git-commit' | 'git-push' | 'read-diffs' | 'long-running-task' | 'dispatch-subagent' | 'read-meta-file' | 'relay-results' | 'read-plan' | 'read-state';
|
|
60
|
+
/**
|
|
61
|
+
* Enforces the "main agent as thin dispatcher" constraint.
|
|
62
|
+
*
|
|
63
|
+
* When embargoActive is true, the main agent is FORBIDDEN from doing
|
|
64
|
+
* ANY substantive work. It can only read meta-files and dispatch subagents.
|
|
65
|
+
*/
|
|
66
|
+
export declare class MainAgentGuard {
|
|
67
|
+
private readonly config;
|
|
68
|
+
private readonly violations;
|
|
69
|
+
private readonly pendingSubagents;
|
|
70
|
+
private embargoActive;
|
|
71
|
+
private tokensUsed;
|
|
72
|
+
constructor(config: RouterConfig);
|
|
73
|
+
/** Create a guard with embargo active (strictest mode). */
|
|
74
|
+
static createEmbargoAll(config: RouterConfig): MainAgentGuard;
|
|
75
|
+
/** Create a guard with embargo lifted (use with extreme caution). */
|
|
76
|
+
static createDisabled(config: RouterConfig): MainAgentGuard;
|
|
77
|
+
/** Convenience: create with default config. */
|
|
78
|
+
static create(config: RouterConfig): MainAgentGuard;
|
|
79
|
+
/**
|
|
80
|
+
* Check whether an operation is permitted for the main agent.
|
|
81
|
+
*
|
|
82
|
+
* @param operation - The operation being attempted
|
|
83
|
+
* @returns GuardCheckResult with allowed flag and reason
|
|
84
|
+
*/
|
|
85
|
+
checkOperation(operation: GuardOperation): GuardCheckResult;
|
|
86
|
+
/**
|
|
87
|
+
* Check whether a file path is permitted to be read by the main agent.
|
|
88
|
+
*/
|
|
89
|
+
checkReadPath(filePath: string): GuardCheckResult;
|
|
90
|
+
/**
|
|
91
|
+
* Comprehensive check combining operation and optional file path/context.
|
|
92
|
+
*/
|
|
93
|
+
check(context: GuardContext): GuardCheckResult;
|
|
94
|
+
/**
|
|
95
|
+
* Assert an operation is allowed. Throws if violated.
|
|
96
|
+
*/
|
|
97
|
+
assertAllowed(context: GuardContext): void;
|
|
98
|
+
/**
|
|
99
|
+
* Record a violation without throwing (for audit trail).
|
|
100
|
+
*/
|
|
101
|
+
recordViolation(context: GuardContext): void;
|
|
102
|
+
/** Activate embargo mode - main agent must delegate everything. */
|
|
103
|
+
activateEmbargo(): void;
|
|
104
|
+
/** Deactivate embargo mode - main agent regains limited capabilities. */
|
|
105
|
+
deactivateEmbargo(): void;
|
|
106
|
+
/** Check if embargo is currently active. */
|
|
107
|
+
isEmbargoActive(): boolean;
|
|
108
|
+
/** Register a dispatched subagent. */
|
|
109
|
+
registerSubagent(id: string): void;
|
|
110
|
+
/** Mark a subagent as completed. */
|
|
111
|
+
completeSubagent(id: string): void;
|
|
112
|
+
/** Check if there are any subagents still pending. */
|
|
113
|
+
hasPendingSubagents(): boolean;
|
|
114
|
+
/** Track token usage by the main agent. */
|
|
115
|
+
trackTokens(count: number): void;
|
|
116
|
+
/** Get current token usage. */
|
|
117
|
+
getTokensUsed(): number;
|
|
118
|
+
/** Percentage of token budget consumed (0-1). */
|
|
119
|
+
tokenBudgetUtilization(): number;
|
|
120
|
+
/** Current guard status snapshot. */
|
|
121
|
+
getStatus(): GuardStatus;
|
|
122
|
+
/**
|
|
123
|
+
* Generate the system prompt fragment that enforces guard rules.
|
|
124
|
+
* Injected into the main agent's system prompt.
|
|
125
|
+
*/
|
|
126
|
+
generateSystemPromptFragment(): string;
|
|
127
|
+
}
|
|
128
|
+
//# sourceMappingURL=guard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"guard.d.ts","sourceRoot":"","sources":["../../../src/router/guard.ts"],"names":[],"mappings":"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"}
|