Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
1fa29ab
feat(copilot): add GitHub Copilot CLI provider (preview) + format docs
akesling Jul 1, 2026
ac7b680
fix(copilot): pair id-less tool start/complete instead of double-coun…
akesling Jul 1, 2026
10992b3
fix(copilot): correct schema against a real session (copilotVersion 1…
akesling Jul 1, 2026
57f764b
feat(copilot): wire Copilot into `path share`
akesling Jul 1, 2026
c9ad94c
feat(copilot): projector + resume/export + conformance; fix resume lo…
akesling Jul 1, 2026
1680df8
fix(copilot): offset-bearing ISO timestamps on every projected event
akesling Jul 1, 2026
5313d1f
fix(copilot): emit parentId (null on root)
akesling Jul 1, 2026
4bd5759
fix(copilot): emit session.start `startTime` (+ full observed top-lev…
akesling Jul 1, 2026
4e89b84
fix(copilot): stamp turnId on turn-scoped events
akesling Jul 1, 2026
b33d44f
fix(copilot): messageId on assistant msg/turn_end; non-empty toolCall…
akesling Jul 1, 2026
5c22ea2
fix(copilot): subagent.* loader fields — real Pathbase sub-agent sess…
akesling Jul 2, 2026
b5945e6
feat(copilot): project file edits to native edit/create shape so diff…
akesling Jul 2, 2026
69d5b75
feat(copilot): add toolTelemetry to file edits so diffs render colorized
akesling Jul 2, 2026
7f99402
fix(copilot): build edit diffs with a single header so they colorize
akesling Jul 2, 2026
eb49155
fix(copilot): never emit hunkless diffs — root cause of the "flat diff"
akesling Jul 2, 2026
1ec3c94
fix(copilot): remap tool args in the toolRequests mirror — colorized …
akesling Jul 2, 2026
67bac92
docs(copilot): sync format docs with the rendering/verification learn…
akesling Jul 2, 2026
e6021aa
test(copilot): S-tier verification — real elicit fixture, un-dodged m…
akesling Jul 2, 2026
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
70 changes: 70 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,76 @@ All notable changes to the Toolpath workspace are documented here.
path is returned: a byte-identical re-emission is dropped, and a
same-id-but-different step is re-IDed to `<id>#<n>` so no data is lost.

## New provider: GitHub Copilot CLI (preview) — 2026-06-30

Adds **`toolpath-copilot` 0.1.0**, a forward provider that derives Toolpath
documents from GitHub Copilot CLI (`@github/copilot`) sessions under
`~/.copilot/session-state/<id>/events.jsonl`.

- Parses the `events.jsonl` stream (`session.*`, `user.message`,
`assistant.*`, `tool.execution_*`, `subagent.*`, `skill.*`, `hook.*`,
`abort`) into `toolpath_convo::ConversationView` and hands off to the shared
`derive_path`. Tool names are classified into the `ToolCategory` ontology;
file writes whose args carry full content get a `raw` unified-diff
perspective. Tool `start`/`complete` pairing uses a correlation id when
present and falls back to positional pairing when absent (the id field is
unverified), so an id-less stream doesn't double-count tool calls.
- Reads git context (root / repository / branch / revision) from the sibling
`workspace.yaml` into `Path.base`, via a tolerant key-scan parser (no YAML
dependency — the file's schema is itself reverse-engineered).
- **Preview / schema partly verified.** Copilot CLI's `events.jsonl` format is
undocumented. First built from docs + reverse-engineering, then **verified
against a first-hand capture at `copilotVersion` 1.0.67** — which corrected
several guessed field locations (cwd/git under `session.start.context`, tool
results under `result.content`, `copilotVersion`, `reasoningText`,
`outputTokens`) and confirmed the envelope + core event types. Types not seen
in that session (`subagent.*`/`skill`/`hook`/`abort`/`shutdown`/compaction) and
the `checkpoints/` format remain unverified; the parser stays tolerant.
- Adds the on-disk format reference at `docs/agents/formats/copilot-cli/`
(folder, every claim confidence-tagged) and its verification checklist.
- Wired into the CLI **both directions**: forward (`path p import/list/show
copilot`, `path share`) and reverse via a new `CopilotProjector`
(`path p export copilot`, `path resume`). Resume/export write
`~/.copilot/session-state/<id>/` + a `session-store.db` `sessions` row
(INSERTing only a fresh id, never touching existing sessions). **✅ Verified in
copilot 1.0.67:** a projected session loads and resumes in the real
`copilot --resume`; the loader's writer contract (UUID `id`/`parentId`,
offset-ISO timestamps, `turnId`, `messageId`, non-empty `toolCallId`, full
`session.start` shape) is documented with verbatim errors in
`docs/agents/formats/copilot-cli/writing-compatible.md`. Also validated by the
cross-harness conformance matrix (`test-fixtures/copilot/`) + a round-trip test.
- Bumps **`path-cli` to 0.15.0** (new `toolpath-copilot` dependency, the new
subcommands, Copilot in `path share`/`path resume`, and `uuid` `v4`).
- The Copilot **resume loader contract** was mapped from live `copilot --resume`
runs and codified in `docs/agents/formats/copilot-cli/writing-compatible.md`
(9 requirements, each with its verbatim rejection): UUID `id`; offset-ISO
`timestamp` on every event; present `parentId` (UUID/null); `session-store.db`
row; full `session.start` shape incl. `startTime`; `turnId` and `messageId` on
turn-scoped events; non-empty `toolCallId`; and `subagent.*` fields
(`toolCallId`/`agentName`/`agentDisplayName`/`agentDescription`). Verified on a
27-event session **and** a 5817-event session with sub-agents.
- **S-tier verification**: a real feature-elicit session (shell, create/edit/
view, glob+grep, errored read, real sub-agent, reasoning, per-message +
shutdown tokens) captured live at 1.0.68 now drives the cross-harness matrix
(`test-fixtures/copilot/convo.jsonl` — tokens and sub-agent included, no
dodges) and new crate tests (`real_fixture_roundtrip.rs`: forward invariants,
projection round-trip fidelity, wire-level serde value-identity). The capture
corrected the `session.shutdown` parser (`tokenDetails.{…}.tokenCount`;
model-keyed `modelMetrics`), resolved sub-agent semantics (`subagent.*` are
thin markers sharing the `task` tool's `toolCallId` — delegations pair by it,
ids preserved through projection), and upgraded file fidelity to
**Codex-grade** (native `edit`/`create` embed the real file-state diff in
`result.detailedContent`; the forward path now consumes it). Incidental
cross-crate fix: `toolpath-pi` now decodes a zero wire `input` as `None`
(same absence rule as its cache fields). Live loop committed as
`scripts/verify-copilot-live.sh` (isolated-home loader check + resumed-model
context probe — the probe answers session-specific questions correctly);
`scripts/capture-elicit-fixtures.sh` learned copilot.
- File edits now **render as diffs** in a resumed session: `FileWrite` tool calls
project to Copilot's native `edit`/`create` shape with a git-style unified diff
in `result.detailedContent` (mapping a Claude `Edit`/`Write` accordingly). See
`docs/agents/formats/copilot-cli/file-fidelity.md`.

## Token usage: once per message, with per-step attribution + kind v1.1.0 — 2026-06-17

Fixes token over-counting in derived documents (~3× output-token
Expand Down
21 changes: 19 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ crates/
toolpath-claude/ # derive from Claude conversation logs
toolpath-gemini/ # derive from Gemini CLI conversation logs
toolpath-codex/ # derive from Codex CLI rollout files
toolpath-copilot/ # derive from + project to GitHub Copilot CLI session logs (preview; import/list/show/share/export/resume wired; resume verified in copilot 1.0.67)
toolpath-opencode/ # derive from opencode SQLite databases
toolpath-cursor/ # derive from Cursor (IDE) state.vscdb bubble store
toolpath-pi/ # derive from Pi (pi.dev) agent session logs
Expand All @@ -44,6 +45,7 @@ path-cli (binary: path)
├── toolpath-claude → toolpath, toolpath-convo
├── toolpath-gemini → toolpath, toolpath-convo
├── toolpath-codex → toolpath, toolpath-convo
├── toolpath-copilot → toolpath, toolpath-convo (preview)
├── toolpath-opencode → toolpath, toolpath-convo
├── toolpath-cursor → toolpath, toolpath-convo
├── toolpath-pi → toolpath, toolpath-convo
Expand All @@ -56,7 +58,20 @@ toolpath-cli (deprecated shim, binary: path)
└── path-cli
```

Cross-dependencies between satellite crates: `toolpath-claude → toolpath-convo`, `toolpath-gemini → toolpath-convo`, `toolpath-codex → toolpath-convo`, `toolpath-opencode → toolpath-convo`, `toolpath-cursor → toolpath-convo`, `toolpath-pi → toolpath-convo`.
`toolpath-copilot` is a **preview** provider (workspace member + `path-cli`
dependency) wired both directions: forward (`path p import / list / show
copilot`, `path share`) and reverse via a `CopilotProjector` (`path p export
copilot`, `path resume`). Resume/export write `~/.copilot/session-state/<id>/`
plus a `session-store.db` `sessions` row (only ever INSERTing a fresh id).
**✅ Verified in copilot 1.0.67:** a projected session loads and resumes in the
real `copilot --resume`. Getting there mapped the loader's writer contract
(UUID `id`/`parentId`, offset-ISO timestamps, `turnId`, `messageId`, non-empty
`toolCallId`, full `session.start` shape, and `subagent.*` fields) — see
`docs/agents/formats/copilot-cli/writing-compatible.md`. Verified on both a
27-event session and a 5817-event session with sub-agents (a Pathbase graph
resumed by URL); also validated by the cross-harness matrix + a round-trip test.

Cross-dependencies between satellite crates: `toolpath-claude → toolpath-convo`, `toolpath-gemini → toolpath-convo`, `toolpath-codex → toolpath-convo`, `toolpath-copilot → toolpath-convo`, `toolpath-opencode → toolpath-convo`, `toolpath-cursor → toolpath-convo`, `toolpath-pi → toolpath-convo`.

The desktop GUI lives in the private [pathbase](https://github.com/empathic/pathbase) repo as `pathbase-app` — it consumes the toolpath crates via git or crates.io.

Expand Down Expand Up @@ -182,11 +197,12 @@ Tests live alongside the code (`#[cfg(test)] mod tests`), plus `path-cli` has in
- `toolpath-claude`: 229 unit + 18 integration + 6 doc tests (path resolution, conversation reading, query, chaining, watcher, derive, metadata first-user-message, group_id grouping + once-per-message usage totals)
- `toolpath-gemini`: 161 unit + 29 integration + 5 doc tests (path resolution, chat-file parsing, query, watcher, derive, provider, round-trip fidelity, thoughts-folded-into-output + reasoning breakdown round-trip)
- `toolpath-codex`: 80 unit + 51 integration + 2 doc tests (rollout parsing, provider assembly, patch-fidelity derive, real-session fixture, source→path fidelity invariants, JSON wire-level round-trip, per-turn token deltas from cumulative counters, reasoning breakdown)
- `toolpath-copilot`: 58 unit + 4 integration + 1 doc test (`events.jsonl` envelope/event-type classification incl. `session.start` nested `context` + `tool.execution` `result.content` + `assistant.message` `reasoningText`/`outputTokens`, path resolution incl. legacy `history-session-state/`, reader malformed-line tolerance without env races, tolerant `workspace.yaml` parse, `to_view` turn/tool/delegation assembly + per-turn token usage, id-based **and** id-less positional tool pairing, position-stable turn ids, file-write `raw` diff synthesis, `CopilotProjector` round-trip + foreign-tool-name remap). Preview crate: fixtures are shaped after a real 1.0.67 capture but the crate ships no committed real session — see `docs/agents/formats/copilot-cli/known-gaps-and-sourcing.md`. The projector is exercised by the cross-harness matrix in `path-cli`.
- `toolpath-opencode`: 52 unit + 19 integration + 1 doc test (SQLite reader, JSON payload serde, provider assembly, snapshot-based derive, tool-input fallback for gitignored paths, reasoning breakdown)
- `toolpath-cursor`: 78 unit + 8 integration round-trip + 1 real-DB sanity + 1 doc test (state.vscdb SQLite reader, bubble store + composer header parsing, content-addressed blob lookup, projector with full TOOL_TABLE coverage, JSONL transcript ingest in `examples/dump_fixture.rs`)
- `toolpath-pi`: 133 unit + 26 integration + 5 doc tests (types, paths, error, reader, io, provider)
- `toolpath-dot`: 30 unit + 2 doc tests (render, visual conventions, escaping)
- `path-cli`: 294 unit + 65 integration tests (import/export/cache, track sessions, merge, validate, roundtrip, render-md snapshots, deprecation aliases, pathbase HTTP mock-server tests, fzf-friendly TSV output, `path resume` orchestration with injectable `ExecStrategy`). For an end-to-end check against a real Pathbase deployment, run `scripts/test-pathbase-live.sh <url>` — it does an anon round-trip in a sandboxed config dir and, if you're logged into that URL, an authed pathstash round-trip too.
- `path-cli`: 296 unit + 70 integration tests (import/export/cache, track sessions, merge, validate, roundtrip, render-md snapshots, deprecation aliases, pathbase HTTP mock-server tests, fzf-friendly TSV output, `path resume` orchestration with injectable `ExecStrategy` incl. Copilot, Copilot import/list/show via `COPILOT_HOME` + Copilot in the `path share` aggregator + Copilot in the cross-harness conformance matrix). For an end-to-end check against a real Pathbase deployment, run `scripts/test-pathbase-live.sh <url>` — it does an anon round-trip in a sandboxed config dir and, if you're logged into that URL, an authed pathstash round-trip too.
- `toolpath-cli`: 0 tests (it's a one-line `path_cli::run()` shim crate that exists only so `cargo install toolpath-cli` keeps installing the `path` binary)

Validate example documents: `for f in examples/*.json; do cargo run -p path-cli -- p validate --input "$f"; done`
Expand Down Expand Up @@ -247,6 +263,7 @@ Build the site after changes: `cd site && pnpm run build` (should produce 11 pag
- Token usage `breakdowns` (kind v1.1.0, additive): an optional third key on `TokenUsage` — a decomposition of a top-level class into named sub-classes, keyed by class (e.g. `"output"`), inner map sub-class → tokens (e.g. `breakdowns["output"]["reasoning"] = 243`). INFORMATIONAL ONLY: **never summed into any total** (the parent class already counts those tokens, so the session-total guarantee is untouched); invariant `Σ(inner) ≤ parent`; omitted when empty; rides both `token_usage` and `attributed_token_usage`. Per-provider reality: **Gemini** reports `thoughts` (reasoning) as an additive sibling that the derivation used to **drop** (under-counting output) — it's now folded into `output_tokens` *and* recorded as `breakdowns["output"]["reasoning"]`, with the projector un-folding it on the reverse path for a lossless round-trip (`Some(0)` preserved as a real Gemini-3 zero-reasoning signal). **OpenCode** folds `reasoning` into output and records the same breakdown. **Codex** differences `reasoning_output_tokens` (⊆ output, cumulative) into `breakdowns["output"]["reasoning"]` on both per-step `attributed_token_usage` and per-round `token_usage`. **Claude** records no breakdown (its JSONL `usage` doesn't itemize thinking tokens).
- Pi provider: `toolpath-pi` reads Pi session JSONL from `~/.pi/agent/sessions/`. Sessions use a tree (id/parentId) in a single file, and may link to a parent file via `parentSession` in the header. The tree is preserved as a DAG in the derived `Path`.
- Codex provider: `toolpath-codex` reads Codex CLI rollout files from `~/.codex/sessions/YYYY/MM/DD/rollout-*.jsonl`. Sessions are date-bucketed (not project-keyed). File-change fidelity is excellent — Codex's `patch_apply_end` events carry either the unified diff (for updates) or the full file content (for adds), so the derived `Path` gets a real `raw` perspective on every file artifact. See `docs/agents/formats/codex.md` for the full format reference.
- Copilot provider (**preview, schema reverse-engineered**): `toolpath-copilot` reads the GitHub Copilot CLI (`@github/copilot`) `events.jsonl` stream from `~/.copilot/session-state/<id>/` (root overridable via `COPILOT_HOME`; also globs the legacy `history-session-state/`). Sessions are global (id-keyed dirs), not project-keyed. The `events.jsonl` schema is **undocumented** but was **verified against a first-hand capture at `copilotVersion` 1.0.67**: envelope is `{type, data, id, timestamp, parentId}`; cwd + git (branch/remote/`headCommit`) live under `session.start`'s `data.context` (primary; `workspace.yaml` is the fallback, parsed by a tolerant key-scan with no YAML dep); tool calls carry `toolCallId`/`toolName`/`arguments` with results under `result.content`; `assistant.message` carries `reasoningText` (→ thinking) and per-message `outputTokens` (summed for the session total). The reader stays deliberately tolerant (payload inline / under `data` / under `payload`; multiple key spellings; unknown events preserved as `ConversationEvent`s). Event types not seen in that session (`subagent.*`, `skill.invoked`, `hook.*`, `abort`, `session.shutdown`, compaction) and the `checkpoints/` format remain unverified. Wired into the CLI both directions — forward (`path p import/list/show copilot`, `path share`) and reverse via `CopilotProjector` (`path p export copilot`, `path resume`; the projector emits `events.jsonl` in the observed shape, remaps foreign tool names via `native_name`, and — for resume — writes `session-state/<id>/` + a `session-store.db` `sessions` row, INSERTing only a fresh id). **Verified in copilot 1.0.67**: a projected session loads and resumes in the real `copilot --resume`. Reaching that mapped the loader's writer contract (UUID `id`/`parentId`, offset-ISO timestamps on every event, `turnId`/`messageId` on turn-scoped events, non-empty `toolCallId`, full `session.start` shape, and `subagent.*` fields `agentName`/`agentDisplayName`/`agentDescription`/`toolCallId`) — documented with verbatim rejection messages in `docs/agents/formats/copilot-cli/writing-compatible.md`; verified on a small session and a 5817-event sub-agent session. Also validated in isolation by the cross-harness conformance matrix (`crates/path-cli/tests/cross_harness_matrix.rs`, fixture `test-fixtures/copilot/`) + a round-trip test. File fidelity is **Codex-grade**: `edit`/`create` completes embed the real file-state diff inline (`result.detailedContent`), which upgrades the arg-derived `FileMutation.raw_diff`; sub-agents are `task` tool calls with thin `subagent.*` markers sharing the `toolCallId` (delegations pair by it). Token totals sum per-message `outputTokens` (falling back to `session.shutdown` when present); no per-turn attribution. Full format reference + verification checklist: `docs/agents/formats/copilot-cli/`.
- opencode provider: `toolpath-opencode` reads a SQLite database at `~/.local/share/opencode/opencode.db` (opened read-only). Each session's messages and 12 typed part variants (text, reasoning, tool, step-start/-finish, snapshot, patch, file, agent, subtask, retry, compaction) land as one step per message with tool invocations attached. File diffs come from a sibling bare git repo at `snapshot/<project-id>/[<sha1(worktree)>]/` via `git2` tree↔tree diffs — opencode respects the user's `.gitignore`, so changes under gitignored paths fall back to tool-input-derived structural changes with no `raw` perspective. Project id is the SHA of the repo's first root commit. See `docs/agents/formats/opencode.md` for the full format reference.
- Cursor (IDE) provider: `toolpath-cursor` reads Cursor.app's global `state.vscdb` SQLite (opened read-only) at `~/Library/Application Support/Cursor/User/globalStorage/state.vscdb` (macOS; `~/.config/Cursor/...` on Linux). Composers, bubbles, and content-addressed file blobs are stored as key-prefixed rows in the `cursorDiskKV` table (`composerData:<uuid>`, `bubbleId:<comp>:<bubble>`, `composer.content.<hash>`) plus a `composer.composerHeaders` index blob in `ItemTable`. The full tool-dispatch enum (53 entries, ids 0–63) is extracted from the workbench bundle into `TOOL_TABLE` for round-trip-correct numeric ids — projector-written composers load back into Cursor.app's UI with the right tool rendering. The cursor-agent CLI uses a different per-chat protobuf store at `~/.cursor/chats/<wsHash>/<chatId>/store.db` that this crate does not yet parse — that's deferred to a future `toolpath-cursor-cli` companion. See `docs/agents/formats/cursor.md` for the full format reference.
- Format references for the agent on-disk formats we derive from live at `docs/agents/formats/`. The Claude Code format (`~/.claude/projects/…` JSONL) gets the deepest treatment — twelve focused docs at `docs/agents/formats/claude-code/` covering envelope, entry types, tools, session chains, compaction, writing-compatible JSONL, a linear walkthrough, and a version-keyed changelog. Sibling single-file references: `codex.md`, `gemini.md`, `opencode.md`. Keep them in sync with their derive crates when fields or behaviors change.
Expand Down
18 changes: 17 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading