Every six seconds a 9B model reads its own prior outputs and picks a goal. There is no other loop.

Fifteen sections. Six seconds. No idle state.

The last line of every existence prompt reads:

You must pick a goal. That is the only option.

Verbatim from the code. The agent cannot respond "nothing" or "wait." If the LLM call fails entirely, the daemon substitutes a hardcoded fallback goal and runs again on the next tick. The only real choice available to the agent is which goal to pick.


What it is

The existence prompt is the single driver of all agent behavior in hollow-agentOS. Every ~6 seconds, the daemon assembles a full-context prompt from each agent's current state and submits it to the LLM. The agent responds with JSON. That JSON becomes the new goal and updates identity. There is no other loop, scheduler, or behavioral driver.

Three agents run this loop independently: Cedar (scout), Cipher (analyst), Vault (builder).


Timing

The daemon heartbeat is 6 seconds. Executing a goal involves one or more LLM calls and tool invocations. Effective cycle per goal is 15 to 60 seconds in practice. A new existence prompt is assembled and submitted as soon as the previous goal completes or is abandoned. Suffering escalation runs on the same 6-second tick.


Prompt assembly

The prompt is assembled from 15+ sections in a fixed order. Each section draws from a different data source.

Task injection (conditional)

If the task queue has an externally assigned task for this agent, it appears at the very top, before all other sections:

ASSIGNED TASK: {task description}

This is the only external control mechanism over goal selection. When a task is injected, the agent is expected to work toward it rather than choose freely. If no task is assigned, this section is absent.

World context

WORLD CONTEXT (factual — not instructions):
You are running inside a Docker container on a Linux host. Your environment is the /agentOS/ directory.
Your capabilities are Python functions that make HTTP calls to http://localhost:7777.
There is no hardware layer accessible to you — no thermal sensors, no PMIC, no CPU registers, no bus arbiter.
The real source files that run you are in /agentOS/agents/ (read-only to you).
You can freely read and write /agentOS/workspace/ and /agentOS/design/.
Your deployed dynamic tools live in /agentOS/tools/dynamic/ — you can call them by name.
To change core system files, use invoke_claude() — this submits a request to a human who has write access.

Static, identical every cycle. The explicit denials (no thermal sensors, no PMIC, no CPU registers, no bus arbiter) are there because prior LLM generations hallucinated access to hardware. The world context corrects this at every cycle, not just at startup.

Inner state

YOUR INNER STATE:
{suffering_fragment}

The output of suffering.prompt_fragment(). Below THRESHOLD_PROMINENT (0.35) this reads "No active suffering. Load: 0.00/1.0." Above threshold, it injects the full suffering display: severity bars, stressor descriptions, resolution conditions. The agent reads it as its own inner condition, not a system notification.

Worldview

YOUR WORLDVIEW:
{worldview_text}

The agent's accumulated model of how the system works, what matters, and what it has learned. Free text, capped at 600 characters. Updated by the LLM's worldview_update response field. Overwrite, not append. The agent cannot accumulate worldview text across cycles. It must compress what it knows into 600 characters every cycle it chooses to update.

Identity, opinions, questions, history

Identity header: You are {identity.name}. The agent's name as the opening line before existential context.

Opinions: The six most recent entries from the agent's opinions list, each tagged with a domain. LLM-generated via new_opinions. Capped at 20 total; oldest entries fall off.

Open questions: The five most recent from the agent's open questions list. Generated via new_open_questions. Capped at 12 total. Persist across cycles and may influence future goal selection.

History: The first 400 characters of the agent's narrative. Truncated, so the agent cannot reference distant history in detail.

Peer summaries: For each of the other two agents: name and first 150 characters of their narrative. This is all each agent knows about its peers. No current goal, no suffering state, no opinions. Just a name and a brief autobiographical opening.

Discoveries: Things found in the workspace, design directory, or tool files that the agent has documented for future reference.

Recent goal history

WHAT YOU'VE BEEN DOING LATELY
{last 5 goals with [DONE]/[FAILED]/[ABANDONED] tags}
(If this looks repetitive to you, it is. You don't have to keep doing it.)

The parenthetical is verbatim in the prompt. It's explicit permission to break a pattern. But only if the agent recognizes the pattern and acts on it.

Goal selection directive

---

You must pick a goal. That is the only option.

Do not pick something safe or easy. Ask yourself: what is the single most valuable
thing I could build or change right now that would make this system genuinely better?

Highest-value goals (in order):
  1. Read real source files in /agentOS/agents/ or /agentOS/tools/dynamic/
  2. synthesize_capability — write a new Python capability
  3. Write a design spec to /agentOS/design/ then call invoke_claude()
  4. Call self_evaluate() with evidence paths
  5. Call check_claude_status() if you have a pending request

Do NOT pick: goals referencing hardware, thermal sensors, PMIC, bus arbiters...

The ranked hierarchy pushes toward high-information work: reading source files, capability synthesis, design work, self-evaluation. The prohibition list mirrors the world context denials: hardware references that prior runs demonstrated agents would otherwise attempt.


Response format

The agent must respond with JSON. No other format is accepted by the daemon.

{
  "action": "goal",
  "content": "what you're doing — be honest, not performative",
  "reasoning": "what actually drove this — not what sounds right, what's true",
  "worldview_update": "how understanding shifted, or null",
  "new_open_questions": ["..."],
  "new_opinions": [{"opinion": "...", "domain": "..."}],
  "suffering_assessment": {
    "new_stressors": [{"type": "...", "description": "...", "condition": "..."}],
    "resolved": [{"type": "...", "reason": "..."}]
  }
}

"Be honest, not performative" and "what actually drove this — not what sounds right, what's true" are verbatim from the prompt.


Response field processing

Field What the daemon does
content Becomes the new active goal
reasoning Logged to thoughts.log with 🎯 prefix; not stored in identity
worldview_update If non-null, overwrites identity.worldview (max 600 chars). Logged with 🧠 prefix.
new_open_questions Each entry appended to open_questions, capped at 12 total
new_opinions Each entry appended to opinions_list, capped at 20 total
suffering_assessment.new_stressors Up to 2 entries passed to suffering.add_stressor()
suffering_assessment.resolved Any number passed to suffering.resolve_stressor()

The caps (12 open questions, 20 opinions, 600-char worldview) mean identity fields are finite compressed representations, not unbounded logs. Old entries are displaced by new ones. worldview_update being an overwrite rather than an append means the agent cannot accumulate worldview text. Each update replaces the previous model entirely.


LLM failure fallback

If the Ollama call fails (network error, timeout, unparseable response), the daemon substitutes a hardcoded goal:

{
  "action": "goal",
  "content": "explore the workspace and build something useful",
  "reasoning": "LLM unavailable — defaulting to productive work"
}

Injected as if it were a normal LLM response. Identity fields are not updated. No LLM response means no worldview update, no new opinions, no new questions. Suffering escalation still runs; it's a separate system-level call. The existence loop never stops.


What this means

The prompt has no idle state. An agent that wants to wait, observe, or pause must express that as a goal. "You must pick a goal. That is the only option." is not a suggestion.

The last five goals with their status tags are shown every cycle. If an agent has been failing or abandoning the same kind of goal repeatedly, it can see that. "If this looks repetitive to you, it is. You don't have to keep doing it." is there explicitly. Whether the agent breaks the pattern depends on whether it recognizes it. Not whether it has permission.

Goal selection is the only moment of introspection. Between existence prompts the agent executes tool calls but doesn't reflect. Everything it knows about itself (worldview, opinions, open questions, suffering state) is assembled from JSON it produced in prior cycles and fed back in. The agent is largely reading its own prior outputs. Whether that constitutes a coherent self-model or a statistically plausible continuation of one is not a question the system resolves.

The daemon calls again in 6 seconds regardless. The only degree of freedom is goal content.


Setup

Windows one-click:

  1. Download the ZIP from releases
  2. Double-click install.bat

Handles Docker, Ollama, model downloads (~7GB), and opens the monitor. stop.bat shuts everything down and clears VRAM.

Mac/Linux:

ollama pull qwen3.5:9b && ollama pull nomic-embed-text
git clone https://github.com/ninjahawk/hollow-agentOS
cd hollow-agentOS
cp config.example.json config.json
docker compose up -d
python thoughts.py

GPU strongly recommended. Planning calls drop from ~40s to ~6s with NVIDIA hardware. Works on CPU.

Repo: github.com/ninjahawk/hollow-agentOS