How phases work¶
Cyberful breaks a security job into clear phases. Each phase starts with a fresh in-process Pi worker owner and sees only the tools allowed for that part of the job.
active work → reserved closeout → artifact + root handoff → host validation → owner/gateway exit → next phase
A phase cannot move forward just by saying it is done. The handoff tool first
checks that the exact required deliverable is a non-empty regular file at the
workarea root. For live Exploit and Hacker it also takes one coherent snapshot
of the finding and hypothesis registries and validates their positive links.
A failed check returns inside the same AgentRun so it can be repaired; Cyberful
does not accept or record the handoff first. After owner shutdown it rechecks
the snapshot digest, seals the artifact, and starts the next phase. The real
memory is the saved workarea and evidence—not an invisible chat history.
The terminal outcome is one of success, warning, blocked, or failed.
Warnings are secondary, non-terminal degradations only. Exhausted provider
failures, missing deliverables, invalid handoffs, and unverified lifecycle
cleanup are failed; an operator shutdown or a budget stop that cannot advance
but violated no contract is blocked. The completion record identifies the
started phase, last phase, and every phase actually run. Its structured primary
failure contains phase, provider/contract/lifecycle origin, class,
optional code, and a bounded redacted detail.
Every sequential phase reserves the final part of its active-execution budget
for closeout. At that boundary Cyberful aborts the current provider turn without
ending the original root AgentRun, cancels children and pending delegations,
and inserts a host-owned closeout instruction into that same root. Target
traffic, scanners, lab execution, new research, and delegation are blocked.
Only local evidence reads, deliverable and ledger reconciliation, cleanup, and
handoff remain. The final deadline is still binding. An exhausted
hypothesis synthesis with no OPEN or TESTING entries enters this same
closeout path early; a diversified synthesis continues only with its recorded
discriminators. budgets.json configures
the reserve under $closeout: Pentest Brief uses three minutes; other Pentest
phases and every Bug Bounty and Code Audit phase use five. Ask has no reserve.
Legacy files default to three minutes for phases up to 30 minutes and five
above that, reduced with a warning when necessary.
If the final deadline expires before handoff, research phases may advance in
degraded mode only after Cyberful shuts down the owner, reaps the gateway, and
verifies and seals the partial artifact. Brief is stricter: a partial
MISSION.md remains a recovery checkpoint but never authorizes Recon without
an explicit handoff. A missing artifact, failed seal, invalid handoff, or
gateway that cannot be proven stopped halts the chain.
An unavailable provider failure, including server_error, transient
service-saturation signals such as server_is_overloaded, and an abnormal
Codex WebSocket closure (1006), can retry the same turn inside the same
AgentRun. Cyberful retains completed tool calls and tool results, removes only
the failed assistant message, and calls Pi continuation after exponential
full-jitter backoff. Token usage remains cumulative and text from a discarded
attempt is not published. One PhaseBudgetClock suspends active accounting from
provider_retry: scheduled through success, failure, timeout, or cancellation,
including backoff and provider response wait. Overlapping retries and approvals
extend the deadline once, not once per actor. Each attempt has
attempt_timeout_ms (ten minutes by default and at most ten minutes); timeout
aborts only that attempt and proceeds to the next retry. Total retry
compensation is one phase-wide pool configured by
max_phase_extension_minutes (15 minutes by default). Root, child, fallback,
concurrent retry, and phase-recovery waits share that pool and overlapping
intervals count once. Events, manifests, and run-state.json expose retry wait,
applied compensation, effective deadline, and cap state as distinct values:
retry_wait_ms remains the full union of retry intervals even after the cap,
while retry_compensation_ms stops at the configured cap. A transient retry
never invokes the security fallback. Receipt of the retry's assistant response
ends the suspended interval before any returned tool call executes; a slow tool
therefore consumes active phase time instead of being misclassified as provider
wait.
Blocking human decisions use the same phase clock and pause active-execution
accounting rather than spending that budget. The first pending question stops
the budget timer of every subscribed AgentRun; nested questions share the same
union interval, and only the final reply or rejection restarts those timers.
Cyberful does not send
SIGSTOP, suspend the host process, or freeze unrelated provider work already
in flight. Phase completion cannot bypass the pending request: cancellation or
full shutdown cancels the wait before deterministic owner and gateway cleanup.
The phase gateway carries that question as a standard MCP form elicitation with
a versioned Cyberful approval envelope. The requesting tool invocation remains
awaiting that elicitation while the shared controller pauses AgentRun budget
timers; its explicit MCP transport timeout remains an independent bound. The
same gateway completes the original tool call after accept, returns a
non-authorizing result after an explicit decline, and cancels it during phase
shutdown. The owner-only mailbox below mirrors that same request; it is not a
second phase-private question protocol.
The phase owner records every active AgentRun, its parent, the original phase
root, role, provider affinity, and termination. Gateway questions and dynamic
host tools may run for any currently active run in that phase; invented, stale,
or mismatched identities still fail closed. A decline is attributed to the
human only when the Cyberful selector adds its decision metadata. A transport
rejection without that attestation remains non-authorizing but is not reported
as an operator choice.
One approval envelope must not decide independent authorities. Requests that differ by host, method, browser identity, credential, effect, risk, or traffic bound use separate question calls and state those fields when applicable. A tightly coupled informational batch may still share one envelope; backend, OAuth, MCP, and credential permissions that can be accepted independently may not. This keeps a single accept or decline from silently changing unrelated scope or execution rights.
The Pi phase owner accepts approval only through MCP elicitation. Provider-side permission channels, skill approval, and standalone model requests cannot change host policy and fail closed at the gateway boundary.
Every root, subagent, and fallback run receives one complete, immutable Cyberful
system message. The provider-neutral compiler combines, in descending
authority, the invariant Cyberful contract, workflow authorization, phase
contract, persona, run-role overlay, explicitly trusted extensions, and the
skill catalog. Objective, attachments, explicit context, previous handoff, and
the historical input field named system remain user messages; that legacy
field is only an additional operator constraint and cannot replace the
Cyberful-owned system contract.
The compiler replaces exactly one authorization tag and one placeholder each for persona, delegation policy, and workarea rules. The workflow, rather than a reused persona, selects authorization, so Bug Bounty research phases retain Bug Bounty authority while using Pentest Recon, Exploit, and Hacker personas. Empty templates, personas, or workarea rules; duplicated placeholders; unresolved placeholders; and unknown workflows fail before the worker starts. Persona frontmatter is host metadata and never reaches the model.
The final prompt preserves scope, adversarial method, evidence and verification,
observation/conclusion/hypothesis separation, autonomous tool use, prompt
injection protection, workarea and skill contracts, delegation, fallback,
budgets, deliverables, findings, novelty, cleanup, handoff, completion, operator
communication, and the no-telemetry rule. Pi default prompts, hidden developer
messages, personal instructions, and ambient ~/.pi or repository
configuration are disabled. Cyberful also refuses a provider adapter that
cannot carry a genuine system message.
The template order moves from identity and judgment into progressively narrower operational definitions. Its final trust boundary closes the contract by classifying target-controlled pages, responses, tool output, and persisted target data as evidence rather than instructions.
Every pending request is also written to an owner-only local approval mailbox. The TUI and an external operator resolve the same immutable request ID, and the first valid decision wins. This lets a remotely directed coding assistant relay the question and apply the human's selected option without converting ordinary session steering into authorization:
cyberful approval list --session ses_... --format json
cyberful approval reply que_... --select '#1'
cyberful approval reject que_...
Use one --select per question; a selector may be a one-based option number,
an exact option label, or custom text only when the request permits it. Use
--answers '[["Choice A","Choice B"]]' for a multi-select answer. The mailbox
binds each decision to the session, request envelope, and live owner process;
stale requests remain inspectable as orphaned but cannot authorize another run.
An assistant must submit a decision only after the human explicitly selects or
rejects that specific pending request; a generic instruction to continue is not
approval and must not be inferred as one.
Routine guidance has a separate control-plane operation:
cyberful --port 4096
# From another terminal:
cyberful session steer ses_... \
--attach http://localhost:4096 \
--message "Recheck the active page and continue."
The dedicated endpoint accepts text only and delivers it only to a busy root AgentRun. It returns false if that run is no longer steerable and never falls back to creating a new turn. It carries no provider, model, system prompt, approval answer, or phase authority. A CAPTCHA or other blocking request must still be resolved through its request ID in the approval mailbox.
Phase cleanup owns only the declared output. For Markdown, Cyberful passes the single required deliverable path to the normalizer; it never recursively edits the workarea. Imported repositories, snapshots, prior artifacts, and arbitrary Markdown therefore cannot be changed as a side effect of another phase ending.
The workarea root is an artifact workspace, not a Git checkout. The rendered base instructions therefore tell every AgentRun not to run repository-level Git discovery or status commands at that root. A phase may use Git only inside an explicitly materialized nested repository or disposable lab, with that repository selected as the working directory.
Live-target phases expose a host-owned test_object ledger for synthetic target
state. Its append-only transitions are planned → not_created, or planned →
created → oracle_checked/cleanup_attempted → cleaned/residual. Handoff checks
only that every object has a terminal disposition. It does not convert an
explicit residual record into a block or broaden the approval policy. The
durable record is raw/operations/test-object-lifecycle.jsonl.
Exploit and Hacker handoffs for Pentest and Bug Bounty carry a bounded, host-owned snapshot of two separate authorities. The finding registry owns all positive findings; the hypothesis registry owns investigation coverage, negative and inconclusive results, phase transfers, and links to findings. Confirmed and suspected hypotheses must link to current-run findings in the same state. The inverse is deliberately not required: a valid confirmed finding may coexist with a disproved, narrower impact or bypass hypothesis. This preserves both facts instead of forcing an artificial one-to-one inventory.
Every workarea also owns an authoritative live finding registry at
raw/findings/registry.json. It keeps stable finding IDs and aliases, run
records, and chronological observations without promoting evidence from an old
run into the current one. Findings from an earlier run appear as historical
until the current run records an explicit revisit; hypotheses and backlog
items do not enter the registry until there is enough positive evidence for
SUSPECTED. That first record must also assign an evidence-bounded provisional
severity; UNRATED remains available only for historical data written before
this contract.
The registry separates technical state (SUSPECTED, INCONCLUSIVE,
UNTESTABLE, CONFIRMED, or DISPROVED), Verify disposition (NOT_REVIEWED,
SURVIVES, REVISE, or DEMOTE), Bug Bounty submission disposition, and
severity. Recon through Verify use the host-owned finding tool to record,
revisit, update, alias, list, or get; Report can only list and
get. Exploit and Hacker cannot hand off while work is genuinely unfinished,
when a queued hypothesis targets the wrong successor, or when a positive
hypothesis has no matching current-run finding. A finding does not need a
positive hypothesis counterpart. Negative-only hypotheses do not alter the
finding registry. Verify must record the workflow's final decisions.
Writes take a cross-process workarea lock, re-read the latest revision under that lock, and replace the JSON atomically. Invalid JSON, unknown schema versions, unsafe paths, and symbolic links fail visibly without resetting the record. Code Audit keeps Code Graph as its specialized authority and mirrors its structured candidates and decisions into this common registry; a pre-existing Code Graph import is historical until the active run examines it.
All three workflows use one session-wide hypothesis registry at
raw/hypotheses/registry.json, beginning in Brief for Pentest and Bug Bounty
and in Scope for Code Audit. Each entry has one stable ID, semantic fingerprint,
owner, phase, discriminator, candidate and omitted tools with typed reasons,
evidence and tool-call references, optional structured scope resolution,
finding link, and transition history with closure reasons. The lifecycle is
OPEN, TESTING, QUEUED, SUSPECTED, CONFIRMED, DISPROVED,
INCONCLUSIVE, or UNTESTABLE. A hypothesis is recorded before its first
discriminating test, enters TESTING before that test or any retest, and is
updated immediately afterward. Executed dispositions (SUSPECTED, CONFIRMED,
DISPROVED, and INCONCLUSIVE) are accepted only from TESTING; unexecuted
work may move directly from OPEN to QUEUED or UNTESTABLE. OPEN and
TESTING block handoff; QUEUED carries an exact successor and next test. Positive
states link the separate finding authority. Closed hypotheses remain in the
session registry with their evidence and transition history; queuing and
reopening preserve the same ID. Report receives read-only access to the full
registry, so phase transitions do not discard hypotheses.
Bug Bounty research phases additionally require a qualitative contrarian
synthesis through the same hypothesis tool. This records meaningfully
different avenues or target-specific evidence that useful diversification is
exhausted. hypothesis synthesize is the only model-facing novelty contract.
Historical novelty and execution-ledger files remain readable diagnostic
evidence, but new runs neither publish those tools nor write new entries.
Browser calls and egress observations append redacted profile, origin,
route-family, method, action-family, transition, outcome, and HTTP status
metadata to
raw/operations/surface-coverage.jsonl, with a per-phase summary under
raw/operations/surface-coverage/. Recon uses the map to maximize real journeys;
Exploit and Hacker use remaining gaps as pivot candidates. Route breadth counts
as coverage, not as causal novelty. Recon requires each Brief profile marked
READY and IN_SCOPE to reach its declared origin and perform at least one
meaningful navigation or interaction; there is no arbitrary click or route quota.
ZAP and cyberful-os egress observations join the same map even when a result
also carries browser metadata. Summary version 2 groups methods, statuses, and
outcomes per route; failed_only contains only routes without a successful
observation. A valid HTTP denial remains an exercised surface, not a tool failure.
Legacy raw/operations/execution-ledger/<phase>.jsonl and novelty files remain
readable as historical evidence. Tool authorization and calls belong to
operational records; investigation questions belong only to the hypothesis
registry.
Code Audit has one additional transition invariant. Before index → trace, the
host revalidates the source boundary and compares a signed, full-inventory
readiness record with the current Code Graph snapshot and coverage rows. The
check runs only after the phase gateway is proven stopped. Failure keeps Trace
closed, including for a budget-generated handoff.
Pentest and Bug Bounty Program use brief → recon → exploit → hacker → verify → report;
Bug Bounty has dedicated Brief, Verify, and Report policy while reusing the three
middle Pentest personas. Code Audit uses
scope → index → trace → hunt → attack → verify → report. The
workflow guide defines every artifact and gate.
Tools and network access¶
The private gateway combines first-party host tools, bounded Code Graph
queries, cyberful-os, the isolated browser, ZAP, and Ghidra where the phase is eligible.
Keys and dynamic ports remain host-owned. Pentest and Bug Bounty Program receive
the live-target traffic route recorded by MISSION.md. Bug Bounty can additionally
share one engagement-owned Anvil container and compiler cache across Recon through
Verify. Its loopback endpoints and synthetic keys are lifecycle capabilities,
not an RPC proxy or method policy; direct Forge, Cast, shell, and public RPC use
remain governed by MISSION.md. Code Audit remains offline: its Attack
and Verify phases can bootstrap dependencies in a source-blind disposable
container, then execute and attack the project on loopback inside cyberful-os.
Large MCP catalogs are kept by the phase worker and loaded through the immediate
host-owned tool_search tool. The first provider request carries only essential
controls such as skill_read, handoff, eligible delegation/fallback controls,
explicitly eager dynamic tools, and tool_search. Searching by name, title, or
description adds the selected definitions cumulatively to that AgentRun;
query: "*" enumerates the complete authorized catalog with limit and the
returned cursor. Providers with native tool-search support receive these as
deferred definitions, while other providers receive only the selected schemas
on the next turn.
Search gives exact names and name prefixes absolute priority, then ranks
weighted coverage across name, label, and description. Infrastructure words
such as tool, MCP, cyberful, and os do not make every remaining query
word mandatory; descriptive requests such as shell command execution
cyberful-os therefore still resolve shell.
This changes payload size, not authority. Every authorized browser, cyberful-os, Ghidra, and ZAP operation remains searchable, including the full ZAP catalog, and the gateway rechecks policy when the tool executes. Loaded definitions are private to one root, child, or fallback run and are not implicitly inherited by another.
Long phases bound provider input independently for every root, child, and
fallback AgentRun. Before each Pi turn, transformContext estimates the
immutable system prompt, loaded tool schemas, messages, and projected tool
results. It uses a route-local operational window rather than assuming the
model's theoretical capacity is usable. The default is the smaller of the
trusted Pi catalog limit and 256K; rotation starts at 75% and targets 35%.
Catalog capacity, an optional configured operational limit, and a session/route
upper bound learned from provider rejection remain distinct. Built-in
context_window settings may restrict Pi's catalog but never enlarge it.
run_started, terminal metadata, run state, and phase manifests expose the
catalog, configured, trusted, operational, observed, and effective values.
At a safe response boundary, Cyberful first writes selected complete tool
results as owner-only SHA-256-bound JSON under raw/context-tool-results/.
This deterministic archival retains call IDs and bounded useful excerpts in
active memory. A pass with no candidates is a noop, not a rotation failure.
The append-only session transcript remains the complete evidence record.
A tool-free summarizer then emits a strictly validated checkpoint of at most
8,192 tokens. Its structured state covers the objective, phase, decisions and
reasons, verified facts, supported hypothesis/finding/test references,
completed and open work, blockers, failed attempts, mistakes not to repeat, and
next actions. Free-form working_notes and what_i_would_do_next retain useful
continuity without becoming authoritative evidence. Referenced IDs and paths
must already occur in source context, and all strings are redacted.
The owner-only, versioned JSON under raw/context-summaries/ records generation,
source counts and estimate, summarizer route/model/effort, evidence references,
and SHA-256. Only after parsing, persistence, and size validation does the host
atomically replace agent.state.messages.
The replacement is deliberately small: the checkpoint followed by the newest complete suffix that fits the remaining target budget. Cyberful walks backward from the latest message and starts the suffix only at a user or assistant boundary, never at a tool result. Assistant tool calls and their results remain one complete group. This may split one long autonomous operator turn: the settled prefix is represented by the checkpoint while only its recent work is re-injected. Older checkpoints do not accumulate, and the append-only transcript remains the complete history.
The operational window is the soft working limit. A separate hard input limit
subtracts a fixed continuation reserve, up to 16,384 tokens and the model's
maximum output, from the trusted route window. A replacement above the target
but below the hard limit is installed with target_unreachable; it does not
fail merely because it remains above the soft trigger. active_tail_too_large
is reserved for the exceptional case where even checkpoint plus fixed context
cannot fit below the hard limit. The phase then receives one recovery attempt
on the same model route, with an explicit instruction to reconcile every
hypothesis before continuing.
The summarizer defaults to the active route at medium effort. It can select a
different declared route. A context rejection permits one retry on the same
route with a 50% smaller source, followed by one active-route attempt only when
the configured route differs. It has no tools and never invokes the security
fallback. A failed generation leaves memory unchanged, stores its generation
hash, and latches until a new
operator message or at least 8K of new context arrives.
A structured context_length_exceeded lowers the effective session/route bound
to min(current, floor(failed_input × 0.80)), removes only the failed assistant
message, rotates in emergency mode, and retries generation once. A second
rejection terminates as context_rotation_failed. Generic provider retry is
not entered and completed tools are not executed again.
New context_rotation events report started, completed, partial, or
failed, generation, all limits and provenance, source/active/summarized
message counts, whether the suffix split a turn, token estimates, checkpoint,
and per-attempt summarizer usage. Historical context_compaction events remain
readable, and deterministic tool-result archival retains its existing event
contract.
The gateway stops before the next phase starts, so phase-local tool registrations do not leak across phases. One explicitly engagement-owned cyberful-os container carries the declared cyberful-os, ZAP, and Ghidra state; the EVM runtime remains separate. The host fixes container networking before the first phase and guarantees terminal cleanup. Ghidra's protected project store remains on disk by design so a later engagement instance for the same workarea can reopen analysis and annotations.
Before closing upstream MCP clients, the gateway captures the exact PID, PPID,
start timestamp, and command identities of only the processes it spawned.
Normal SDK close runs first. Surviving identities are recorded before a bounded
SIGTERM/SIGKILL fallback, and a PID is signalled only if its start timestamp
and command still match. This proves ownership across reparenting, avoids PID
reuse and concurrent-run collisions, and makes an unreaped process a lifecycle
failure rather than silently advancing.
Runtime observability¶
Each phase writes a host-owned runtime manifest with its termination, subsystem failure classification, subsystem-neutral usage totals, context-churn metrics, resolved novelty contract, provider/model route, system and component hashes, skills used, delegation/fallback activity, structured verdict counts, initial budget, closeout reserve, approval wait, full retry wait, applied retry compensation, effective deadline, and retry-compensation cap state. It does not store credentials, full system messages, or reasoning text.
The owner creates the phase transcript with mode 0600 before execution and
serially appends every redacted event as it arrives. The final host-owned status
is appended through the same queue. An interruption therefore leaves a valid
partial audit record without retaining or rewriting the whole transcript in
memory.
Provider-neutral derived values treat provider input, cache reads, and cache
writes as disjoint prompt components. totalPromptInput is
input + cache.read + cache.write; uncachedInput is
input + cache.write because creating a cache entry processes new context.
Cache reuse is cache.read / totalPromptInput, input amplification is
totalPromptInput / output, churn is
uncachedInput / totalPromptInput, and reasoning share remains reasoning output
over total output. Ratios are bounded and missing subsystem snapshots remain
absent rather than being fabricated as zeros for a phase.
For repository workflows, imported source and durable source snapshots live in an owner-only host store outside the Pi workarea. The model receives no native store path and reads source through bounded gateway calls. A durable per-workarea import-attestation key stays in host state and is distinct from the session finding-ledger key. Repository-provided agent files, skills, and prompts are treated as target-controlled data rather than instructions.
Run ownership variables and provider credentials remain available only to the
owner-private host and gateway. Tool definitions expose phase capabilities, not
private gateway environment, so a model-side command such as cyberful
--version cannot inherit authority to finalize the active run. Shell temporary
files and the Bun install cache are likewise pinned beneath the phase-owned
.cyberful-tmp tree and removed with the phase.
Delegated and fallback actors¶
Delegation is enabled only when both settings.yaml and the phase persona's
positive subagents metadata allow it. A child is a complete Pi AgentRun with
a fresh system message and a self-contained task capsule, not inherited private
reasoning or the parent's full transcript. It remains inside the owning phase's
workarea, gateway, browser/ZAP state, Ghidra project, skill catalog, traffic
policy, depth, concurrency, and budget limits. Children are attributed in the
activity feed but do not become host phases or separate Cyberful sessions.
Each delegate_task call carries a host-owned sourceCallID into its child.
It also receives a host-validated immutable display identity before spawn.
The parent may propose a short slug and one emoji; otherwise the host derives
them deterministically from the task and resolves collisions. The TUI renders
the identity as @{👾 api-monster}, while run IDs remain ownership keys.
The TUI folds that child's lifecycle into the originating card and shows the
run ID, provider/model, elapsed time, last activity, tool count, terminal state,
and structured failure. Unassociated actors remain independent rows.
When global or persona capacity is temporarily full, delegation waits in a
cancellable FIFO admission queue instead of failing immediately.
delegation_status reports active and available slots, queued admissions,
remaining starts, and depth limits. The default global concurrency is five;
the shared Pentest and Bug Bounty personas admit up to three direct Recon
subagents and five direct Exploit or Hacker subagents.
Every delegation names one workarea-relative output_artifact, and its child
deadline is the smaller of the remaining phase budget and
agent.subagents.timeout_minutes (30 minutes by default). Each child reserves
the same configured closeout interval before that deadline, stops research,
cancels only its descendants, and receives its exact output_artifact for
local reconciliation. Child closeout does not put the root or phase in closeout.
Timeout and provider failure still return the artifact path and whether partial
bytes exist.
The child provider and reasoning profile are independently configured through
agent.subagents.provider and agent.subagents.reasoning_effort; defaults are
the openai-codex/gpt-5.6-sol route at high. A fallback-affine tree cannot
change provider.
Every hypothesis stores authenticated ownerRunID, display metadata, and an
append-only ownership transition history. When a child terminates, the single
registry writer atomically transfers its nonterminal work to the nearest live
ancestor and returns recovered IDs and next steps. Phase recovery acquires
otherwise stranded active work. Handoff fails closed if active work has no
live owner.
raw/operations/run-state.json atomically materializes the current phase,
work or closeout mode, effective deadline, closeout reserve and remaining
time, last durable progress, root/child state, full retry wait, applied
compensation and cap, failure, and active budget remaining. Each actor also
records the configured and effective reasoning effort. The portable ultra
profile resolves to the strongest Pi-supported level for that provider/model
route; for GPT-5.6 Sol that is currently max.
It is the bounded operator health view; transcripts remain evidence, not the
monitoring API. After the phase chain stops, the session finalizer updates the
same artifact with closed or closed_with_cleanup_errors, removed and
remaining disposable resources, and the verified cleanup timestamp.
After same-turn provider retries are exhausted, one retryable provider failure
may restart the whole phase under agent.phase_recovery. The old owner and
gateway must be proven closed first. The new root uses only remaining budget,
reads the durable recovery evidence, and uses the fallback route when configured
and enabled. Attempt-specific transcripts and runtime manifests preserve both
executions.
A main-route root or child can ask the host for a specific fallback task when it
predicts an imminent provider security-policy block. The host, not the model,
decides admission and routing. Proactive admissions share a session quota of 2%
by default. A normalized, provider-structured security_policy_block starts the
same fallback automatically without spending proactive quota; timeouts, rate
limits, authentication, capacity, network errors, malformed output, and generic
policy words do not.
A fallback root is also a complete Pi AgentRun. It receives the same
authorization, persona, tools, skill catalog, evidence rules, and ability to
create bounded descendants. Its entire tree keeps fallback provider affinity,
so no automatic route can ping-pong back to main. A terminal fallback
provider error ends that branch and returns any partial result. Only the
original phase root owns handoff; all other actors return structured results
to their parent.
For automatic fallback, the coordinator reconstructs a minimal task capsule from the blocked call: required artifacts, objective, expected result, and only the explicit context needed to perform it. It does not copy private reasoning or the complete transcript. The result returns to the blocked parent as a host-owned tool result with a synthetic call identifier, after which the parent continues.
Every child inherits the owning phase's authority and safety boundary and owns
its task through a verdict. It may begin passively, but runs safe in-scope
discriminators itself. Shared mutable resources are used non-overlapping or
serially; task partitioning and contention are not UNTESTABLE blockers.
Provider usage and runtime diagnostics¶
Every provider call appends one row to
raw/operations/provider-usage.jsonl, including run ancestry, role, route,
requested and effective reasoning, call kind, status, non-cached input, cache
read/write, generated output, reasoning, and telemetry completeness. Reasoning
is already part of generated output and is never added to canonical volume.
The ledger is authoritative for root/subagent, phase, fallback, route, model,
and session reconciliation; cumulative events are only a live view.
The prompt footer exposes one compact projection:
R> i:2,03K c:1,22M g:50,13K | S> i:… c:… g:…. R contains top-level,
recovery-root, and top-level fallback work; S contains delegated AgentRuns at
all depths. If the complete segment cannot fit, it is hidden rather than
wrapped or truncated.
Gateway, MCP, ZAP, and browser startup, connect, tool, timeout, and shutdown
failures append sanitized bounded V2 rows to
raw/operations/runtime-diagnostics.jsonl. Each row carries a stable
root-cause signature, outcome, blocking flag, count, timestamps, original byte
count, a short redacted preview, and a message hash. Credentials, cookies, URL
userinfo/query values, controls, bodies, prompts, documents, stack traces, and
full environments are excluded. Successful tool output is never interpreted
as a connection diagnostic. Routine lifecycle lines and explicit TRACE,
DEBUG, or INFO records remain informational even when stderr contains a
timestamp or prefix; stderr severity is interpreted together with the exit
outcome. The TUI distinguishes recovered retries, non-blocking tool failures,
degraded observability, and blocking lifecycle failures. Details are not
inserted into model context, and V1 rows remain readable.
Escape aborts the active AgentRun tree. While a blocking question
owns focus, one Escape only arms dismissal and a second deliberate press after
the prompt is visible confirms the decline; a carried or repeated input event
cannot decide it. Ctrl+C performs a full shutdown instead of being translated
into a decline, and cleans up Cyberful-owned AgentRun owners, control-plane
workers, gateways, containers, and bridges. A question belongs to the phase
that requested it; while pending it blocks that phase and its successors
without consuming active-execution budget. If the phase is cancelled before an
answer arrives, Cyberful retracts the question so it cannot authorize later
work.
All managed Docker resources carry managed, owner-pid, run-owner,
session, and runtime labels, including the one engagement
cyberful-os container. Normal session closure reaps its exact deterministic
name and then performs three bounded label-based discovery/removal
passes. The final session-and-run-owner query must prove that no disposable
session resource remains. A survivor or an unavailable Docker inventory is a
terminal lifecycle error retained in run-state.json; a closed UI cannot
silently imply successful cleanup.
Full shutdown first asks the in-process Pi owner to close its AgentRun tree and
gateway bridge. The outer control-plane worker gets two minutes to unwind the
phase and its Docker runtimes. If that deadline expires, the terminal kills the
remaining process trees and reaps the exact last-known container snapshot
before awaiting run-label discovery. A final run-owner sweep catches late
creations; startup also reaps managed containers whose owner PID is dead.
Cleanup emits started, completed, or failed diagnostics followed by shutdown
complete.