agent-board-runner 0.1.21 → 0.1.23
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/cli.js +51 -10
- package/dist/index.js +51 -10
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -572,7 +572,15 @@ async function savePastedImage(sessionId, extCode, bytes) {
|
|
|
572
572
|
return path;
|
|
573
573
|
}
|
|
574
574
|
function registerPtySession(sessionId, pty, ticket) {
|
|
575
|
-
const handle = {
|
|
575
|
+
const handle = {
|
|
576
|
+
pty,
|
|
577
|
+
ticket,
|
|
578
|
+
outputBuffer: [],
|
|
579
|
+
sockets: /* @__PURE__ */ new Set(),
|
|
580
|
+
readOnly: false,
|
|
581
|
+
exited: false,
|
|
582
|
+
lastAssistantMessage: null
|
|
583
|
+
};
|
|
576
584
|
pty.onExit(() => {
|
|
577
585
|
handle.exited = true;
|
|
578
586
|
});
|
|
@@ -588,6 +596,10 @@ function registerPtySession(sessionId, pty, ticket) {
|
|
|
588
596
|
function getPtySession(sessionId) {
|
|
589
597
|
return registry.get(sessionId);
|
|
590
598
|
}
|
|
599
|
+
function recordLastAssistantMessage(sessionId, message) {
|
|
600
|
+
const handle = registry.get(sessionId);
|
|
601
|
+
if (handle) handle.lastAssistantMessage = message;
|
|
602
|
+
}
|
|
591
603
|
function setPtySessionReadOnly(sessionId, readOnly) {
|
|
592
604
|
const handle = registry.get(sessionId);
|
|
593
605
|
if (handle) handle.readOnly = readOnly;
|
|
@@ -1297,10 +1309,10 @@ function detectMoment(input) {
|
|
|
1297
1309
|
return null;
|
|
1298
1310
|
}
|
|
1299
1311
|
|
|
1300
|
-
function reportToRunner(sessionId, ticket, port, moment) {
|
|
1312
|
+
function reportToRunner(sessionId, ticket, port, moment, lastAssistantMessage) {
|
|
1301
1313
|
return new Promise((resolve) => {
|
|
1302
1314
|
try {
|
|
1303
|
-
const body = JSON.stringify({ sessionId, ticket, moment });
|
|
1315
|
+
const body = JSON.stringify({ sessionId, ticket, moment, lastAssistantMessage });
|
|
1304
1316
|
const req = http.request(
|
|
1305
1317
|
{
|
|
1306
1318
|
host: "127.0.0.1",
|
|
@@ -1349,7 +1361,10 @@ function reportToRunner(sessionId, ticket, port, moment) {
|
|
|
1349
1361
|
const sessionId = process.env.AGENT_BOARD_SESSION_ID;
|
|
1350
1362
|
const ticket = process.env.AGENT_BOARD_STATUSLINE_TICKET;
|
|
1351
1363
|
const port = Number(process.env.AGENT_BOARD_STATUSLINE_PORT);
|
|
1352
|
-
if (sessionId && ticket && port)
|
|
1364
|
+
if (sessionId && ticket && port) {
|
|
1365
|
+
const lastAssistantMessage = moment === "turn_stopped" && typeof input.lastAssistantMessage === "string" ? input.lastAssistantMessage : undefined;
|
|
1366
|
+
await reportToRunner(sessionId, ticket, port, moment, lastAssistantMessage);
|
|
1367
|
+
}
|
|
1353
1368
|
}
|
|
1354
1369
|
} catch {
|
|
1355
1370
|
// A malformed/unexpected hook payload must never block the agent's own turn.
|
|
@@ -2088,6 +2103,7 @@ async function handleStatuslineReport(req, res, supabase, config) {
|
|
|
2088
2103
|
sendJson(res, 500, { error: "Erro interno do runner." });
|
|
2089
2104
|
}
|
|
2090
2105
|
}
|
|
2106
|
+
var MAX_LAST_ASSISTANT_MESSAGE_CHARS = 4e3;
|
|
2091
2107
|
async function handleAgentHookReport(req, res) {
|
|
2092
2108
|
let body;
|
|
2093
2109
|
try {
|
|
@@ -2106,6 +2122,9 @@ async function handleAgentHookReport(req, res) {
|
|
|
2106
2122
|
sendJson(res, 403, { error: "Ticket inv\xE1lido para esta sess\xE3o." });
|
|
2107
2123
|
return;
|
|
2108
2124
|
}
|
|
2125
|
+
if (typeof body.lastAssistantMessage === "string" && body.lastAssistantMessage.length > 0) {
|
|
2126
|
+
recordLastAssistantMessage(body.sessionId, body.lastAssistantMessage.slice(0, MAX_LAST_ASSISTANT_MESSAGE_CHARS));
|
|
2127
|
+
}
|
|
2109
2128
|
handleAgentHookEvent(body.sessionId, body.moment);
|
|
2110
2129
|
res.writeHead(204, { "Access-Control-Allow-Origin": "*" });
|
|
2111
2130
|
res.end();
|
|
@@ -4373,7 +4392,11 @@ async function openWorkflowShell(supabase, config, payload) {
|
|
|
4373
4392
|
}
|
|
4374
4393
|
);
|
|
4375
4394
|
registerPtySession(executionId, pty, shellTicket);
|
|
4376
|
-
|
|
4395
|
+
const activityWatcher = watchWorkflowShellActivity(supabase, executionId, pty);
|
|
4396
|
+
pty.onExit(({ exitCode }) => {
|
|
4397
|
+
activityWatcher.stop();
|
|
4398
|
+
void handleWorkflowShellExit(supabase, executionId, exitCode);
|
|
4399
|
+
});
|
|
4377
4400
|
waitForIdleThenRun(pty, () => {
|
|
4378
4401
|
try {
|
|
4379
4402
|
writePasteAndSubmit(pty, executionId, instructions, "the workflow runbook", logger.runner);
|
|
@@ -4383,13 +4406,27 @@ async function openWorkflowShell(supabase, config, payload) {
|
|
|
4383
4406
|
});
|
|
4384
4407
|
return { claudeSessionId, shellTicket };
|
|
4385
4408
|
}
|
|
4386
|
-
async function
|
|
4387
|
-
const
|
|
4388
|
-
const { error } = await supabase.rpc("complete_workflow_execution", { p_execution_id: executionId, p_status: status });
|
|
4409
|
+
async function completeWorkflowExecution(supabase, executionId, status) {
|
|
4410
|
+
const summary = getPtySession(executionId)?.lastAssistantMessage ?? null;
|
|
4411
|
+
const { error } = await supabase.rpc("complete_workflow_execution", { p_execution_id: executionId, p_status: status, p_summary: summary });
|
|
4389
4412
|
if (error) {
|
|
4390
|
-
logger.runner.error("WORKFLOW_SHELL", `Failed to record
|
|
4413
|
+
logger.runner.error("WORKFLOW_SHELL", `Failed to record ${status} status for workflow execution ${executionId}:`, error);
|
|
4391
4414
|
}
|
|
4392
4415
|
}
|
|
4416
|
+
async function handleWorkflowShellExit(supabase, executionId, exitCode) {
|
|
4417
|
+
unregisterAgentActivityWatcher(executionId);
|
|
4418
|
+
await completeWorkflowExecution(supabase, executionId, exitCode === 0 ? "done" : "failed");
|
|
4419
|
+
}
|
|
4420
|
+
function watchWorkflowShellActivity(supabase, executionId, pty) {
|
|
4421
|
+
const watcher = new AgentActivityWatcher((state) => {
|
|
4422
|
+
if (state === "done" || state === "waiting_user") {
|
|
4423
|
+
void completeWorkflowExecution(supabase, executionId, "done");
|
|
4424
|
+
}
|
|
4425
|
+
});
|
|
4426
|
+
pty.onData((chunk) => watcher.push(chunk));
|
|
4427
|
+
registerAgentActivityWatcher(executionId, watcher);
|
|
4428
|
+
return watcher;
|
|
4429
|
+
}
|
|
4393
4430
|
async function reconcileOrphanedWorkflowExecutions(supabase, config) {
|
|
4394
4431
|
const { data, error } = await supabase.from("workflow_executions").select("id, project_id, claude_session_id, shell_ticket").eq("runner_id", config.runnerId).in("status", ["running", "paused_on_question"]);
|
|
4395
4432
|
if (error) {
|
|
@@ -4410,7 +4447,11 @@ async function reconcileOrphanedWorkflowExecutions(supabase, config) {
|
|
|
4410
4447
|
}
|
|
4411
4448
|
const pty = resumeAgentPty(registered.repoPath, claudeSessionId, executionId, shellTicket);
|
|
4412
4449
|
registerPtySession(executionId, pty, shellTicket);
|
|
4413
|
-
|
|
4450
|
+
const activityWatcher = watchWorkflowShellActivity(supabase, executionId, pty);
|
|
4451
|
+
pty.onExit(({ exitCode }) => {
|
|
4452
|
+
activityWatcher.stop();
|
|
4453
|
+
void handleWorkflowShellExit(supabase, executionId, exitCode);
|
|
4454
|
+
});
|
|
4414
4455
|
logger.runner.info("WORKFLOW_SHELL", `Re-spawned orphaned workflow execution ${executionId} (runner restarted while it was still active).`);
|
|
4415
4456
|
}
|
|
4416
4457
|
}
|
package/dist/index.js
CHANGED
|
@@ -292,7 +292,15 @@ async function savePastedImage(sessionId, extCode, bytes) {
|
|
|
292
292
|
return path;
|
|
293
293
|
}
|
|
294
294
|
function registerPtySession(sessionId, pty, ticket) {
|
|
295
|
-
const handle = {
|
|
295
|
+
const handle = {
|
|
296
|
+
pty,
|
|
297
|
+
ticket,
|
|
298
|
+
outputBuffer: [],
|
|
299
|
+
sockets: /* @__PURE__ */ new Set(),
|
|
300
|
+
readOnly: false,
|
|
301
|
+
exited: false,
|
|
302
|
+
lastAssistantMessage: null
|
|
303
|
+
};
|
|
296
304
|
pty.onExit(() => {
|
|
297
305
|
handle.exited = true;
|
|
298
306
|
});
|
|
@@ -308,6 +316,10 @@ function registerPtySession(sessionId, pty, ticket) {
|
|
|
308
316
|
function getPtySession(sessionId) {
|
|
309
317
|
return registry.get(sessionId);
|
|
310
318
|
}
|
|
319
|
+
function recordLastAssistantMessage(sessionId, message) {
|
|
320
|
+
const handle = registry.get(sessionId);
|
|
321
|
+
if (handle) handle.lastAssistantMessage = message;
|
|
322
|
+
}
|
|
311
323
|
function setPtySessionReadOnly(sessionId, readOnly) {
|
|
312
324
|
const handle = registry.get(sessionId);
|
|
313
325
|
if (handle) handle.readOnly = readOnly;
|
|
@@ -1017,10 +1029,10 @@ function detectMoment(input) {
|
|
|
1017
1029
|
return null;
|
|
1018
1030
|
}
|
|
1019
1031
|
|
|
1020
|
-
function reportToRunner(sessionId, ticket, port, moment) {
|
|
1032
|
+
function reportToRunner(sessionId, ticket, port, moment, lastAssistantMessage) {
|
|
1021
1033
|
return new Promise((resolve) => {
|
|
1022
1034
|
try {
|
|
1023
|
-
const body = JSON.stringify({ sessionId, ticket, moment });
|
|
1035
|
+
const body = JSON.stringify({ sessionId, ticket, moment, lastAssistantMessage });
|
|
1024
1036
|
const req = http.request(
|
|
1025
1037
|
{
|
|
1026
1038
|
host: "127.0.0.1",
|
|
@@ -1069,7 +1081,10 @@ function reportToRunner(sessionId, ticket, port, moment) {
|
|
|
1069
1081
|
const sessionId = process.env.AGENT_BOARD_SESSION_ID;
|
|
1070
1082
|
const ticket = process.env.AGENT_BOARD_STATUSLINE_TICKET;
|
|
1071
1083
|
const port = Number(process.env.AGENT_BOARD_STATUSLINE_PORT);
|
|
1072
|
-
if (sessionId && ticket && port)
|
|
1084
|
+
if (sessionId && ticket && port) {
|
|
1085
|
+
const lastAssistantMessage = moment === "turn_stopped" && typeof input.lastAssistantMessage === "string" ? input.lastAssistantMessage : undefined;
|
|
1086
|
+
await reportToRunner(sessionId, ticket, port, moment, lastAssistantMessage);
|
|
1087
|
+
}
|
|
1073
1088
|
}
|
|
1074
1089
|
} catch {
|
|
1075
1090
|
// A malformed/unexpected hook payload must never block the agent's own turn.
|
|
@@ -1808,6 +1823,7 @@ async function handleStatuslineReport(req, res, supabase, config) {
|
|
|
1808
1823
|
sendJson(res, 500, { error: "Erro interno do runner." });
|
|
1809
1824
|
}
|
|
1810
1825
|
}
|
|
1826
|
+
var MAX_LAST_ASSISTANT_MESSAGE_CHARS = 4e3;
|
|
1811
1827
|
async function handleAgentHookReport(req, res) {
|
|
1812
1828
|
let body;
|
|
1813
1829
|
try {
|
|
@@ -1826,6 +1842,9 @@ async function handleAgentHookReport(req, res) {
|
|
|
1826
1842
|
sendJson(res, 403, { error: "Ticket inv\xE1lido para esta sess\xE3o." });
|
|
1827
1843
|
return;
|
|
1828
1844
|
}
|
|
1845
|
+
if (typeof body.lastAssistantMessage === "string" && body.lastAssistantMessage.length > 0) {
|
|
1846
|
+
recordLastAssistantMessage(body.sessionId, body.lastAssistantMessage.slice(0, MAX_LAST_ASSISTANT_MESSAGE_CHARS));
|
|
1847
|
+
}
|
|
1829
1848
|
handleAgentHookEvent(body.sessionId, body.moment);
|
|
1830
1849
|
res.writeHead(204, { "Access-Control-Allow-Origin": "*" });
|
|
1831
1850
|
res.end();
|
|
@@ -4261,7 +4280,11 @@ async function openWorkflowShell(supabase, config, payload) {
|
|
|
4261
4280
|
}
|
|
4262
4281
|
);
|
|
4263
4282
|
registerPtySession(executionId, pty, shellTicket);
|
|
4264
|
-
|
|
4283
|
+
const activityWatcher = watchWorkflowShellActivity(supabase, executionId, pty);
|
|
4284
|
+
pty.onExit(({ exitCode }) => {
|
|
4285
|
+
activityWatcher.stop();
|
|
4286
|
+
void handleWorkflowShellExit(supabase, executionId, exitCode);
|
|
4287
|
+
});
|
|
4265
4288
|
waitForIdleThenRun(pty, () => {
|
|
4266
4289
|
try {
|
|
4267
4290
|
writePasteAndSubmit(pty, executionId, instructions, "the workflow runbook", logger.runner);
|
|
@@ -4271,13 +4294,27 @@ async function openWorkflowShell(supabase, config, payload) {
|
|
|
4271
4294
|
});
|
|
4272
4295
|
return { claudeSessionId, shellTicket };
|
|
4273
4296
|
}
|
|
4274
|
-
async function
|
|
4275
|
-
const
|
|
4276
|
-
const { error } = await supabase.rpc("complete_workflow_execution", { p_execution_id: executionId, p_status: status });
|
|
4297
|
+
async function completeWorkflowExecution(supabase, executionId, status) {
|
|
4298
|
+
const summary = getPtySession(executionId)?.lastAssistantMessage ?? null;
|
|
4299
|
+
const { error } = await supabase.rpc("complete_workflow_execution", { p_execution_id: executionId, p_status: status, p_summary: summary });
|
|
4277
4300
|
if (error) {
|
|
4278
|
-
logger.runner.error("WORKFLOW_SHELL", `Failed to record
|
|
4301
|
+
logger.runner.error("WORKFLOW_SHELL", `Failed to record ${status} status for workflow execution ${executionId}:`, error);
|
|
4279
4302
|
}
|
|
4280
4303
|
}
|
|
4304
|
+
async function handleWorkflowShellExit(supabase, executionId, exitCode) {
|
|
4305
|
+
unregisterAgentActivityWatcher(executionId);
|
|
4306
|
+
await completeWorkflowExecution(supabase, executionId, exitCode === 0 ? "done" : "failed");
|
|
4307
|
+
}
|
|
4308
|
+
function watchWorkflowShellActivity(supabase, executionId, pty) {
|
|
4309
|
+
const watcher = new AgentActivityWatcher((state) => {
|
|
4310
|
+
if (state === "done" || state === "waiting_user") {
|
|
4311
|
+
void completeWorkflowExecution(supabase, executionId, "done");
|
|
4312
|
+
}
|
|
4313
|
+
});
|
|
4314
|
+
pty.onData((chunk) => watcher.push(chunk));
|
|
4315
|
+
registerAgentActivityWatcher(executionId, watcher);
|
|
4316
|
+
return watcher;
|
|
4317
|
+
}
|
|
4281
4318
|
async function reconcileOrphanedWorkflowExecutions(supabase, config) {
|
|
4282
4319
|
const { data, error } = await supabase.from("workflow_executions").select("id, project_id, claude_session_id, shell_ticket").eq("runner_id", config.runnerId).in("status", ["running", "paused_on_question"]);
|
|
4283
4320
|
if (error) {
|
|
@@ -4298,7 +4335,11 @@ async function reconcileOrphanedWorkflowExecutions(supabase, config) {
|
|
|
4298
4335
|
}
|
|
4299
4336
|
const pty = resumeAgentPty(registered.repoPath, claudeSessionId, executionId, shellTicket);
|
|
4300
4337
|
registerPtySession(executionId, pty, shellTicket);
|
|
4301
|
-
|
|
4338
|
+
const activityWatcher = watchWorkflowShellActivity(supabase, executionId, pty);
|
|
4339
|
+
pty.onExit(({ exitCode }) => {
|
|
4340
|
+
activityWatcher.stop();
|
|
4341
|
+
void handleWorkflowShellExit(supabase, executionId, exitCode);
|
|
4342
|
+
});
|
|
4302
4343
|
logger.runner.info("WORKFLOW_SHELL", `Re-spawned orphaned workflow execution ${executionId} (runner restarted while it was still active).`);
|
|
4303
4344
|
}
|
|
4304
4345
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-board-runner",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.23",
|
|
4
4
|
"description": "Local runner for Agent Board — signs in with your own Supabase account (no shared secrets) and drives Claude Code against repos under your local repos root.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|