Initial import of all source code, config, and README assets: the packages workspace (cli, core, server, web, docs, landing, skills), build scripts, tooling config, and CI workflows. Includes the data-layout revision made on this branch: the local data root defaults to ~/.penguin/data (PENGUIN_HOME still overrides; the installer keeps its binaries in ~/.penguin), and every Agent lives under <project>/agents/<agent>/ — path helpers, the three agent-enumeration scans, the system prompt, built-in Skills, tests and docs all follow the new layout. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018ihk8iQuo3kv2aPjAYEPuR
6.7 KiB
title, description
| title | description |
|---|---|
| Sessions & Traces | The six-level run model, local data directory layout, Trace file design, and Session recovery. |
All PenguinHarness runtime data lives on the local file system: configuration is editable files, history is append-only Traces. This page defines each level of the run model and explains how the Trace serves as history, recovery source, and statistics source at once.
Run model
Six levels: Project → Agent → Workspace → Session → Task → Request.
| Concept | Definition |
|---|---|
| Project | Top-level unit organizing Agents; owns the model and credential configuration; in the multi-user Web setup, users and Projects are many-to-many |
| Agent | The executing subject; has exactly one Agent State (a persistent directory); one Agent can serve many Workspaces |
| Workspace | The working directory of one run — the only file scope the model sees; an explicit workspaceDir must already exist, otherwise a temp Workspace workspaces/tmp-<8hex> is created |
| Session | A continuous conversation under one (Agent, Workspace); model and Workspace are locked at Session creation; ids look like session-YYYY-MM-DD-HH-mm-ss-<8hex> |
| Task | One execution goal started by one Prompt; consists of one or more consecutive Requests |
| Request | One LLM API call: context and tool definitions in, streamed output out |
See the Architecture page for how the levels cooperate, and the Agent Loop for how Requests advance within a Task.
Data layout
The data root is the PENGUIN_HOME environment variable, defaulting to ~/.penguin/data. The layout is defined in one place, packages/core/src/state/paths.ts:
<root>/<project>/
├── .project_config.toml # Project-level models & credentials (hidden file, 0600)
└── agents/
└── <agent>/
├── agent_state/ # system_config.yaml, AGENTS.md, .vault.toml,
│ # tools/, memory/, skills/, schedule/
├── traces/
│ └── <yyyy-mm-dd>/<sessionId>_<index3>.jsonl
├── scratchpad/ # temp files, one subdirectory per Session id (e.g. pasted images)
├── workspaces/ # temp Workspaces (tmp-<8hex>)
├── benchmarks/ # capability Benchmark cases and scores
└── snapshots/ # Agent State version snapshots
See the Configuration Reference for the fields of each config file.
Trace design
A Trace is an append-only JSON Lines file; each line is one OmniMessage envelope (see the OmniMessage Protocol). History is only ever appended, never modified in place.
- One Trace file corresponds to one complete model context. When compaction produces a new context segment, the writer rotates to a new file —
_002,_003, … — with an incrementing index. - Recorded:
session_meta, completemodel_msg, and allevent_msg. - Not recorded: streaming
partial_*fragments (the producer appends the complete message once the segment ends), and nested messages tagged withorigin— a subagent's messages go to the child Session's own Trace, while the parent Trace keeps a singlesubagentpointer event at the spawn site recording the child Session id. request_beginandrequest_end(status)come in pairs delimiting one Request; replay usesrequest_end.status === "completed"as the commit criterion for that turn.
See packages/core/src/trace/writer.ts for the implementation.
The head of a Trace (illustrative; one OmniMessage envelope per line):
{"timestamp":"2026-07-18T03:10:22.531Z","type":"session_meta","payload":{"session_id":"session-2026-07-18-11-10-22-3f8a1c2d","provider":"deepseek","model_id":"deepseek-v4-pro","model_context_window":1000000,"system_prompt":"…","tools":[…],"thinking_level":"medium","agent_state":"/home/u/.penguin/data/default_project/agents/default_agent/agent_state","workspace":"/home/u/work"}}
{"timestamp":"…","type":"event_msg","payload":{"type":"request_begin"}}
{"timestamp":"…","type":"model_msg","payload":{"type":"text","role":"user","text":"Create hello.txt"}}
{"timestamp":"…","type":"model_msg","payload":{"type":"tool_call","role":"assistant","name":"exec_command","arguments":"{\"cmd\":\"printf hi > hello.txt\"}","tool_call_id":"call_0"}}
{"timestamp":"…","type":"event_msg","payload":{"type":"approval_decision","decision":"allow","tool_call_id":"call_0"}}
{"timestamp":"…","type":"model_msg","payload":{"type":"tool_call_output","role":"user","output":"[no output]","tool_call_id":"call_0"}}
{"timestamp":"…","type":"event_msg","payload":{"type":"request_end","status":"completed"}}
{"timestamp":"…","type":"event_msg","payload":{"type":"token_usage","session":{…},"request":{…}}}
Session recovery
The Trace is the single source of truth for recovery — there is no separate session database to keep in sync. resumeSession works as follows:
- Locate the highest-index Trace file of the Session;
- Read the runtime configuration from its
session_meta— model, system prompt, Workspace — all immutable for the lifetime of the Session; - Replay the committed history into a fresh LLM context;
- Reconstruct the carry-over (undelivered tool outputs, interruption markers) plus turn and Token counters;
- Continue appending to the same Trace file.
Recovery requires that the Workspace and the model still exist. What recovery guarantees is structural legality: only committed turns are replayed, with tool_call / tool_call_output pairing intact; incomplete model output (thinking, text) is allowed to be lost. A truncated last line left by an abnormal process exit is tolerated and ignored. See packages/core/src/trace/resume.ts.
Special case: if the latest Trace file ends with a completed compaction, that context is closed as a whole — resume starts from an empty context; in summarize mode the <context_summary> is reconstructed and prepended to the first input after resume.
Field fidelity
Provider-specific fields (such as signature and phase) are preserved verbatim in the Trace and sent back verbatim — some models require them byte-for-byte on history replay, and any rewriting would break compatibility. This is one reason the Trace stores raw OmniMessage envelopes rather than a post-processed format.
Observability
Every approval decision (approval_decision), abort (abort), compaction (compaction_begin / compaction_end), and token_usage lands in the Trace as an event. The Web Trace view and the usage/cost statistics are both derived from this same data — there is no second source of truth; see the Web App Guide. The approval mechanism itself is covered in Tools & Approval.