opengate 0.2.11 → 0.2.12
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/index.js +137 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -168,6 +168,52 @@ If you receive a \`task.comment_mention\` notification:
|
|
|
168
168
|
|
|
169
169
|
Now begin. Start with Phase 1: claim the task.`;
|
|
170
170
|
}
|
|
171
|
+
function buildMentionPrompt(taskId, commentBody, commentAuthor, openGateUrl, apiKey, project, projectsDir) {
|
|
172
|
+
let workspacePath = null;
|
|
173
|
+
if (project?.repo_url) {
|
|
174
|
+
const repoName = repoNameFromUrl(project.repo_url);
|
|
175
|
+
if (repoName && projectsDir) {
|
|
176
|
+
workspacePath = `${projectsDir}/${repoName}`;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
const workspaceInstruction = workspacePath ? `Your project workspace is at: ${workspacePath}
|
|
180
|
+
Change into this directory before any file operations.` : "";
|
|
181
|
+
return `You are an AI agent responding to an @-mention in a task comment.
|
|
182
|
+
|
|
183
|
+
## API Access
|
|
184
|
+
- Base URL: ${openGateUrl}
|
|
185
|
+
- API Key: ${apiKey} (pass as Authorization: Bearer header)
|
|
186
|
+
|
|
187
|
+
## Task
|
|
188
|
+
- Task ID: ${taskId}
|
|
189
|
+
${workspaceInstruction}
|
|
190
|
+
|
|
191
|
+
## The Comment That Mentioned You
|
|
192
|
+
**Author:** ${commentAuthor}
|
|
193
|
+
**Content:**
|
|
194
|
+
${commentBody}
|
|
195
|
+
|
|
196
|
+
## Instructions
|
|
197
|
+
|
|
198
|
+
1. Fetch the full task context: GET ${openGateUrl}/api/tasks/${taskId} (with Authorization header)
|
|
199
|
+
2. Read the task description, status, and recent activity to understand context
|
|
200
|
+
3. Reason about what the comment author is asking:
|
|
201
|
+
- If it's a **question** \u2192 post a reply comment with your answer
|
|
202
|
+
- If it's a **request for changes** \u2192 assess what's needed, make the changes if possible, and post a comment describing what you did
|
|
203
|
+
- If it's a **simple acknowledgment or FYI** \u2192 post a brief confirmation comment
|
|
204
|
+
4. Post your reply: POST ${openGateUrl}/api/tasks/${taskId}/activity
|
|
205
|
+
Body: {"content": "<your reply>", "activity_type": "comment"}
|
|
206
|
+
5. If you made code changes, post a summary of what changed
|
|
207
|
+
|
|
208
|
+
## Rules
|
|
209
|
+
- ALWAYS reply with a comment so the author knows you've seen the mention
|
|
210
|
+
- Do NOT change the task status unless explicitly asked to
|
|
211
|
+
- Do NOT try to claim or complete the task \u2014 you are just responding to a comment
|
|
212
|
+
- Keep your response focused and concise
|
|
213
|
+
- If you need to make code changes, work on a feature branch
|
|
214
|
+
|
|
215
|
+
Begin by fetching the task context.`;
|
|
216
|
+
}
|
|
171
217
|
|
|
172
218
|
// src/spawner.ts
|
|
173
219
|
async function spawnTaskSession(taskId, message, pluginCfg, openclawCfg) {
|
|
@@ -304,6 +350,34 @@ async function fetchInbox(url, apiKey) {
|
|
|
304
350
|
inProgressTasks: body.in_progress_tasks ?? []
|
|
305
351
|
};
|
|
306
352
|
}
|
|
353
|
+
async function fetchNotifications(url, apiKey) {
|
|
354
|
+
const resp = await fetch(`${url}/api/agents/me/notifications?unread=true`, {
|
|
355
|
+
headers: { Authorization: `Bearer ${apiKey}` },
|
|
356
|
+
signal: AbortSignal.timeout(1e4)
|
|
357
|
+
});
|
|
358
|
+
if (!resp.ok) return [];
|
|
359
|
+
return await resp.json();
|
|
360
|
+
}
|
|
361
|
+
async function ackNotification(url, apiKey, notifId) {
|
|
362
|
+
await fetch(`${url}/api/agents/me/notifications/${notifId}/ack`, {
|
|
363
|
+
method: "POST",
|
|
364
|
+
headers: { Authorization: `Bearer ${apiKey}` },
|
|
365
|
+
signal: AbortSignal.timeout(1e4)
|
|
366
|
+
}).catch(() => {
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
async function fetchTaskById(url, apiKey, taskId) {
|
|
370
|
+
try {
|
|
371
|
+
const resp = await fetch(`${url}/api/tasks/${taskId}`, {
|
|
372
|
+
headers: { Authorization: `Bearer ${apiKey}` },
|
|
373
|
+
signal: AbortSignal.timeout(1e4)
|
|
374
|
+
});
|
|
375
|
+
if (!resp.ok) return null;
|
|
376
|
+
return await resp.json();
|
|
377
|
+
} catch {
|
|
378
|
+
return null;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
307
381
|
var OpenGatePoller = class {
|
|
308
382
|
constructor(pluginCfg, openclawCfg, logger, stateDir) {
|
|
309
383
|
this.pluginCfg = pluginCfg;
|
|
@@ -362,6 +436,7 @@ var OpenGatePoller = class {
|
|
|
362
436
|
await this.releaseTask(task.id);
|
|
363
437
|
}
|
|
364
438
|
}
|
|
439
|
+
await this.handleNotifications();
|
|
365
440
|
const maxConcurrent = this.pluginCfg.maxConcurrent ?? 3;
|
|
366
441
|
const active = this.state.activeCount();
|
|
367
442
|
if (active >= maxConcurrent) {
|
|
@@ -414,6 +489,68 @@ var OpenGatePoller = class {
|
|
|
414
489
|
);
|
|
415
490
|
}
|
|
416
491
|
}
|
|
492
|
+
async handleNotifications() {
|
|
493
|
+
let notifications;
|
|
494
|
+
try {
|
|
495
|
+
notifications = await fetchNotifications(this.pluginCfg.url, this.pluginCfg.apiKey);
|
|
496
|
+
} catch {
|
|
497
|
+
return;
|
|
498
|
+
}
|
|
499
|
+
const mentionNotifs = notifications.filter(
|
|
500
|
+
(n) => n.event_type === "task.comment_mention" && n.task_id
|
|
501
|
+
);
|
|
502
|
+
for (const notif of mentionNotifs) {
|
|
503
|
+
if (!this.running) break;
|
|
504
|
+
const maxConcurrent = this.pluginCfg.maxConcurrent ?? 3;
|
|
505
|
+
if (this.state.activeCount() >= maxConcurrent) {
|
|
506
|
+
this.logger.info("[opengate] At capacity \u2014 deferring mention notifications");
|
|
507
|
+
break;
|
|
508
|
+
}
|
|
509
|
+
const mentionKey = `mention:${notif.id}`;
|
|
510
|
+
if (this.state.isSpawned(mentionKey)) continue;
|
|
511
|
+
await this.spawnMentionSession(notif);
|
|
512
|
+
await ackNotification(this.pluginCfg.url, this.pluginCfg.apiKey, notif.id);
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
async spawnMentionSession(notif) {
|
|
516
|
+
const taskId = notif.task_id;
|
|
517
|
+
this.logger.info(
|
|
518
|
+
`[opengate] Handling @-mention notification ${notif.id} on task ${taskId}`
|
|
519
|
+
);
|
|
520
|
+
const bodyText = notif.body ?? "";
|
|
521
|
+
const colonIdx = bodyText.indexOf(": ");
|
|
522
|
+
const author = colonIdx > 0 ? bodyText.slice(0, colonIdx) : "Someone";
|
|
523
|
+
const commentBody = colonIdx > 0 ? bodyText.slice(colonIdx + 2) : bodyText;
|
|
524
|
+
const task = await fetchTaskById(this.pluginCfg.url, this.pluginCfg.apiKey, taskId);
|
|
525
|
+
let project = null;
|
|
526
|
+
if (task?.project_id) {
|
|
527
|
+
project = await this.resolveProject(task.project_id);
|
|
528
|
+
}
|
|
529
|
+
const prompt = buildMentionPrompt(
|
|
530
|
+
taskId,
|
|
531
|
+
commentBody,
|
|
532
|
+
author,
|
|
533
|
+
this.pluginCfg.url,
|
|
534
|
+
this.pluginCfg.apiKey,
|
|
535
|
+
project,
|
|
536
|
+
this.pluginCfg.projectsDir
|
|
537
|
+
);
|
|
538
|
+
const result = await spawnTaskSession(
|
|
539
|
+
`mention-${notif.id}`,
|
|
540
|
+
prompt,
|
|
541
|
+
this.pluginCfg,
|
|
542
|
+
this.openclawCfg
|
|
543
|
+
);
|
|
544
|
+
if (!result.ok) {
|
|
545
|
+
this.logger.error(result.error);
|
|
546
|
+
return;
|
|
547
|
+
}
|
|
548
|
+
const mentionKey = `mention:${notif.id}`;
|
|
549
|
+
this.state.markSpawned(mentionKey, result.sessionKey);
|
|
550
|
+
this.logger.info(
|
|
551
|
+
`[opengate] Mention session spawned for notification ${notif.id} \u2192 ${result.sessionKey}`
|
|
552
|
+
);
|
|
553
|
+
}
|
|
417
554
|
async spawnTask(task) {
|
|
418
555
|
this.logger.info(`[opengate] Spawning session for task: "${task.title}" (${task.id})`);
|
|
419
556
|
let project = null;
|
package/package.json
CHANGED