From b3bda8467a39865582ca927bc44fa6db24bbbca1 Mon Sep 17 00:00:00 2001 From: Fabro Date: Mon, 4 May 2026 04:22:04 +0000 Subject: [PATCH] fabro(01KQRF918YQT8T17RHTE6DYCTE): implement (succeeded) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fabro-Run: 01KQRF918YQT8T17RHTE6DYCTE Fabro-Completed: 5 Fabro-Checkpoint: af1b54f5f8b186fb9d60354be880e79d67944553 ⚒️ Generated with [Fabro](https://fabro.sh) --- .../app/components/steer-composer.tsx | 92 ++++++ apps/fabro-web/app/lib/mutations.ts | 27 +- apps/fabro-web/app/lib/query-keys.ts | 3 +- apps/fabro-web/app/lib/run-events.ts | 13 +- apps/fabro-web/app/routes/runs.tsx | 21 +- docs/public/api-reference/fabro-api.yaml | 75 ++++- lib/crates/fabro-agent/src/lib.rs | 6 +- lib/crates/fabro-agent/src/session.rs | 256 +++++++++++++---- lib/crates/fabro-agent/src/types.rs | 20 +- .../fabro-agent/tests/it/parity_matrix.rs | 8 +- lib/crates/fabro-cli/src/args.rs | 25 +- lib/crates/fabro-cli/src/commands/mod.rs | 3 +- .../fabro-cli/src/commands/run/runner.rs | 42 ++- lib/crates/fabro-cli/src/commands/steer.rs | 37 +++ lib/crates/fabro-cli/src/main.rs | 5 +- lib/crates/fabro-cli/tests/it/cmd/fabro.rs | 1 + lib/crates/fabro-client/src/client.rs | 27 +- .../fabro-interview/src/control_protocol.rs | 50 +++- lib/crates/fabro-interview/src/lib.rs | 3 +- lib/crates/fabro-server/src/server.rs | 3 +- lib/crates/fabro-types/src/lib.rs | 4 +- lib/crates/fabro-types/src/run_event/agent.rs | 22 ++ lib/crates/fabro-types/src/run_event/mod.rs | 18 +- lib/crates/fabro-types/src/steering.rs | 9 + .../fabro-workflow/src/event/convert.rs | 30 +- lib/crates/fabro-workflow/src/event/events.rs | 35 ++- lib/crates/fabro-workflow/src/event/names.rs | 6 +- .../fabro-workflow/src/event/stored_fields.rs | 12 +- .../fabro-workflow/src/handler/llm/api.rs | 78 +++++- lib/crates/fabro-workflow/src/lib.rs | 3 +- .../fabro-workflow/src/lifecycle/git.rs | 3 +- .../fabro-workflow/src/operations/start.rs | 8 +- .../src/pipeline/execute/tests.rs | 5 +- .../fabro-workflow/src/pipeline/finalize.rs | 4 +- .../fabro-workflow/src/pipeline/initialize.rs | 19 +- .../fabro-workflow/src/pipeline/retro.rs | 4 +- .../fabro-workflow/src/pipeline/types.rs | 4 +- lib/crates/fabro-workflow/src/services.rs | 7 +- lib/crates/fabro-workflow/src/steering_hub.rs | 262 ++++++++++++++++++ lib/crates/fabro-workflow/src/test_support.rs | 3 +- 40 files changed, 1146 insertions(+), 107 deletions(-) create mode 100644 apps/fabro-web/app/components/steer-composer.tsx create mode 100644 lib/crates/fabro-cli/src/commands/steer.rs create mode 100644 lib/crates/fabro-types/src/steering.rs create mode 100644 lib/crates/fabro-workflow/src/steering_hub.rs diff --git a/apps/fabro-web/app/components/steer-composer.tsx b/apps/fabro-web/app/components/steer-composer.tsx new file mode 100644 index 000000000..b2b613fd6 --- /dev/null +++ b/apps/fabro-web/app/components/steer-composer.tsx @@ -0,0 +1,92 @@ +import { useState, useCallback, useRef, useEffect } from "react"; + +import { useSteerRun } from "../lib/mutations"; + +interface SteerComposerProps { + runId: string; + onClose: () => void; +} + +export function SteerComposer({ runId, onClose }: SteerComposerProps) { + const [text, setText] = useState(""); + const [error, setError] = useState(null); + const textareaRef = useRef(null); + const steerMutation = useSteerRun(runId); + + useEffect(() => { + textareaRef.current?.focus(); + }, []); + + const submit = useCallback( + async (interrupt: boolean) => { + if (!text.trim()) return; + setError(null); + try { + await steerMutation.trigger({ text: text.trim(), interrupt }); + setText(""); + onClose(); + } catch (err: unknown) { + const message = + err instanceof Error ? err.message : "Failed to deliver steer."; + setError(message); + } + }, + [text, steerMutation, onClose], + ); + + const handleKeyDown = useCallback( + (e: React.KeyboardEvent) => { + if (e.key === "Enter" && !e.shiftKey) { + e.preventDefault(); + submit(false); + } + }, + [submit], + ); + + return ( +
+
+
+
+
+

Steer agent

+ +
+