Collection and controls
Follow native activity into Space, separate local collection from external reporting, and understand retention and feature controls.
Space has several collection paths. The watcher observes supported runtime activity, APIs record explicit work, providers build session telemetry, and optional pollers retrieve connected-service data. Daily usage reporting is a separate process with its own authentication gate.
The baseline collection paths describe xo-space 5221119; manual command controls are updated against revision 84f0737e23b6bb369fc6b8853afb510a02dd8b21.
Read Where the data lives alongside it for
the full path catalog and the meaning of projects root, state root, and native
home.
Follow one file edit
Suppose an agent edits src/app.py inside a known project. Its runtime first
records the interaction in its own transcript. If a compatible source can
attribute that record to the project, the watcher can turn the tool call into
a project-relative file-touch event, increment usage/tool statistics, and append
an event to local history.
- 1An agent edits a file
The working file changes. A supported native runtime also records the tool activity in its own session store.
- 2The watcher reads the next records
An adapter attributes supported activity to a project. Saved cursors track how far the source has been read.
- 3Space records an observable event
A supported file touch becomes a timeline event with a timestamp, runtime, session ID and relative path. Other sinks maintain activity and counters.
- 4Workspace views refresh
Project records feed workspace rollups and materialized views. Their refresh schedule means the browser can briefly show an older result.
- 5You inspect the result
Use Projects for activity and files. Opening a file reads its contents on demand; Git history supplies committed versions and the Timeline tab's dated map.
Several qualifications matter when reading this flow:
- A normalized file-touch event contains the path and event kind, not a copy of the edited file or tool arguments. Opening a file preview reads content separately.
- Live presence is polled separately from events. A file-edit event does not guarantee a live badge.
- Workspace graph/Dashboard projections refresh from project and git data.
The Timeline UI's dated file map uses git history; it is not a direct
rendering of every line in
workspace/timeline.jsonl. - The watcher is an observation pipeline. A recorded tool call or token count does not establish that the edit succeeded, passed tests, or completed the user's work.
The implementation is in the watcher, event definitions, timeline sink, and workspace view builder.
Two session data paths
The watcher and the aggregate Sessions page discover different adapter capabilities. The active chat backend is a third choice. Selecting one runtime for chat does not by itself describe everything visible in Sessions.
| Path | How it works | Current coverage |
|---|---|---|
| Watcher | Discovers visualizer_source adapters, polls native files/databases, attributes events to projects, and writes project/workspace history and live presence. | Claude Code, Codex, OpenClaw, Hermes, and Antigravity have source adapters. Their discovery and presence support differ. |
| Aggregate Sessions | Discovers session_telemetry adapters and combines their summaries into workspace/sessions.json. | Claude Code through Argus, Codex through its SQLite index and rollouts, and Cursor through native transcript/chat/desktop stores. |
| Prompt details | A request for one session loads that adapter's session_prompts capability. It is not part of the aggregate payload. | Claude Code and Codex. Other sources show unsupported rather than inventing prompt text. |
The local installer defaults watcher source mode to all; standalone service
code defaults to active. QUIRQ_WATCHER_SOURCE_MODE=active limits watcher
ingestion to the active runtime; it does not filter aggregate Sessions providers.
See watcher discovery,
telemetry aggregation,
and the prompt endpoint.
Source discovery and project attribution
Claude Code and Codex sources can discover native sessions by working directory and connect them to projects under the configured root. OpenClaw, Hermes, and Antigravity sources also use the existing project session indexes to identify which native session belongs to which project. A readable transcript outside the configured project scope is not automatically a project event.
For JSONL sources, the watcher stores the source's absolute path, byte offset, and inode. First discovery reads from the beginning; replacement or truncation resets that file's cursor. Incomplete final lines wait for a terminating newline. Hermes instead keeps per-session SQLite message-row cursors. These mechanisms avoid normal repeat reads but are not a transactional, exactly-once event log: manually resetting cursors, restoring old state, or partial failures can replay or lose increments. Do not reset cursors as a way to refresh the UI.
See the JSONL tailer and source adapters.
What the watcher extracts
All normalized events carry a timestamp, runtime, native session ID, and attributed project ID. The fields below describe the current event contract; each adapter can supply only what its native records expose.
| Observation | Retained fields and use |
|---|---|
| First session observation | Native session identity and reported working directory; seeds session metadata and a session.started history event. |
| User/assistant message | Role and, when available, model. Used for counts; the normalized message event has no message body. |
| Usage | Input, output, cache-read, and cache-write counts; model; optional user-to-assistant latency. Used by session/day/model/runtime stats. |
| Tool call | Tool name for counts. Ordinary tool arguments and results are not retained in this normalized event. |
| File touch | Project-relative path and create/edit classification. A path outside the project is rejected by the path filter. |
| Presence | Runtime/session and known process timestamps; enriched with model, user identity, and optional host. Stored as a current snapshot. |
The schema also defines native task observations, but the current watcher
explicitly removes TaskCreated and TaskStatusChanged before sending events
to its sinks. Native TaskCreate, TaskUpdate, or equivalent runtime tools
therefore do not populate Space's todos, task timeline, or task counters.
This is a narrow field allowlist, not a claim that every file in the installation is free of content or identifying information. Paths, identifiers, tool/model names, authored work, native stores, connected-service text, and diagnostic logs all have their own rules. See event types, filtering, watcher task exclusion, and presence writer.
Other local writers
Authored work and project files
Todo and Workitems APIs record explicit content and lifecycle changes. The Todo API writes the portable record and updates session task counters and timeline; Workitems/claims write work lifecycle history with fields such as title, assignee, issue reference, and state reason. Peer changes write roster data. These actions can create data even when the watcher is disabled.
The workspace file scan reads directory/file information and git history to build the graph and Dashboard. File-preview requests can read current content, or retrieve a committed version through git. They are separate from the watcher's path-only events. See the Todo store, Workitems store, space index, and file preview.
GitHub issues
The issue poller reads GitHub through gh api graphql using available GitHub
authentication. An initial poll seeds open issues; later polls include changes
to open and closed issues since the cursor. Stored fields include issue ID,
number, title, state/reason, assignees, URL, and update time. The mirror also
retains polling/rate-limit/error state. It is not a full copy of issue bodies or
comments. Authored workitems can contain a body independently of that mirror.
See the GitHub query
and mirror.
Connected services
Configuring polling creates a connection's local config. Each successful poll executes the chosen collector through the connected toolkit, normalizes results, deduplicates by event key, appends new events, and updates polling state. The account-label cache is separate and can be populated without a polling config.
| Toolkit | Examples of selected data |
|---|---|
| Gmail | Message IDs, subjects, snippets, sender/time information, and links from bounded unread/recent-inbox queries. These collectors request metadata rather than full message payloads. |
| Google Calendar | Calendar/event IDs, summary, start time, description/location, and event link from the upcoming window. |
| Notion | Recently edited page IDs, titles, timestamps, and URLs. |
| Slack | Selected message text, channel/sender context, timestamps, and links from the collector's bounded query. |
| Telegram | Bot updates with message/chat/sender context and text when present. |
Normalized titles are capped at 300 characters and bodies at 4,000. These limits bound a record; they do not make its contents non-sensitive. Provider IDs are the deduplication keys, so a change to an already-seen record need not create a new event. See collectors, poller, and store.
Inbox
Inbox feeders copy selected timeline events, blocked todos, sharing status,
open issues, and connection events into one local store. The API can also add
an item directly. The store retains its own item content, deduplication keys,
source cursors, and human new/seen/done choices.
Feeders are incremental and bounded: timeline and connection ingestion starts with a recent window, connection ingestion reads the current event log rather than its rotations, and the issue feeder initially focuses on recent issues. Inbox therefore is an attention list, not a complete event archive. A feeder may automatically close a resolved issue or unblocked todo; if it returns, an automatically closed item can reopen. A person's Done choice is preserved. See feeders and Inbox behavior.
Argus is a separate ingestion store
xo-space pins argus-code==0.5.1 and starts its installed daemon for the Claude
Sessions capability. Space then queries a subset of the Argus database for
aggregate metrics. The aggregate payload excludes prompt text, but the Argus
database is not metadata-only.
In the pinned Argus release, the database contains:
- Sessions and turns with project paths, models, timing, token breakdowns, estimated costs, source/pricing metadata, and parent/subagent relationships where known.
- Tool-call identity, name, timestamp, error flag, input size, and subagent type.
- Source-file offsets and parse failures; a malformed input can leave a raw excerpt of up to 200 characters in the error table.
- Prompt display text from Claude's
history.jsonl, with timestamp, project path, pasted-content length, and slash-command marker. Prompt ingestion is independent of the transcript-search toggle; display text is bounded to approximately 8 KiB. - Optional transcript segments and a full-text index when transcript indexing is enabled. Segments can include user, assistant, thinking, and tool-result text, with a per-segment cap. Settings, detector alerts, and their status are also stored.
The Argus search/indexing setting controls transcript-segment indexing and visibility; switching it off does not purge old segments or stop prompt-history ingestion. Its explicit index-clear operation removes transcript segments, not the whole database or prompt table. Native transcript deletion does not automatically purge Argus's retained rows.
ARGUS_DB changes the path Space reads. The daemon is launched as
argus daemon start without that path argument, so redirecting the reader alone
does not relocate Argus ingestion. Argus's data directory also holds its own
daemon log and PID file. See the xo-space requirement pin,
adapter lifecycle,
and the published Argus 0.5.1 package.
These underlying-store details were checked against that release's packaged
source; an older installed Argus version can behave differently.
Prompt details and transient content
Opening a supported session's details reads native human prompts on demand,
up to the newest 200 prompts and 4,000 characters per prompt. The response
groups human turns and adds reply/tool-call counts where available; the Space
detail view does not show full assistant replies. It can temporarily duplicate
prompt text in server and browser memory even though /xo/sessions.json does
not contain it.
Current adapters write session metadata into the runtime tier rather than maintaining another transcript copy there. Hermes has an additional process-memory exchange cache containing user/assistant text for up to 60 seconds, bridging the delay before its native database commit is readable. Old migrated session directories may still contain historical files from earlier versions. See prompt endpoint, Claude prompt extraction, Codex prompt extraction, and Hermes exchange cache.
Daily usage reporting to XO
The daily reporter is services/usage_sync.py. It uses the active agent's
usage module, independently of the watcher's all/active choice and the
Sessions page's selected sources. First use without a watermark can backfill
historical days; subsequent runs reprocess dates from the watermark onward.
The default scheduled time is 02:00 UTC, controlled by USAGE_SYNC_HOUR_UTC.
Before posting a report, the service requires an XO auth token and successfully probes the reporting endpoint with an empty record list. A missing token sends no report. A rejected token or inconclusive/offline probe sends no usage records. Setup shows the last decision as off, pending, on, or blocked; the stored probe state describes the last check, not a guarantee of current network connectivity.
| Report field group | What leaves the installation after the gate succeeds |
|---|---|
| Identity/time | Report date, Space identifier, and workspace display name when available. Legacy wire fields workspace_id and project_id carry the Space ID, not a local project directory. |
| Usage | Input/output/cache-read/cache-write/total tokens; estimated total and component costs; message, session, and tool-call counts. |
| Breakdowns | Per-model and per-tool summaries produced by the active usage module. |
Diagnostic note | Empty/partial/error explanations. At this revision, parser and aggregation failures can include filenames or raw exception text. |
The normal metric fields do not send prompts, assistant responses, file bodies, or filesystem paths. Diagnostic notes are an exception to a blanket “no filenames or paths” claim: a module-load or aggregation exception is included as text, and parse-error summaries can include input basenames and exception details. A zero-valued placeholder can carry such a note when no usable usage is available. It passes the same authentication probe.
The local watermark advances only after a successful report and keeps the current day eligible for a later update. Removing it can trigger a historical backfill; it is not a deletion request to XO. See report orchestration and payload and usage parsing.
Other external boundaries
Usage-reporting status describes only the daily XO report. Other enabled features can communicate independently.
| Feature | Boundary |
|---|---|
| Agent execution | The selected runtime contacts its configured model/provider or gateway and handles prompts, replies, tools, and native persistence under its own behavior. |
| Connected toolkits | Account linking and tool execution reach XO/Composio and the selected service. Pollers retrieve the content described above; action restrictions and account pins govern the enabled toolkit session. |
| Direct connectors | OAuth exchanges and provider API requests use that connector's service and credential store. |
| GitHub issue polling/actions | Repository/issue queries and explicitly requested mutations reach GitHub. |
| Git operations | Clone/fetch/push communicate with the configured git remote. Committed repository content travels on a push. |
| Sharing relay | With auth and a Space ID, exchanges repository identity, commit hashes, and ledger/bookmark data with XO; permitted cloning creates local repository copies. |
| Encrypted backup/restore | Sends encrypted project chunks plus plaintext snapshot metadata to private GitHub backup repositories; restore downloads them. |
| Opening external item links | The browser navigates to the linked service. |
The sharing loop can be parked independently by its feature switch, missing Space ID, or missing auth. A signed-out local install is not the same thing as network isolation for installed runtimes and connected services. See sharing gates, Composio scope, and backup implementation.
Retention is per store
There is no one retention setting that deletes every copy. The table describes the current writers' bounds; source runtime and provider retention remain separate.
| Store | Current behavior |
|---|---|
| Portable todos/workitems | No automatic age-based purge. Deletion keeps tombstones. Peers are removed directly rather than tombstoned. |
| Project timeline | Rotates the live file when it has reached 8 MiB, retaining the newest five rotations. |
| Workspace timeline | Append-only with no current automatic rotation. |
| Project/workspace stats | Daily buckets keep up to 35 dates. Rolling views are 7/30 days; per-session accumulators are separate and are not all removed by that daily limit. |
| Session metadata/enrichment | No general age-based cleanup in the current writers. |
| GitHub issue mirror | Keeps open rows and at most 500 closed rows. |
| Live presence | Replaced from the current source snapshot each tick; it is not a session archive. |
| Connection events | Live log rotates at 2 MiB; newest three rotations retained. Each collector's deduplication cursor keeps the newest 500 keys. |
| Inbox | On changed writes, removes done items older than 30 days using item time, then caps the store at 500 items by dropping oldest done items first, then oldest remaining items. |
| Shared command log | Output capped at 4,096 characters per entry; live log rotates at 5 MiB to one .1 backup. |
| Server log / explicit command logs | The local installer appends its server log. Explicit command logs, including scheduler logs, have no generic rotation in the current writer. |
| Scheduler run history | Appended without a general age cap. Deleting a job removes its definition/state but deliberately keeps its run history and log. |
| Argus | Retained database rows are not purged when native logs disappear. Its daemon log rotates at 1,000,000 bytes with three backups. Index clearing affects transcript segments only. |
| Prompt caches | Server: default 30-second TTL, maximum 32 session keys. Browser: page lifetime. |
| Project backups | Keeps ten snapshot directories per backup repository's current tree. Removing an older directory is not a rewrite of that git repository's prior history. |
These rules come from the timeline, workspace timeline, stats, connection, Inbox, command, scheduler, and backup writers.
Control the producing feature
| Intent | Control and scope |
|---|---|
| Pause the watcher | QUIRQ_WATCHER_ENABLED=false, then restart as required. Stops watcher ingestion/presence and automatic command launches driven by its ticks. Manual commands in Setup still run and collect results on status reads. It does not disable direct API writes, Sessions reads, Argus, or other pollers. |
| Narrow watcher sources | QUIRQ_WATCHER_SOURCE_MODE=active rather than all. Changes watcher coverage, not aggregate Sessions discovery. |
| Change watcher cadence | QUIRQ_WATCHER_INTERVAL_SECONDS, normally 1 second, clamped to 0.25–60 seconds. |
| Change projection cadence | XO_VIEWS_REFRESH_S, default 30 seconds. HTTP stale-view threshold is XO_VIEW_MAX_AGE_S, default 120 seconds. Neither is a history-retention limit. |
| Pause background connection polling | Global XO_CONNECTIONS_POLL_ENABLED or a connection's saved enabled choice. The connection interval defaults to 900 seconds and is bounded to 60–86,400 seconds. |
| Run a deliberate connection check | Poll now bypasses background enable/due checks but still needs a saved config, enabled toolkit scope, valid identity, and its execution lock. |
| Restrict a connected toolkit | Use Connectors to select the account, enable it for this Space, and disable individual actions. Account connection, toolkit scope, action preferences, and polling are separate choices. |
| Pause automatic commands | XO_SCHEDULER_ENABLED=false stops automatic launches. Manual Run still works, and neither this setting nor disabling the watcher cancels an active process. |
| Stop shared command logging | QUIRQ_COMMAND_LOG=off; use QUIRQ_COMMAND_LOG_PATH to change its destination. This does not turn off explicit per-command logs or scheduler result records. |
| Pause sharing | PROJECT_SHARING_ENABLED=false; automatic cloning has its own PROJECT_SHARING_AUTO_CLONE switch. |
| Inspect reporting state | Setup's usage-reporting status and probe time. Reporting requires an accepted XO credential; there is no independent per-source reporting toggle in this build. |
Pausing collection preserves existing files. Disconnecting a service, clearing a browser cache, removing a local record, and deleting a remote copy are different operations. Before deleting data, use the path catalog to identify its writer and any other retained copies, including project git history, Inbox summaries, Argus, and backup repositories.
For ordinary investigation, start with Reading the data. It shows which UI/API surface answers each question and how to distinguish missing, stale, partial, and unsupported data without resetting the underlying stores.