commit 3343fc667ec0e6e0743640e9828d4fb040ba0f7f
Author: Coder Agent <coder@agents.omni>
Date: Thu Apr 9 15:50:11 2026
agentd: remove steering-file mechanism and pi rpc remnants
Remove steering-file control paths now that persistent sessions are
stdin-framed and signal-driven.
- Drop AGENT_STEERING_FILE polling from Omni/Agent
- Remove oneshot steering-file writes from Omni/Agentd
- Remove AGENT_STEERING_FILE container env wiring
- Update daemon/docs comments away from pi --mode rpc wording
- Update docs/spec/k8s notes from steering files to signal control
Validated repo cleanup with:
- rg "mode rpc"
- rg "steering.file" -i
Task-Id: t-759.4
diff --git a/Omni/Agent.hs b/Omni/Agent.hs
index 1efe1304..6a2ccfc9 100755
--- a/Omni/Agent.hs
+++ b/Omni/Agent.hs
@@ -62,7 +62,7 @@ import qualified Omni.Agent.Tools as Tools
import qualified Omni.Agent.Trace as Trace
import qualified Omni.Cli as Cli
import qualified Omni.Test as Test
-import System.Directory (createDirectoryIfMissing, doesFileExist, getCurrentDirectory, removeFile)
+import System.Directory (createDirectoryIfMissing, doesFileExist, getCurrentDirectory)
import qualified System.Environment as Environment
import qualified System.Exit as Exit
import System.FilePath (isAbsolute, takeDirectory, (</>))
@@ -844,8 +844,6 @@ runAgentWithState signalState opts initialState = do
TimeFormat.defaultTimeLocale
"%A, %B %d, %Y at %I:%M:%S %p %Z"
(Time.ZonedTime localTime tz)
- mSteeringPath <- resolveSteeringPath opts' cwd
-
-- Load AGENTS.md from cwd and parent directories (like pi does)
contextFiles <- loadProjectContextFiles cwd
@@ -986,7 +984,7 @@ runAgentWithState signalState opts initialState = do
cancelRequested <- consumeSignalFlag (ssSigIntRequested signalState)
if cancelRequested
then pure (Just Op.SteeringCancel)
- else checkSteeringFile mSteeringPath
+ else pure Nothing
onCheckpoint :: Text -> OpAgent.AgentState -> Trace.Trace -> IO (Either Text ())
onCheckpoint cpName' cpState' cpTrace' =
@@ -1064,36 +1062,6 @@ lookupEventMessage (Aeson.Object obj) =
_ -> Nothing
lookupEventMessage _ = Nothing
-resolveSteeringPath :: AgentOptions -> FilePath -> IO (Maybe FilePath)
-resolveSteeringPath opts cwd = do
- mPath <- Environment.lookupEnv "AGENT_STEERING_FILE"
- case mPath of
- Just path -> pure (Just path)
- Nothing -> do
- mRunIdEnv <- Environment.lookupEnv "AGENT_RUN_ID"
- let mRunId = optRunId opts <|> (Text.pack </ mRunIdEnv)
- pure <| (\runId -> cwd </> "events" </> Text.unpack runId <> ".steering") </ mRunId
-
-checkSteeringFile :: Maybe FilePath -> IO (Maybe Op.Steering)
-checkSteeringFile mPath =
- case mPath of
- Nothing -> pure Nothing
- Just path -> do
- exists <- doesFileExist path
- if not exists
- then pure Nothing
- else do
- readResult <- Exception.try (TextIO.readFile path) :: IO (Either Exception.IOException Text)
- case readResult of
- Left _ -> pure Nothing
- Right content -> do
- let trimmed = Text.strip content
- if Text.null trimmed
- then pure Nothing
- else do
- _ <- Exception.try (removeFile path) :: IO (Either Exception.IOException ())
- pure <| Just <| Op.SteeringMessage trimmed
-
-- | Resolve provider from name and optional model
resolveProvider :: Text -> Maybe Text -> IO (Either Text Provider.Provider)
resolveProvider providerName mModel = do
diff --git a/Omni/Agent/README.md b/Omni/Agent/README.md
index 62e6883b..e5b544b0 100644
--- a/Omni/Agent/README.md
+++ b/Omni/Agent/README.md
@@ -42,15 +42,13 @@ Then select the provider/model explicitly:
agent --provider=openai-codex --model=gpt-5.2-codex "fix the build"
```
-## Steering
+## Interactive Control
-Send guidance to a running agent between iterations:
+In stdin prompt mode (`agent` with no prompt arg):
-```bash
-agentd steer <run-id> "try a different approach"
-```
-
-The agent reads steering from `AGENT_STEERING_FILE` (defaults to `./events/<run-id>.steering` when `AGENT_RUN_ID` is set) and emits a `steering` event in the trace.
+- send prompts as UTF-8 chunks terminated by `\0`
+- send `SIGINT` to cancel the current turn
+- send `SIGTERM` to finish the current turn and exit cleanly
## Shutdown Handling
diff --git a/Omni/Agentd.hs b/Omni/Agentd.hs
index fa9e46f2..cbc7b577 100755
--- a/Omni/Agentd.hs
+++ b/Omni/Agentd.hs
@@ -212,10 +212,6 @@ runDir root runId = root </> Text.unpack runId
eventsFile :: FilePath -> Text -> FilePath
eventsFile root runId = runDir root runId </> "events.jsonl"
--- | Steering file for a run: /var/log/agentd/<run-id>/steering
-steeringFile :: FilePath -> Text -> FilePath
-steeringFile root runId = runDir root runId </> "steering"
-
-- | Run mode file for a run: /var/log/agentd/<run-id>/mode
runModeFile :: FilePath -> Text -> FilePath
runModeFile root runId = runDir root runId </> "mode"
@@ -239,15 +235,6 @@ readRunMode logRoot runId = do
pure <| Just (Text.strip content)
else pure Nothing
--- | Write a steering message to a run's steering file
-writeSteeringMessage :: FilePath -> Text -> Text -> IO FilePath
-writeSteeringMessage logRoot runId message = do
- let dir = runDir logRoot runId
- path = steeringFile logRoot runId
- Dir.createDirectoryIfMissing True dir
- TextIO.appendFile path (message <> "\n")
- pure path
-
startModeText :: StartMode -> String
startModeText = \case
StartOneshot -> "oneshot"
@@ -1216,10 +1203,6 @@ runAgent prompt mName foreground verbose maxCost maxIter timeoutSecs mProvider m
Just name -> pure name
Nothing -> Events.runIdToText </ Events.newRunId
- let workspaceEventsDir = cwd </> "events"
- Dir.createDirectoryIfMissing True workspaceEventsDir
- let containerSteeringFile = "/workspace/events/" <> Text.unpack runId <> ".steering"
-
-- Get current user/group for file ownership
uid <- Posix.getRealUserID
gid <- Posix.getRealGroupID
@@ -1311,9 +1294,7 @@ runAgent prompt mName foreground verbose maxCost maxIter timeoutSecs mProvider m
"KAGI_API_KEY",
-- Pass run ID so agent can use it
"-e",
- "AGENT_RUN_ID=" <> Text.unpack runId,
- "-e",
- "AGENT_STEERING_FILE=" <> containerSteeringFile
+ "AGENT_RUN_ID=" <> Text.unpack runId
]
++ extraEnv
++ [
@@ -2306,14 +2287,8 @@ showPersistentStatusById runId asJson = do
else printPersistentAgentDetail pa
sendOneshotInput :: Text -> Text -> IO ()
-sendOneshotInput runId message = do
- let trimmed = Text.strip message
- when (Text.null trimmed) <| do
- TextIO.hPutStrLn stderr "Error: steering message is empty"
- Exit.exitWith (Exit.ExitFailure 1)
- logRoot <- getLogRoot
- path <- writeSteeringMessage logRoot runId trimmed
- TextIO.putStrLn <| "Steering queued: " <> Text.pack path
+sendOneshotInput _runId _message =
+ exitWithTextError "Oneshot input injection is no longer supported. Use persistent sessions and 'agentd send'."
removeOneshotRun :: Text -> IO (Either Text ())
removeOneshotRun runId = do
diff --git a/Omni/Agentd/Daemon.hs b/Omni/Agentd/Daemon.hs
index c3bdfa2a..ac3f0ccd 100644
--- a/Omni/Agentd/Daemon.hs
+++ b/Omni/Agentd/Daemon.hs
@@ -6,12 +6,12 @@
{-# LANGUAGE NoImplicitPrelude #-}
{-# OPTIONS_GHC -Wno-unused-top-binds #-}
--- | Agentd Daemon - HTTP API for agent lifecycle management with native Pi adapter
+-- | Agentd Daemon - HTTP API for agent lifecycle management.
--
-- Provides a long-running daemon process with:
--- - HTTP API for spawning/managing pi agents
+-- - HTTP API for spawning/managing agents
-- - SQLite persistence for agent state
--- - Direct process management of pi --mode rpc
+-- - Direct process management of agent stdin-mode processes
-- - Webhook notifications on agent completion
--
-- : out agentd-daemon
@@ -433,7 +433,7 @@ instance ToJSON PersistentAgent where
-- * Servant API
type AgentAPI =
- -- POST /agents - create + start a persistent pi agent
+ -- POST /agents - create + start a persistent agent
"agents" :> ReqBody '[JSON] SpawnRequest :> Post '[JSON] SpawnResponse
-- GET /agents - list all agents
:<|> "agents" :> Get '[JSON] [AgentInfo]
@@ -455,7 +455,7 @@ agentAPI = Proxy
-- * Server State
--- | Running pi agent process info
+-- | Running agent process info
data RunningAgent = RunningAgent
{ raProcessHandle :: Process.ProcessHandle,
raStdinHandle :: IO.Handle,
@@ -1301,7 +1301,7 @@ streamAgentLogs runId follow = do
-- * Pi Agent Management (Native)
--- | Default model for pi agents (already in pi-compatible format)
+-- | Default model for agents.
defaultModel :: Text
defaultModel = Models.defaultModel
diff --git a/Omni/Agentd/SPEC.md b/Omni/Agentd/SPEC.md
index 505fe433..6eddc021 100644
--- a/Omni/Agentd/SPEC.md
+++ b/Omni/Agentd/SPEC.md
@@ -88,7 +88,6 @@ The container inherits these from the host:
Agentd also sets:
- `AGENT_RUN_ID`
-- `AGENT_STEERING_FILE`
- `PATH=/repo/_/bin:/bin`
- `CODEROOT=/repo`
- `GIT_CONFIG_COUNT=1`
@@ -98,15 +97,14 @@ Agentd also sets:
OAuth tokens are accessed via the mounted data directory (`~/.local/share/agent/auth.json`).
-## Steering
+## Runtime Control
-Send guidance to a running agent between iterations:
+Persistent sessions are controlled with standard Unix signals:
-```bash
-agentd steer <run-id> "try a different approach"
-```
+- `SIGINT` cancels the current turn
+- `SIGTERM` lets the current turn finish, then exits
-This writes to `<workspace>/events/<run-id>.steering` (or legacy `<workspace>/_/events/<run-id>.steering`). The agent reads `AGENT_STEERING_FILE`, injects the message, and emits a `steering` event in the trace.
+Prompt delivery to persistent sessions uses stdin with NUL-delimited frames.
## Examples
diff --git a/k8s/agents/README.md b/k8s/agents/README.md
index a9ca0364..76d4ab5e 100644
--- a/k8s/agents/README.md
+++ b/k8s/agents/README.md
@@ -62,14 +62,10 @@ kubectl create job agent-$(date +%s) \
-- agent --json "fix the bug"
```
-## Steering
+## Runtime control
-To steer a running job, append to the steering file inside the pod:
-
-```bash
-kubectl exec -n agents job/agent-123 -- \
- sh -c 'echo "try a different approach" >> /workspace/events/agent-123.steering'
-```
+To cancel the current turn for a running job, send SIGINT to the agent process.
+For graceful shutdown, send SIGTERM.
## Cleanup
diff --git a/k8s/agents/job-template.yaml b/k8s/agents/job-template.yaml
index aec1b023..91ad1475 100644
--- a/k8s/agents/job-template.yaml
+++ b/k8s/agents/job-template.yaml
@@ -41,8 +41,6 @@ spec:
valueFrom:
fieldRef:
fieldPath: metadata.name
- - name: AGENT_STEERING_FILE
- value: /workspace/events/$(AGENT_RUN_ID).steering
resources:
requests:
cpu: "500m"