From 2d133a3506bb6442d05ad3530ab67481c131ad8e Mon Sep 17 00:00:00 2001 From: Alain Prasquier Date: Wed, 18 Mar 2026 14:21:03 +0200 Subject: [PATCH 1/2] =?UTF-8?q?=E2=9C=A8=20feat:=20custom=5Froutes=20+=20s?= =?UTF-8?q?cheduled=20steps=20+=20executor=20+=20workbench=20UI?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Platform extensions for agent-driven orchestration: - custom_routes: agents mount their own FastAPI routers at /agents/{slug}/api/ - scheduled steps: CaseNodeUpdate gains scheduled_at/method/params/status fields - executor: background asyncio loop polls every 60s, executes due steps - workbench UI: countdown display, Execute now / Cancel buttons on pending steps - routes: POST execute, POST cancel, PATCH reschedule for scheduled steps - Case.cancel_scheduled_steps() for job stop cascading - Cases.get_due_scheduled_steps() for executor polling - dialog_renderer: lightweight markdown rendering for text content - 433 tests passing --- docs/CHANGELOG.md | 10 ++ .../admin/static/js/workbench-form.js | 37 ++++++ .../templates/components/dialog_renderer.html | 62 +++++++++- .../admin/templates/workbench.html | 2 +- .../admin/templates/workbench_monitor.html | 26 ++++ src/supervaizer/admin/workbench_routes.py | 113 ++++++++++++++++++ src/supervaizer/agent.py | 11 +- src/supervaizer/case.py | 50 +++++++- src/supervaizer/server.py | 50 ++++++++ 9 files changed, 355 insertions(+), 6 deletions(-) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 30cc75a..e0169b0 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -21,6 +21,16 @@ All notable changes to this project will be documented in this file. ### v0.11.0 +- **Custom routes** — Agents can mount their own FastAPI routers via `custom_routes` field on Agent. Supervaizer mounts them at `/agents/{slug}/api/` without inspecting or managing the routes. Enables agents to expose tool endpoints, webhooks, or any HTTP API alongside the workbench. + +- **Scheduled steps** — CaseNodeUpdate gains `scheduled_at`, `scheduled_method`, `scheduled_params`, `scheduled_status` fields. Steps with `scheduled_at` are deferred until the time arrives. A background executor polls every 60s and calls the agent method. Workbench shows countdown, "Execute now", and "Cancel" controls on pending scheduled steps. Enables time-based orchestration (call scheduling, retries with backoff, follow-up actions). + - Model: `CaseNodeUpdate.scheduled_at/scheduled_method/scheduled_params/scheduled_status` + - Executor: `_run_scheduled_step_loop` in server.py (local mode) + - Routes: `POST/PATCH .../steps/{case_id}/{step_index}/execute|cancel|schedule` + - UI: status badges, execute now / cancel buttons in workbench monitor + - Case: `cancel_scheduled_steps()` for job stop cascading + - Cases: `get_due_scheduled_steps()` for executor polling + - **Job Poll mechanism** — New optional `job_poll` method in `AgentMethods` for manual external service polling. When defined, the workbench shows a "Check for updates" button on active jobs. Clicking it calls the agent's poll handler, which checks external services (email inboxes, call status APIs, etc.) and updates cases accordingly. Enables local development without webhooks — production uses real-time webhooks, local mode uses the poll button. - `AgentMethods`: new `job_poll: AgentMethod | None` field - Route: `POST /workbench/jobs/{job_id}/poll` triggers the agent's poll handler diff --git a/src/supervaizer/admin/static/js/workbench-form.js b/src/supervaizer/admin/static/js/workbench-form.js index f1d3bb8..392ffe7 100644 --- a/src/supervaizer/admin/static/js/workbench-form.js +++ b/src/supervaizer/admin/static/js/workbench-form.js @@ -151,6 +151,43 @@ class WorkbenchForm { } } + async executeStepNow(caseId, stepIndex) { + if (!this.activeJobId) return; + try { + const response = await fetch( + `${this.basePath}/jobs/${this.activeJobId}/steps/${caseId}/${stepIndex}/execute`, + { method: 'POST', headers: { 'X-API-Key': this.getApiKey() } }, + ); + const result = await response.json(); + if (!response.ok) { + this.onError(result.detail || 'Execute failed'); + } else { + this.onError(''); + this.refreshMonitor(true); + } + } catch (e) { + this.onError(`Network error: ${e.message}`); + } + } + + async cancelStep(caseId, stepIndex) { + if (!this.activeJobId) return; + try { + const response = await fetch( + `${this.basePath}/jobs/${this.activeJobId}/steps/${caseId}/${stepIndex}/cancel`, + { method: 'POST', headers: { 'X-API-Key': this.getApiKey() } }, + ); + if (!response.ok) { + this.onError('Cancel failed'); + } else { + this.onError(''); + this.refreshMonitor(true); + } + } catch (e) { + this.onError(`Network error: ${e.message}`); + } + } + async stopJob(jobId) { const targetJobId = jobId || this.activeJobId; if (!targetJobId) return; diff --git a/src/supervaizer/admin/templates/components/dialog_renderer.html b/src/supervaizer/admin/templates/components/dialog_renderer.html index 8075fbe..6fa2651 100644 --- a/src/supervaizer/admin/templates/components/dialog_renderer.html +++ b/src/supervaizer/admin/templates/components/dialog_renderer.html @@ -77,7 +77,45 @@ {% elif content_type == "code" %}
{{ content_raw }}
{% else %} -
{{ content_raw }}
+ {# Text/markdown: render with lightweight markdown via Alpine #} +
'; inList = true; } + html += '
  • ' + trimmed.slice(2) + '
  • '; + } else if (trimmed === '') { + if (inList) { html += ''; inList = false; } + html += '
    '; + } else { + if (inList) { html += ''; inList = false; } + html += '

    ' + trimmed + '

    '; + } + } + if (inList) html += ''; + // Inline formatting: bold, italic, code + html = html.replace(/\*\*(.+?)\*\*/g, '$1'); + html = html.replace(/\*(.+?)\*/g, '$1'); + html = html.replace(/`(.+?)`/g, '$1'); + rendered = html; + " + x-html="rendered"> +
    {% endif %} {% endif %} @@ -178,7 +216,27 @@ {% else %} -
    {{ content_raw }}
    +
    • ' + t.slice(2) + '

    '; + else if (t === '') html += '
    '; + else html += '

    ' + t + '

    '; + } + html = html.replace(/\*\*(.+?)\*\*/g, '$1'); + html = html.replace(/`(.+?)`/g, '$1'); + rendered = html; + " + x-html="rendered"> +
    {% endif %} {% endmacro %} diff --git a/src/supervaizer/admin/templates/workbench.html b/src/supervaizer/admin/templates/workbench.html index bdb7ca5..6643c3e 100644 --- a/src/supervaizer/admin/templates/workbench.html +++ b/src/supervaizer/admin/templates/workbench.html @@ -389,7 +389,7 @@

    Execution Monitor

    {# end workbenchPage #} {# ── Workbench JS ────────────────────────────────────────────────── #} - +