flywheel

Get Active Cycle Agent Route

Flywheel V1.3 — cockpit-mount payload for a standalone agent.

Returns the most-recent non-terminal FlywheelRun for the agent enriched with its cycle conversation_id so V1.3's standalone cockpit can decide readonly-vs-editing on first paint AND wire its ChatPane straight into the right conversation. null means "no active cycle" → cockpit renders the readonly mosaic pill and falls through to GET /fde/conversations/ambient for the chat surface.

get/api/v1/voice-agents/{voice_agent_id}/active-cycle

Path parameters

voice_agent_idstring uuid required

Response

Successful Response

run_idstring uuid required
status'draft' | 'aggregating' | 'plan_ready' | 'applied' | 'sim_running' | 'sim_passed' | 'sim_failed' | 'promoted' | 'rolled_back' | 'dismissed' required

Cycle state machine for FlywheelRun (Flywheel V1.0 cockpit).

Single source of truth for cockpit mode (readonly ↔ editing), promote-button gating, and rail-card vocabulary. Per V1 invariant #3 — never derived from aggregator_outcome; the column is the canonical signal. aggregator_outcome remains as the V0.6/V0.7 audit detail (per V1 invariant #7 — additive over renaming); V1.9 deprecates it at the Pydantic layer.

Allowed transitions (enforced in tests, not in DB):

DRAFT → AGGREGATING → PLAN_READY → APPLIED → SIM_RUNNING
      → (SIM_PASSED | SIM_FAILED) → PROMOTED → ROLLED_BACK
DRAFT | PLAN_READY | APPLIED | SIM_RUNNING
      | SIM_PASSED | SIM_FAILED → DISMISSED

Lifecycle:

  • DRAFT: row created at intent-confirm; no plan yet.
  • AGGREGATING: aggregator running.
  • PLAN_READY: FDE produced a PromptEditPlan; not applied.
  • APPLIED: candidate VoiceAgentVersion written.
  • SIM_RUNNING: simulator kicked off, awaiting webhook.
  • SIM_PASSED / SIM_FAILED: webhook reported verdict.
  • PROMOTED: candidate flipped to prod active_version_id.
  • ROLLED_BACK: a prior promote was reverted in a later cycle.
  • DISMISSED: operator closed the cycle without promoting.

Column-width note: the underlying VARCHAR is VARCHAR(32) (set in the V1.0 migration via explicit ALTER COLUMN ... TYPE VARCHAR(32)), even though SQLAlchemy's Enum(native_enum=False) auto-derives VARCHAR(11) from the longest member name ("aggregating"). Same drift pattern as AggregatorOutcome after V0.7.1 (see migration d4a8b3c5e7f1). The next engineer who runs alembic revision --autogenerate will see SQLAlchemy propose narrowing back to 11 — the column comment on FlywheelRun.status explains why the override is intentional.

intentstring nullable required
conversation_idstring uuid required
touched_agent_idsstring[] required

Changes

Changed in 1 of the 5 revisions of this API.2

    • added the optional property // to the response with the status

      response-optional-property-added

    • added the optional property // to the response with the status

      response-optional-property-added

    This revision also has 8 changes that name no endpoint, such as unreferenced schemas being removed. See the revision's changelog