Skip to content

fix: last-response viewer follows /clear to the new Claude conversation#76

Open
TeigenZhang wants to merge 1 commit intoArk0N:masterfrom
TeigenZhang:fix/eye-icon-follows-clear
Open

fix: last-response viewer follows /clear to the new Claude conversation#76
TeigenZhang wants to merge 1 commit intoArk0N:masterfrom
TeigenZhang:fix/eye-icon-follows-clear

Conversation

@TeigenZhang
Copy link
Copy Markdown
Contributor

Summary

The last-response viewer (eye icon) keeps showing the pre-/clear transcript after a user runs /clear in the Claude CLI. This is because the Session's _claudeSessionId is only refreshed when Claude emits JSON on stdout, which never happens in the interactive PTY mode Codeman spawns.

This PR adds two complementary update paths:

  • Session.adoptClaudeSessionId() — public setter mirroring the existing no-op-if-same guard. Called from POST /api/hook-event whenever Claude Code hooks carry data.session_id (works automatically once hooks are configured in the target case).

  • /api/sessions/:id/last-response now resolves the active conversation id from ~/.claude/history.jsonl before reading the transcript. This is the only source-of-truth that does not require hooks, which matters because Codeman intentionally does not write hook config into arbitrary user repos (see the comment at session-routes.ts POST /api/sessions).

The history scan:

  • Skips entries held by other Codeman sessions in the same cwd so concurrent tabs don't shadow each other.
  • Requires the candidate's jsonl mtime to exceed the initial <session.id>.jsonl mtime, so a stale id inherited from a prior dead session is never adopted.

Why history.jsonl

Claude Code writes one line per user prompt to ~/.claude/history.jsonl with {sessionId, project, timestamp}. After /clear the new conversation uuid shows up there on the first prompt. ~/.claude/sessions/<pid>.json would be nicer but older Claude CLI versions don't update it after /clear, so it isn't reliable across versions.

Test plan

  • Verified against a session that had been /cleared: claudeSessionId adopts the post-/clear uuid on the next eye-icon request, and the returned transcript reflects the new conversation.
  • Verified non-cleared sessions are unaffected (claudeSessionId stays equal to session.id).
  • Synthetic hook POST with a fake session_id flips claudeSessionId and a follow-up POST with the original id restores it.
  • tsc --noEmit clean, npm run build succeeds.

Known limitation

If two concurrent Codeman sessions in the same cwd both run /clear before either has had its id re-resolved, the history scan can't disambiguate them (history.jsonl doesn't record which Codeman session typed). Single-session /clear and single-tab-per-cwd setups are unaffected.

Interactive Claude CLI never emits session_id on stdout, so the
Session's _claudeSessionId stayed pinned to the pre-/clear jsonl and
the last-response viewer kept showing the old conversation.

Two complementary update paths:

- Session.adoptClaudeSessionId() — public setter mirroring the existing
  no-op-if-same guard. Called from POST /api/hook-event when Claude Code
  hooks carry data.session_id (works once hooks are configured).

- /api/sessions/:id/last-response now resolves the active id from
  ~/.claude/history.jsonl before reading the transcript. This is the
  only source-of-truth that does not require hooks, and we intentionally
  don't write hooks into arbitrary user repos.

History scan filters out sessionIds held by other Codeman sessions in
the same cwd, and validates via jsonl mtime to avoid inheriting a dead
prior session's id.
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