Managed Linux job semantics
On Linux, managed jobs on an available systemd user manager run in a transient service named
from canonical device and job UUIDs. The supervisor claims its PID/start ticks,
boot ID and systemd invocation ID before starting the shell. Commands and
environment cross a private stdin pipe; they are not stored in unit arguments,
job metadata or Activity. The workload has NoNewPrivileges=yes, OOM adjustment
500, OOMPolicy=kill, and KillMode=control-group. There is no privileged helper.
The installer retains the existing Agent identity and user service. Jobs created by earlier Agent versions retain their existing metadata and supervision path; they are not rewritten during upgrade.
Non-systemd development environments retain independent process groups and report
failure_domain=shared_parent_cgroup. An installed CommandCore service refuses
to launch a new shared-cgroup job when its user bus is unavailable.
| Event | Behavior |
|---|---|
| Normal shell exit | Persist known exit code/time, exited; retrieve bounded output. Transient service cleanup ends remaining descendants after supervisor exit. |
| Explicit stop | Supervisor signals the original child group after checking start identity, escalates after three seconds; stopped_by_request. |
| MCP client disconnect | Managed workload continues; remote authorization is still checked on every new operation. |
| Network / server / WebSocket interruption | Workload continues locally; same process_id and files survive reconnection. |
| Agent restart / crash / targeted Agent OOM | Separate job service continues while the user manager remains alive; restarted Agent reconciles protected metadata. |
| Workload cgroup OOM | systemd ends that job cgroup; reconciled reason workload_oom when the original invocation's retained Result proves OOM. |
| Supervisor lost | worker_lost unless stronger unit evidence exists. Legacy surviving children are reported and require local reconciliation; remote stop does not claim success. |
| Host reboot | Workload ends; retained identity/output, host_rebooted; no automatic workload resume. |
| PID reused | PID + start ticks + boot ID prevent treating the replacement as the original job. No stop is sent to a reused identity. |
| Global OOM kills user manager | User services can all be removed. New jobs record manager identity and classify its disappearance as user_manager_lost. Root [email protected] Restart=no requires another activation such as login. No default claim of automatic recovery from that parent failure. |
completed_at is known supervisor-observed completion time. When reconciliation
cannot establish completion time, it remains null and reconciled_at records
the observation instead. A surviving authorized workload never bypasses revoked
grants or the local permission ceiling.
Output
Live request output uses a bounded 64-frame transport queue and chunks of at
most 8 KiB, applying backpressure rather than accumulating an unbounded queue
when the network stalls. Socket sends time out after 15 seconds; an otherwise
silent connection times out after 45 seconds. Authenticated heartbeat replies
are additive and ignored by legacy Python Agents. Foreground request processes
are cancelled on transport failure; durable managed jobs continue separately.
The existing server request-output byte cap remains in force. The response's
output_capture reports its combined stdout/stderr byte limit, observed and
retained bytes, truncation and whether the request transport finished normally.
It does not claim unseen output is complete after a connection failure.
Private per-job files retain at most 1 MiB stdout and 1 MiB stderr. Excess is
drained and discarded. Per-stream capture metadata records observed bytes,
retained bytes, truncation, EOF completion and last output time. After abrupt
supervisor loss the counters are a lower bound and complete=false; absence of
legacy counters means truncation is unknown, not false. complete=true describes
capture reaching EOF, not whether a particular paginated response includes all
retained bytes. Offsets apply independently to both byte streams.
Completed metadata/output are retained up to 100 jobs / seven days, pruned on the next job launch; at most 32 running jobs are admitted. No process output is added to the central Activity database. Activity remains content-free and bounded to its existing 10,000 entries / 30 days.
Resource limits and remaining boundary
An owner may create private job-resources.json beside Agent state with
{"memory_max_bytes": 268435456} (example 256 MiB, not a production default).
It caps each newly launched systemd job and disables that job's swap. A malformed
or unprotected policy fails closed. Requests cannot choose a cgroup memory limit.
It is an OS-user policy, not an immutable security boundary against that same
user's filesystem access.
No universal memory cap is silently imposed on existing research. Cgroup RAM
limits and OOM preferences do not guarantee accounting of NVIDIA driver/unified
allocations or survival of global OOM. Independently protecting/restarting the
root-owned user manager requires a separately approved administrator policy;
the normal installer does not install one. The reviewable
install/user-manager-recovery.conf.example sets only Restart=on-failure and
RestartSec=5s for the intended user's root-owned [email protected]. It affects
all services of that user and must be a distinct administrator opt-in. It does
not resume workloads ended during parent cleanup. The disposable acceptance
separately tests the default failure and this optional policy, then removes the
test drop-in. The normal installer does not change parent recovery policy automatically.
Small compatibility additions
Core tool names remain unchanged. Capable Agents add contextual health/PSI and
optional accelerator data to system.metrics, and host health at process.start.
GPU probing is bounded; process launch does not run a GPU query. No automatic
disk cleaning or unrelated workload termination occurs.
Process filters remain literal. New Linux filters narrow explicitly and older
Agents reject unsupported narrowing arguments rather than ignore them. Search
supports exact directory-name exclusions and a text size bound; existing defaults
remain. .gitignore/glob language is left to client-side repository tools.
Local status is friendly; scripts must select status --json to retain the
prior JSON fields. Connection status verifies process start identity and boot ID,
and labels the last local connection observation; the panel confirms server
reachability. activity follows the existing journal; --last N reads and exits,
--errors filters failure/recovery entries. Custom installs use --service.