Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ Each release is published to [crates.io](https://crates.io/crates/txcript),

### Fixed

- Support OpenCode 2.1 ([#96](https://github.com/skillsynchq/txcript/issues/96)): discover and load sessions from its `session_v2`/`session_message` tables, write its flat transcript with the 2.1 tool names and input keys (`shell`, `subagent`, `path`, …), and import it with `opencode session import --directory <cwd>`. A 2.0 database still reads, and a 2.0 CLI (found through `opencode --version`) still gets the `{info, parts}` export through `opencode import`. The `opencode` text form — the WASM `convert`/`fromCommon` output — is now the 2.1 document; `from_text` reads both layouts.
- Keep the results of parallel tool calls when converting to OpenCode ([#98](https://github.com/skillsynchq/txcript/issues/98)). A source that records each call in its own assistant message, with each result in its own later user message, used to be written with every call's output empty, because the writer only looked for a result in the very next message; it now looks through the following user messages of the turn.
- Drop the compaction summary from `claude_code` conversions ([#100](https://github.com/skillsynchq/txcript/issues/100)). The `user` line flagged `isCompactSummary` used to become a user prompt that repeated, condensed, the earlier history the file still holds before the `compact_boundary`. It now stays in the native body only.
- Resolve a session id that exists in several stores to the most recently modified copy. `view`, `export`, `continue` and `crop` used to load whichever copy the file system listed first; discovery now breaks start-time ties by file mtime.

### Added
Expand Down
1 change: 1 addition & 0 deletions docs/formats/claude-code.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ everything else is preserved verbatim so a native load/save round-trips byte-los
| `<command-name>` markup | Slash command the user ran, as XML-ish tags filling a body | `Block::ToolUse` with `Tool::Command` |
| `<local-command-stdout>` markup | What the command printed | `Block::ToolResult` paired via `parentUuid` |
| `<local-command-caveat>` markup | Fixed boilerplate, regenerated by the CLI | dropped |
| `user` line with `isCompactSummary` | The summary a compaction writes after its `compact_boundary` (with `isVisibleInTranscriptOnly`); the file still holds the history it condenses, before the boundary | kept in the native body, no Common turn |
| `summary` line | Session title with a `leafUuid` anchor | `Meta.title` (lowest precedence) |
| `custom-title` / `agent-name` line | User-set or agent-derived title | `Meta.title` (custom-title wins) |
| `teamName` / `agentName` envelope fields | Present on every line of a teammate session the team feature starts (distinct from the `agent-name` *line type* above) | `Meta.lineage` — see Lineage |
Expand Down
214 changes: 128 additions & 86 deletions docs/formats/opencode.md

Large diffs are not rendered by default.

11 changes: 11 additions & 0 deletions src/harness/claude_code.rs
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,9 @@ pub(crate) fn records_to_messages(records: &[Record], fallback_ts: DateTime<Utc>

for record in records {
let (role, entry) = match record {
// The file still holds the history a compaction summary condenses
// (#100).
Record::User(e) if is_compact_summary(e) => continue,
Record::User(e) => (Role::User, e),
Record::Assistant(e) => (Role::Assistant, e),
// Newer CLI versions write the same command envelopes on
Expand Down Expand Up @@ -224,6 +227,14 @@ pub(crate) fn records_to_messages(records: &[Record], fallback_ts: DateTime<Utc>
messages
}

/// Whether a `user` line is the summary a compaction writes after its
/// `compact_boundary`. Reading in file order already carries the history it
/// condenses, which the file keeps before the boundary, so the summary would
/// repeat it as a user turn.
fn is_compact_summary(entry: &EntryLine) -> bool {
entry.extra.get("isCompactSummary") == Some(&Value::Bool(true))
}

/// The native records for a canonical conversation: one entry line per
/// message (local-command turns as their envelopes), a linear `parentUuid`
/// chain, and a leading summary line when `meta.title` is set. `meta.id` is
Expand Down
Loading