fix: pipeline — case-insensitive agent join, remove stale model routing, single-task dispatch, 300s timeout

- JOIN agents uses lower() for case-insensitive name matching
- Removed hardcoded 9router/cc/ model IDs — agents use their own OC config
- Dispatch LIMIT 3 → 1 to prevent concurrent gateway saturation
- Gateway timeout 120s → 300s (engineer agent has 130k token context)
- Direct API classifyDirectModel simplified — all routing via OC gateway
This commit is contained in:
James 2026-03-29 07:07:52 -04:00
parent 4931e008b2
commit b2ae69e3b8
1 changed files with 11 additions and 56 deletions

View File

@ -49,31 +49,8 @@ function classifyTaskModel(task: DispatchableTask): string | null {
const text = `${task.title} ${task.description ?? ''}`.toLowerCase()
const priority = task.priority?.toLowerCase() ?? ''
// Complex signals → Opus
const complexSignals = [
'debug', 'diagnos', 'architect', 'design system', 'security audit',
'root cause', 'investigate', 'incident', 'failure', 'broken', 'not working',
'refactor', 'migration', 'performance optim', 'why is',
]
if (priority === 'critical' || complexSignals.some(s => text.includes(s))) {
return '9router/cc/claude-opus-4-6'
}
// Routine signals → Haiku
const routineSignals = [
'status check', 'health check', 'ping', 'list ', 'fetch ', 'format',
'rename', 'move file', 'read file', 'update readme', 'bump version',
'send message', 'post to', 'notify', 'summarize', 'translate',
'quick ', 'simple ', 'routine ', 'minor ',
]
if (priority === 'low' && routineSignals.some(s => text.includes(s))) {
return '9router/cc/claude-haiku-4-5-20251001'
}
if (routineSignals.some(s => text.includes(s)) && priority !== 'high' && priority !== 'critical') {
return '9router/cc/claude-haiku-4-5-20251001'
}
// Default: let the agent's own configured model handle it (no override)
// Let the agent's own configured model handle it — no dispatch-side model override.
// The OC gateway agents already have their models set in openclaw.json.
return null
}
@ -213,34 +190,12 @@ function classifyDirectModel(task: DispatchableTask): string {
try {
const cfg = JSON.parse(task.agent_config)
if (typeof cfg.dispatchModel === 'string' && cfg.dispatchModel) {
// Return model ID as-is — full provider/model paths are valid
return cfg.dispatchModel
}
} catch { /* ignore */ }
}
const text = `${task.title} ${task.description ?? ''}`.toLowerCase()
const priority = task.priority?.toLowerCase() ?? ''
// Complex → Opus
const complexSignals = [
'debug', 'diagnos', 'architect', 'design system', 'security audit',
'root cause', 'investigate', 'incident', 'refactor', 'migration',
]
if (priority === 'critical' || complexSignals.some(s => text.includes(s))) {
return 'claude-opus-4-6'
}
// Routine → Haiku
const routineSignals = [
'status check', 'health check', 'format', 'rename', 'summarize',
'translate', 'quick ', 'simple ', 'routine ', 'minor ',
]
if (routineSignals.some(s => text.includes(s)) && priority !== 'high' && priority !== 'critical') {
return 'claude-haiku-4-5-20251001'
}
// Default → Sonnet
// Default → Sonnet (agents configure their own models via OC gateway)
return 'claude-sonnet-4-6'
}
@ -441,7 +396,7 @@ export async function runAegisReviews(): Promise<{ ok: boolean; message: string
p.ticket_prefix, t.project_ticket_no, a.config as agent_config
FROM tasks t
LEFT JOIN projects p ON p.id = t.project_id AND p.workspace_id = t.workspace_id
LEFT JOIN agents a ON a.name = t.assigned_to AND a.workspace_id = t.workspace_id
LEFT JOIN agents a ON lower(a.name) = lower(t.assigned_to) AND a.workspace_id = t.workspace_id
WHERE t.status = 'review'
ORDER BY t.updated_at ASC
LIMIT 3
@ -489,8 +444,8 @@ export async function runAegisReviews(): Promise<{ ok: boolean; message: string
deliver: false,
}
const finalResult = await runOpenClaw(
['gateway', 'call', 'agent', '--expect-final', '--timeout', '120000', '--params', JSON.stringify(invokeParams), '--json'],
{ timeoutMs: 125_000 }
['gateway', 'call', 'agent', '--expect-final', '--timeout', '300000', '--params', JSON.stringify(invokeParams), '--json'],
{ timeoutMs: 305_000 }
)
const finalPayload = parseGatewayJson(finalResult.stdout)
?? parseGatewayJson(String((finalResult as any)?.stderr || ''))
@ -732,7 +687,7 @@ export async function requeueStaleTasks(): Promise<{ ok: boolean; message: strin
SELECT t.id, t.title, t.assigned_to, t.dispatch_attempts, t.workspace_id,
a.status as agent_status, a.last_seen as agent_last_seen
FROM tasks t
LEFT JOIN agents a ON a.name = t.assigned_to AND a.workspace_id = t.workspace_id
LEFT JOIN agents a ON lower(a.name) = lower(t.assigned_to) AND a.workspace_id = t.workspace_id
WHERE t.status = 'in_progress'
AND t.updated_at < ?
`).all(staleThreshold) as Array<{
@ -808,14 +763,14 @@ export async function dispatchAssignedTasks(): Promise<{ ok: boolean; message: s
SELECT t.*, a.name as agent_name, a.id as agent_id, a.config as agent_config,
p.ticket_prefix, t.project_ticket_no
FROM tasks t
JOIN agents a ON a.name = t.assigned_to AND a.workspace_id = t.workspace_id
JOIN agents a ON lower(a.name) = lower(t.assigned_to) AND a.workspace_id = t.workspace_id
LEFT JOIN projects p ON p.id = t.project_id AND p.workspace_id = t.workspace_id
WHERE t.status = 'assigned'
AND t.assigned_to IS NOT NULL
ORDER BY
CASE t.priority WHEN 'critical' THEN 0 WHEN 'high' THEN 1 WHEN 'medium' THEN 2 ELSE 3 END ASC,
t.created_at ASC
LIMIT 3
LIMIT 1
`).all() as (DispatchableTask & { tags?: string })[]
if (tasks.length === 0) {
@ -921,8 +876,8 @@ export async function dispatchAssignedTasks(): Promise<{ ok: boolean; message: s
// response payload (result.payloads[0].text). The two-step agent → agent.wait
// pattern only returns lifecycle metadata and never includes the agent's text.
const finalResult = await runOpenClaw(
['gateway', 'call', 'agent', '--expect-final', '--timeout', '120000', '--params', JSON.stringify(invokeParams), '--json'],
{ timeoutMs: 125_000 }
['gateway', 'call', 'agent', '--expect-final', '--timeout', '300000', '--params', JSON.stringify(invokeParams), '--json'],
{ timeoutMs: 305_000 }
)
const finalPayload = parseGatewayJson(finalResult.stdout)
?? parseGatewayJson(String((finalResult as any)?.stderr || ''))