@winspan/claude-forge 8.26.3 → 8.27.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/daemon/handlers/pre-tool-use.d.ts +5 -0
- package/dist/daemon/handlers/pre-tool-use.d.ts.map +1 -1
- package/dist/daemon/handlers/pre-tool-use.js +85 -0
- package/dist/daemon/handlers/pre-tool-use.js.map +1 -1
- package/dist/daemon/handlers/user-prompt.d.ts.map +1 -1
- package/dist/daemon/handlers/user-prompt.js +29 -7
- package/dist/daemon/handlers/user-prompt.js.map +1 -1
- package/dist/daemon/routing-state.d.ts +37 -0
- package/dist/daemon/routing-state.d.ts.map +1 -0
- package/dist/daemon/routing-state.js +39 -0
- package/dist/daemon/routing-state.js.map +1 -0
- package/dist/engine/conventions/routing.yaml +5 -0
- package/package.json +1 -1
|
@@ -16,6 +16,11 @@ export declare class PreToolUseHandler {
|
|
|
16
16
|
private sessionStartTime;
|
|
17
17
|
constructor(ruleEngine: RuleEngine, contextBuilder: ContextBuilder);
|
|
18
18
|
handle(event: ForgeEvent): Promise<HookResult>;
|
|
19
|
+
/**
|
|
20
|
+
* Enforce routing decisions: if session was routed to an agent,
|
|
21
|
+
* block direct tool calls that bypass the agent.
|
|
22
|
+
*/
|
|
23
|
+
private checkRoutingEnforcement;
|
|
19
24
|
handleWithDecision(event: ForgeEvent): Promise<{
|
|
20
25
|
result: HookResult;
|
|
21
26
|
decision: GovernanceDecision;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pre-tool-use.d.ts","sourceRoot":"","sources":["../../../src/daemon/handlers/pre-tool-use.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAClE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAC9D,OAAO,KAAK,EAAE,kBAAkB,EAAe,MAAM,2BAA2B,CAAC;AACjF,OAAO,KAAK,EAAE,cAAc,EAAyB,MAAM,iCAAiC,CAAC;
|
|
1
|
+
{"version":3,"file":"pre-tool-use.d.ts","sourceRoot":"","sources":["../../../src/daemon/handlers/pre-tool-use.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAClE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAC9D,OAAO,KAAK,EAAE,kBAAkB,EAAe,MAAM,2BAA2B,CAAC;AACjF,OAAO,KAAK,EAAE,cAAc,EAAyB,MAAM,iCAAiC,CAAC;AAmB7F,qBAAa,iBAAiB;IAI1B,OAAO,CAAC,UAAU;IAClB,OAAO,CAAC,cAAc;IAJxB,OAAO,CAAC,gBAAgB,CAAc;gBAG5B,UAAU,EAAE,UAAU,EACtB,cAAc,EAAE,cAAc;IAGlC,MAAM,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC;IAYpD;;;OAGG;IACH,OAAO,CAAC,uBAAuB;IAmEzB,kBAAkB,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC;QAAE,MAAM,EAAE,UAAU,CAAC;QAAC,QAAQ,EAAE,kBAAkB,CAAA;KAAE,CAAC;IAyD1G,OAAO,CAAC,gBAAgB;CASzB"}
|
|
@@ -7,6 +7,20 @@
|
|
|
7
7
|
* 3. Return block/warn/allow decision
|
|
8
8
|
*/
|
|
9
9
|
import { logger } from '../../core/utils/logger.js';
|
|
10
|
+
import { routingState } from '../routing-state.js';
|
|
11
|
+
// Tools that are allowed even when routing is active (investigation phase)
|
|
12
|
+
const ROUTING_WHITELIST = new Set([
|
|
13
|
+
'Read', 'Grep', 'Glob', // Code exploration
|
|
14
|
+
'AskUserQuestion', // Clarification
|
|
15
|
+
'WebSearch', 'WebFetch', // Research
|
|
16
|
+
]);
|
|
17
|
+
// Tools that trigger routing enforcement (decision/action phase)
|
|
18
|
+
const ROUTING_ENFORCEMENT_TOOLS = new Set([
|
|
19
|
+
'Edit', 'Write', 'Bash', // Code modification & execution
|
|
20
|
+
'Agent', 'Task', // Delegation (must match routed agent)
|
|
21
|
+
'NotebookEdit', // Notebook modification
|
|
22
|
+
'EnterPlanMode', 'ExitPlanMode', // Planning (should be done by agent)
|
|
23
|
+
]);
|
|
10
24
|
export class PreToolUseHandler {
|
|
11
25
|
ruleEngine;
|
|
12
26
|
contextBuilder;
|
|
@@ -16,9 +30,80 @@ export class PreToolUseHandler {
|
|
|
16
30
|
this.contextBuilder = contextBuilder;
|
|
17
31
|
}
|
|
18
32
|
async handle(event) {
|
|
33
|
+
// 1. Check routing enforcement FIRST (before governance rules)
|
|
34
|
+
const routingCheck = this.checkRoutingEnforcement(event);
|
|
35
|
+
if (!routingCheck.allow) {
|
|
36
|
+
return routingCheck;
|
|
37
|
+
}
|
|
38
|
+
// 2. Then run governance rules
|
|
19
39
|
const { result } = await this.handleWithDecision(event);
|
|
20
40
|
return result;
|
|
21
41
|
}
|
|
42
|
+
/**
|
|
43
|
+
* Enforce routing decisions: if session was routed to an agent,
|
|
44
|
+
* block direct tool calls that bypass the agent.
|
|
45
|
+
*/
|
|
46
|
+
checkRoutingEnforcement(event) {
|
|
47
|
+
const routing = routingState.getRouting(event.session_id);
|
|
48
|
+
if (!routing) {
|
|
49
|
+
return { allow: true }; // No active routing, allow everything
|
|
50
|
+
}
|
|
51
|
+
const toolName = event.tool_name;
|
|
52
|
+
if (!toolName) {
|
|
53
|
+
return { allow: true };
|
|
54
|
+
}
|
|
55
|
+
// Allow whitelisted tools (investigation phase)
|
|
56
|
+
if (ROUTING_WHITELIST.has(toolName)) {
|
|
57
|
+
return { allow: true };
|
|
58
|
+
}
|
|
59
|
+
// Check if this is the correct Agent/Task call
|
|
60
|
+
if (toolName === 'Agent' || toolName === 'Task') {
|
|
61
|
+
const subagentType = event.tool_input?.subagent_type;
|
|
62
|
+
if (subagentType === routing.agentName) {
|
|
63
|
+
// Correct agent invoked - clear routing state and allow
|
|
64
|
+
routingState.clearRouting(event.session_id);
|
|
65
|
+
logger.info(`[PreToolUse] Routing enforced: ${routing.agentName} invoked correctly`);
|
|
66
|
+
return { allow: true };
|
|
67
|
+
}
|
|
68
|
+
// Wrong agent - block
|
|
69
|
+
logger.warn(`[PreToolUse] Routing violation: expected ${routing.agentName}, got ${subagentType}`);
|
|
70
|
+
return {
|
|
71
|
+
allow: false,
|
|
72
|
+
reason: [
|
|
73
|
+
`⚠️ 路由冲突:当前任务已被路由到 ${routing.agentName} agent。`,
|
|
74
|
+
``,
|
|
75
|
+
`你尝试调用的是 ${subagentType},这与路由决策不符。`,
|
|
76
|
+
``,
|
|
77
|
+
`请使用正确的 agent:`,
|
|
78
|
+
` Tool: Agent`,
|
|
79
|
+
` subagent_type: "${routing.agentName}"`,
|
|
80
|
+
` prompt: <用户原始 prompt>`,
|
|
81
|
+
].join('\n'),
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
// Enforcement tools (Edit/Write/Bash) - block and force Agent call
|
|
85
|
+
if (ROUTING_ENFORCEMENT_TOOLS.has(toolName)) {
|
|
86
|
+
logger.warn(`[PreToolUse] Routing enforcement: blocked ${toolName}, must use ${routing.agentName}`);
|
|
87
|
+
return {
|
|
88
|
+
allow: false,
|
|
89
|
+
reason: [
|
|
90
|
+
`⚠️ 路由强制执行:当前任务必须通过 ${routing.agentName} agent 处理。`,
|
|
91
|
+
``,
|
|
92
|
+
`你尝试直接调用 ${toolName} 工具,这违反了路由决策。`,
|
|
93
|
+
``,
|
|
94
|
+
`正确做法:使用 Agent 工具调用专业 agent`,
|
|
95
|
+
` Tool: Agent`,
|
|
96
|
+
` subagent_type: "${routing.agentName}"`,
|
|
97
|
+
` description: "执行用户请求的任务"`,
|
|
98
|
+
` prompt: <将用户的完整 prompt 传入>`,
|
|
99
|
+
``,
|
|
100
|
+
`Agent 职责:${routing.agentDescription}`,
|
|
101
|
+
].join('\n'),
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
// Other tools - allow (e.g., AskUserQuestion for clarification)
|
|
105
|
+
return { allow: true };
|
|
106
|
+
}
|
|
22
107
|
async handleWithDecision(event) {
|
|
23
108
|
try {
|
|
24
109
|
const options = {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pre-tool-use.js","sourceRoot":"","sources":["../../../src/daemon/handlers/pre-tool-use.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAMH,OAAO,EAAE,MAAM,EAAE,MAAM,4BAA4B,CAAC;
|
|
1
|
+
{"version":3,"file":"pre-tool-use.js","sourceRoot":"","sources":["../../../src/daemon/handlers/pre-tool-use.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"user-prompt.d.ts","sourceRoot":"","sources":["../../../src/daemon/handlers/user-prompt.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAClE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AACzE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kCAAkC,CAAC;AACtE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,wCAAwC,CAAC;AAClF,OAAO,KAAK,EAAE,gBAAgB,EAAsB,MAAM,mCAAmC,CAAC;AAC9F,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAClE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"user-prompt.d.ts","sourceRoot":"","sources":["../../../src/daemon/handlers/user-prompt.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAClE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AACzE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kCAAkC,CAAC;AACtE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,wCAAwC,CAAC;AAClF,OAAO,KAAK,EAAE,gBAAgB,EAAsB,MAAM,mCAAmC,CAAC;AAC9F,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAClE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAI9D,eAAO,MAAM,iBAAiB,8BAA8B,CAAC;AAE7D,qBAAa,iBAAiB;IAK1B,OAAO,CAAC,UAAU;IAClB,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,mBAAmB;IAC3B,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,OAAO;IACf,OAAO,CAAC,QAAQ;IAVlB,OAAO,CAAC,cAAc,CAAqB;IAC3C,OAAO,CAAC,kBAAkB,CAAqB;gBAGrC,UAAU,EAAE,gBAAgB,EAC5B,MAAM,GAAE,aAAa,GAAG,IAAW,EACnC,mBAAmB,GAAE,mBAAmB,GAAG,IAAW,EACtD,MAAM,GAAE,gBAAgB,GAAG,IAAW,EACtC,MAAM,GAAE,aAAa,GAAG,IAAW,EACnC,OAAO,GAAE,aAAa,GAAG,IAAW,EACpC,QAAQ,GAAE,eAAe,GAAG,IAAW;IAG3C,MAAM,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC;CAmIrD"}
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
* 4. Persist routing_events row for Web UI observation
|
|
12
12
|
*/
|
|
13
13
|
import { logger } from '../../core/utils/logger.js';
|
|
14
|
+
import { routingState } from '../routing-state.js';
|
|
14
15
|
export const INJECTION_VERSION = 'v1-declarative-2026-05-08';
|
|
15
16
|
export class UserPromptHandler {
|
|
16
17
|
classifier;
|
|
@@ -67,6 +68,7 @@ export class UserPromptHandler {
|
|
|
67
68
|
// 2. Route
|
|
68
69
|
let decision = null;
|
|
69
70
|
let routedAgentName = null;
|
|
71
|
+
let routedAgentDef = null;
|
|
70
72
|
let routedType = 'none';
|
|
71
73
|
if (this.router) {
|
|
72
74
|
decision = this.router.decide({ taskType: analysis.taskType, complexity: analysis.complexity }, event.session_id);
|
|
@@ -75,6 +77,7 @@ export class UserPromptHandler {
|
|
|
75
77
|
const agentDef = this.agents?.get(agentName);
|
|
76
78
|
if (agentDef) {
|
|
77
79
|
routedAgentName = agentName;
|
|
80
|
+
routedAgentDef = agentDef;
|
|
78
81
|
routedType = 'agent';
|
|
79
82
|
systemParts.push(formatAgentDirective(agentName, agentDef.description));
|
|
80
83
|
logger.info(`[UserPrompt] Routed to agent: ${agentName}`);
|
|
@@ -90,7 +93,13 @@ export class UserPromptHandler {
|
|
|
90
93
|
// 4. Persist routing_events row
|
|
91
94
|
if (this.storage) {
|
|
92
95
|
try {
|
|
93
|
-
|
|
96
|
+
// Always use Date.now() as the server-side wall-clock reference.
|
|
97
|
+
// event.timestamp comes from Claude Code as an ISO string without
|
|
98
|
+
// timezone (e.g. "2026-05-09T07:57:47"); Date.parse treats it as
|
|
99
|
+
// UTC, but Claude Code actually emits it in *local* time, which
|
|
100
|
+
// silently offsets ts from first_tool_ts (taken via Date.now)
|
|
101
|
+
// by the TZ delta and makes latency math meaningless.
|
|
102
|
+
const ts = Date.now();
|
|
94
103
|
const routeRequestId = `${event.session_id}:${ts}`;
|
|
95
104
|
const eventId = this.storage.writeRoutingEvent({
|
|
96
105
|
session_id: event.session_id,
|
|
@@ -111,6 +120,16 @@ export class UserPromptHandler {
|
|
|
111
120
|
if (routedType === 'agent' && this.observer) {
|
|
112
121
|
this.observer.beginPending(event.session_id, eventId, routeRequestId, routedAgentName);
|
|
113
122
|
}
|
|
123
|
+
// Record routing state for PreToolUse enforcement
|
|
124
|
+
if (routedType === 'agent' && routedAgentName && routedAgentDef) {
|
|
125
|
+
routingState.setRouting(event.session_id, {
|
|
126
|
+
agentName: routedAgentName,
|
|
127
|
+
agentDescription: routedAgentDef.description,
|
|
128
|
+
timestamp: ts,
|
|
129
|
+
routeRequestId,
|
|
130
|
+
});
|
|
131
|
+
logger.info(`[UserPrompt] Routing state recorded for session ${event.session_id}`);
|
|
132
|
+
}
|
|
114
133
|
}
|
|
115
134
|
catch (err) {
|
|
116
135
|
logger.warn(`[UserPrompt] Failed to persist routing_events: ${err}`);
|
|
@@ -137,16 +156,19 @@ export class UserPromptHandler {
|
|
|
137
156
|
function formatAgentDirective(agentName, agentDescription) {
|
|
138
157
|
return [
|
|
139
158
|
'━━━ Claude Forge: Agent Routing ━━━',
|
|
140
|
-
|
|
159
|
+
`⚠️ 本次任务必须通过 ${agentName} agent 执行。`,
|
|
160
|
+
'',
|
|
161
|
+
'**强制要求**:立即使用 Agent 工具调用该 agent,不要尝试自行处理。',
|
|
141
162
|
'',
|
|
142
|
-
'
|
|
163
|
+
'调用方式:',
|
|
164
|
+
` Tool: Agent`,
|
|
143
165
|
` subagent_type: "${agentName}"`,
|
|
144
|
-
|
|
145
|
-
|
|
166
|
+
` description: "执行用户请求的任务"`,
|
|
167
|
+
` prompt: <将用户的原始 prompt 完整传入>`,
|
|
146
168
|
'',
|
|
147
|
-
|
|
169
|
+
`Agent 职责:${agentDescription}`,
|
|
148
170
|
'',
|
|
149
|
-
'
|
|
171
|
+
'注意:直接调用其他工具(Read/Bash/Edit 等)将被系统拦截。',
|
|
150
172
|
'━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━',
|
|
151
173
|
].join('\n');
|
|
152
174
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"user-prompt.js","sourceRoot":"","sources":["../../../src/daemon/handlers/user-prompt.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAUH,OAAO,EAAE,MAAM,EAAE,MAAM,4BAA4B,CAAC;
|
|
1
|
+
{"version":3,"file":"user-prompt.js","sourceRoot":"","sources":["../../../src/daemon/handlers/user-prompt.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RoutingState — in-memory session routing state for PreToolUse enforcement
|
|
3
|
+
*
|
|
4
|
+
* Tracks which sessions have active routing decisions, so PreToolUse can
|
|
5
|
+
* enforce "must use Agent tool" when Claude tries to bypass the routing.
|
|
6
|
+
*/
|
|
7
|
+
export interface RoutingDecision {
|
|
8
|
+
agentName: string;
|
|
9
|
+
agentDescription: string;
|
|
10
|
+
timestamp: number;
|
|
11
|
+
routeRequestId: string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Global routing state manager (singleton pattern)
|
|
15
|
+
*/
|
|
16
|
+
declare class RoutingStateManager {
|
|
17
|
+
private state;
|
|
18
|
+
/**
|
|
19
|
+
* Record that a session has been routed to a specific agent
|
|
20
|
+
*/
|
|
21
|
+
setRouting(sessionId: string, decision: RoutingDecision): void;
|
|
22
|
+
/**
|
|
23
|
+
* Get the active routing decision for a session (if any)
|
|
24
|
+
*/
|
|
25
|
+
getRouting(sessionId: string): RoutingDecision | null;
|
|
26
|
+
/**
|
|
27
|
+
* Clear routing state for a session (called after agent is invoked or session ends)
|
|
28
|
+
*/
|
|
29
|
+
clearRouting(sessionId: string): void;
|
|
30
|
+
/**
|
|
31
|
+
* Check if a session has an active routing decision
|
|
32
|
+
*/
|
|
33
|
+
hasRouting(sessionId: string): boolean;
|
|
34
|
+
}
|
|
35
|
+
export declare const routingState: RoutingStateManager;
|
|
36
|
+
export {};
|
|
37
|
+
//# sourceMappingURL=routing-state.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routing-state.d.ts","sourceRoot":"","sources":["../../src/daemon/routing-state.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,gBAAgB,EAAE,MAAM,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED;;GAEG;AACH,cAAM,mBAAmB;IACvB,OAAO,CAAC,KAAK,CAAsC;IAEnD;;OAEG;IACH,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,eAAe,GAAG,IAAI;IAI9D;;OAEG;IACH,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,eAAe,GAAG,IAAI;IAIrD;;OAEG;IACH,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAIrC;;OAEG;IACH,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;CAGvC;AAGD,eAAO,MAAM,YAAY,qBAA4B,CAAC"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RoutingState — in-memory session routing state for PreToolUse enforcement
|
|
3
|
+
*
|
|
4
|
+
* Tracks which sessions have active routing decisions, so PreToolUse can
|
|
5
|
+
* enforce "must use Agent tool" when Claude tries to bypass the routing.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Global routing state manager (singleton pattern)
|
|
9
|
+
*/
|
|
10
|
+
class RoutingStateManager {
|
|
11
|
+
state = new Map();
|
|
12
|
+
/**
|
|
13
|
+
* Record that a session has been routed to a specific agent
|
|
14
|
+
*/
|
|
15
|
+
setRouting(sessionId, decision) {
|
|
16
|
+
this.state.set(sessionId, decision);
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Get the active routing decision for a session (if any)
|
|
20
|
+
*/
|
|
21
|
+
getRouting(sessionId) {
|
|
22
|
+
return this.state.get(sessionId) ?? null;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Clear routing state for a session (called after agent is invoked or session ends)
|
|
26
|
+
*/
|
|
27
|
+
clearRouting(sessionId) {
|
|
28
|
+
this.state.delete(sessionId);
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Check if a session has an active routing decision
|
|
32
|
+
*/
|
|
33
|
+
hasRouting(sessionId) {
|
|
34
|
+
return this.state.has(sessionId);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
// Singleton instance
|
|
38
|
+
export const routingState = new RoutingStateManager();
|
|
39
|
+
//# sourceMappingURL=routing-state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routing-state.js","sourceRoot":"","sources":["../../src/daemon/routing-state.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AASH;;GAEG;AACH,MAAM,mBAAmB;IACf,KAAK,GAAG,IAAI,GAAG,EAA2B,CAAC;IAEnD;;OAEG;IACH,UAAU,CAAC,SAAiB,EAAE,QAAyB;QACrD,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;IACtC,CAAC;IAED;;OAEG;IACH,UAAU,CAAC,SAAiB;QAC1B,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,IAAI,CAAC;IAC3C,CAAC;IAED;;OAEG;IACH,YAAY,CAAC,SAAiB;QAC5B,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IAC/B,CAAC;IAED;;OAEG;IACH,UAAU,CAAC,SAAiB;QAC1B,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IACnC,CAAC;CACF;AAED,qBAAqB;AACrB,MAAM,CAAC,MAAM,YAAY,GAAG,IAAI,mBAAmB,EAAE,CAAC"}
|
|
@@ -137,8 +137,13 @@ rules:
|
|
|
137
137
|
- when:
|
|
138
138
|
taskType: compare_solutions
|
|
139
139
|
action: { type: route_to_agent, name: researcher }
|
|
140
|
+
# Only route "explain" when the topic is complex enough to warrant a
|
|
141
|
+
# researcher dive. Plain clarification questions ("what does X mean",
|
|
142
|
+
# "does this work?") should stay with the main Claude — routing them out
|
|
143
|
+
# just produces disobey events since the main session will answer inline.
|
|
140
144
|
- when:
|
|
141
145
|
taskType: explain
|
|
146
|
+
complexity: complex
|
|
142
147
|
action: { type: route_to_agent, name: researcher }
|
|
143
148
|
|
|
144
149
|
# -- Documentation ---------------------------------------------------------
|