Skip to content

feat(openclaw): load chat history on agent chat page#790

Open
shivammittal274 wants to merge 1 commit intodevfrom
feat/openclaw-chat-history
Open

feat(openclaw): load chat history on agent chat page#790
shivammittal274 wants to merge 1 commit intodevfrom
feat/openclaw-chat-history

Conversation

@shivammittal274
Copy link
Copy Markdown
Contributor

Summary

  • Add listSessions() and getChatHistory() to OpenClawCliClient using existing podman exec CLI pattern
  • Add GET /claw/agents/:id/history route that fetches sessions + chat history, filters system noise, and returns unified entries
  • AgentChat loads history on mount and displays past conversations

How it works

  • listSessions() runs sessions --json (file-based, no gateway auth needed)
  • getChatHistory() runs gateway call chat.history (gateway WS RPC via CLI)
  • Server-side filtering removes: HEARTBEAT responses, system event triggers, context-replay messages (extracts actual user message from context bundles)
  • Frontend renders user messages + assistant text responses (no tool cards in history, matching Telegram display)

Known limitations

  • Context-replay extraction is pattern-based ([Current message - respond to this] marker) — could break if OpenClaw changes the format
  • chat.history mixes user content with OpenClaw internal messages — long-term solution should track conversations locally in BrowserOS rather than reconstructing from OpenClaw's session format
  • No cron-specific session display yet (cron messages go into the user-chat session)

Test plan

  • Open agent chat → history loads with past conversations
  • Heartbeat/system messages filtered out
  • User messages extracted correctly from context replays
  • New messages via SSE still work after history loads
  • Empty history (fresh setup) shows empty chat correctly

🤖 Generated with Claude Code

- Add listSessions() and getChatHistory() to OpenClawCliClient
- listSessions: runs `sessions --json` (file-based, no gateway auth)
- getChatHistory: runs `gateway call chat.history` (gateway WS RPC)
- Add GET /claw/agents/:id/history route with server-side filtering
  of heartbeats, system events, and context-replay extraction
- AgentChat loads history on mount, shows user messages and assistant
  text responses (no tool cards in history, matching Telegram display)
- Handles proactive/cron responses without user messages
@github-actions
Copy link
Copy Markdown
Contributor

✅ Tests passed — 798/801

Suite Passed Failed Skipped
agent-sdk 44/44 0 0
agent 20/20 0 0
build 7/7 0 0
eval 8/8 0 0
server-agent 261/261 0 0
server-api 134/134 0 0
server-browser 3/3 0 0
server-integration 9/9 0 0
server-root 39/42 0 3
server-sdk 11/11 0 0
server-skills 31/31 0 0
server-tools 231/231 0 0

View workflow run

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Apr 23, 2026

Greptile Summary

This PR adds chat history loading to the AgentChat page by wiring two new CLI methods (listSessions, getChatHistory) through a service layer and a new GET /claw/agents/:id/history route, then rendering past turns on mount behind a loading spinner. The server-side filterSystemMessages function handles the three known noise categories (context replays, heartbeat responses, system event triggers), and the frontend historyToTurns helper maps filtered messages into the existing ChatTurn shape with source labelling.

Confidence Score: 5/5

Safe to merge; all remaining findings are P2 style/minor-logic suggestions that do not block correctness on the primary path.

No P0 or P1 defects found. The three P2 findings (session sort key, limit=0 edge case, missing assertGatewayReady comment) are quality improvements that don't affect the happy path or cause data loss.

AgentChat.tsx (sort key fix) and openclaw.ts (limit parsing) have the most actionable P2 suggestions.

Important Files Changed

Filename Overview
packages/browseros-agent/apps/agent/entrypoints/app/agents/AgentChat.tsx Adds history loading on mount with a loading-spinner gate; minor issue: sort key uses optional message timestamp instead of reliable session.updatedAt
packages/browseros-agent/apps/server/src/api/routes/openclaw.ts New GET /agents/:id/history route with session filtering and system-noise removal; limit=0 edge case and redundant filter are minor issues
packages/browseros-agent/apps/server/src/api/services/openclaw/openclaw-cli-client.ts Adds listSessions/getChatHistory CLI wrappers with safe typed-mapping helpers; consistent with existing parsing patterns
packages/browseros-agent/apps/server/src/api/services/openclaw/openclaw-service.ts Thin service wrappers; listSessions intentionally skips assertGatewayReady without a comment, creating a confusing asymmetry
packages/browseros-agent/apps/agent/entrypoints/app/agents/useOpenClaw.ts Adds exported types and fetchAgentHistory helper; clean and consistent with existing patterns

Sequence Diagram

sequenceDiagram
    participant UI as AgentChat (React)
    participant Hook as useOpenClaw
    participant Server as /claw/agents/:id/history
    participant Svc as OpenClawService
    participant CLI as OpenClawCliClient (podman exec)

    UI->>Hook: fetchAgentHistory(agentId)
    Hook->>Server: GET /claw/agents/:id/history?limit=10
    Server->>Svc: listSessions(agentId)
    Svc->>CLI: sessions --json --agent id
    CLI-->>Svc: OpenClawSessionEntry[]
    Svc-->>Server: filtered & sorted sessions
    loop Per session (Promise.all)
        Server->>Svc: getChatHistory(sessionKey)
        Svc->>CLI: gateway call chat.history --params
        CLI-->>Svc: OpenClawChatMessage[]
        Svc-->>Server: raw messages
        Server->>Server: filterSystemMessages()
    end
    Server-->>Hook: { entries: ChatHistoryEntry[] }
    Hook-->>UI: ChatHistoryEntry[]
    UI->>UI: historyToTurns() → setTurns()
    UI->>UI: setHistoryLoaded(true) → render history
Loading
Prompt To Fix All With AI
This is a comment left during a code review.
Path: packages/browseros-agent/apps/agent/entrypoints/app/agents/AgentChat.tsx
Line: 59-63

Comment:
**Sort key falls back to 0 for all timestamp-less sessions**

Sessions are sorted by `messages[0]?.timestamp ?? 0`, but `timestamp` is an optional field on `ChatHistoryMessage`, so every session without timestamps collapses to the same key and ordering becomes undefined. The session object already carries a reliable `updatedAt` field — prefer that as the sort key.

```suggestion
  const sorted = [...entries].sort((a, b) => {
    const aTs = a.session.updatedAt ?? a.messages[0]?.timestamp ?? 0
    const bTs = b.session.updatedAt ?? b.messages[0]?.timestamp ?? 0
    return aTs - bTs
  })
```

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: packages/browseros-agent/apps/server/src/api/routes/openclaw.ts
Line: 413

Comment:
**`limit=0` silently becomes 10**

`Number('0') || 10` evaluates to `10` because `0` is falsy. A caller explicitly passing `?limit=0` would get 10 sessions back, not 0. Use `Number(...) || 10` only when `NaN` is the expected bad input — otherwise check for `NaN` explicitly.

```suggestion
      const limitParam = Number(c.req.query('limit'))
      const limit = Number.isNaN(limitParam) || limitParam <= 0 ? 10 : limitParam
```

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: packages/browseros-agent/apps/server/src/api/routes/openclaw.ts
Line: 416-420

Comment:
**Redundant filter after agent-scoped `listSessions` call**

`listSessions(id)` already passes `--agent ${id}` to the CLI, so the result should already be scoped to that agent. The subsequent `.filter((s) => s.agentId === id || s.key.includes(...))` is a no-op in the happy path but could silently drop sessions if the CLI returns them with an unexpected `agentId` format. If the filter is meant as a defensive guard, a brief comment would clarify intent; if the CLI guarantee is reliable, the second filter can be removed.

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: packages/browseros-agent/apps/server/src/api/services/openclaw/openclaw-service.ts
Line: 580-583

Comment:
**`listSessions` skips `assertGatewayReady` — intentional but worth a comment**

Every other service method that invokes the CLI client goes through `assertGatewayReady()` (e.g., `listAgents`, `getChatHistory`). The PR description says session listing is "file-based, no gateway auth needed", which would justify skipping the check — but the asymmetry is surprising to future readers. A short inline comment would prevent accidental re-introduction of the guard or its unintentional removal elsewhere.

How can I resolve this? If you propose a fix, please make it concise.

Reviews (1): Last reviewed commit: "feat(openclaw): load chat history on age..." | Re-trigger Greptile

Comment on lines +59 to +63
const sorted = [...entries].sort((a, b) => {
const aTs = a.messages[0]?.timestamp ?? 0
const bTs = b.messages[0]?.timestamp ?? 0
return aTs - bTs
})
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Sort key falls back to 0 for all timestamp-less sessions

Sessions are sorted by messages[0]?.timestamp ?? 0, but timestamp is an optional field on ChatHistoryMessage, so every session without timestamps collapses to the same key and ordering becomes undefined. The session object already carries a reliable updatedAt field — prefer that as the sort key.

Suggested change
const sorted = [...entries].sort((a, b) => {
const aTs = a.messages[0]?.timestamp ?? 0
const bTs = b.messages[0]?.timestamp ?? 0
return aTs - bTs
})
const sorted = [...entries].sort((a, b) => {
const aTs = a.session.updatedAt ?? a.messages[0]?.timestamp ?? 0
const bTs = b.session.updatedAt ?? b.messages[0]?.timestamp ?? 0
return aTs - bTs
})
Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/browseros-agent/apps/agent/entrypoints/app/agents/AgentChat.tsx
Line: 59-63

Comment:
**Sort key falls back to 0 for all timestamp-less sessions**

Sessions are sorted by `messages[0]?.timestamp ?? 0`, but `timestamp` is an optional field on `ChatHistoryMessage`, so every session without timestamps collapses to the same key and ordering becomes undefined. The session object already carries a reliable `updatedAt` field — prefer that as the sort key.

```suggestion
  const sorted = [...entries].sort((a, b) => {
    const aTs = a.session.updatedAt ?? a.messages[0]?.timestamp ?? 0
    const bTs = b.session.updatedAt ?? b.messages[0]?.timestamp ?? 0
    return aTs - bTs
  })
```

How can I resolve this? If you propose a fix, please make it concise.


.get('/agents/:id/history', async (c) => {
const { id } = c.req.param()
const limit = Number(c.req.query('limit')) || 10
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 limit=0 silently becomes 10

Number('0') || 10 evaluates to 10 because 0 is falsy. A caller explicitly passing ?limit=0 would get 10 sessions back, not 0. Use Number(...) || 10 only when NaN is the expected bad input — otherwise check for NaN explicitly.

Suggested change
const limit = Number(c.req.query('limit')) || 10
const limitParam = Number(c.req.query('limit'))
const limit = Number.isNaN(limitParam) || limitParam <= 0 ? 10 : limitParam
Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/browseros-agent/apps/server/src/api/routes/openclaw.ts
Line: 413

Comment:
**`limit=0` silently becomes 10**

`Number('0') || 10` evaluates to `10` because `0` is falsy. A caller explicitly passing `?limit=0` would get 10 sessions back, not 0. Use `Number(...) || 10` only when `NaN` is the expected bad input — otherwise check for `NaN` explicitly.

```suggestion
      const limitParam = Number(c.req.query('limit'))
      const limit = Number.isNaN(limitParam) || limitParam <= 0 ? 10 : limitParam
```

How can I resolve this? If you propose a fix, please make it concise.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant