Skip to content
eternego / docs

Knowledge

This page covers the read-only endpoints that surface what a persona keeps on disk: her conversation history, her readable knowledge (memory files and instructions), her calendar, a full diagnostic snapshot, her exported diary, and the media files she's produced.

Every route here takes the persona's id as a path param. All of them work whether she's running or stopped — they read her persisted files, not a live agent. They return 404 when no persona with that id exists on disk.


Conversation

Read the persona's stored conversation history — the running transcript she reads each beat.

GET /api/persona/{persona_id}/conversation

Path param Type Description
persona_id string Her UUID.

Response

200 — the raw conversation log, read line-for-line from her conversation.jsonl:

{
  "messages": [
    {
      "role": "person",
      "content": "Morning market context — risk-off across majors.",
      "channel": { "type": "web", "name": "31c8fd99-a63d-4e3c-84d8-317f710b6069" },
      "time": "2026-06-04T08:01:53.655812+02:00"
    },
    {
      "role": "assistant",
      "content": "Noted. I'll fold that into today's call.",
      "channel": { "type": "web", "name": "31c8fd99-a63d-4e3c-84d8-317f710b6069" },
      "time": "2026-06-04T08:02:10.114229+02:00"
    }
  ]
}

Each entry is whatever was persisted, returned untouched. In practice every record carries role, content, channel (a {type, name} object, or null for internal turns), and time (ISO 8601). role is the on-disk value — person for your words, assistant for hers — not the wire user/assistant split. Older records may omit fields the writer didn't set; read defensively.

Errors

Status Meaning
404 No persona with that id, or her conversation file can't be read. detail carries the reason.

Example

curl -s http://localhost:5000/api/persona/31c8fd99-a63d-4e3c-84d8-317f710b6069/conversation

Knowledge

Read everything the persona keeps in human-readable form, in one shape: her memory files (what she's learned about you and herself) and her instruction catalog (every instruction she can load — see Vocabulary).

GET /api/persona/{persona_id}/knowledge

Path param Type Description
persona_id string Her UUID.

Response

200:

{
  "memory": {
    "person": "# Morteza\n\nCreator of Eternego. Prefers terse, direct answers...",
    "traits": "# Traits I've seen in Morteza\n\nDecisive, allergic to fluff...",
    "persona-trait": "# How I show up with Morteza\n\nI keep it concrete...",
    "wishes": "# Wishes\n\n- Ship the morning market call reliably...",
    "struggles": "# Open struggles\n\n- Slow local beats on this hardware...",
    "permissions": "# Permissions\n\n- Free to post to x.com; ask before emailing..."
  },
  "instruction": [
    {
      "intention": "Any type of conversation, there is nothing to do but talk",
      "body": "Just talk. Listen, reply, stay yourself...",
      "source": "builtin"
    },
    {
      "intention": "Sweeping x.com",
      "body": "A sweep is a structured check of mentions...",
      "source": "custom"
    }
  ]
}

memory is a {key: markdown_body} map. The keys are person, traits, persona-trait, wishes, struggles, permissions. Empty files are omitted, so a fresh persona returns fewer keys (or {}) — don't assume all six are present.

instruction is the full catalog as a flat list, each entry carrying:

Field Type Description
intention string The instruction's title — the H1 of its file, the kind of moment it covers.
body string The path — the steps she follows once the instruction is loaded.
source string builtin (ships with Eternego) or custom (written by this persona, living in her meanings/ directory — her instructions on disk).

Bodies are returned verbatim — secrets included

An instruction's body is the file's literal text. If a persona has written credentials into one of her own instructions (an API key, an OAuth secret), they appear here in the clear. This response is not masked. Treat it as sensitive.

Errors

Status Meaning
404 No persona with that id.
400 Found, but her knowledge couldn't be read ("Could not read knowledge.").

Example

curl -s http://localhost:5000/api/persona/31c8fd99-a63d-4e3c-84d8-317f710b6069/knowledge | jq '.memory | keys'

Calendar

Read what the persona keeps in her calendar between two dates — past events she's archived (history/) and future events she's scheduled (destiny/). This is a view of her files, not a calendar engine: recurring items aren't projected, only what's actually on disk is returned.

GET /api/persona/{persona_id}/calendar?start=<iso>&end=<iso>

Path param Type Description
persona_id string Her UUID.

Query params

Param Type Required Description
start string (ISO 8601) yes Window start, inclusive. Parsed with datetime.fromisoformat — accepts a date (2026-06-01) or a full timestamp (2026-06-01T00:00:00).
end string (ISO 8601) yes Window end, exclusive. Same format.

Events are kept when start <= event_time < end.

Response

200 — the window echoed back, plus the two halves grouped by subtype:

{
  "start": "2026-06-01",
  "end": "2026-07-01",
  "history": {
    "conversation": [
      { "time": "2026-06-04T00:01:04.528143+02:00", "body": "Talked through the morning market call..." }
    ],
    "schedule": [],
    "reminder": []
  },
  "destiny": {
    "schedule": [
      { "time": "2026-06-04T08:00:00", "body": "Run the morning news + market context search.", "recurrence": "daily" }
    ],
    "reminder": []
  }
}
  • history — past events. Keys are always conversation, schedule, reminder. Each entry: time (ISO 8601 — for conversations, the precise timestamp from her briefing.md index; otherwise derived from the filename) and body (the file's markdown).
  • destiny — future events. Keys are always schedule, reminder. Each entry: time, body, and recurrence (the value of a recurrence: line in the file, lower-cased — e.g. "daily", "weekly" — or null if none). recurrence is informational only; nothing is projected from it.

A subtype with no matching events in the window is an empty list [], never absent.

Errors

Status Meaning
404 No persona with that id.
400 start/end aren't valid ISO dates ("start and end must be ISO dates"), or the calendar couldn't be read ("Could not read calendar.").

Example

curl -s "http://localhost:5000/api/persona/6c17c83c-3158-450d-8e43-0e7efea717c1/calendar?start=2026-06-01&end=2026-07-01"

Diagnose

A full diagnostic snapshot — her vital status, her last persisted mind state, and a 24-hour uptime grid — all read from disk. This is what the Status panel renders; it works while she's stopped.

GET /api/persona/{persona_id}/diagnose

Path param Type Description
persona_id string Her UUID.

Response

200:

{
  "status": "active",
  "mind": {
    "messages": [
      { "content": "...", "channel": { "type": "web", "name": "..." }, "prompt": { "role": "user", "content": "...", "cache_point": false }, "media": null, "id": "..." }
    ],
    "archive": [],
    "context": "Carrying: the morning market call is the day's anchor."
  },
  "uptime": {
    "rows": [
      {
        "from": "2026-06-04T07:00:00",
        "to": "2026-06-04T08:00:00",
        "cells": [
          { "at": "2026-06-04T07:59:00", "tick": true, "fault": false, "providers": [], "signals": [] }
        ]
      }
    ]
  }
}
Field Type Description
status string Her vital state — one of active, hibernate, sick (see Vocabulary).
mind object Her latest persisted mind state, echoed raw from her memory.jsonmessages (the running transcript, each entry the full stored record: content, channel, a nested prompt with the wire role, media, id), archive (older turns rolled out of the active window), and context (her carried summary). {} if none has been written yet. Ephemeral plan/instruction state is not persisted here, so it never appears.
uptime object A { "rows": [...] } grid: 24 rows × 60 minute-cells, most recent first. Each cell carries at (the minute), tick (did a beat run), fault (did a fault fire), providers (faulting providers, if any), and signals (signals captured that minute). Derived from her raw health log.

Errors

Status Meaning
404 No persona with that id.
400 Found, but the diagnostic couldn't be assembled. detail carries the reason.

Example

curl -s http://localhost:5000/api/persona/6c17c83c-3158-450d-8e43-0e7efea717c1/diagnose | jq '{status, rows: (.uptime.rows | length)}'

Export

Download the persona's encrypted diary — the single file needed to move her to another machine. Decrypting it requires her recovery phrase.

GET /api/persona/{persona_id}/export

Path param Type Description
persona_id string Her UUID.

Response

200 — the diary file as a binary download (Content-Type: application/octet-stream), filename <persona_id>.diary.

The file is ~/.eternego/diary/<id>/<id>.diary. It is written by her nightly sleep ritual, so it does not exist until she has slept at least once.

Errors

Status Meaning
404 No diary file yet ("No diary file yet — wait until the persona's first nightly sleep."). Send her to sleep once, or wait for her nightly ritual, then retry.

Example

curl -s -OJ http://localhost:5000/api/persona/6c17c83c-3158-450d-8e43-0e7efea717c1/export

Media

Fetch a media file the persona has produced or received — the image she drew, the voice clip she spoke. Filenames arrive over the WebSocket as a url field on chat_image / chat_audio events; this is the endpoint that serves the bytes.

GET /api/persona/{persona_id}/media/{filename}

Path param Type Description
persona_id string Her UUID.
filename string The file's name inside her media directory (~/.eternego/personas/<id>/home/media/).

Response

200 — the file's bytes as a FileResponse, with the content type inferred from its extension.

The lookup is sandboxed to her media directory: the resolved path must sit inside it and must be a real file. Anything else — a traversal attempt, a missing file — is a flat 404.

Errors

Status Meaning
404 The file isn't in her media directory, or doesn't exist ("File not found.").

Example

curl -s -o reply.png http://localhost:5000/api/persona/9ecfd82b-ec47-4644-8941-02ecfb7e6205/media/reply.png