# Bot App Hermes History System

## Purpose

The Bot app now exposes real Hermes bot history inside Saikan.io.

The implementation replaced the previous hard-coded/single-bot behavior with a local Hermes profile discovery and history-export system. The app can now show conversation history and file history for every Hermes bot profile available on this machine.

Current local Hermes sources:

| Bot in Saikan.io | Hermes storage location |
|---|---|
| `b-skander` | `C:\Users\rafaa\AppData\Local\hermes\` |
| `b-central-mensageiros` | `C:\Users\rafaa\AppData\Local\hermes\profiles\central-mensageiros\` |
| `b-norbidel` | `C:\Users\rafaa\AppData\Local\hermes\profiles\norbidel\` |
| `b-piccolo` | `C:\Users\rafaa\AppData\Local\hermes\profiles\piccolo\` |
| `b-skander1` | `C:\Users\rafaa\AppData\Local\hermes\profiles\skander1\` |
| `b-skander2` | `C:\Users\rafaa\AppData\Local\hermes\profiles\skander2\` |

Important: the default Skander profile is not under `profiles/`. It lives at the Hermes root and maps to `b-skander`.

---

## User-facing behavior

### Bot list

The Bot app route:

```text
/apps/bots
```

now discovers real local Hermes profiles and renders one bot per profile.

Expected current list:

```text
Central Mensageiros
Norbidel
Piccolo
Skander
Skander1
Skander2
```

When local Hermes profiles exist, they are treated as the authoritative local fallback list. The old fake fallback bot `Krillin` is not shown in this case.

### Bot detail

Each bot detail route follows the same structure:

```text
/apps/bots/<botId>
```

Example:

```text
/apps/bots/b-skander
/apps/bots/b-norbidel
/apps/bots/b-piccolo
```

The top card is a command-center summary. It shows:

- bot identity/status
- model/provider
- total messages
- conversation days
- file count
- latest conversation
- latest file
- storage status
- capture status
- quick actions

### History routes

The History tab is first-class and route-addressable:

```text
/apps/bots/<botId>/history
/apps/bots/<botId>/history/summary
/apps/bots/<botId>/history/conversations
/apps/bots/<botId>/history/conversations/<YYYY-MM>
/apps/bots/<botId>/history/conversations/<YYYY-MM>/<YYYY-MM-DD>
/apps/bots/<botId>/history/files
```

`/history` defaults to Conversation History.

### Conversation History layout

Conversation history is organized as requested:

1. Month list.
2. Day list inside a month.
3. Full daily conversation when a day is selected.

Example:

```text
/apps/bots/b-skander/history
```

shows:

```text
Junho De 2026
4 dia(s) · 284 mensagem(ns)
```

Example day route:

```text
/apps/bots/b-piccolo/history/conversations/2026-06/2026-06-22
```

shows a Telegram-like thread with:

- inbound/outbound message bubbles
- timestamps
- author role labels
- long-message collapse/expand via `Mostrar mais` / `Mostrar menos`
- files from that day inline above the message thread

### File History layout

File history route:

```text
/apps/bots/<botId>/history/files
```

shows a file explorer with:

- filters: `All`, `Input`, `Output`
- files grouped by month/day
- selected file preview panel
- preview/download actions

---

## Architecture overview

The system has four layers:

```text
Hermes profile files
  ↓
Python exporter: apps/web/scripts/hermes_history_export.py
  ↓
Next/server bridge: apps/web/lib/hermes-history.ts + apps/web/lib/bots.ts
  ↓
UI/routes: apps/web/app/apps/[appId]/_components/*
```

---

## Data sources

### Hermes profile data

Each Hermes profile can contain:

```text
state.db
sessions/sessions.json
config.yaml
cache/documents/
```

The app uses these as follows:

| Source | Used for |
|---|---|
| `config.yaml` | Bot model/provider metadata |
| `state.db` | Canonical stored Hermes messages/sessions |
| `sessions/sessions.json` | Telegram route/session metadata such as chat/thread/display names |
| `cache/documents/` | Locally cached files uploaded to or used by the bot |

### Default profile special case

Named profiles live at:

```text
C:\Users\rafaa\AppData\Local\hermes\profiles\<profile>
```

The default Skander profile lives at:

```text
C:\Users\rafaa\AppData\Local\hermes
```

So the mapping logic must include both:

```text
root Hermes profile -> b-skander
named profile       -> b-<profile>
```

If code only scans `profiles/`, Skander disappears.

---

## Main implementation files

### `apps/web/lib/hermes-history.ts`

Responsibilities:

- locate the Hermes root
- discover all local Hermes bots
- include the default root profile as `b-skander`
- map bot IDs to profile homes
- read bot metadata from `config.yaml`
- call the Python exporter for history data

Important functions:

| Function | Purpose |
|---|---|
| `listLocalHermesBots()` | Returns one Bot model per local Hermes profile, including root Skander |
| `profileNameForBot(botId)` | Maps `b-skander` to `skander`; other IDs remove `b-` |
| `profileHomeForBot(botId)` | Resolves root/default vs named profile path |
| `getLocalHermesBotHistory(botId)` | Runs the Python exporter and parses the BotHistoryBundle JSON |

Path behavior:

```ts
b-skander  -> C:/Users/rafaa/AppData/Local/hermes
b-piccolo  -> C:/Users/rafaa/AppData/Local/hermes/profiles/piccolo
b-norbidel -> C:/Users/rafaa/AppData/Local/hermes/profiles/norbidel
```

### `apps/web/scripts/hermes_history_export.py`

Responsibilities:

- open a profile's `state.db`
- read Telegram session metadata from `sessions/sessions.json`
- export messages into Bot App compatible JSON
- group messages by day into daily transcripts
- export cached files from `cache/documents/`

The script outputs a `BotHistoryBundle` compatible object:

```ts
{
  conversations: BotConversation[];
  messages: BotMessage[];
  files: BotFileRecord[];
  transcripts: BotDailyTranscript[];
}
```

It exits non-zero and writes to stderr on failure. This is intentional so silent history failures are visible.

### `apps/web/lib/bots.ts`

Responsibilities:

- expose Bot app repo methods used by the UI
- prefer local Hermes bots when they exist
- fall back to Supabase/runtime/mock data only when local Hermes bots are not present
- return local Hermes history before DB/mock history

Important behavior:

```ts
botsRepo.list()
```

If local Hermes profiles exist:

```text
return listLocalHermesBots()
```

Otherwise:

```text
return Supabase/runtime bots, or mock fallback
```

For history:

```ts
botsRepo.history(botId)
```

First attempts:

```text
getLocalHermesBotHistory(botId)
```

If unavailable, it falls back to DB/mock history.

### `apps/web/app/api/bots/files/[fileId]/route.ts`

Responsibilities:

- serve local Hermes cached files for preview/download
- resolve file root using `profileHomeForBot(botId)`
- enforce path safety so files cannot escape the profile's `cache/documents/`

Preview/download URL shape:

```text
/api/bots/files/<fileId>?botId=<botId>
/api/bots/files/<fileId>?botId=<botId>&download=1
```

Examples:

```text
/api/bots/files/hermes-file-...?botId=b-piccolo
/api/bots/files/hermes-file-...?botId=b-norbidel
/api/bots/files/hermes-file-...?botId=b-skander
```

### `apps/web/app/apps/[appId]/_components/bots-app.tsx`

Responsibilities:

- render the Bot list
- render bot detail/profile
- route History subtabs
- render month/day conversation navigation
- provide command-center metrics

### `apps/web/app/apps/[appId]/_components/bot-message-thread.tsx`

Client component.

Responsibilities:

- render Telegram-like message bubbles
- support long-message collapse/expand
- render inline files for the selected day

### `apps/web/app/apps/[appId]/_components/bot-file-explorer.tsx`

Client component.

Responsibilities:

- render file history explorer
- group files by month/day
- support `All`, `Input`, `Output` filters
- show selected file metadata and preview/download actions

### `packages/ui/src/styles.css`

Contains the Bot History UI styles:

- command center
- conversation browser cards
- chat message bubbles
- inline thread attachments
- file explorer
- preview panel
- mobile-first responsive layout

---

## Data model used by the UI

The UI consumes the shared Bot History types from:

```text
packages/types/src/index.ts
```

Main types:

```ts
Bot
BotConversation
BotMessage
BotFileRecord
BotDailyTranscript
BotHistoryBundle
```

The local exporter maps Hermes data into these existing shared types so the frontend does not need to know whether history came from Supabase or from local Hermes state.

---

## Conversation export behavior

The exporter:

1. Loads Telegram session metadata from `sessions/sessions.json`.
2. Selects active user/assistant messages from `state.db`.
3. Converts Hermes roles:

| Hermes role | Bot App role | Direction |
|---|---|---|
| `user` | `human` | `inbound` |
| `assistant` | `bot` | `outbound` |

4. Builds conversations keyed by Hermes session ID.
5. Groups messages by date.
6. Creates daily transcript records.

Transcript display path format:

```text
Conversation History/<Month YYYY>/Conversation - <DD/MM/YYYY>.md
```

Example:

```text
Conversation History/June 2026/Conversation - 22/06/2026.md
```

The filesystem-safe storage path uses dash dates internally where needed because `/` cannot be used inside a filename.

---

## File export behavior

The exporter scans:

```text
<profileHome>/cache/documents/
```

For each file it records:

- stable ID derived from SHA-256
- original filename
- MIME type
- size
- checksum
- local absolute storage path
- display path
- direction

Current local fallback direction is `inbound` for cached files because Hermes cache does not always preserve full input/output direction metadata. Future Supabase live capture should store exact inbound/outbound direction at event time.

Display path format:

```text
Input/Files History/<Month YYYY>/<DD/MM/YYYY>/<filename>
```

---

## Current storage status

This implementation is a local bridge over Hermes state, not the final canonical storage layer.

Current:

```text
Hermes state.db + cache/documents -> Saikan Bot App UI
```

Planned/final direction:

```text
Telegram/gateway capture events
  -> POST /api/bots/history
  -> Supabase Postgres metadata
  -> Supabase Storage file bytes
  -> Saikan Bot App UI
```

The local bridge is useful now because it exposes real historical data immediately while Supabase capture/storage migration is still pending.

---

## Norbidel pattern and Saikan replacement

The Norbidel bot had a controlled archive pattern:

1. Telegram inbound event arrives.
2. Human message is intercepted.
3. Inbound files are downloaded and archived.
4. Bot generation/validation happens.
5. Outbound files/messages are archived.
6. Message/file records are stored in Google Drive.

For Saikan.io, the desired replacement is:

```text
Telegram/gateway event
  -> record inbound/outbound message
  -> upload file bytes to Supabase Storage
  -> store metadata in Supabase Postgres
  -> generate daily transcript
  -> show in Bot App History
```

So the useful concept from Norbidel is the capture pipeline, not Google Drive itself.

---

## Routes and examples

### Bot list

```text
/apps/bots
```

### Skander default profile

```text
/apps/bots/b-skander
/apps/bots/b-skander/history
/apps/bots/b-skander/history/conversations
/apps/bots/b-skander/history/files
```

### Piccolo named profile

```text
/apps/bots/b-piccolo
/apps/bots/b-piccolo/history
/apps/bots/b-piccolo/history/conversations/2026-06/2026-06-22
/apps/bots/b-piccolo/history/files
```

### Norbidel named profile

```text
/apps/bots/b-norbidel
/apps/bots/b-norbidel/history
```

---

## Tests added

### `tests/bots-history-layout.test.ts`

Covers UI/layout regressions:

- command center exists
- History defaults to Conversation History
- chat thread uses client-side collapsible bubbles
- file explorer exists
- required styles exist

### `tests/hermes-profile-bots.test.ts`

Covers profile discovery regressions:

- named profiles become bots
- default root Hermes profile becomes `b-skander`
- `b-skander` maps to Hermes root, not `profiles/skander`
- model/provider metadata is read from `config.yaml`
- Bot app fallback list uses local Hermes profiles

---

## Verification performed

Commands executed successfully during implementation:

```text
pnpm exec vitest run tests/bots-history-layout.test.ts
pnpm exec vitest run tests/hermes-profile-bots.test.ts
pnpm --filter @saikan/web typecheck
pnpm test
pnpm build
```

Observed results:

```text
27 test files passed
104 tests passed
10 build tasks successful
```

Browser verification performed:

```text
/apps/bots
/apps/bots/b-skander/history
/apps/bots/b-norbidel/history
/apps/bots/b-piccolo/history/conversations/2026-06/2026-06-22
/apps/bots/b-piccolo/history/files
```

Browser console on verified pages:

```text
0 JS errors
```

Build note: Next/Turbopack still emits an existing NFT tracing warning involving `apps/web/app/api/bots/history/route.ts`. The build passes; this warning is not caused by the Bot History UI/profile discovery changes.

---

## Known limitations

1. **Local bridge, not canonical storage.**  
   The current visible system reads local Hermes files/state. The final production system should persist messages/file metadata to Supabase and file bytes to Supabase Storage.

2. **Cached file direction is approximate.**  
   Files found only in `cache/documents/` are currently treated as `inbound`. Exact direction requires live capture from Telegram/gateway events.

3. **No automatic Supabase migration application yet.**  
   Supabase schema/storage work exists in code/migrations, but remote application depends on available DB credentials/environment.

4. **Profile naming is derived from filesystem names.**  
   Example: `central-mensageiros` becomes `Central Mensageiros`. A future DB registry can provide richer display names, avatars, ownership, and permissions.

5. **Only local machine profiles are visible in local fallback mode.**  
   This is correct for the current development environment. A server deployment should read from canonical DB/storage rather than a developer machine's Hermes home.

---

## Operational rules for future changes

- Do not hard-code `b-piccolo` as the only history source.
- Do not scan only `profiles/`; include root Hermes as `b-skander`.
- Do not move interactive state into the main server component. Keep interactive UI in small `"use client"` subcomponents.
- Do not replace real Hermes history with mock data if `state.db` exists.
- Keep file preview/download path checks in place; never serve arbitrary absolute paths.
- If changing date/path formats, update both writer/exporter and reader/UI expectations together.
- If adding Supabase live capture, keep local fallback until server capture is fully verified.

---

## Quick troubleshooting

### Bot missing from `/apps/bots`

Check whether its Hermes profile has either:

```text
state.db
config.yaml
```

For named profiles:

```text
C:\Users\rafaa\AppData\Local\hermes\profiles\<profile>\state.db
```

For default Skander:

```text
C:\Users\rafaa\AppData\Local\hermes\state.db
```

### History page empty

Check:

```text
<profileHome>/sessions/sessions.json
<profileHome>/state.db
```

The exporter only builds Telegram history when Telegram session metadata exists in `sessions.json` and matching active user/assistant messages exist in `state.db`.

### File preview 404

Check:

```text
<profileHome>/cache/documents/
```

Then confirm the file ID exists in:

```text
getLocalHermesBotHistory(botId).files
```

### File preview 403

The file path failed the safety check. This usually means the stored file path is outside:

```text
<profileHome>/cache/documents/
```

That is intentional protection.
