Skip to content

feat: conversation projection — toolpath as agent session interchange#22

Merged
akesling merged 22 commits intomainfrom
akesling/incept
Apr 17, 2026
Merged

feat: conversation projection — toolpath as agent session interchange#22
akesling merged 22 commits intomainfrom
akesling/incept

Conversation

@akesling
Copy link
Copy Markdown
Contributor

Summary

Enables projecting toolpath documents back into functional agent conversation sessions. A toolpath document derived from a Claude session can be projected into Claude JSONL that Claude Code loads and resumes — verified empirically.

This is the "serialize" half of a serde-like pattern for conversation portability:

  • ConversationProvider (existing) reads native formats into ConversationView
  • ConversationProjector (new) writes ConversationView into native formats
  • Both pass through ConversationView as the narrow waist — M providers = M+N, not M×N

What's new

toolpath-convo:

  • ConversationProjector trait + AnyProjector type-erasing wrapper
  • extract_conversation() — reconstructs ConversationView from a toolpath Path using the conversation sub-protocol
  • ConversationEvent type for non-message session entries (hooks, snapshots, metadata)
  • Conversation sub-protocol vocabulary: conversation.init, conversation.append, tool.invoke, conversation.event structural change types; agent://<provider>/<session> URN scheme; actor patterns (human:*, agent:*, agent:*/tool:*, tool:*)

toolpath-claude:

  • ClaudeProjector — projects ConversationView into Claude Conversation
  • Enriched derive: full untruncated text, tool invocation steps with agent:claude-code/tool:<Name> actors, agent://claude/<session> URNs, full UUID step IDs, token usage, stop_reason, model, conversation.init steps, per-entry metadata, non-message event capture
  • Tool results round-trip via cross-entry assembly (to_view() before derive)
  • Conversation.preamble for raw JSON entries (permission-mode) that don't fit ConversationEntry

toolpath-cli:

  • path project claude — project a toolpath document into Claude JSONL
  • path incept — project and write directly to ~/.claude/projects/ so Claude Code can claude -r it. Reads from file or stdin, supports piping: path derive claude ... | path incept --project /target

Breaking changes (toolpath-claude, pre-1.0)

  • Conversation artifact key: claude://<id>agent://claude/<id>
  • Tool invocations: inline name lists → separate steps with tool.invoke
  • Step IDs: 8-char prefix → full UUIDs

Test plan

  • Round-trip integration test: Claude JSONL → derive → Path → extract → project → Claude JSONL with semantic equivalence assertions (turn count, text, tool names, tool result content/is_error, token usage)
  • path project claude CLI integration test
  • path incept writes to correct ~/.claude/projects/ path
  • Empirical: path derive claude ... | path incept ... then claude -r loads the session in Claude Code
  • All 889 workspace tests pass, clippy clean
  • All 12 example documents validate

akesling added 22 commits April 17, 2026 16:30
Defines architecture for projecting toolpath documents back into
functional agent conversation sessions, enabling conversation
portability between AI coding agents (Claude, Codex, Gemini).

Key decisions:
- Conversation sub-protocol in toolpath-convo (structural change
  vocabulary: conversation.init, conversation.append, tool.invoke)
- ConversationProjector trait with AnyProjector type-erasing wrapper
- Enriched derive in toolpath-claude for round-trip fidelity
- Tool invocations modeled as separate steps with per-tool actors
- Replace claude:// with agent://claude/<session-id> hierarchy
- Document non-file artifacts as "virtual artifacts"
- Call out breaking changes in migration section (URN scheme,
  tool steps, full UUID step IDs)
- Resolve artifact URN open question
9 tasks covering the full pipeline:
1. Add toolpath dep to toolpath-convo
2. ConversationProjector trait + AnyProjector
3. extract_conversation function
4. Enriched derive in toolpath-claude
5. ClaudeProjector implementation
6. Round-trip integration test
7. CLI project command
8. Version bumps and changelog
9. Final verification
Update derive_path() to produce sub-protocol-compliant output:

- Change conversation artifact key from claude:// to agent://claude/
- Use full entry UUID as step ID instead of truncated 8-char prefix
- Preserve full text (remove truncation)
- Add model, stop_reason, and token usage fields to conversation.append extra
- Separate thinking from text in conversation.append extra
- Emit tool invocation steps (one per tool type, grouped) with tool.invoke
  structural changes, correct actor strings, and category mapping
- Tool steps use file_path as artifact key for file tools, agent:// URI for others
- Tool steps do not advance the parent chain

Updated all existing tests and added new tests for: tool invocation actors,
token usage capture, full text preservation, multiple tool uses (same and
different types), non-file tool artifact keys, thinking inclusion/exclusion,
tool step parent chain behavior, and tool input preservation.
toolpath-convo 0.5.0 → 0.6.0 (new API + new dependency)
toolpath-claude 0.6.2 → 0.7.0 (breaking derive changes + new API)
toolpath-cli 0.3.0 → 0.3.1 (new project command)
Tool result content and is_error flags are now verified to survive
the full Claude JSONL → derive → Path → extract → project pipeline.
…on-mode

The ClaudeProjector now emits a permission-mode preamble entry as the
first line (matching real Claude JSONL) and populates per-entry metadata
(cwd, gitBranch, version, userType, requestId, entrypoint, isMeta, slug,
etc.) from Turn.environment and Turn.extra["claude"]. Tool-result entries
inherit metadata from their parent turn and include sourceToolAssistantUUID.
Non-message JSONL entries (attachment, system, file-history-snapshot,
permission-mode, etc.) are now derived as steps with structural change
type `conversation.event` instead of being silently skipped. Each event
step records the entry_type, metadata fields, and entry extras for
round-trip fidelity. Event steps use actor `tool:claude-code` and do
not advance the conversation parent chain.
Real Claude JSONL permission-mode lines have only type/permissionMode/
sessionId. ConversationEntry always serializes uuid/timestamp/isSidechain
which caused Claude Code to reject the projected file.

Add Conversation.preamble field for raw JSON entries that don't fit
the ConversationEntry shape. CLI serializes preamble before entries.
Reads a toolpath document (file or stdin), projects it into Claude
JSONL, and writes it to ~/.claude/projects/ so Claude Code can
load and resume it. Supports piping: path derive ... | path incept

Usage:
  path derive claude --project /path | path incept --project /target
  path incept --input doc.json --project /target
Claude Code calls .map() on assistant message content, expecting an
array. Simple text-only assistant turns now emit Parts([Text]) instead
of Text(string).
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 17, 2026

🔍 Preview deployed: https://32ae43be.toolpath.pages.dev

@akesling akesling merged commit a71c98e into main Apr 17, 2026
2 checks passed
eliothedeman added a commit that referenced this pull request Apr 20, 2026
Resolves conflicts with the conversation-projection changes (PR #22) that
landed on main while this PR was in review:

- Cargo.toml workspace deps: keep toolpath 0.2.0 (this branch) alongside
  the toolpath-claude 0.7.0 bump from main.
- crates/toolpath-cli/Cargo.toml and site/_data/crates.json: keep
  toolpath-cli 0.4.0 (higher bump supersedes main's 0.3.1).
- Cargo.lock: take main's version and regenerate via cargo build.
- Add `graph_ref: None` to four new PathIdentity struct literals
  introduced by main:
  - crates/toolpath-cli/src/cmd_incept.rs
  - crates/toolpath-cli/src/cmd_project.rs
  - crates/toolpath-cli/tests/roundtrip.rs
  - crates/toolpath-convo/src/extract.rs
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant