Skip to content

feat(session): add includeArchived to chat.history and sessions_history#73883

Open
CadanHu wants to merge 3 commits into
openclaw:mainfrom
CadanHu:feat/sessions-history-include-archived
Open

feat(session): add includeArchived to chat.history and sessions_history#73883
CadanHu wants to merge 3 commits into
openclaw:mainfrom
CadanHu:feat/sessions-history-include-archived

Conversation

@CadanHu

@CadanHu CadanHu commented Apr 29, 2026

Copy link
Copy Markdown

Summary

Implements the read-only path of #45003's 3-PR roadmap: give agents and chat.history callers a way to recover context that lives in .reset.<ts> archives, without resurrecting those archives as live sessions.

This PR closes the three concrete upstream gaps identified by clawsweeper / Codex review on issue #45003 (2026-04-28T22:20:54Z):

  • sessions_history has no archive selector
  • chat.history reads current active transcript only
  • Protocol schema has no archived-history parameter

Changes

Protocol (additive, backwards-compatible)

  • src/gateway/protocol/schema/logs-chat.tsChatHistoryParamsSchema gains an optional includeArchived: boolean. Default false preserves all current behavior.

Gateway helpers

  • src/gateway/session-transcript-files.fs.ts — new listResetArchivesForSession(sessionId, searchDirs) lists every <sessionId>.jsonl.reset.<ts> archive sorted oldest-first.
  • src/gateway/session-utils.fs.ts — new readSessionMessagesIncludingArchives(sessionId, storePath, sessionFile) chains archive segments before the primary transcript with synthetic system messages tagged __openclaw.kind: "session-reset" so callers can render dividers. Archive segments are parsed line-by-line; the primary segment delegates to the existing readSessionMessages so it still gets SessionManager active-branch treatment for tree-format transcripts.

Gateway handler

  • src/gateway/server-methods/chat.tschat.history reads includeArchived from params and dispatches to the new aggregator only when explicitly true.

Agent tool

  • src/agents/tools/sessions-history-tool.tsSessionsHistoryToolSchema gains optional includeArchived. The tool forwards the flag verbatim to the chat.history call.

Tests

  • src/gateway/session-utils.fs.test.ts — 6 new tests:
    • default behavior unchanged when no archives exist
    • archive content surfaced when primary missing
    • multiple archives chained chronologically before primary, each with a session-reset boundary marker
    • empty result when neither primary nor any archive exists
    • other sessions' archives are not pulled in
    • empty sessionId short-circuits
  • src/agents/tools/sessions-history-tool.test.ts — 2 new tests verifying the param is plumbed through from tool → chat.history.

Design notes

This is the read-only path of the #45003 roadmap. It does not rename, move, or otherwise mutate archive files: archives stay archived, the user opts into visibility for one query at a time. That keeps it consistent with the existing "archive lifecycle is one-way" precedent (sessions.delete has no undo) and avoids the compaction-chain checkpoint and session-memory-hook double-summary risks that come with any unarchive/restore design.

includeArchived is also a user-explicit opt-in (not an automatic fallback), so it is consistent with /reset semantics: the user is asking for the recovery view, not having archived content silently re-injected into automated paths.

Verification

  • Scoped tests: pnpm test src/gateway/session-utils.fs.test.ts — 60/60 pass (54 originals + 6 new).
  • Type-check: pnpm tsgo:core introduces 0 new errors on the touched files (verified by stash-toggle baseline comparison).
  • Manual: production gateway built from this branch and started on :18789 against the local production data dir; chat.history RPC with includeArchived: true returns chained archive segments with session-reset boundary markers, while the default (false / omitted) returns the same set as before.

Related work

Implements PR 3 of the issue openclaw#45003 archive UX roadmap (read-only
path): give agents and the chat.history endpoint a way to recover
context that lives in .reset.<ts> archives, without resurrecting
those archives as live sessions.

Changes
-------

Protocol (additive, backwards-compatible):
- src/gateway/protocol/schema/logs-chat.ts:
  ChatHistoryParamsSchema gains an optional includeArchived: boolean.
  Default false preserves all current behavior.

Gateway helpers:
- src/gateway/session-transcript-files.fs.ts:
  New listResetArchivesForSession(sessionId, searchDirs) lists every
  <sessionId>.jsonl.reset.<ts> archive sorted oldest-first.
- src/gateway/session-utils.fs.ts:
  New readSessionMessagesIncludingArchives(sessionId, storePath, sessionFile)
  chains archive segments before the primary transcript with
  synthetic system messages tagged __openclaw.kind: "session-reset"
  so the Web UI / agent can render boundaries. Archive segments are
  parsed line-by-line; the primary segment delegates to the existing
  readSessionMessages so it still gets SessionManager active-branch
  treatment for tree-format transcripts.

Gateway handler:
- src/gateway/server-methods/chat.ts:
  chat.history reads includeArchived from params and dispatches to
  the new aggregator only when explicitly true.

Agent tool:
- src/agents/tools/sessions-history-tool.ts:
  SessionsHistoryToolSchema gains optional includeArchived. The tool
  forwards the flag verbatim to the chat.history call.

Tests
-----

src/gateway/session-utils.fs.test.ts (6 new tests):
- Default behavior unchanged when no archives exist
- Archive content surfaced when primary missing
- Multiple archives chained chronologically before primary, each with
  a session-reset boundary marker
- Empty result when neither primary nor any archive exists
- Other sessions' archives are not pulled in
- Empty sessionId short-circuits

src/agents/tools/sessions-history-tool.test.ts (2 new tests):
- includeArchived=true forwards through to chat.history
- includeArchived omitted forwards as false

Design notes
------------

This is the read-only path of the openclaw#45003 roadmap. It does not rename,
move, or otherwise mutate archive files: archives stay archived, the
user opts into visibility for one query at a time. That keeps it
consistent with existing "archive lifecycle is one-way" precedent
(sessions.delete has no undo) and removes the compaction-chain
checkpoint and session-memory-hook double-summary risks that come
with any unarchive/restore design.

Refs
----

Issue: openclaw#45003
Companion PRs in the same area:
- openclaw#60409 (chat.history fallback to most recent reset archive when
  primary is missing) — different code path, no overlap
- openclaw#71537 (session-memory hook + session-logs skill archive support) —
  different code path, no overlap

Co-Authored-By: Claude Opus 4.7 <[email protected]>
@openclaw-barnacle openclaw-barnacle Bot added app: web-ui App: web-ui gateway Gateway runtime agents Agent runtime and tooling size: M labels Apr 29, 2026
@greptile-apps

greptile-apps Bot commented Apr 29, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

Adds optional includeArchived: boolean to chat.history and sessions_history so callers can opt into a chained view of all .reset.<ts> archive segments, with synthetic session-reset boundary markers between segments. The change is fully additive and backwards-compatible: the default false preserves existing behaviour. Implementation is clean and well-tested (6 new unit tests for the core aggregator, 2 for tool plumbing).

Confidence Score: 4/5

Safe to merge; the one flagged issue is a non-blocking P2 about seq uniqueness that won't affect existing callers.

No P0 or P1 issues found. The single P2 concern (non-unique __openclaw.seq across chained archive segments) is speculative and only matters if a consumer treats seq as a globally unique identifier, which is a new use-case introduced by this feature.

src/gateway/session-utils.fs.ts — readArchivedTranscriptMessages local seq counter

Prompt To Fix All With AI
This is a comment left during a code review.
Path: src/gateway/session-utils.fs.ts
Line: 213-257

Comment:
**`seq` resets to 0 for every archive segment**

`messageSeq` is declared locally inside `readArchivedTranscriptMessages` and restarts at 1 for each archive file. When `readSessionMessagesIncludingArchives` chains N archives plus the primary, the combined result contains duplicate `__openclaw.seq` values — e.g., the first message from archive-1 and the first message from archive-2 both carry `seq: 1`. If any consumer (Web UI renderer, deduplication logic, React key prop) relies on `seq` being unique across the combined list, this will produce silent bugs or duplicate-key warnings.

Consider threading a shared offset into the helper so the combined output has monotonically increasing `seq` across all segments:

```typescript
function readArchivedTranscriptMessages(filePath: string, seqOffset = 0): { messages: unknown[]; nextSeq: number } {
  // ...
  let messageSeq = seqOffset;
  // ...
  return { messages, nextSeq: messageSeq };
}
```

And in `readSessionMessagesIncludingArchives`:
```typescript
let seqOffset = 0;
for (const archive of archives) {
  const { messages, nextSeq } = readArchivedTranscriptMessages(archive.path, seqOffset);
  combined.push(...messages);
  seqOffset = nextSeq;
  // push boundary marker ...
}
```

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

Reviews (1): Last reviewed commit: "feat(session): add includeArchived to ch..." | Re-trigger Greptile

Comment on lines +213 to +257
function readArchivedTranscriptMessages(filePath: string): unknown[] {
let lines: string[];
try {
lines = fs.readFileSync(filePath, "utf-8").split(/\r?\n/);
} catch {
return [];
}
const messages: unknown[] = [];
let messageSeq = 0;
for (const line of lines) {
if (!line.trim()) {
continue;
}
try {
const parsed = JSON.parse(line);
if (parsed?.message) {
messageSeq += 1;
messages.push(
attachOpenClawTranscriptMeta(parsed.message, {
...(typeof parsed.id === "string" ? { id: parsed.id } : {}),
seq: messageSeq,
}),
);
continue;
}
if (parsed?.type === "compaction") {
const ts = typeof parsed.timestamp === "string" ? Date.parse(parsed.timestamp) : Number.NaN;
const timestamp = Number.isFinite(ts) ? ts : Date.now();
messageSeq += 1;
messages.push({
role: "system",
content: [{ type: "text", text: "Compaction" }],
timestamp,
__openclaw: {
kind: "compaction",
id: typeof parsed.id === "string" ? parsed.id : undefined,
seq: messageSeq,
},
});
}
} catch {
// ignore malformed lines
}
}
return messages;

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 seq resets to 0 for every archive segment

messageSeq is declared locally inside readArchivedTranscriptMessages and restarts at 1 for each archive file. When readSessionMessagesIncludingArchives chains N archives plus the primary, the combined result contains duplicate __openclaw.seq values — e.g., the first message from archive-1 and the first message from archive-2 both carry seq: 1. If any consumer (Web UI renderer, deduplication logic, React key prop) relies on seq being unique across the combined list, this will produce silent bugs or duplicate-key warnings.

Consider threading a shared offset into the helper so the combined output has monotonically increasing seq across all segments:

function readArchivedTranscriptMessages(filePath: string, seqOffset = 0): { messages: unknown[]; nextSeq: number } {
  // ...
  let messageSeq = seqOffset;
  // ...
  return { messages, nextSeq: messageSeq };
}

And in readSessionMessagesIncludingArchives:

let seqOffset = 0;
for (const archive of archives) {
  const { messages, nextSeq } = readArchivedTranscriptMessages(archive.path, seqOffset);
  combined.push(...messages);
  seqOffset = nextSeq;
  // push boundary marker ...
}
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/gateway/session-utils.fs.ts
Line: 213-257

Comment:
**`seq` resets to 0 for every archive segment**

`messageSeq` is declared locally inside `readArchivedTranscriptMessages` and restarts at 1 for each archive file. When `readSessionMessagesIncludingArchives` chains N archives plus the primary, the combined result contains duplicate `__openclaw.seq` values — e.g., the first message from archive-1 and the first message from archive-2 both carry `seq: 1`. If any consumer (Web UI renderer, deduplication logic, React key prop) relies on `seq` being unique across the combined list, this will produce silent bugs or duplicate-key warnings.

Consider threading a shared offset into the helper so the combined output has monotonically increasing `seq` across all segments:

```typescript
function readArchivedTranscriptMessages(filePath: string, seqOffset = 0): { messages: unknown[]; nextSeq: number } {
  // ...
  let messageSeq = seqOffset;
  // ...
  return { messages, nextSeq: messageSeq };
}
```

And in `readSessionMessagesIncludingArchives`:
```typescript
let seqOffset = 0;
for (const archive of archives) {
  const { messages, nextSeq } = readArchivedTranscriptMessages(archive.path, seqOffset);
  combined.push(...messages);
  seqOffset = nextSeq;
  // push boundary marker ...
}
```

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

@clawsweeper

clawsweeper Bot commented Apr 29, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs real behavior proof before merge. Reviewed July 3, 2026, 4:10 PM ET / 20:10 UTC.

Summary
The branch adds an optional includeArchived selector to Gateway chat.history, the sessions_history agent tool, reset-archive transcript helpers, tests, and generated Swift protocol models.

PR surface: Source +168, Tests +183, Other +8. Total +359 across 10 files.

Reproducibility: not applicable. as a bug reproduction; this PR adds a new Gateway/tool selector. Source review confirms current main lacks this exact selector, and the cleanup decision rests on the viable canonical replacement plus this branch's conflict and risk profile.

Review metrics: 2 noteworthy metrics.

  • Archive Visibility Selectors: 2 added. The PR adds one Gateway RPC parameter and one model-facing tool parameter that change who can request reset archive content.
  • Generated Native Request Surface: 2 Swift model copies changed. The generated native clients would expose the new request field after release, making API spelling and default behavior compatibility-sensitive.

Stored data model
Persistent data-model change detected: serialized state: src/agents/tools/sessions-history-tool.test.ts, serialized state: src/agents/tools/sessions-history-tool.ts, serialized state: src/gateway/session-transcript-files.fs.ts, serialized state: src/gateway/session-utils.fs.test.ts, serialized state: src/gateway/session-utils.fs.ts, unknown-data-model-change: src/agents/tools/sessions-history-tool.test.ts, and 2 more. Confirm migration or upgrade compatibility proof before merge.

Root-cause cluster
Relationship: superseded
Canonical: #90239
Summary: This PR and the replacement PR address the same archived/reset history retrieval gap, but the replacement PR is the viable canonical landing path because it is current-main, mergeable, proof-positive, bounded, and header-checked.

Members:

Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything.

Merge readiness
Overall: 🧂 unranked krab
Proof: 🧂 unranked krab
Patch quality: 🦪 silver shellfish
Result: blocked until real behavior proof is added.

Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch.

Rank-up moves:

Proof guidance:

  • [P1] Needs real behavior proof before merge: The PR body claims a production Gateway RPC check, but it does not include redacted terminal output, logs, linked artifacts, copied live output, or a recording showing the after-fix behavior. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.

Risk before merge

  • [P1] Merging this branch would create a broad public includeArchived API and model-facing tool parameter that exposes reset archive content without the replacement PR's accepted bounded family-history contract.
  • [P1] The branch is currently conflicting and still lacks inspected real behavior proof, so it is no longer a safe landing path compared with the clean, proof-sufficient replacement PR.

Maintainer options:

  1. Close In Favor Of Family History (recommended)
    Use the open includeFamily replacement PR as the canonical implementation because it is clean, proof-sufficient, bounded, and header-checked.
  2. Rebuild Only If The API Direction Changes
    If maintainers intentionally reject includeFamily, ask for a fresh narrow PR on current main rather than repairing this conflicting branch.
  3. Accept Broad Archive Access Explicitly
    Maintainers could choose a broader includeArchived API, but that needs a new design with bounded reads, ownership checks, generated clients, docs, and proof.

Next step before merge

  • [P1] No repair lane is useful because this PR should close in favor of the open canonical replacement rather than be patched on its conflicting branch.

Security
Needs attention: The diff is security-sensitive because it can expose archived reset transcript content through a model-facing history tool.

Review details

Best possible solution:

Keep this PR open until a linked canonical PR proves it covers this PR's unique work, or a maintainer confirms closure.

Do we have a high-confidence way to reproduce the issue?

Not applicable as a bug reproduction; this PR adds a new Gateway/tool selector. Source review confirms current main lacks this exact selector, and the cleanup decision rests on the viable canonical replacement plus this branch's conflict and risk profile.

Is this the best way to solve the issue?

No. The recovery need is valid, but this branch is no longer the best solution because the replacement PR provides a current-main, bounded, header-checked includeFamily contract with positive proof.

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against 5361e5a0b455.

Label changes

Label justifications:

  • P2: Archived session-history recovery is a normal-priority session-state improvement with meaningful but bounded user impact.
  • merge-risk: 🚨 compatibility: The PR adds a public Gateway parameter, a model-facing tool parameter, and generated native client fields that callers could rely on after release.
  • merge-risk: 🚨 session-state: The PR changes how reset archive transcripts can be associated with live session history and agent recall.
  • merge-risk: 🚨 security-boundary: The PR exposes archived reset content through a model-facing tool parameter without the accepted bounded family/ownership boundary used by the replacement path.
  • rating: 🧂 unranked krab: Overall readiness is 🧂 unranked krab; proof is 🧂 unranked krab and patch quality is 🦪 silver shellfish.
  • status: 📣 needs proof: The PR needs real behavior proof before ClawSweeper can clear the contributor ask. Needs real behavior proof before merge: The PR body claims a production Gateway RPC check, but it does not include redacted terminal output, logs, linked artifacts, copied live output, or a recording showing the after-fix behavior. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.
Evidence reviewed

PR surface:

Source +168, Tests +183, Other +8. Total +359 across 10 files.

View PR surface stats
Area Files Added Removed Net
Source 6 171 3 +168
Tests 2 183 0 +183
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 2 10 2 +8
Total 10 364 5 +359

Security concerns:

  • [medium] Archived reset context becomes agent-readable — src/agents/tools/sessions-history-tool.ts:34
    Adding includeArchived to sessions_history lets a model request reset archive content without the bounded, header-checked family/ownership boundary used by the replacement path.
    Confidence: 0.88

What I checked:

  • PR close coverage proof: PR close coverage proof kept this PR open against [AI-assisted] Add session history family lookup #90239: model PR close coverage proof was incomplete: PR B clearly carries PR A's useful archived/reset history retrieval intent and is the current, better-bounded canonical review path; PR A's different API spelling and broader implementation details are not material independent work that still needs separate review.
  • Repository policy applied: Read the full root AGENTS.md plus scoped Gateway and agent-tool guides; session history, protocol parameters, model-facing tools, archived transcript exposure, and bounded reads are review-sensitive under this repo policy. (AGENTS.md:1, 5361e5a0b455)
  • Current main protocol surface: Current main validates chat.history in the protocol package with sessionKey, optional agentId, limit, offset, and maxChars; it has no includeArchived selector and the old src/gateway/protocol path is not the active source used by current main. (packages/gateway-protocol/src/schema/logs-chat.ts:30, 5361e5a0b455)
  • Current main tool surface: Current sessions_history accepts sessionKey, limit, offset, and includeTools, then forwards only sessionKey, limit, and optional offset to chat.history; there is no current-main includeArchived tool parameter. (src/agents/tools/sessions-history-tool.ts:38, 5361e5a0b455)
  • Current main reset fallback boundary: Current main already has active-missing reset archive fallback, but it returns the active transcript first and only consults reset archives when the active path is absent, so it does not implement this PR's chained archived-history selector. (src/gateway/session-utils.fs.ts:1191, 5361e5a0b455)
  • Current main archive ownership guardrails: Current reset-archive discovery is bounded and header-checks custom/topic archives before accepting them, which is the safer boundary the canonical replacement builds on. (src/gateway/session-transcript-files.fs.ts:179, 5361e5a0b455)

Likely related people:

  • galiniliev: Recent merged work added sessions_history offset pagination and touched the same tool, Gateway history, generated protocol, and prompt-snapshot surfaces that the replacement PR builds on. (role: recent area contributor; confidence: high; commits: dc575d148a7c; files: src/agents/tools/sessions-history-tool.ts, src/gateway/server-methods/chat.ts, packages/gateway-protocol/src/schema/logs-chat.ts)
  • vincentkoc: Recent history shows repeated work in reset archive candidate handling, generated transcript parsing, and session/gateway surfaces adjacent to this archived-history boundary. (role: recent session/gateway contributor; confidence: medium; commits: 2052a3bf4e53, d7cebdc215bf, b9e8e6d66ea6; files: src/gateway/session-transcript-files.fs.ts, src/gateway/session-utils.fs.ts)
  • jalehman: Merged history shows transcript-reader seam work across the active history files that current chat.history and sessions_history consume. (role: transcript reader seam contributor; confidence: medium; commits: 8ded75628437; files: src/gateway/session-utils.fs.ts, src/gateway/server-methods/chat.ts, src/agents/tools/sessions-history-tool.ts)
  • masatohoshino: The landed active-missing reset archive fallback credits this contributor and is directly adjacent to the archived-history retrieval problem. (role: reset archive fallback contributor; confidence: medium; commits: b9e8e6d66ea6; files: src/gateway/session-utils.fs.ts, src/gateway/session-transcript-files.fs.ts, src/gateway/sessions-history-http.ts)
What the crustacean ranks mean
  • 🦀 challenger crab: rare, exceptional readiness with strong proof, clean implementation, and convincing validation.
  • 🦞 diamond lobster: very strong readiness with only minor maintainer review expected.
  • 🐚 platinum hermit: good normal PR, likely mergeable with ordinary maintainer review.
  • 🦐 gold shrimp: useful signal, but proof or patch confidence is still limited.
  • 🦪 silver shellfish: thin signal; proof, validation, or implementation needs work.
  • 🧂 unranked krab: not merge-ready because proof is missing/unusable or there are serious correctness or safety concerns.
  • 🌊 off-meta tidepool: rating does not apply to this item.

Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

How this review workflow works
  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.
Review history (1 earlier review cycle)
  • reviewed 2026-06-29T07:26:05.994Z sha 5eeba23 :: needs real behavior proof before merge. :: [P1] Gate archived history before exposing it to agents | [P1] Keep archive history reads bounded | [P2] Move the schema change to the current protocol package

…udeArchived

Updates the auto-generated GatewayModels.swift in both
apps/macos and apps/shared/OpenClawKit to reflect the new
optional includeArchived field added to ChatHistoryParamsSchema.

Run: pnpm protocol:gen && pnpm protocol:gen:swift

Co-Authored-By: Claude Opus 4.7 <[email protected]>
@openclaw-barnacle openclaw-barnacle Bot added the app: macos App: macos label Apr 29, 2026
The previous run failed in command-execution-startup.test.ts:75
(banner-emit assertion) which passes locally 3/3 — flaky CI run.

Co-Authored-By: Claude Opus 4.7 <[email protected]>
masatohoshino added a commit to masatohoshino/openclaw that referenced this pull request May 2, 2026
…reads when active is missing

When the active session transcript file is missing, async read paths used by
chat.history (`readSessionMessagesAsync`, `readRecentSessionMessagesAsync`)
and the session title/preview lookup
(`readSessionTitleFieldsFromTranscriptAsync`) returned an empty result instead
of any archived content. Reset rotates the active `<id>.jsonl` to a sibling
`<id>.jsonl.reset.<UTC timestamp>` and creates a new empty active file, so
sessions reset before the next read became invisible through the production
async API even when the archive still existed on disk.

This change adds a narrow auto-fallback on the async paths only:

- New `findLatestResetArchiveAsync(candidatePaths)` helper in
  `session-transcript-files.fs.ts` does an async `readdir` of each candidate
  parent directory, validates archive entries via the existing
  `parseSessionArchiveTimestamp(entry, "reset")` primitive, and returns only
  the path with the newest archive timestamp.
- A small internal helper `findActiveOrLatestResetArchiveAsync` in
  `session-utils.fs.ts` reuses the existing active-candidate `existsSync`
  check first; only when no active candidate exists does it consult the new
  helper.
- The three async functions above are re-routed through this helper.

Behavior boundaries (kept narrow on purpose):

- Active wins when present (no active+archive merging).
- Only the single newest archive is returned (no chain aggregation, no
  `previousFragments`, no rolled logical-history reconstruction).
- Sync paths, `findExistingTranscriptPath`, the protocol schema, the
  `includeArchived` opt-in, sessions_history schema, the session-memory hook,
  and the session-logs skill are untouched.

Refs openclaw#56131 openclaw#43929 openclaw#45003 openclaw#60409 openclaw#73883
masatohoshino added a commit to masatohoshino/openclaw that referenced this pull request May 2, 2026
…hive fallback as readers

Codex/clawsweeper review on PR openclaw#76134 found that
`readRecentSessionMessagesWithStatsAsync` still derived
`totalMessages` from `readSessionMessageCountAsync`, which kept the
active-only `findExistingTranscriptPath` resolution. After the new
async reader fallback, an archive-only transcript longer than the
bounded tail window therefore returned the newest messages with low
`seq` values and `hasMore = false`. The sessions-history HTTP first
page passes `boundedSnapshot.totalMessages` into
`buildSessionHistorySnapshot`, so a `nextCursor` follow-up could skip
the middle archive page.

This commit routes the async count and visit helpers
(`readSessionMessageCountAsync` and `visitSessionMessagesAsync`)
through the same `findActiveOrLatestResetArchiveAsync` resolution that
the message readers use, so an archive-only transcript reports a
consistent total, gives messages the right tail-end `seq` values, and
exposes the archive's earlier pages via `nextCursor`.

Behavior boundaries (kept narrow, same as the original PR):

- Active wins when present (no active+archive merging).
- Only the single newest archive is returned (no chain aggregation).
- Sync paths, `findExistingTranscriptPath`, the protocol schema, the
  `includeArchived` opt-in, sessions_history schema, the session-memory
  hook, and the session-logs skill are still untouched.

Tests: 4 new cases in `session-utils.fs.test.ts` cover the count and
stats helpers (active-only, archive-only with archive longer than the
tail window, both-exist active-priority, both-missing) and assert the
returned `seq` metadata matches the archive's tail position. One new
case in `sessions-history-http.test.ts` exercises the archive-only
HTTP pagination path: archive a 3-message transcript, request the
first `limit=2` page, follow `nextCursor`, and assert the older
archive message is visible without skipping middle pages.

Refs openclaw#56131 openclaw#43929 openclaw#45003 openclaw#60409 openclaw#73883
masatohoshino added a commit to masatohoshino/openclaw that referenced this pull request May 17, 2026
…reads when active is missing

When the active session transcript file is missing, async read paths used by
chat.history (`readSessionMessagesAsync`, `readRecentSessionMessagesAsync`)
and the session title/preview lookup
(`readSessionTitleFieldsFromTranscriptAsync`) returned an empty result instead
of any archived content. Reset rotates the active `<id>.jsonl` to a sibling
`<id>.jsonl.reset.<UTC timestamp>` and creates a new empty active file, so
sessions reset before the next read became invisible through the production
async API even when the archive still existed on disk.

This change adds a narrow auto-fallback on the async paths only:

- New `findLatestResetArchiveAsync(candidatePaths)` helper in
  `session-transcript-files.fs.ts` does an async `readdir` of each candidate
  parent directory, validates archive entries via the existing
  `parseSessionArchiveTimestamp(entry, "reset")` primitive, and returns only
  the path with the newest archive timestamp.
- A small internal helper `findActiveOrLatestResetArchiveAsync` in
  `session-utils.fs.ts` reuses the existing active-candidate `existsSync`
  check first; only when no active candidate exists does it consult the new
  helper.
- The three async functions above are re-routed through this helper.

Behavior boundaries (kept narrow on purpose):

- Active wins when present (no active+archive merging).
- Only the single newest archive is returned (no chain aggregation, no
  `previousFragments`, no rolled logical-history reconstruction).
- Sync paths, `findExistingTranscriptPath`, the protocol schema, the
  `includeArchived` opt-in, sessions_history schema, the session-memory hook,
  and the session-logs skill are untouched.

Refs openclaw#56131 openclaw#43929 openclaw#45003 openclaw#60409 openclaw#73883
masatohoshino added a commit to masatohoshino/openclaw that referenced this pull request May 17, 2026
…hive fallback as readers

Codex/clawsweeper review on PR openclaw#76134 found that
`readRecentSessionMessagesWithStatsAsync` still derived
`totalMessages` from `readSessionMessageCountAsync`, which kept the
active-only `findExistingTranscriptPath` resolution. After the new
async reader fallback, an archive-only transcript longer than the
bounded tail window therefore returned the newest messages with low
`seq` values and `hasMore = false`. The sessions-history HTTP first
page passes `boundedSnapshot.totalMessages` into
`buildSessionHistorySnapshot`, so a `nextCursor` follow-up could skip
the middle archive page.

This commit routes the async count and visit helpers
(`readSessionMessageCountAsync` and `visitSessionMessagesAsync`)
through the same `findActiveOrLatestResetArchiveAsync` resolution that
the message readers use, so an archive-only transcript reports a
consistent total, gives messages the right tail-end `seq` values, and
exposes the archive's earlier pages via `nextCursor`.

Behavior boundaries (kept narrow, same as the original PR):

- Active wins when present (no active+archive merging).
- Only the single newest archive is returned (no chain aggregation).
- Sync paths, `findExistingTranscriptPath`, the protocol schema, the
  `includeArchived` opt-in, sessions_history schema, the session-memory
  hook, and the session-logs skill are still untouched.

Tests: 4 new cases in `session-utils.fs.test.ts` cover the count and
stats helpers (active-only, archive-only with archive longer than the
tail window, both-exist active-priority, both-missing) and assert the
returned `seq` metadata matches the archive's tail position. One new
case in `sessions-history-http.test.ts` exercises the archive-only
HTTP pagination path: archive a 3-message transcript, request the
first `limit=2` page, follow `nextCursor`, and assert the older
archive message is visible without skipping middle pages.

Refs openclaw#56131 openclaw#43929 openclaw#45003 openclaw#60409 openclaw#73883
masatohoshino added a commit to masatohoshino/openclaw that referenced this pull request May 17, 2026
…reads when active is missing

When the active session transcript file is missing, async read paths used by
chat.history (`readSessionMessagesAsync`, `readRecentSessionMessagesAsync`)
and the session title/preview lookup
(`readSessionTitleFieldsFromTranscriptAsync`) returned an empty result instead
of any archived content. Reset rotates the active `<id>.jsonl` to a sibling
`<id>.jsonl.reset.<UTC timestamp>` and creates a new empty active file, so
sessions reset before the next read became invisible through the production
async API even when the archive still existed on disk.

This change adds a narrow auto-fallback on the async paths only:

- New `findLatestResetArchiveAsync(candidatePaths)` helper in
  `session-transcript-files.fs.ts` does an async `readdir` of each candidate
  parent directory, validates archive entries via the existing
  `parseSessionArchiveTimestamp(entry, "reset")` primitive, and returns only
  the path with the newest archive timestamp.
- A small internal helper `findActiveOrLatestResetArchiveAsync` in
  `session-utils.fs.ts` reuses the existing active-candidate `existsSync`
  check first; only when no active candidate exists does it consult the new
  helper.
- The three async functions above are re-routed through this helper.

Behavior boundaries (kept narrow on purpose):

- Active wins when present (no active+archive merging).
- Only the single newest archive is returned (no chain aggregation, no
  `previousFragments`, no rolled logical-history reconstruction).
- Sync paths, `findExistingTranscriptPath`, the protocol schema, the
  `includeArchived` opt-in, sessions_history schema, the session-memory hook,
  and the session-logs skill are untouched.

Refs openclaw#56131 openclaw#43929 openclaw#45003 openclaw#60409 openclaw#73883
masatohoshino added a commit to masatohoshino/openclaw that referenced this pull request May 17, 2026
…hive fallback as readers

Codex/clawsweeper review on PR openclaw#76134 found that
`readRecentSessionMessagesWithStatsAsync` still derived
`totalMessages` from `readSessionMessageCountAsync`, which kept the
active-only `findExistingTranscriptPath` resolution. After the new
async reader fallback, an archive-only transcript longer than the
bounded tail window therefore returned the newest messages with low
`seq` values and `hasMore = false`. The sessions-history HTTP first
page passes `boundedSnapshot.totalMessages` into
`buildSessionHistorySnapshot`, so a `nextCursor` follow-up could skip
the middle archive page.

This commit routes the async count and visit helpers
(`readSessionMessageCountAsync` and `visitSessionMessagesAsync`)
through the same `findActiveOrLatestResetArchiveAsync` resolution that
the message readers use, so an archive-only transcript reports a
consistent total, gives messages the right tail-end `seq` values, and
exposes the archive's earlier pages via `nextCursor`.

Behavior boundaries (kept narrow, same as the original PR):

- Active wins when present (no active+archive merging).
- Only the single newest archive is returned (no chain aggregation).
- Sync paths, `findExistingTranscriptPath`, the protocol schema, the
  `includeArchived` opt-in, sessions_history schema, the session-memory
  hook, and the session-logs skill are still untouched.

Tests: 4 new cases in `session-utils.fs.test.ts` cover the count and
stats helpers (active-only, archive-only with archive longer than the
tail window, both-exist active-priority, both-missing) and assert the
returned `seq` metadata matches the archive's tail position. One new
case in `sessions-history-http.test.ts` exercises the archive-only
HTTP pagination path: archive a 3-message transcript, request the
first `limit=2` page, follow `nextCursor`, and assert the older
archive message is visible without skipping middle pages.

Refs openclaw#56131 openclaw#43929 openclaw#45003 openclaw#60409 openclaw#73883
@openclaw-barnacle

Copy link
Copy Markdown

This pull request has been automatically marked as stale due to inactivity.
Please add updates or it will be closed.

@openclaw-barnacle

Copy link
Copy Markdown

This pull request has been automatically marked as stale due to inactivity.
Please add updates or it will be closed.

@openclaw-barnacle openclaw-barnacle Bot added the stale Marked as stale due to inactivity label Jun 14, 2026
@clawsweeper clawsweeper Bot added rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. P2 Normal backlog priority with limited blast radius. merge-risk: 🚨 session-state 🚨 May lose, corrupt, stale, or mis-associate session, agent, or context state. merge-risk: 🚨 security-boundary 🚨 May affect sandboxing, authorization, credentials, or sensitive data. merge-risk: 🚨 availability 🚨 May cause crashes, hangs, restart loops, stalls, or process outages. and removed rating: 🌊 off-meta tidepool PR readiness rating does not apply to this item. labels Jun 14, 2026
@openclaw-barnacle openclaw-barnacle Bot removed the stale Marked as stale due to inactivity label Jun 15, 2026
@clawsweeper clawsweeper Bot added merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. and removed merge-risk: 🚨 availability 🚨 May cause crashes, hangs, restart loops, stalls, or process outages. labels Jun 24, 2026
@openclaw-barnacle openclaw-barnacle Bot added the triage: needs-pr-context Candidate: external PR body lacks required problem context or evidence. label Jun 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agents Agent runtime and tooling app: macos App: macos app: web-ui App: web-ui gateway Gateway runtime merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. merge-risk: 🚨 security-boundary 🚨 May affect sandboxing, authorization, credentials, or sensitive data. merge-risk: 🚨 session-state 🚨 May lose, corrupt, stale, or mis-associate session, agent, or context state. P2 Normal backlog priority with limited blast radius. rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. size: M status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. triage: needs-pr-context Candidate: external PR body lacks required problem context or evidence. triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant