Skip to content

feat(transcript): decide how far the phone's conversation view extends beyond claude #1822

Description

@ystknsh

背景

スマホのターミナル詳細ページに会話ビューが入った(#1275#1751、スマホ側は receptron/mulmoserver#194)。tmux の画面キャプチャの代わりに TranscriptView を受け取り、ターンごとのブロックとして折り返して描く。

読めるのは claude のセッションだけ。codex / antigravity / grok / muse のセッションを開くと status: "none" が返り、スマホは画面キャプチャの表示に戻る。#1275 は claude を対象に閉じたので、他のエージェントをどうするかは決まっていない。

決めること

  1. どのエージェントを対象にするか — codex / antigravity / grok / muse のうち、会話ビューを出す価値があるのはどれか
  2. どの順で入れるか
  3. muse を含めるか — 1 つだけ sqlite を経由し、読み出しの形が他と違う(下記)

この issue は上の 3 つを決めるところまで。実装は決まったエージェントごとに別 issue へ切る(置き場も id の引き当ても畳む規則もエージェントごとに違うので、1 本にまとめると測り直しの対象が混ざる)。

判断に要るのに、この issue からは出せない材料が 1 つあります。実際にどのエージェントでセッションを回しているか。コスト側は下に揃えたので、そこに使用実績を重ねて決める形になります。

基準として想定しているのは「そのエージェントでスマホから会話を追いたい場面がどれだけあるか」で、実装コストはその次です。会話ビューが無いエージェントも壊れるわけではなく、これまでどおり画面キャプチャの表示に戻るだけなので、対象から外す判断も普通に取れます。

判断材料

対象範囲

getTerminalTranscript(sessionId) が答える範囲、つまりいま開けるセッション。launcher の "or resume here" が並べる過去の会話を読めるようにする話は含まない。

1 本目だけが払うコスト

入口は server/index.tscaptureTerminalTranscript: (sessionId) => sessionTranscriptView(cwdOfSession(sessionId), sessionId) で、server/session/transcript-view-read.tssessionTranscriptView が claude の置き場を直接組み立てている — cwd から ~/.claude/projects/<encoded-cwd>/ を導き、MulmoTerminal のセッション id をそのままファイル名として <id>.jsonl を開く。他のエージェントはこの場所にファイルを持たないので none になる。

エージェントで分岐していないのは意図的で、ここが実装の最大の制約になる。同関数の doc コメントいわく、agentOfSessionserver/index.ts)は再起動をまたいだ claude セッションを shell と報告する — claude pane の pane_current_command がバージョン文字列(2.1.233)で、agentFromPaneCommand に該当エントリが無いため。だから今は agent を聞かずファイルの存在を聞いている。素直に agentOfSession で reader を選ぶ実装にすると、再起動後の claude セッションが会話ビューを失う。

つまり「reader をどう選ぶか」の骨格を最初の 1 本が作る。2 本目以降はその骨格に乗る。ただし乗るのは骨格だけで、下の「レコード → 行」はエージェントごとに測り直しになるので、2 本目が無料になるわけではない。

エージェント別のコスト

agent 置き場と、セッション id からの引き当て レコード → 行の下地
grok ~/.grok/sessions/<encodeURIComponent(cwd)>/<uuid>/引き当て不要 — このサーバが --session-id <UUID> で自分の id を渡して会話を作るので、セッション id が grok の会話 id(server/agents/grok-session.ts、grok 0.2.118 で実測済み) 無し。ただし updates.jsonltranscript-fold で追う前例はある(#1377
codex ~/.codex/sessions/<日付ツリー>/rollout-*.jsonl。rollout id は codex が自分で mint するので別物で、対応は server/session/agent-conversations.ts の append log が持つ。id からファイルへは codexRolloutPathserver/agents/codex-sessions.ts ありserver/agents/codex-events.tsevent_msg の payload を読み、server/session/last-turn.tslastTurnFromCodexRollout がターン境界と最終返信を既に取り出している
antigravity brain ディレクトリ。同じ append log を引く。cwd は agy 自身のファイルからは答えられないので、これが唯一の経路(server/agents/antigravity-sessions.ts 冒頭) 無し。加えて prompt が <USER_REQUEST> / <ADDITIONAL_METADATA> / <USER_SETTINGS_CHANGE> でラップされるので、剥がす処理が要る
muse ~/.local/share/muse/session-index.db(sqlite)を session_id で引き、その行の session_log_path が指す session.jsonl を読む(workspace_root は cwd の照合用。server/agents/muse-session.ts 無し。ただし server/agents/muse-usage.ts が同じログを transcript-fold で追っていて、sqlite を経由する読み出し自体はバッジのポーリングで既に動いている。ログは実測で 1 日 33 MB に育つ

JSONL の head を読む部分(server/agents/transcript-head.ts)は codex / antigravity / grok の 3 つで既に共有されている。ただしこれは launcher の一覧がタイトルを取り出すための共有で、会話ビューの畳み処理はここに含まれない。

気をつけること

  • 畳む規則が claude のレコード形に対して書かれているserver/session/transcript-view.ts のターン境界(user プロンプトのレコード)、TOOL_RESULT_MAX_LINESthinking を落とす判断、知らない content ブロックを unknown 行にする扱いは、いずれも claude の JSONL を実測して決めたもの。エージェントごとに測り直すことになる
  • TRANSCRIPT_LINE_BUDGET / TRANSCRIPT_MAX_BYTES はエージェントに依らない上限なので、そのまま効く
  • status: "cleared" は claude 固有/clear が新しい id と新しい transcript を作る前提(server/session/cleared-transcripts.ts)。他のエージェントに同等の状態があるかは未調査で、無ければ単に返さなければよい
  • スマホ側は、ホストが同じ TranscriptView の形で status: "ok" を返す限り変更が要らない。描画は kinduser / assistant / tool / unknown)だけを見ていて、どのエージェントの会話かは見ていない。エージェント名を表示に出したくなった場合だけ、スマホ側にも作業が出る

考えられるアプローチ

スコープの候補を 3 つ挙げます。どれも「1 本目が reader の選び方の骨格を作る」ぶんを含みます。

  • A: grok だけ — 骨格を最も安く確かめられます。id の引き当てが無く claude と同じ形(セッション id で直接引ける)なので、新しく書くのは畳む規則だけ
  • B: grok + codex — A に、畳む規則の下地がある codex を足す形。会話ビューが claude / codex / grok で使えるようになります
  • C: 全部 — antigravity と muse まで。この 2 つは畳む規則を丸ごと新規に書くことになり、antigravity は prompt のラップを剥がす処理、muse は sqlite を経由する読み出しが加わります

関連

本文の実装記述は remote main(a71872a1)のソースを読んで書いています。

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions