Merge pull request #314 from builderz-labs/fix/task-dispatch-agent-id

fix(tasks): use gateway agent ID instead of display name for dispatch
This commit is contained in:
nyk 2026-03-13 13:04:42 +07:00 committed by GitHub
commit 14890e7630
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 36 additions and 11 deletions

View File

@ -66,7 +66,7 @@
"vitest": "^2.1.5" "vitest": "^2.1.5"
}, },
"engines": { "engines": {
"node": "22.x || 24.x" "node": ">=22"
}, },
"keywords": [ "keywords": [
"openclaw", "openclaw",

View File

@ -1,16 +1,15 @@
#!/usr/bin/env node #!/usr/bin/env node
const SUPPORTED_NODE_MAJORS = [22, 24] const MIN_NODE_MAJOR = 22
const current = process.versions.node const current = process.versions.node
const currentMajor = Number.parseInt(current.split('.')[0] || '', 10) const currentMajor = Number.parseInt(current.split('.')[0] || '', 10)
if (!SUPPORTED_NODE_MAJORS.includes(currentMajor)) { if (currentMajor < MIN_NODE_MAJOR) {
const supported = SUPPORTED_NODE_MAJORS.map((major) => `${major}.x`).join(' or ')
console.error( console.error(
[ [
`error: Mission Control supports Node ${supported}, but found ${current}.`, `error: Mission Control requires Node ${MIN_NODE_MAJOR} or later, but found ${current}.`,
'use `nvm use 22` (recommended LTS) or `nvm use 24` before installing, building, or starting the app.', 'use `nvm use 22` (recommended LTS) or any later version before installing, building, or starting the app.',
].join('\n') ].join('\n')
) )
process.exit(1) process.exit(1)

View File

@ -13,12 +13,25 @@ interface DispatchableTask {
workspace_id: number workspace_id: number
agent_name: string agent_name: string
agent_id: number agent_id: number
agent_config: string | null
ticket_prefix: string | null ticket_prefix: string | null
project_ticket_no: number | null project_ticket_no: number | null
project_id: number | null project_id: number | null
tags?: string[] tags?: string[]
} }
/** Extract the gateway agent identifier from the agent's config JSON.
* Falls back to agent_name (display name) if openclawId is not set. */
function resolveGatewayAgentId(task: DispatchableTask): string {
if (task.agent_config) {
try {
const cfg = JSON.parse(task.agent_config)
if (typeof cfg.openclawId === 'string' && cfg.openclawId) return cfg.openclawId
} catch { /* ignore */ }
}
return task.agent_name
}
function buildTaskPrompt(task: DispatchableTask, rejectionFeedback?: string | null): string { function buildTaskPrompt(task: DispatchableTask, rejectionFeedback?: string | null): string {
const ticket = task.ticket_prefix && task.project_ticket_no const ticket = task.ticket_prefix && task.project_ticket_no
? `${task.ticket_prefix}-${String(task.project_ticket_no).padStart(3, '0')}` ? `${task.ticket_prefix}-${String(task.project_ticket_no).padStart(3, '0')}`
@ -94,11 +107,22 @@ interface ReviewableTask {
description: string | null description: string | null
resolution: string | null resolution: string | null
assigned_to: string | null assigned_to: string | null
agent_config: string | null
workspace_id: number workspace_id: number
ticket_prefix: string | null ticket_prefix: string | null
project_ticket_no: number | null project_ticket_no: number | null
} }
function resolveGatewayAgentIdForReview(task: ReviewableTask): string {
if (task.agent_config) {
try {
const cfg = JSON.parse(task.agent_config)
if (typeof cfg.openclawId === 'string' && cfg.openclawId) return cfg.openclawId
} catch { /* ignore */ }
}
return task.assigned_to || 'jarv'
}
function buildReviewPrompt(task: ReviewableTask): string { function buildReviewPrompt(task: ReviewableTask): string {
const ticket = task.ticket_prefix && task.project_ticket_no const ticket = task.ticket_prefix && task.project_ticket_no
? `${task.ticket_prefix}-${String(task.project_ticket_no).padStart(3, '0')}` ? `${task.ticket_prefix}-${String(task.project_ticket_no).padStart(3, '0')}`
@ -154,9 +178,10 @@ export async function runAegisReviews(): Promise<{ ok: boolean; message: string
const tasks = db.prepare(` const tasks = db.prepare(`
SELECT t.id, t.title, t.description, t.resolution, t.assigned_to, t.workspace_id, SELECT t.id, t.title, t.description, t.resolution, t.assigned_to, t.workspace_id,
p.ticket_prefix, t.project_ticket_no p.ticket_prefix, t.project_ticket_no, a.config as agent_config
FROM tasks t FROM tasks t
LEFT JOIN projects p ON p.id = t.project_id AND p.workspace_id = t.workspace_id 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
WHERE t.status = 'review' WHERE t.status = 'review'
ORDER BY t.updated_at ASC ORDER BY t.updated_at ASC
LIMIT 3 LIMIT 3
@ -181,8 +206,8 @@ export async function runAegisReviews(): Promise<{ ok: boolean; message: string
try { try {
const prompt = buildReviewPrompt(task) const prompt = buildReviewPrompt(task)
// Use the assigned agent or fall back to a default reviewer agent // Resolve the gateway agent ID from config, falling back to assigned_to or default
const reviewAgent = task.assigned_to || 'jarv' const reviewAgent = resolveGatewayAgentIdForReview(task)
const invokeParams = { const invokeParams = {
message: prompt, message: prompt,
@ -290,7 +315,7 @@ export async function dispatchAssignedTasks(): Promise<{ ok: boolean; message: s
const db = getDatabase() const db = getDatabase()
const tasks = db.prepare(` const tasks = db.prepare(`
SELECT t.*, a.name as agent_name, a.id as agent_id, SELECT t.*, a.name as agent_name, a.id as agent_id, a.config as agent_config,
p.ticket_prefix, t.project_ticket_no p.ticket_prefix, t.project_ticket_no
FROM tasks t FROM tasks t
JOIN agents a ON a.name = t.assigned_to AND a.workspace_id = t.workspace_id JOIN agents a ON a.name = t.assigned_to AND a.workspace_id = t.workspace_id
@ -350,9 +375,10 @@ export async function dispatchAssignedTasks(): Promise<{ ok: boolean; message: s
const prompt = buildTaskPrompt(task, rejectionFeedback) const prompt = buildTaskPrompt(task, rejectionFeedback)
// Step 1: Invoke via gateway // Step 1: Invoke via gateway
const gatewayAgentId = resolveGatewayAgentId(task)
const invokeParams = { const invokeParams = {
message: prompt, message: prompt,
agentId: task.agent_name, agentId: gatewayAgentId,
idempotencyKey: `task-dispatch-${task.id}-${Date.now()}`, idempotencyKey: `task-dispatch-${task.id}-${Date.now()}`,
deliver: false, deliver: false,
} }