(req, res, params)
| 453 | * AGENTATION_WEBHOOKS environment variables). |
| 454 | */ |
| 455 | const requestActionHandler: RouteHandler = async (req, res, params) => { |
| 456 | try { |
| 457 | const sessionId = params.id; |
| 458 | const body = await parseBody<{ output: string }>(req); |
| 459 | |
| 460 | // Verify session exists |
| 461 | const session = getSessionWithAnnotations(sessionId); |
| 462 | if (!session) { |
| 463 | return sendError(res, 404, "Session not found"); |
| 464 | } |
| 465 | |
| 466 | if (!body.output) { |
| 467 | return sendError(res, 400, "output is required"); |
| 468 | } |
| 469 | |
| 470 | // Build action request payload |
| 471 | const actionRequest: ActionRequest = { |
| 472 | sessionId, |
| 473 | annotations: session.annotations, |
| 474 | output: body.output, |
| 475 | timestamp: new Date().toISOString(), |
| 476 | }; |
| 477 | |
| 478 | // Emit event (will be sent to all SSE subscribers) |
| 479 | eventBus.emit("action.requested", sessionId, actionRequest); |
| 480 | |
| 481 | // Send webhooks (fire and forget, non-blocking) |
| 482 | const webhookUrls = getWebhookUrls(); |
| 483 | sendWebhooks(actionRequest); |
| 484 | |
| 485 | // Return delivery info so client knows if anyone received it |
| 486 | // Only count agent connections (with ?agent=true), not browser toolbar connections |
| 487 | const agentListeners = agentConnections.size; |
| 488 | const webhooks = webhookUrls.length; |
| 489 | |
| 490 | sendJson(res, 200, { |
| 491 | success: true, |
| 492 | annotationCount: session.annotations.length, |
| 493 | delivered: { |
| 494 | sseListeners: agentListeners, |
| 495 | webhooks: webhooks, |
| 496 | total: agentListeners + webhooks, |
| 497 | }, |
| 498 | }); |
| 499 | } catch (err) { |
| 500 | sendError(res, 400, (err as Error).message); |
| 501 | } |
| 502 | }; |
| 503 | |
| 504 | /** |
| 505 | * POST /annotations/:id/thread - Add a thread message. |
nothing calls this directly
no test coverage detected
searching dependent graphs…