anthropics / anthropics/claude-agent-sdk-typescript

Feature request: expose `parentUuid` on `SessionMessage` so the conversation chain can be reconstructed

Offen
#431 1 Kommentar 0 Reaktionen 0 zugewiesene Personen Auf GitHub ansehen
enhancement
Vorherrschende Sprache
Shell
Sterne
1.8k
Forks
226
PR-Merge-Kennzahlen
Keine gemergten PRs in 30 T.

Beschreibung

### Summary

`getSessionMessages()` projects a fixed field list that omits `parentUuid`, so the conversation chain cannot be reconstructed from its output. This makes it impossible to compute a correct fork point for `resumeSessionAt`, even though the SDK documents precise rules for choosing one.

Tested against `@anthropic-ai/claude-agent-sdk@0.3.238`.

### Why this matters

The `resumeDropsTurn` docs give a general rule for truncating resumes:

> fork at the KEPT turn's last chain entry, whatever it is — `resumeSessionAt` accepts any chain UUID

and `SDKCompactBoundaryMessage.compact_metadata.preserved_messages` documents that readers must "relink `uuids[i]` to `uuids[i-1]` (`uuids[0]` to `anchor_uuid`) instead of walking the parentUuid chain."

Both rules are about chain structure. Neither is derivable from what `getSessionMessages()` returns, so a client that wants to rewind a session to a given message has no supported way to locate the fork point. Picking the array-order predecessor is wrong in at least three situations, below.

### Repro

Writes a synthetic transcript containing a user message, an assistant message, a `structured_output` attachment, a `compact_boundary` carrying `preserved_messages`, and a second user message — then reads it back. Creates and removes only its own project directory.

```js
import { getSessionMessages } from "@anthropic-ai/claude-agent-sdk";
import fs from "fs";
import os from "os";

const sid = "11111111-2222-4333-8444-555555555555";
fs.mkdirSync(`${os.homedir()}/.claude/projects/-tmp-rewind-probe`, { recursive: true });
const file = `${os.homedir()}/.claude/projects/-tmp-rewind-probe/${sid}.jsonl`;
const ts = "2026-08-27T12:00:00.000Z";
const rec = (o) => ({ sessionId: sid, cwd: "/tmp/rewind-probe", version: "1.0.0", timestamp: ts, ...o });

fs.writeFileSync(file, [
rec({ type: "user", uuid: "u1", parentUuid: null,
message: { role: "user", content: [{ type: "text", text: "first prompt" }] } }),
rec({ type: "assistant", uuid: "a1", parentUuid: "u1",
message: { id: "msg_1", role: "assistant", content: [{ type: "text", text: "reply" }] } }),
rec({ type: "attachment", uuid: "at1", parentUuid: "a1",
attachment: { type: "structured_output", data: { ok: true } } }),
rec({ type: "system", subtype: "compact_boundary", uuid: "s1", parentUuid: "at1",
compact_metadata: { trigger: "auto", pre_tokens: 1000,
preserved_messages: { anchor_uuid: "a1", uuids: ["u2"] } } }),
rec({ type: "user", uuid: "u2", parentUuid: "s1",
message: { role: "user", content: [{ type: "text", text: "second prompt" }] } }),
].map((l) => JSON.stringify(l)).join("\n") + "\n");

const out = await getSessionMessages(sid, { dir: "/tmp/rewind-probe", includeSystemMessages: true });
console.log(out.length);
for (const m of out) console.log(m.type, m.uuid, Object.keys(m).join(","));
```

### Actual output

```
4
user u1 type,uuid,session_id,message,parent_tool_use_id,parent_agent_id,timestamp
assistant a1 type,uuid,session_id,message,parent_tool_use_id,parent_agent_id,timestamp
system s1 type,uuid,session_id,message,parent_tool_use_id,parent_agent_id,timestamp
user u2 type,uuid,session_id,message,parent_tool_use_id,parent_agent_id,timestamp
```

Three gaps:

1. **`parentUuid` is dropped from every entry.** The transcript record carries it; the projection doesn't include it. There is no other field from which the chain link can be recovered.
2. **`attachment` entries are filtered out entirely**, with `includeSystemMessages` either false or true (`at1` is absent above). When the kept turn's last chain entry is a `structured_output` attachment — exactly the case the `resumeDropsTurn` docs single out — that entry is invisible to the API.
3. **`compact_boundary` survives but is stripped.** With `includeSystemMessages: true` the entry returns with no `message` and no `compact_metadata`, so `preserved_messages` / `anchor_uuid` are unavailable and the documented relink rule cannot be applied.

### Request

Primary, and sufficient on its own: **add `parentUuid` to `SessionMessage`.** The underlying record already has it, so this looks additive.

Secondary, if they're cheap: preserve `compact_metadata` on `compact_boundary` entries, and provide some way to observe `attachment` entries (a flag alongside `includeSystemMessages` would do). With `parentUuid` alone a caller can walk the chain, but attachments would still be missing from the walk, so the fork point could land earlier than intended and trip the `resumeDropsTurn` validator.

### Context

This came up building session rewind for the ACP adapter — see [agentclientprotocol/claude-agent-acp#1048](https://github.com/agentclientprotocol/claude-agent-acp/issues/1048). The goal is letting an editor client edit an already-sent message and re-run from that point, which needs a correct `resumeSessionAt` target.

Possibly related: #387 reports a different freshness gap in the same API.

Happy to send a PR if `parentUuid` passthrough is the shape you'd want.

Beitragsleitfaden

Für dieses Repository ist kein Beitragsleitfaden indexiert

Bewertung

Dieses Issue wurde noch nicht bewertet.

Neue Issues direkt in Ihr Postfach

Eine kurze Übersicht über anfängerfreundliche GitHub-Issues.