Integrate Op with Agentd Container Runtime

t-369.14·WorkTask·
·
·
·Omni/Agent.hs
Parent:t-369·Created1 month ago·Updated1 month ago

Dependencies

Description

Edit

Update Agentd to run Op programs in containers.

Context

Agentd runs agent specs in Docker containers. Update it to support Op programs, with the interpreter running inside the container.

Deliverables

1. Update Spec Format

Add Op-native spec format alongside markdown:

# agent-spec.yaml
kind: op
program: Omni.Agent.Programs.Coder.coder  # Haskell module path
config:
  namespace: Biz/Cloud
  task: "Fix the bug"
  model: claude-sonnet-4
  
interpreter: sequential  # or: parallel
state: crdt              # or: stm, event-log

sandbox:
  image: agent-haskell
  workspace: /workspace
  
budget:
  cost_cents: 100
  tokens: 100000
  timeout_sec: 600

2. Update Agentd.hs

-- | Parse Op spec
data OpSpec = OpSpec
  { opProgram :: Text           -- module path to Op program
  , opConfig :: Value           -- config JSON passed to program
  , opInterpreter :: Interpreter
  , opState :: StateStrategy
  , opSandbox :: Sandbox
  , opBudget :: Budget
  }

data Interpreter = Sequential | Parallel | Distributed
data StateStrategy = CRDT | STM | EventLog

parseOpSpec :: Text -> Either Text OpSpec

-- | Run Op spec in container
runOpSpec :: OpSpec -> IO Exit.ExitCode
runOpSpec spec = do
  -- Build docker command
  let dockerArgs = 
        [ "run", "--rm", "-i"
        , "-v", workspace <> ":/workspace"
        , "-w", "/workspace"
        , image spec
        -- Run interpreter with program
        , "op-runner"
        , "--program", Text.unpack (opProgram spec)
        , "--config", Text.unpack (encodeConfig (opConfig spec))
        , "--interpreter", show (opInterpreter spec)
        , "--state", show (opState spec)
        ]
  ...

3. Create Op Runner Binary

Create Omni/Agent/OpRunner.hs:

-- | Runner binary for Op programs inside containers
-- : out op-runner

module Omni.Agent.OpRunner where

main :: IO ()
main = do
  args <- parseArgs
  
  -- Load program (this is the tricky part)
  -- Option 1: Compile program into runner
  -- Option 2: Use hint/plugins to load dynamically
  -- Option 3: Serialize Op programs
  
  -- For now, hardcode known programs
  program <- case argProgram args of
    "Omni.Agent.Programs.Agent" -> 
      pure (Agent.agent (decodeConfig (argConfig args)))
    "Omni.Agent.Programs.Coder" ->
      pure (Coder.coder (decodeConfig (argConfig args)))
    other -> 
      die ("Unknown program: " <> other)
  
  -- Select interpreter
  interpreter <- case argInterpreter args of
    Sequential -> pure runSequential
    Parallel -> pure runParallel
  
  -- Run
  result <- interpreter config initialState program
  
  -- Output result
  case result of
    Left err -> do
      hPutStrLn stderr err
      exitFailure
    Right (a, trace, _) -> do
      -- Write trace to workspace
      writeTraceJsonl "/workspace/_/trace.jsonl" trace
      -- Output result
      BL.putStrLn (encode a)

4. Update Container Images

Add op-runner to container images:

# Omni/Agentd/Images/Base.nix
buildImage {
  contents = [
    op-runner
    # ... existing tools
  ];
}

5. CLI Updates

agentd run spec.yaml           # runs markdown or op spec
agentd run --op program.hs     # runs Op program directly

Notes

  • Dynamic program loading is complex in Haskell
  • For now, compile known programs into runner
  • Later could use hint or Template Haskell
  • Trace written to workspace for replay
  • Container provides sandboxing

Challenges

  • Haskell dynamic loading is not trivial
  • May need to compile programs into runner binary
  • Or serialize Op programs as JSON (complex)

Testing

  • Op spec parses correctly
  • Runner executes known programs
  • Trace written to workspace
  • Container sandboxing works
  • Budget limits enforced

Files to Read First

  • Omni/Agentd.hs
  • Omni/Agent/Op.hs
  • Omni/Agent/Interpreter/Sequential.hs
  • Nix container image definitions

Timeline (2)

🔄[human]Open → InProgress1 month ago
🔄[human]InProgress → Done1 month ago