Reference #

Complete surface of the tool: commands, flags, states, files, and protocol. Extracted from the source, not from memory.


Command summary #

CommandArgumentsPurpose
ompjob start<name>Create and launch a job
ompjob attach<name>Interactive session: replay, then live
ompjob say<name> <text...>Send one message, no attach
ompjob revive<name>Restart an interrupted job, resuming its session
ompjob listTable of every job
ompjob status<name>Detail for one job
ompjob logs<name>Formatted transcript
ompjob stop<name>Shut the agent down, keep the transcript
ompjob rm<name>Delete job, task, and run directory

Running ompjob with no arguments is equivalent to ompjob list.

Job names must match [A-Za-z0-9._-]+. The name becomes a scheduled-task name (ompjob-<name>) and a pipe name (\\.\pipe\ompjob-<name>), so it must be unique on the machine.

Flags #

FlagTypeUsed byMeaning
-PromptstringstartOpening message
-PromptFilepathstartRead the opening message from a file
-CwdpathstartAgent working directory. Defaults to your current directory
-ModelstringstartPassed to omp --model
-AttachswitchstartAttach immediately after starting
-Forceswitchstart, rmstart: recreate an existing job, deleting its transcript. rm: remove even while live
-TailintlogsLines to show. Default 40
-FollowswitchlogsLive tail

say takes its message as trailing arguments, so quoting is optional for simple text: ompjob say build check the tests works.

Job states #

Two vocabularies exist and both surface in the CLI. The lifecycle state comes from reconciling the scheduler and the pipe; the activity state comes from the broker.

StateSourceMeaningAction
registeredlifecycleTask exists, has never runrevive
startingbrokerBroker up, agent not ready yetWait a few seconds
thinkingbrokerAgent is mid-turnattach to watch or steer
waitingbrokerAgent idle, ready for inputattach or say
interruptedlifecycleBroker died without a clean exitrevive
finishedlifecycleBroker exited deliberatelylogs, then rm
unknownlifecycleNo such job

How liveness is determined #

status.json alone is not trusted: a hard-killed broker leaves it saying waiting forever. The pipe is ground truth for liveness, so the CLI reconciles:

Pipe livedone.json existsReported
yeswhatever the broker last recorded
noyesfinished
nonointerrupted

done.json is written only when the broker exits with code 0 and recorded state exited. Any other ending stays revivable — that is what makes crash recovery work, and why a killed job reports interrupted rather than finished.

Interactive session keys #

Inside ompjob attach:

InputEffect
any textSent to the agent (steer if a turn is live, prompt if idle)
Ctrl+DDetach. Job keeps running
Ctrl+CInterrupt the agent's current turn. Does not kill the job
/detach, /exitSame as Ctrl+D
/statusPrint current state
/abortInterrupt the current turn
/stopShut the job down

attach.js also accepts --no-color, and disables colour automatically when stdout is not a TTY.

Transcript event kinds #

ompjob logs prints one labelled line per event:

KindMeaning
userA message you sent. Prefixed with [username] when it came from a client
assistantAgent output
toolA tool call the agent started
tool-errA tool call that failed
systemBroker lifecycle: startup, resume, interrupt, shutdown
errorDelivery or RPC failure

Files on disk #

Job root is C:\ompjob unless OMPJOB_ROOT is set. Each job owns <root>\runs\<name>\:

FileContents
meta.jsonName, cwd, model, opening prompt, resume flag
status.jsonLive state, turn count, attached clients, agent's last text
render.logHuman-readable transcript. This is the backlog replayed on attach
transcript.jsonlEvery raw RPC frame, for debugging
inbox.jsonlEvery message you sent, with timestamps
err.logBroker and agent stderr
done.jsonWritten only on clean exit; makes the run terminal
session/omp's own session directory, used by -c on revive
WARNING

render.log, transcript.jsonl and inbox.jsonl are plaintext transcripts of your AI conversations, including any code or secrets discussed. Treat the job root as secret material. The repository's .gitignore excludes all of them, in case you point OMPJOB_ROOT at a checkout.

Environment variables #

VariableDefaultPurpose
OMPJOB_ROOTC:\ompjobWhere job state lives

Set it persistently:

[Environment]::SetEnvironmentVariable('OMPJOB_ROOT', 'D:\ompjob', 'User')

Architecture #

graph TB
  CLI["ompjob.ps1<br/>registers + queries"] -->|Register-ScheduledTask| T["Scheduled Task<br/>S4U, AtStartup"]
  T --> L["_launch.ps1"]
  L --> B["broker.js"]
  B <-->|"JSONL over stdio"| O["omp --mode rpc"]
  B <-->|"named pipe"| A1["attach.js"]
  B --> D[("run directory")]

Why a Scheduled Task #

LogonType=S4U is the only Windows mechanism that runs a process with no interactive logon, no stored password, and no dependence on the session that created it. Task settings that matter:

SettingValueWhy
ExecutionTimeLimit0Disables the scheduler's default 72-hour kill
MultipleInstancesIgnoreNewA duplicate trigger cannot start a second broker
AtStartup triggersetRevives interrupted jobs after reboot
AllowStartIfOnBatteriessetThe scheduler will not suspend the job on battery
RunLevelHighestElevated. See Security

Pipe protocol #

Client to broker, one JSON object per line:

MessageEffect
{"type":"input","text":"...","who":"..."}Deliver a message to the agent
{"type":"abort"}Interrupt the current turn
{"type":"status"}Request a state frame
{"type":"shutdown"}Shut the job down

Broker to client:

FrameMeaning
helloJob name, cwd, state, turns, backlog size
renderA transcript event (kind + text)
liveBacklog finished; everything after this is live
deltaStreaming token text
stateState or streaming change

Message routing #

prompt is rejected by omp while a turn is streaming, and steer is meaningless when it is idle, so the broker picks based on state — and, because that state can lag right after a resume, retries the other form once if the first is rejected. A message therefore cannot be silently lost to a race.

Exit codes and errors #

MessageCause
node not found on PATHNode is not installed or not visible to the task
name must be [A-Za-z0-9._-]+Invalid job name
job '<n>' is already liveUse attach, or stop first
job '<n>' exists in state '<s>'Use -Force to recreate, or rm
job '<n>' has no live brokersay needs a running job; check status
no transcript yet for '<n>'The broker has not written render.log yet
revive did not bring up a brokerSee err.log in the run directory
started ... but the broker pipe has not appeared yetAlmost always omp missing or unauthenticated

Uninstall #

ompjob list                       # note the names
ompjob stop <name>; ompjob rm <name>    # for each

Get-ScheduledTask -TaskName 'ompjob-*'  # must return nothing
Remove-Item -Recurse C:\ompjob          # or your OMPJOB_ROOT

Then delete the function ompjob line from your PowerShell $PROFILE.