Spaces:
Sleeping
Sleeping
Nyk commited on
Commit ·
8965499
1
Parent(s): 234a2ac
fix(tasks): use gateway agent ID instead of display name for dispatch
Browse filesTask dispatch and Aegis review were sending `agents.name` (the display
name) as `agentId` to the gateway. When `identity.name` differs from
the gateway agent `id`, the gateway rejects the call with
"unknown agent id".
Now extracts `openclawId` from the agent's config JSON (set during
agent sync) and uses that for gateway invocations, falling back to
the display name for backwards compatibility.
Closes #310
- src/lib/task-dispatch.ts +31 -5
src/lib/task-dispatch.ts
CHANGED
|
@@ -13,12 +13,25 @@ interface DispatchableTask {
|
|
| 13 |
workspace_id: number
|
| 14 |
agent_name: string
|
| 15 |
agent_id: number
|
|
|
|
| 16 |
ticket_prefix: string | null
|
| 17 |
project_ticket_no: number | null
|
| 18 |
project_id: number | null
|
| 19 |
tags?: string[]
|
| 20 |
}
|
| 21 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 22 |
function buildTaskPrompt(task: DispatchableTask, rejectionFeedback?: string | null): string {
|
| 23 |
const ticket = task.ticket_prefix && task.project_ticket_no
|
| 24 |
? `${task.ticket_prefix}-${String(task.project_ticket_no).padStart(3, '0')}`
|
|
@@ -94,11 +107,22 @@ interface ReviewableTask {
|
|
| 94 |
description: string | null
|
| 95 |
resolution: string | null
|
| 96 |
assigned_to: string | null
|
|
|
|
| 97 |
workspace_id: number
|
| 98 |
ticket_prefix: string | null
|
| 99 |
project_ticket_no: number | null
|
| 100 |
}
|
| 101 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 102 |
function buildReviewPrompt(task: ReviewableTask): string {
|
| 103 |
const ticket = task.ticket_prefix && task.project_ticket_no
|
| 104 |
? `${task.ticket_prefix}-${String(task.project_ticket_no).padStart(3, '0')}`
|
|
@@ -154,9 +178,10 @@ export async function runAegisReviews(): Promise<{ ok: boolean; message: string
|
|
| 154 |
|
| 155 |
const tasks = db.prepare(`
|
| 156 |
SELECT t.id, t.title, t.description, t.resolution, t.assigned_to, t.workspace_id,
|
| 157 |
-
p.ticket_prefix, t.project_ticket_no
|
| 158 |
FROM tasks t
|
| 159 |
LEFT JOIN projects p ON p.id = t.project_id AND p.workspace_id = t.workspace_id
|
|
|
|
| 160 |
WHERE t.status = 'review'
|
| 161 |
ORDER BY t.updated_at ASC
|
| 162 |
LIMIT 3
|
|
@@ -181,8 +206,8 @@ export async function runAegisReviews(): Promise<{ ok: boolean; message: string
|
|
| 181 |
|
| 182 |
try {
|
| 183 |
const prompt = buildReviewPrompt(task)
|
| 184 |
-
//
|
| 185 |
-
const reviewAgent = task
|
| 186 |
|
| 187 |
const invokeParams = {
|
| 188 |
message: prompt,
|
|
@@ -290,7 +315,7 @@ export async function dispatchAssignedTasks(): Promise<{ ok: boolean; message: s
|
|
| 290 |
const db = getDatabase()
|
| 291 |
|
| 292 |
const tasks = db.prepare(`
|
| 293 |
-
SELECT t.*, a.name as agent_name, a.id as agent_id,
|
| 294 |
p.ticket_prefix, t.project_ticket_no
|
| 295 |
FROM tasks t
|
| 296 |
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
|
|
| 350 |
const prompt = buildTaskPrompt(task, rejectionFeedback)
|
| 351 |
|
| 352 |
// Step 1: Invoke via gateway
|
|
|
|
| 353 |
const invokeParams = {
|
| 354 |
message: prompt,
|
| 355 |
-
agentId:
|
| 356 |
idempotencyKey: `task-dispatch-${task.id}-${Date.now()}`,
|
| 357 |
deliver: false,
|
| 358 |
}
|
|
|
|
| 13 |
workspace_id: number
|
| 14 |
agent_name: string
|
| 15 |
agent_id: number
|
| 16 |
+
agent_config: string | null
|
| 17 |
ticket_prefix: string | null
|
| 18 |
project_ticket_no: number | null
|
| 19 |
project_id: number | null
|
| 20 |
tags?: string[]
|
| 21 |
}
|
| 22 |
|
| 23 |
+
/** Extract the gateway agent identifier from the agent's config JSON.
|
| 24 |
+
* Falls back to agent_name (display name) if openclawId is not set. */
|
| 25 |
+
function resolveGatewayAgentId(task: DispatchableTask): string {
|
| 26 |
+
if (task.agent_config) {
|
| 27 |
+
try {
|
| 28 |
+
const cfg = JSON.parse(task.agent_config)
|
| 29 |
+
if (typeof cfg.openclawId === 'string' && cfg.openclawId) return cfg.openclawId
|
| 30 |
+
} catch { /* ignore */ }
|
| 31 |
+
}
|
| 32 |
+
return task.agent_name
|
| 33 |
+
}
|
| 34 |
+
|
| 35 |
function buildTaskPrompt(task: DispatchableTask, rejectionFeedback?: string | null): string {
|
| 36 |
const ticket = task.ticket_prefix && task.project_ticket_no
|
| 37 |
? `${task.ticket_prefix}-${String(task.project_ticket_no).padStart(3, '0')}`
|
|
|
|
| 107 |
description: string | null
|
| 108 |
resolution: string | null
|
| 109 |
assigned_to: string | null
|
| 110 |
+
agent_config: string | null
|
| 111 |
workspace_id: number
|
| 112 |
ticket_prefix: string | null
|
| 113 |
project_ticket_no: number | null
|
| 114 |
}
|
| 115 |
|
| 116 |
+
function resolveGatewayAgentIdForReview(task: ReviewableTask): string {
|
| 117 |
+
if (task.agent_config) {
|
| 118 |
+
try {
|
| 119 |
+
const cfg = JSON.parse(task.agent_config)
|
| 120 |
+
if (typeof cfg.openclawId === 'string' && cfg.openclawId) return cfg.openclawId
|
| 121 |
+
} catch { /* ignore */ }
|
| 122 |
+
}
|
| 123 |
+
return task.assigned_to || 'jarv'
|
| 124 |
+
}
|
| 125 |
+
|
| 126 |
function buildReviewPrompt(task: ReviewableTask): string {
|
| 127 |
const ticket = task.ticket_prefix && task.project_ticket_no
|
| 128 |
? `${task.ticket_prefix}-${String(task.project_ticket_no).padStart(3, '0')}`
|
|
|
|
| 178 |
|
| 179 |
const tasks = db.prepare(`
|
| 180 |
SELECT t.id, t.title, t.description, t.resolution, t.assigned_to, t.workspace_id,
|
| 181 |
+
p.ticket_prefix, t.project_ticket_no, a.config as agent_config
|
| 182 |
FROM tasks t
|
| 183 |
LEFT JOIN projects p ON p.id = t.project_id AND p.workspace_id = t.workspace_id
|
| 184 |
+
LEFT JOIN agents a ON a.name = t.assigned_to AND a.workspace_id = t.workspace_id
|
| 185 |
WHERE t.status = 'review'
|
| 186 |
ORDER BY t.updated_at ASC
|
| 187 |
LIMIT 3
|
|
|
|
| 206 |
|
| 207 |
try {
|
| 208 |
const prompt = buildReviewPrompt(task)
|
| 209 |
+
// Resolve the gateway agent ID from config, falling back to assigned_to or default
|
| 210 |
+
const reviewAgent = resolveGatewayAgentIdForReview(task)
|
| 211 |
|
| 212 |
const invokeParams = {
|
| 213 |
message: prompt,
|
|
|
|
| 315 |
const db = getDatabase()
|
| 316 |
|
| 317 |
const tasks = db.prepare(`
|
| 318 |
+
SELECT t.*, a.name as agent_name, a.id as agent_id, a.config as agent_config,
|
| 319 |
p.ticket_prefix, t.project_ticket_no
|
| 320 |
FROM tasks t
|
| 321 |
JOIN agents a ON a.name = t.assigned_to AND a.workspace_id = t.workspace_id
|
|
|
|
| 375 |
const prompt = buildTaskPrompt(task, rejectionFeedback)
|
| 376 |
|
| 377 |
// Step 1: Invoke via gateway
|
| 378 |
+
const gatewayAgentId = resolveGatewayAgentId(task)
|
| 379 |
const invokeParams = {
|
| 380 |
message: prompt,
|
| 381 |
+
agentId: gatewayAgentId,
|
| 382 |
idempotencyKey: `task-dispatch-${task.id}-${Date.now()}`,
|
| 383 |
deliver: false,
|
| 384 |
}
|