anthropics / anthropics/claude-agent-sdk-typescript
Feature request: expose `parentUuid` on `SessionMessage` so the conversation chain can be reconstructed
- 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.