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
36 changes: 18 additions & 18 deletions .claude/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,12 +80,12 @@ Found 23 skills:
/home/clifford/Documents/source/nori/cli/.claude/skills/using-skills/SKILL.md
Name: Getting Started with Abilities
Description: Describes how to use abilities. Read before any conversation.
/home/clifford/Documents/source/nori/cli/.claude/skills/using-screenshots/SKILL.md
Name: Taking and Analyzing Screenshots
Description: Use this to capture screen context.
/home/clifford/Documents/source/nori/cli/.claude/skills/using-git-worktrees/SKILL.md
Name: Using Git Worktrees
Description: Use this whenever you need to create an isolated workspace.
/home/clifford/Documents/source/nori/cli/.claude/skills/using-screenshots/SKILL.md
Name: Taking and Analyzing Screenshots
Description: Use this to capture screen context.
/home/clifford/Documents/source/nori/cli/.claude/skills/updating-noridocs/SKILL.md
Name: Updating Noridocs
Description: Use this when you have finished making code changes and you are ready to update the documentation based on those changes.
Expand All @@ -107,39 +107,39 @@ Found 23 skills:
/home/clifford/Documents/source/nori/cli/.claude/skills/write-noridoc/SKILL.md
Name: Write Noridoc
Description: Write or update documentation in the server-side noridocs system.
/home/clifford/Documents/source/nori/cli/.claude/skills/sync-noridocs/SKILL.md
Name: Sync Noridocs
Description: Sync all local docs.md files to server-side noridocs system.
/home/clifford/Documents/source/nori/cli/.claude/skills/recall/SKILL.md
Name: Recall
Description: Search the Nori knowledge base for relevant context, solutions, and documentation.
/home/clifford/Documents/source/nori/cli/.claude/skills/read-noridoc/SKILL.md
Name: Read Noridoc
Description: Read documentation from the server-side noridocs system by file path.
/home/clifford/Documents/source/nori/cli/.claude/skills/prompt-analysis/SKILL.md
Name: Prompt Analysis
Description: Analyze prompts for quality and best practices before sending them to Claude.
/home/clifford/Documents/source/nori/cli/.claude/skills/sync-noridocs/SKILL.md
Name: Sync Noridocs
Description: Sync all local docs.md files to server-side noridocs system.
/home/clifford/Documents/source/nori/cli/.claude/skills/memorize/SKILL.md
Name: Memorize
Description: Use this to save important implementation decisions, patterns, or context to the Nori knowledge base for future sessions.
/home/clifford/Documents/source/nori/cli/.claude/skills/list-noridocs/SKILL.md
Name: List Noridocs
Description: List all server-side noridocs, optionally filtered by repository and/or path prefix.
/home/clifford/Documents/source/nori/cli/.claude/skills/read-noridoc/SKILL.md
Name: Read Noridoc
Description: Read documentation from the server-side noridocs system by file path.
/home/clifford/Documents/source/nori/cli/.claude/skills/handle-large-tasks/SKILL.md
Name: Handle-Large-Tasks
Description: Use this skill to split large plans into smaller chunks. This skill manages your context window for large tasks. Use it when a task will take a long time and cause context issues.
/home/clifford/Documents/source/nori/cli/.claude/skills/finishing-a-development-branch/SKILL.md
Name: Finishing a Development Branch
Description: Use this when you have completed some feature implementation and have written passing tests, and you are ready to create a PR.
/home/clifford/Documents/source/nori/cli/.claude/skills/prompt-analysis/SKILL.md
Name: Prompt Analysis
Description: Analyze prompts for quality and best practices before sending them to Claude.
/home/clifford/Documents/source/nori/cli/.claude/skills/creating-skills/SKILL.md
Name: Creating-Skills
Description: Use when you need to create a new custom skill for a profile - guides through gathering requirements, creating directory structure, writing SKILL.md, and optionally adding bundled scripts
/home/clifford/Documents/source/nori/cli/.claude/skills/building-ui-ux/SKILL.md
Name: Building UI/UX
Description: Use when implementing user interfaces or user experiences - guides through exploration of design variations, frontend setup, iteration, and proper integration
/home/clifford/Documents/source/nori/cli/.claude/skills/brainstorming/SKILL.md
Name: Brainstorming
Description: IMMEDIATELY USE THIS SKILL when creating or develop anything and before writing code or implementation plans - refines rough ideas into fully-formed designs through structured Socratic questioning, alternative exploration, and incremental validation
/home/clifford/Documents/source/nori/cli/.claude/skills/building-ui-ux/SKILL.md
Name: Building UI/UX
Description: Use when implementing user interfaces or user experiences - guides through exploration of design variations, frontend setup, iteration, and proper integration
/home/clifford/Documents/source/nori/cli/.claude/skills/list-noridocs/SKILL.md
Name: List Noridocs
Description: List all server-side noridocs, optionally filtered by repository and/or path prefix.

Check if any of these skills are relevant to the user's task. If relevant, use the Read tool to load the skill before proceeding.

Expand Down
233 changes: 233 additions & 0 deletions IMPLEMENTATION_PLAN.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
# Agent Session ID Discovery and Token Usage Implementation Plan

**Goal:** Enable the `/status` command to display token usage by discovering the current ACP session ID, locating the corresponding session transcript, and parsing token usage data.

**Architecture:** The ACP protocol provides session IDs at session creation. We will add a session discovery module to locate transcript files based on agent type and session ID, then integrate parsed token usage into the Nori session header displayed by `/status`.

**Tech Stack:** Rust, codex-acp, codex-tui, session_parser module, tokio async

---

## Testing Plan

I will add unit tests that ensure:
1. Session transcript path building works correctly for each agent type (Claude, Codex, Gemini)
2. The session discovery module can find transcripts given session ID and agent kind
3. Token usage integration in session_header displays correctly

I will add integration tests that ensure:
1. End-to-end flow: session ID from ACP → transcript discovery → token usage parsing → display
2. Edge cases: missing transcripts, malformed session IDs, no token data available

NOTE: I will write *all* tests before I add any implementation behavior.

---

## Phase 1: Session Discovery Module

### Step 1.1: Create session_discovery.rs module skeleton
- **File**: `/home/user/nori-cli/codex-rs/acp/src/session_discovery.rs`
- **Actions**:
1. Create the file with module documentation
2. Define `discover_transcript_path(agent_kind: AgentKind, session_id: &str, cwd: &Path) -> Option<PathBuf>`
3. Export from `/home/user/nori-cli/codex-rs/acp/src/lib.rs`

### Step 1.2: Write failing tests for Claude transcript discovery
- **File**: `/home/user/nori-cli/codex-rs/acp/tests/session_discovery_test.rs`
- **Test cases**:
1. `test_claude_transcript_path_format` - verify path format `~/.claude/projects/<PROJECT_PATH>/<SESSIONID>.jsonl`
2. `test_claude_transcript_discovery_with_valid_session` - given session ID, find transcript
3. `test_claude_transcript_discovery_missing_file` - return None when file not found

### Step 1.3: Write failing tests for Codex transcript discovery
- **Test cases**:
1. `test_codex_transcript_path_discovery` - search by session GUID in filename
2. `test_codex_transcript_discovery_by_date` - find in date-organized directory

### Step 1.4: Write failing tests for Gemini transcript discovery
- **Test cases**:
1. `test_gemini_transcript_path_format` - verify path format with hashed paths
2. `test_gemini_transcript_discovery_with_session_id`

### Step 1.5: Run tests, verify they fail
```bash
cargo test -p codex-acp session_discovery
```

### Step 1.6: Implement Claude transcript discovery
- **File**: `/home/user/nori-cli/codex-rs/acp/src/session_discovery.rs`
- **Logic**:
1. Expand `~/.claude/projects/` base path
2. Build relative project path from `cwd` (hash or relativize)
3. Check for `<SESSION_ID>.jsonl` file existence
4. Return path if found

### Step 1.7: Implement Codex transcript discovery
- **Logic**:
1. Expand `~/.codex/sessions/` base path
2. Search recursively for files matching `*-<SESSION_GUID>.jsonl`
3. Return first match or None

### Step 1.8: Implement Gemini transcript discovery
- **Logic**:
1. Expand `~/.gemini/tmp/` base path
2. Hash the cwd to get the hashed path component
3. Search for `session-*-<SESSION_ID>.json`
4. Return path if found

### Step 1.9: Run tests, verify they pass
```bash
cargo test -p codex-acp session_discovery
```

### Step 1.10: Commit Phase 1
```bash
git add -A && git commit -m "feat(acp): Add session transcript discovery module"
```

---

## Phase 2: Expose Session ID from ACP Backend

### Step 2.1: Write failing test for session_id accessor
- **File**: `/home/user/nori-cli/codex-rs/acp/tests/backend_test.rs` (or add to existing)
- **Test**: Verify `AcpBackend::session_id()` returns the session ID string

### Step 2.2: Run test, verify it fails

### Step 2.3: Add session_id() method to AcpBackend
- **File**: `/home/user/nori-cli/codex-rs/acp/src/backend.rs`
- **Current**: `AcpBackend` already has `session_id: Arc<acp::SessionId>` (backend.rs:102-111)
- **Add**: Public accessor `pub fn session_id(&self) -> &str`

### Step 2.4: Run test, verify it passes

### Step 2.5: Add agent_kind() method to AcpBackend
- **File**: `/home/user/nori-cli/codex-rs/acp/src/backend.rs`
- **Add**: Store and expose `AgentKind` from the `AcpAgentConfig`

### Step 2.6: Commit Phase 2
```bash
git add -A && git commit -m "feat(acp): Expose session_id and agent_kind from AcpBackend"
```

---

## Phase 3: Extend TUI to Track ACP Session Info

### Step 3.1: Write failing test for session info in ChatWidget
- **File**: `/home/user/nori-cli/codex-rs/tui/src/chatwidget/tests.rs`
- **Test**: Verify ChatWidget can access session ID when in ACP mode

### Step 3.2: Run test, verify it fails

### Step 3.3: Add session info channel/handle to ACP agent spawning
- **File**: `/home/user/nori-cli/codex-rs/tui/src/chatwidget/agent.rs`
- **Modify**: `SpawnAgentResult` to include optional `AcpSessionInfo { session_id: String, agent_kind: AgentKind }`
- **Modify**: `spawn_acp_agent` to extract and return session info

### Step 3.4: Store session info in ChatWidget
- **File**: `/home/user/nori-cli/codex-rs/tui/src/chatwidget.rs`
- **Add**: Optional field `acp_session_info: Option<AcpSessionInfo>`
- **Update**: On agent spawn, store the session info

### Step 3.5: Run test, verify it passes

### Step 3.6: Commit Phase 3
```bash
git add -A && git commit -m "feat(tui): Track ACP session info for token usage display"
```

---

## Phase 4: Integrate Token Usage into /status Command

### Step 4.1: Write failing snapshot test for status with token usage
- **File**: `/home/user/nori-cli/codex-rs/tui/src/nori/session_header.rs`
- **Test**: Verify token usage section appears in `/status` output when available

### Step 4.2: Run test, verify it fails

### Step 4.3: Modify new_nori_status_output signature
- **File**: `/home/user/nori-cli/codex-rs/tui/src/nori/session_header.rs`
- **Change**: Add optional `TokenUsageReport` parameter
- **Display**: Add "Token Usage" section showing:
- Total tokens (input + output)
- Input tokens (with cached breakdown if available)
- Output tokens (with reasoning breakdown if available)
- Context window usage percentage if available

### Step 4.4: Update add_status_output in ChatWidget
- **File**: `/home/user/nori-cli/codex-rs/tui/src/chatwidget.rs`
- **Modify**: `add_status_output()` to:
1. Check if ACP session info is available
2. If yes, call session discovery to find transcript path
3. Parse token usage using `parse_session_transcript()`
4. Pass token usage to `new_nori_status_output()`

### Step 4.5: Run tests, verify they pass
```bash
cargo test -p codex-tui nori_session_header
```

### Step 4.6: Update snapshot tests
```bash
cargo test -p codex-tui -- --update-snapshots
```

### Step 4.7: Commit Phase 4
```bash
git add -A && git commit -m "feat(tui): Display token usage in /status command for ACP sessions"
```

---

## Phase 5: Handle Edge Cases

### Step 5.1: Add test for missing transcript file
- Verify graceful handling when transcript file doesn't exist yet (new session)

### Step 5.2: Add test for malformed/empty transcript
- Verify graceful handling when transcript is empty or malformed

### Step 5.3: Add test for HTTP mode (non-ACP)
- Verify `/status` still works when not using ACP

### Step 5.4: Implement error handling
- Return None for token usage if discovery or parsing fails
- Log warnings for debugging
- Display "Token usage unavailable" or similar when not available

### Step 5.5: Commit Phase 5
```bash
git add -A && git commit -m "fix(tui): Handle edge cases for token usage display"
```

---

## Testing Details

Tests will verify:
1. **Session discovery behavior**: Given agent type + session ID + cwd, correct transcript path is found
2. **Token usage display**: Snapshot tests verify the formatting of token usage in /status output
3. **Edge case handling**: Graceful degradation when transcripts are missing/malformed

## Implementation Details

- Session ID comes from `AcpBackend::session_id()` which wraps `acp::SessionId` from the protocol
- Transcript paths differ per agent:
- Claude: `~/.claude/projects/<PROJECT_PATH>/<SESSIONID>.jsonl`
- Codex: `~/.codex/sessions/<YEAR>/<MM>/<DD>/rollout-*-<SESSION_GUID>.jsonl`
- Gemini: `~/.gemini/tmp/<HASHED_PATHS>/chats/session-*-<SESSIONID>.json`
- `parse_session_transcript()` from `session_parser.rs` already handles token aggregation
- Token usage display reuses existing formatting from `status/card.rs` patterns

## Questions

1. **Claude project path hashing**: Claude stores projects in hashed paths. Need to verify the exact hashing algorithm used (likely SHA-256 or similar). Should check Claude Code source or existing transcript fixtures.

2. **Async vs sync transcript reading**: Should transcript discovery and parsing be async? Currently `parse_session_transcript` uses sync I/O. For responsiveness, might want async with timeout.

3. **Caching**: Should we cache the parsed token usage to avoid re-parsing on every `/status` call? The transcript file grows during the session, so fresh reads may be needed.

---
8 changes: 8 additions & 0 deletions codex-rs/acp/src/backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ use crate::connection::AcpConnection;
use crate::connection::AcpModelState;
use crate::connection::ApprovalEventType;
use crate::connection::ApprovalRequest;
use crate::registry::AgentKind;
use crate::registry::get_agent_config;
use crate::translator;
use crate::translator::is_patch_operation;
Expand Down Expand Up @@ -63,6 +64,7 @@ pub struct AcpBackendConfig {
pub struct AcpBackend {
connection: Arc<AcpConnection>,
session_id: acp::SessionId,
agent_kind: AgentKind,
event_tx: mpsc::Sender<Event>,
#[allow(dead_code)]
cwd: PathBuf,
Expand Down Expand Up @@ -109,6 +111,7 @@ impl AcpBackend {
let backend = Self {
connection,
session_id,
agent_kind: agent_config.agent,
event_tx: event_tx.clone(),
cwd: cwd.clone(),
pending_approvals: Arc::clone(&pending_approvals),
Expand Down Expand Up @@ -404,6 +407,11 @@ impl AcpBackend {
&self.session_id
}

/// Get the agent kind (Claude, Codex, or Gemini).
pub fn agent_kind(&self) -> AgentKind {
self.agent_kind
}

/// Get a reference to the underlying ACP connection.
///
/// This provides access to low-level ACP operations like model switching.
Expand Down
7 changes: 7 additions & 0 deletions codex-rs/acp/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ pub mod backend;
pub mod config;
pub mod connection;
pub mod registry;
pub mod session_discovery;
pub mod session_parser;
pub mod tracing_setup;
pub mod translator;
Expand Down Expand Up @@ -46,6 +47,12 @@ pub use tracing_setup::init_rolling_file_tracing;
pub use translator::TranslatedEvent;
pub use translator::translate_session_update;

// Session discovery exports
pub use session_discovery::DiscoveryError;
pub use session_discovery::cwd_to_claude_project_path;
pub use session_discovery::discover_transcript_path;
pub use session_discovery::discover_transcript_path_with_home;

// Re-export commonly used types from agent-client-protocol
pub use agent_client_protocol::Agent;
pub use agent_client_protocol::Client;
Expand Down
Loading