agent-board-runner 0.1.18 → 0.1.20

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 CHANGED
@@ -320,7 +320,7 @@ var BOX_DRAWING_PATTERN = /[─-╿]/g;
320
320
  var WORKING_PATTERN = /esc to interrupt/i;
321
321
  var BACKGROUND_SHELL_PATTERN = /\b[1-9]\d*\s+shells?\b/i;
322
322
  function isAgentHookMoment(value) {
323
- return value === "implementing" || value === "tool_active" || value === "waiting_plan_approval" || value === "waiting_prototype_review" || value === "commit_detected" || value === "turn_stopped" || value === "notification";
323
+ return value === "implementing" || value === "tool_active" || value === "waiting_plan_approval" || value === "waiting_prototype_review" || value === "waiting_workflow_answer" || value === "commit_detected" || value === "turn_stopped" || value === "notification";
324
324
  }
325
325
  var USAGE_LIMIT_PATTERN = /(usage limit reached|limit reached[^\n]{0,60}resets|reached your (?:usage|session) limit|out of (?:weekly|session) (?:usage|limit))/i;
326
326
  var PLAN_APPROVAL_PATTERN = /would you like to proceed\??/i;
@@ -372,6 +372,8 @@ var AgentActivityWatcher = class {
372
372
  * "waiting_user" — see the guard in reportHookMoment's "turn_stopped" case.
373
373
  */
374
374
  prototypeReviewPending = false;
375
+ /** Same shape as prototypeReviewPending, for ask_workflow_question (see docs/ai-knowledges/workflows-visuais-mecanismo.md). */
376
+ workflowAnswerPending = false;
375
377
  toolCallPending = false;
376
378
  commitDetected = false;
377
379
  timer;
@@ -414,6 +416,7 @@ var AgentActivityWatcher = class {
414
416
  this.usageLimited = false;
415
417
  this.planApprovalPending = false;
416
418
  this.prototypeReviewPending = false;
419
+ this.workflowAnswerPending = false;
417
420
  this.commitDetected = false;
418
421
  this.setState("implementing");
419
422
  break;
@@ -429,12 +432,17 @@ var AgentActivityWatcher = class {
429
432
  this.prototypeReviewPending = true;
430
433
  this.setState("waiting_prototype_review");
431
434
  break;
435
+ case "waiting_workflow_answer":
436
+ this.workflowAnswerPending = true;
437
+ this.setState("waiting_workflow_answer");
438
+ break;
432
439
  case "commit_detected":
433
440
  this.commitDetected = true;
434
441
  break;
435
442
  case "turn_stopped":
436
443
  if (this.usageLimited) break;
437
444
  if (this.prototypeReviewPending) break;
445
+ if (this.workflowAnswerPending) break;
438
446
  if (BACKGROUND_SHELL_PATTERN.test(this.recentOutput())) {
439
447
  this.setState("waiting_background");
440
448
  break;
@@ -1255,6 +1263,11 @@ function detectMoment(input) {
1255
1263
  // prototypeReviewPending latch in agentActivity.ts's reportHookMoment.
1256
1264
  if (eventName === "PostToolUse" && input.tool_name === "mcp__agent-board__publish_prototype") return "waiting_prototype_review";
1257
1265
 
1266
+ // Same shape as publish_prototype above, but for a workflow graph's "ask" node (see
1267
+ // ask_workflow_question in boardTools.ts and docs/ai-knowledges/workflows-visuais-mecanismo.md)
1268
+ // \u2014 see the workflowAnswerPending latch in agentActivity.ts's reportHookMoment.
1269
+ if (eventName === "PostToolUse" && input.tool_name === "mcp__agent-board__ask_workflow_question") return "waiting_workflow_answer";
1270
+
1258
1271
  // A commit that actually succeeded, read off the Bash tool's own structured result instead
1259
1272
  // of guessing from scrollback text (exact, and immune to locale/color/format changes to
1260
1273
  // git's own banner). Any other Bash completion still reports "tool_active" below, same as
@@ -3229,7 +3242,7 @@ async function dispatchRunnerQueue(supabase, config) {
3229
3242
  for (const project of config.registeredProjects) {
3230
3243
  const running = await getRunningSessionForProject(supabase, project.id, config.runnerId);
3231
3244
  if (!running) continue;
3232
- if ((running.agentState === "done" || running.agentState === "waiting_prototype_review") && queuedProjectIds.has(project.id)) {
3245
+ if ((running.agentState === "done" || running.agentState === "waiting_prototype_review" || running.agentState === "waiting_workflow_answer") && queuedProjectIds.has(project.id)) {
3233
3246
  await pauseFinishedSession(supabase, config, project.id, running.cardId, running.id);
3234
3247
  continue;
3235
3248
  }
@@ -3294,6 +3307,14 @@ async function publishQueueSnapshots(supabase, config, queued, promotedCardIds)
3294
3307
  function buildPrototypeReviewFollowUp(prototypeApproved, prototypeUrl) {
3295
3308
  return prototypeApproved ? "O prot\xF3tipo foi revisado e aprovado \u2014 prossiga para a implementa\xE7\xE3o real, usando-o como refer\xEAncia." : `O prot\xF3tipo ainda n\xE3o foi aprovado${prototypeUrl ? ` (${prototypeUrl})` : ""} \u2014 confira os coment\xE1rios deixados nele (e, se for um prot\xF3tipo de marca\xE7\xE3o de pontos, os pontos salvos) e ajuste, publicando a nova vers\xE3o com publish_prototype. Depois, pare o turno de novo: a sess\xE3o ser\xE1 retomada quando houver outra revis\xE3o.`;
3296
3309
  }
3310
+ async function buildWorkflowAnswerFollowUp(supabase, cardId) {
3311
+ const { data } = await supabase.from("card_workflow_questions").select("question, answer").eq("card_id", cardId).eq("status", "answered").order("answered_at", { ascending: false }).limit(1).maybeSingle();
3312
+ if (!data) return "A pergunta foi respondida \u2014 confira os coment\xE1rios do card e prossiga.";
3313
+ return `Pergunta: "${data.question}"
3314
+ Resposta do humano: "${data.answer}"
3315
+
3316
+ Prossiga com o workflow a partir daqui.`;
3317
+ }
3297
3318
  async function promoteQueuedSession(supabase, config, entry) {
3298
3319
  const existingHandle = getPtySession(entry.id);
3299
3320
  if (existingHandle) {
@@ -3313,6 +3334,9 @@ async function promoteQueuedSession(supabase, config, entry) {
3313
3334
  buildPrototypeReviewFollowUp(entry.cards.prototype_approved, entry.cards.prototype_url)
3314
3335
  );
3315
3336
  }
3337
+ if (entry.agent_state === "waiting_workflow_answer" && !existingHandle.exited) {
3338
+ sendMessageToIdleSession(existingHandle.pty, entry.card_id, await buildWorkflowAnswerFollowUp(supabase, entry.card_id));
3339
+ }
3316
3340
  return;
3317
3341
  }
3318
3342
  const spawned = await spawnSessionPty(supabase, config, entry);
@@ -3431,7 +3455,7 @@ function watchAgentActivity(supabase, config, projectId, cardId, sessionId, pty)
3431
3455
  } else {
3432
3456
  cancelUsageResume(sessionId);
3433
3457
  }
3434
- if (state === "done" || state === "waiting_prototype_review") void dispatchRunnerQueue(supabase, config);
3458
+ if (state === "done" || state === "waiting_prototype_review" || state === "waiting_workflow_answer") void dispatchRunnerQueue(supabase, config);
3435
3459
  });
3436
3460
  pty.onData((chunk) => watcher.push(chunk));
3437
3461
  registerAgentActivityWatcher(sessionId, watcher);
@@ -3466,6 +3490,14 @@ async function requeuePausedCard(supabase, config, card) {
3466
3490
  logger.card.info(card.id, `Session ${active.id} was never paused (alone in its project) \u2014 sent the prototype review follow-up directly.`);
3467
3491
  }
3468
3492
  }
3493
+ if (active.status === "running" && active.agentState === "waiting_workflow_answer") {
3494
+ const handle = getPtySession(active.id);
3495
+ await updateCard(supabase, card.id, { requeueRequestedAt: null });
3496
+ if (handle && !handle.exited) {
3497
+ sendMessageToIdleSession(handle.pty, card.id, await buildWorkflowAnswerFollowUp(supabase, card.id));
3498
+ logger.card.info(card.id, `Session ${active.id} was never paused (alone in its project) \u2014 sent the workflow answer follow-up directly.`);
3499
+ }
3500
+ }
3469
3501
  }
3470
3502
  async function reconcileOrphanedSessions(supabase, config) {
3471
3503
  for (const project of config.registeredProjects) {
package/dist/index.js CHANGED
@@ -40,7 +40,7 @@ var BOX_DRAWING_PATTERN = /[─-╿]/g;
40
40
  var WORKING_PATTERN = /esc to interrupt/i;
41
41
  var BACKGROUND_SHELL_PATTERN = /\b[1-9]\d*\s+shells?\b/i;
42
42
  function isAgentHookMoment(value) {
43
- return value === "implementing" || value === "tool_active" || value === "waiting_plan_approval" || value === "waiting_prototype_review" || value === "commit_detected" || value === "turn_stopped" || value === "notification";
43
+ return value === "implementing" || value === "tool_active" || value === "waiting_plan_approval" || value === "waiting_prototype_review" || value === "waiting_workflow_answer" || value === "commit_detected" || value === "turn_stopped" || value === "notification";
44
44
  }
45
45
  var USAGE_LIMIT_PATTERN = /(usage limit reached|limit reached[^\n]{0,60}resets|reached your (?:usage|session) limit|out of (?:weekly|session) (?:usage|limit))/i;
46
46
  var PLAN_APPROVAL_PATTERN = /would you like to proceed\??/i;
@@ -92,6 +92,8 @@ var AgentActivityWatcher = class {
92
92
  * "waiting_user" — see the guard in reportHookMoment's "turn_stopped" case.
93
93
  */
94
94
  prototypeReviewPending = false;
95
+ /** Same shape as prototypeReviewPending, for ask_workflow_question (see docs/ai-knowledges/workflows-visuais-mecanismo.md). */
96
+ workflowAnswerPending = false;
95
97
  toolCallPending = false;
96
98
  commitDetected = false;
97
99
  timer;
@@ -134,6 +136,7 @@ var AgentActivityWatcher = class {
134
136
  this.usageLimited = false;
135
137
  this.planApprovalPending = false;
136
138
  this.prototypeReviewPending = false;
139
+ this.workflowAnswerPending = false;
137
140
  this.commitDetected = false;
138
141
  this.setState("implementing");
139
142
  break;
@@ -149,12 +152,17 @@ var AgentActivityWatcher = class {
149
152
  this.prototypeReviewPending = true;
150
153
  this.setState("waiting_prototype_review");
151
154
  break;
155
+ case "waiting_workflow_answer":
156
+ this.workflowAnswerPending = true;
157
+ this.setState("waiting_workflow_answer");
158
+ break;
152
159
  case "commit_detected":
153
160
  this.commitDetected = true;
154
161
  break;
155
162
  case "turn_stopped":
156
163
  if (this.usageLimited) break;
157
164
  if (this.prototypeReviewPending) break;
165
+ if (this.workflowAnswerPending) break;
158
166
  if (BACKGROUND_SHELL_PATTERN.test(this.recentOutput())) {
159
167
  this.setState("waiting_background");
160
168
  break;
@@ -975,6 +983,11 @@ function detectMoment(input) {
975
983
  // prototypeReviewPending latch in agentActivity.ts's reportHookMoment.
976
984
  if (eventName === "PostToolUse" && input.tool_name === "mcp__agent-board__publish_prototype") return "waiting_prototype_review";
977
985
 
986
+ // Same shape as publish_prototype above, but for a workflow graph's "ask" node (see
987
+ // ask_workflow_question in boardTools.ts and docs/ai-knowledges/workflows-visuais-mecanismo.md)
988
+ // \u2014 see the workflowAnswerPending latch in agentActivity.ts's reportHookMoment.
989
+ if (eventName === "PostToolUse" && input.tool_name === "mcp__agent-board__ask_workflow_question") return "waiting_workflow_answer";
990
+
978
991
  // A commit that actually succeeded, read off the Bash tool's own structured result instead
979
992
  // of guessing from scrollback text (exact, and immune to locale/color/format changes to
980
993
  // git's own banner). Any other Bash completion still reports "tool_active" below, same as
@@ -3117,7 +3130,7 @@ async function dispatchRunnerQueue(supabase, config) {
3117
3130
  for (const project of config.registeredProjects) {
3118
3131
  const running = await getRunningSessionForProject(supabase, project.id, config.runnerId);
3119
3132
  if (!running) continue;
3120
- if ((running.agentState === "done" || running.agentState === "waiting_prototype_review") && queuedProjectIds.has(project.id)) {
3133
+ if ((running.agentState === "done" || running.agentState === "waiting_prototype_review" || running.agentState === "waiting_workflow_answer") && queuedProjectIds.has(project.id)) {
3121
3134
  await pauseFinishedSession(supabase, config, project.id, running.cardId, running.id);
3122
3135
  continue;
3123
3136
  }
@@ -3182,6 +3195,14 @@ async function publishQueueSnapshots(supabase, config, queued, promotedCardIds)
3182
3195
  function buildPrototypeReviewFollowUp(prototypeApproved, prototypeUrl) {
3183
3196
  return prototypeApproved ? "O prot\xF3tipo foi revisado e aprovado \u2014 prossiga para a implementa\xE7\xE3o real, usando-o como refer\xEAncia." : `O prot\xF3tipo ainda n\xE3o foi aprovado${prototypeUrl ? ` (${prototypeUrl})` : ""} \u2014 confira os coment\xE1rios deixados nele (e, se for um prot\xF3tipo de marca\xE7\xE3o de pontos, os pontos salvos) e ajuste, publicando a nova vers\xE3o com publish_prototype. Depois, pare o turno de novo: a sess\xE3o ser\xE1 retomada quando houver outra revis\xE3o.`;
3184
3197
  }
3198
+ async function buildWorkflowAnswerFollowUp(supabase, cardId) {
3199
+ const { data } = await supabase.from("card_workflow_questions").select("question, answer").eq("card_id", cardId).eq("status", "answered").order("answered_at", { ascending: false }).limit(1).maybeSingle();
3200
+ if (!data) return "A pergunta foi respondida \u2014 confira os coment\xE1rios do card e prossiga.";
3201
+ return `Pergunta: "${data.question}"
3202
+ Resposta do humano: "${data.answer}"
3203
+
3204
+ Prossiga com o workflow a partir daqui.`;
3205
+ }
3185
3206
  async function promoteQueuedSession(supabase, config, entry) {
3186
3207
  const existingHandle = getPtySession(entry.id);
3187
3208
  if (existingHandle) {
@@ -3201,6 +3222,9 @@ async function promoteQueuedSession(supabase, config, entry) {
3201
3222
  buildPrototypeReviewFollowUp(entry.cards.prototype_approved, entry.cards.prototype_url)
3202
3223
  );
3203
3224
  }
3225
+ if (entry.agent_state === "waiting_workflow_answer" && !existingHandle.exited) {
3226
+ sendMessageToIdleSession(existingHandle.pty, entry.card_id, await buildWorkflowAnswerFollowUp(supabase, entry.card_id));
3227
+ }
3204
3228
  return;
3205
3229
  }
3206
3230
  const spawned = await spawnSessionPty(supabase, config, entry);
@@ -3319,7 +3343,7 @@ function watchAgentActivity(supabase, config, projectId, cardId, sessionId, pty)
3319
3343
  } else {
3320
3344
  cancelUsageResume(sessionId);
3321
3345
  }
3322
- if (state === "done" || state === "waiting_prototype_review") void dispatchRunnerQueue(supabase, config);
3346
+ if (state === "done" || state === "waiting_prototype_review" || state === "waiting_workflow_answer") void dispatchRunnerQueue(supabase, config);
3323
3347
  });
3324
3348
  pty.onData((chunk) => watcher.push(chunk));
3325
3349
  registerAgentActivityWatcher(sessionId, watcher);
@@ -3354,6 +3378,14 @@ async function requeuePausedCard(supabase, config, card) {
3354
3378
  logger.card.info(card.id, `Session ${active.id} was never paused (alone in its project) \u2014 sent the prototype review follow-up directly.`);
3355
3379
  }
3356
3380
  }
3381
+ if (active.status === "running" && active.agentState === "waiting_workflow_answer") {
3382
+ const handle = getPtySession(active.id);
3383
+ await updateCard(supabase, card.id, { requeueRequestedAt: null });
3384
+ if (handle && !handle.exited) {
3385
+ sendMessageToIdleSession(handle.pty, card.id, await buildWorkflowAnswerFollowUp(supabase, card.id));
3386
+ logger.card.info(card.id, `Session ${active.id} was never paused (alone in its project) \u2014 sent the workflow answer follow-up directly.`);
3387
+ }
3388
+ }
3357
3389
  }
3358
3390
  async function reconcileOrphanedSessions(supabase, config) {
3359
3391
  for (const project of config.registeredProjects) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-board-runner",
3
- "version": "0.1.18",
3
+ "version": "0.1.20",
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": {