Chapter 20: Processes And PTYs

Part III: Host Capabilities and Local App Surfaces

Why this chapter matters

Process and PTY control lets Ricochet coordinate external tools. This is high-trust host power, so the chapter teaches deliberate opt-ins and clear boundaries.

What you will build

You will build a harmless local tool-runner that executes short Windows commands, captures bounded output, starts and reads a retained process job, requests cancellation for a long-running job, and captures output from a PTY session.

Concepts in plain English

A process runs another program. A PTY gives that program an interactive terminal-like session.

The chapter uses these concepts:

Vocabulary and commands

Primary coverage: process_spawn, process_spawn_task, process_start, process_jobs, process_job, process_cancel, process_release, process_read, process_env_put, pty_start, pty_write, pty_read, pty_resize, pty_stop, pty_release, pty_list, and pty_detail.

Guided example

Open examples/learn/20-processes-and-ptys/tool-runner.rco and run:

coderco run --allow-process --allow-pty examples/learn/20-processes-and-ptys/tool-runner.rco

The runnable example uses Windows cmd /C echo ... commands so it can stay harmless and deterministic. Process and PTY power is disabled unless you grant it with flags.

Blocking process calls are best for short commands whose output you want all at once:

codeprocessArgs array
$processArgs "/C" push drop
$processArgs "echo %RICOCHET_LEARN_CHILD%" push drop

processOptions map
$processOptions "timeout_ms" 5000 put drop
$processOptions "stdout_max_bytes" 200 put drop
$processOptions "RICOCHET_LEARN_CHILD" "process-ok" process_env_put value processOptions set

"cmd" $processArgs $processOptions process_spawn value spawnResult var
$spawnResult "stdout" at trim println

Use the task variant when the current flow can do other work before collecting the result:

code"cmd" $processArgs $processOptions process_spawn_task spawnTask var
$spawnTask await value taskResult var
$taskResult "stdout" at trim println
$spawnTask release_task drop

Retained jobs let you inspect output incrementally. Start the job, poll its snapshot until it exits, then read retained stdout/stderr and release it:

code"cmd" $jobArgs $jobOptions process_start value job var

nil jobSnapshot var
0 attempts var
$attempts 50 < while
  $job "id" at process_job value jobSnapshot set
  $jobSnapshot "status" at "exited" = if
    break
  end
  50 sleep
  $attempts 1 + attempts set
end

readOptions map
$job "id" at $readOptions process_read value jobRead var
$jobRead "stdout" at trim println
$job "id" at process_release value println

process_cancel requests cancellation for a retained job:

code"cmd" $cancelArgs $cancelOptions process_start value cancelJob var
$cancelJob "id" at process_cancel value "cancelled" at println

nil cancelSnapshot var
0 cancelAttempts var
$cancelAttempts 50 < while
  $cancelJob "id" at process_job value cancelSnapshot set
  $cancelSnapshot "running" at false = if
    break
  end
  50 sleep
  $cancelAttempts 1 + cancelAttempts set
end

$cancelJob "id" at process_release value println

PTYs are for programs that expect a terminal. A PTY session is retained like a process job, but reads come from terminal output:

codeptyArgs array
$ptyArgs "/C" push drop
$ptyArgs "echo ricochet-pty" push drop

ptyOptions map
$ptyOptions "rows" 12 put drop
$ptyOptions "cols" 80 put drop
$ptyOptions "output_max_bytes" 4096 put drop

"cmd" $ptyArgs $ptyOptions pty_start value session var
$session "id" at 100 24 pty_resize value drop

ptyReadOptions map
$session "id" at $ptyReadOptions pty_read value ptyRead var
$ptyRead "output" at "ricochet-pty" contains? println
$session "id" at pty_release value println

For an interactive shell, use pty_write to send input such as a command plus a newline, pty_read with offsets to consume output, and pty_stop when the session should end.

How to read the example

Read the command first, then the code. The command grants the host powers the program may use. Inside the program, host calls usually return Result values or retained resource handles, so keep the same habit from Chapter 09: check the result, unwrap deliberately, and release or close resources when you are done.

Try it

Remove --allow-process and rerun the example. Then restore it and remove --allow-pty. Each failure should name the missing capability. After that, lower stdout_max_bytes or output_max_bytes and watch truncation metadata change.

Check your understanding

Common mistakes

Safety notes

Process and PTY execution can run arbitrary local programs. Keep command names and arguments explicit, avoid unchecked user input, use timeouts and output caps, and prefer narrow --process-root bounds for real tools. This chapter’s runnable example uses short cmd /C echo ... commands and a loopback-only ping cancellation target.

Production guidance

Production process integrations should model command construction as a data boundary. Build argument arrays directly instead of concatenating shell strings when possible, bound output, handle cancellation paths, and release retained jobs. PTY integrations should be reserved for truly terminal-oriented tools; plain processes are easier to automate and test.

What you know now

You know how to call local tools without treating processes as invisible side effects: grant the capability explicitly, bound the output, inspect retained state, cancel when needed, and release process or PTY handles when finished.

Next step

Continue to Chapter 21: Terminal UI.