Skip to content
Merged
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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

### Added (CLI)
- `codeburn sync push --attribution` (opt-in): sends git attribution spans — the session→commit correlation from `codeburn yield` (`codeburn.session.attribution` and `codeburn.commit` span types with normalized repo remote, commit SHAs, merged/reverted state, and PR links). Nothing new is sent without the flag; local-only repos and Windows filesystem paths are never emitted as repo identities, and sessions whose project path no longer resolves never inherit the push-time working directory's repo. See docs/sync/README.md "Git attribution".
- **Codex throughput tracking**: per-model Tok/s in the dashboard (active time excludes recorded tool wait), plus `codeburn codex-tps` for a retrospective generated-tokens/sec estimate off a rollout's checkpoints. Timing is attributed per task, from `task_started` to `task_complete`, and split across the task's calls by generated tokens; a task's calls are emitted only once that window is known, so no recorded call is ever revised after the fact. The codex results cache takes v12 and the codex parse version bumps in lockstep, so already-cached sessions re-derive once and pick the fields up. (#940, thanks @ozymandiashh)
- **Cline CLI provider** — the standalone Cline command-line agent (npm `cline`, 3.x), separate from the VS Code extension's [Cline](docs/providers/cline.md) provider. Reads session metadata and rolled-up usage from `~/.cline/data/sessions/<session-id>/<session-id>.json`, and per-message `metrics` (input, output, cache read/write, cost) from the co-located `<session-id>.messages.json`; cost is read per message rather than estimated. (#940, thanks @ozymandiashh)

### Changed (@codeburn/core — breaking; version bump deferred to the next release, which must take at least a minor under 0.x)
- **The `model` field is bounded at the observation boundary.** `model` (and the optional `pricingModel`) on a `CallObservation` must now match the identifier charset `[A-Za-z0-9._:/@-]`, max 128 chars, in both the zod schema and the published `schemas/observation-0.2.0.json`. Every provider's `toObservations` normalizes through `normalizeModelIdentifier`, so a provider display name — Antigravity's `"Gemini 3.5 Flash (High)"`, Warp's and Devin's display strings — surfaces as `unknown` in an emitted observation rather than rejecting the whole envelope. **CLI output is unaffected:** normalization happens only at the observation boundary, so terminal, dashboard, menubar, and desktop numbers and model labels are byte-identical to before. The bound is a tightening of `observation-0.2.0` in place; an ARCHIVED pre-hardening 0.2.0 envelope whose `model` held a display name now fails validation against that same version string and must be re-normalized before re-validating.
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -665,6 +665,7 @@ These are starting points, not verdicts. A 60% cache hit on a single experimenta
| **Pi / OMP** | `~/.pi/agent/sessions/<sanitized-cwd>/*.jsonl` (Pi), `~/.omp/agent/sessions/<sanitized-cwd>/*.jsonl` (OMP) | Each assistant message carries usage (input, output, cacheRead, cacheWrite) plus inline `toolCall` blocks. Tool names normalize to the standard set (`bash` → `Bash`, `dispatch_agent` → `Agent`); bash commands come from `toolCall.arguments.command`. |
| **Codebuff** (formerly Manicode) | `~/.config/manicode/projects/<project>/chats/<chatId>/chat-messages.json` (honors `CODEBUFF_DATA_DIR`; walks `manicode-dev` / `manicode-staging`) | Bills in credits, so each completed assistant message is costed at the public rate of $0.01/credit via `msg.credits`. When an upstream provider's stashed RunState records token-level usage (`message.metadata.runState.sessionState.mainAgentState.messageHistory[*].providerOptions`), the real tokens and LiteLLM cost take precedence. Native tool names (`read_files`, `str_replace`, `run_terminal_command`, `spawn_agents`) normalize to `Read`, `Edit`, `Bash`, `Agent`. |
| **Cline / Roo Code / KiloCode** | VS Code `globalStorage`: Cline at `saoudrizwan.claude-dev` and `~/.cline/data`; Roo Code and KiloCode across VS Code, VS Code Insiders, and VSCodium | Cline-family agents. CodeBurn reads `ui_messages.json` from each task directory, extracting token counts from `type: "say"` entries with `say: "api_req_started"`. |
| **Cline CLI** | `~/.cline/data/sessions/<session-id>/` (honors `CLINE_SESSION_DATA_DIR`, `CLINE_DATA_DIR`, `CLINE_DIR`) | The Cline command-line agent, whose layout is unrelated to the VS Code extension's. Reads `<session-id>.json` for session metadata and the rolled-up `usage`, and `<session-id>.messages.json` for the per-message `metrics` block (input, output, cacheRead, cacheWrite, cost) that becomes one call each. |
| **CodeWhale** | `~/.codewhale/sessions/*.json` plus unmigrated legacy `~/.deepseek/sessions/*.json`; `$CODEWHALE_HOME/sessions` is an exact override | Emits one cumulative record per saved session. CodeWhale exposes only `total_tokens`, so CodeBurn preserves that aggregate in the input column rather than inventing an input/output split. Cost is the exact stored parent-session plus subagent USD total; model pricing is used only when the cost snapshot is absent. Tool blocks, shell commands, skills, and subagent types are retained. |
| **IBM Bob** | `User/globalStorage/ibm.bob-code/tasks/<task-id>/` (GA `IBM Bob` and preview `Bob-IDE` app folders) | Reads `ui_messages.json` for API request token/cost records and `api_conversation_history.json` for the selected model. |
| **Kimi Code CLI** | `$KIMI_SHARE_DIR/sessions/<workdir-hash>/<session-id>/` or `~/.kimi/sessions/<workdir-hash>/<session-id>/` | Reads `wire.jsonl` `StatusUpdate.token_usage` records, mapping `input_other`, `input_cache_read`, `input_cache_creation`, and `output` into the standard token columns; includes subagents under each session's `subagents/` folder. |
Expand Down
1 change: 1 addition & 0 deletions docs/providers/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ For the architectural picture, see `../architecture.md`.
|---|---|---|---|
| [Claude](claude.md) | JSONL (no parser) | `src/providers/claude.ts` | none (covered indirectly) |
| [Cline](cline.md) | JSON | `src/providers/cline.ts` | `tests/providers/cline.test.ts` |
| [Cline CLI](cline-cli.md) | JSON | `src/providers/cline-cli.ts` | `tests/providers/cline-cli.test.ts` |
| [CodeWhale](codewhale.md) | JSON | `src/providers/codewhale.ts` | `tests/providers/codewhale.test.ts` |
| [Codex](codex.md) | JSONL | `src/providers/codex.ts` | `tests/providers/codex.test.ts` |
| [Copilot](copilot.md) | JSONL + SQLite (OTel) + Nitrite .db (JetBrains) | `src/providers/copilot.ts` | `tests/providers/copilot.test.ts` |
Expand Down
58 changes: 58 additions & 0 deletions docs/providers/cline-cli.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Cline CLI

The Cline command-line agent (npm `cline`, 3.x). Separate from the [Cline](cline.md) provider, which reads the VS Code extension's task tree.

- **Source:** `src/providers/cline-cli.ts`
- **Loading:** eager (`src/providers/index.ts:3`)
- **Test:** `tests/providers/cline-cli.test.ts`

## Where it reads from

One root, resolved exactly as the CLI resolves it — each level independently overridable:

| Level | Env var | Default |
|---|---|---|
| sessions | `CLINE_SESSION_DATA_DIR` | `<data>/sessions` |
| data | `CLINE_DATA_DIR` | `<root>/data` |
| root | `CLINE_DIR` | `~/.cline` |

A directory is a session only when it contains `<sessionId>/<sessionId>.json`. `probeRoots()` reports the resolved sessions dir, so `codeburn doctor` distinguishes "CLI not installed" from "override pointing somewhere else".

## Storage format

```
sessions/<sessionId>/
<sessionId>.json metadata + rolled-up usage
<sessionId>.messages.json per-message metrics
```

`<sessionId>.json` carries `session_id`, `provider`, `model`, `cwd`, `workspace_root`, `started_at` / `ended_at`, `messages_path`, and a `metadata.usage` rollup (`inputTokens`, `outputTokens`, `cacheReadTokens`, `cacheWriteTokens`, `totalCost`).

`<sessionId>.messages.json` holds `{ version, updated_at, agent, sessionId, messages[], system_prompt }`. Assistant messages carry Anthropic-style content blocks (`thinking` / `text` / `tool_use`) plus:

```jsonc
"modelInfo": { "id": "z-ai/glm-5.2", "provider": "cline-pass" },
"metrics": { "inputTokens": 6937, "outputTokens": 213,
"cacheReadTokens": 0, "cacheWriteTokens": 0, "cost": 0.002108502 }
```

One `metrics` block becomes one parsed call. Dedup key: `cline-cli:<sessionId>:<messageId>`.

## Caching

None at the provider level; the metadata file is the cached source path and the normal parser/cache layers apply.

## Quirks

- **`provider` in the session file is the upstream LLM route** (e.g. `cline-pass`), not the tool. The codeburn provider name is always `cline-cli`.
- **Model strings are not normalized by the CLI.** The same model appears as `z-ai/glm-5.2`, `cline-pass/glm-5.2`, and `GLM-5.2` across sessions, so pricing lookups may need a `model-alias`.
- **Cost is reported per message**, so `costIsEstimated` is false on the normal path; it falls back to `calculateCost` only when a message omits `cost`.
- **Rollup fallback.** A session whose messages carry no metrics (interrupted, or an older layout) emits a single call from `metadata.usage`. This reads `usage`, deliberately *not* `aggregateUsage` / `aggregatedAgentsCost`, which fold in spawned subagents that are themselves separate session directories and would double count.
- **`messages_path` is absolute** and goes stale when a session directory is copied between machines, so the co-located `<sessionId>.messages.json` is preferred and `messages_path` is only the fallback.
- **Tool names differ from the extension's.** `run_commands`, `read_files`, `search_codebase`, `editor`, `apply_patch`, `fetch_web_content`, `skills`, `spawn_agent`, and the `team_*` family. `run_commands` carries a JSON-encoded array of command lines in a single string field.

## When fixing a bug here

1. Reproduce with a minimal session directory: `<id>.json` plus `<id>.messages.json`.
2. Run `tests/providers/cline-cli.test.ts`.
3. This provider shares no code with `vscode-cline-parser.ts` — changes here cannot affect Cline, Roo Code, KiloCode, or IBM Bob.
2 changes: 2 additions & 0 deletions docs/providers/cline.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

Cline VS Code extension and Cline home-data task storage.

Sessions from the Cline **command-line** agent use an unrelated layout and are handled by [Cline CLI](cline-cli.md); this provider does not see them.

- **Source:** `src/providers/cline.ts`
- **Loading:** eager (`src/providers/index.ts:2`)
- **Test:** `tests/providers/cline.test.ts`
Expand Down
19 changes: 18 additions & 1 deletion packages/cli/src/codex-cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,17 @@ import type { ParsedProviderCall } from './providers/types.js'
// re-parse of multi-GB rollout corpora over that is a bad trade — deliberately
// NOT bumped. The daily-cache bump alone propagates the discovery widening:
// newly-eligible files aren't in this cache yet and parse fresh regardless.
const CODEX_CACHE_VERSION = 8
//
// v12: tool-excluded active timing. Every cached call can now carry
// activeDurationMs / activeGeneratedTokens / toolWaitMs, the stored `state` can
// carry the task-boundary window flag, and `callCount` marks how much of
// `calls` a resumed decode may replay (the rest re-derives from the last
// task_started). Cached entries have none of that, so bump once and let
// unchanged sessions re-decode. This takes 12 rather than 9: main's own ladder
// has since reached 11 (#1078), and a shared version number on two different
// payload shapes would let a cache written by either line be read as current by
// the other.
const CODEX_CACHE_VERSION = 12
const CACHE_FILE = 'codex-results.json'

type FileFingerprint = { mtimeMs: number; sizeBytes: number }
Expand All @@ -41,8 +51,15 @@ type FileEntry = {
mtimeMs: number
sizeBytes: number
project: string
// Resume point: the byte offset after the last `task_started` line this file
// decoded past, with `state` snapshotted there and `callCount` counting the
// calls emitted before it. An appended tail resumes from that boundary and
// re-derives the calls beyond it, so a task whose task_complete lands in the
// appended region gets its timing from a whole re-read window instead of a
// patch applied to calls already served.
byteOffset: number
state: CodexDecodeState
callCount: number
calls: ParsedProviderCall[]
}

Expand Down
Loading
Loading