- ホーム
- Documentation
- セッション
- セッション操作: export、dump、share、fork、resume/continue
セッション操作: export、dump、share、fork、resume/continue
このドキュメントでは、現在実装されているセッションのエクスポート/共有/フォーク/再開操作について、オペレーターから見える動作を説明します。
実装ファイル
Section titled “実装ファイル”../src/modes/controllers/command-controller.ts../src/session/agent-session.ts../src/session/session-manager.ts../src/export/html/index.ts../src/export/custom-share.ts../src/main.ts
操作マトリクス
Section titled “操作マトリクス”| 操作 | エントリパス | セッション変更 | セッションファイルの作成/切り替え | 出力アーティファクト |
|---|---|---|---|---|
/dump | インタラクティブスラッシュコマンド | なし | なし | クリップボードテキスト |
/export [path] | インタラクティブスラッシュコマンド | なし | なし | HTMLファイル |
--export <session.jsonl> [outputPath] | CLIスタートアップファストパス | ランタイムセッション変更なし | アクティブセッションなし、対象ファイルを読み取り | HTMLファイル |
/share | インタラクティブスラッシュコマンド | なし | なし | 一時HTML + 共有URL/gist |
/fork | インタラクティブスラッシュコマンド | あり(アクティブセッションのIDが変更) | 新しいセッションファイルを作成し、現在のセッションをそれに切り替え(永続モードのみ) | アーティファクトディレクトリが存在する場合、新しいセッション名前空間にコピー |
/resume | インタラクティブスラッシュコマンド | あり(アクティブなインメモリ状態が置換) | 選択された既存セッションファイルに切り替え | なし |
--resume | CLIスタートアップ(ピッカー) | セッション作成後にあり | 選択された既存セッションファイルを開く | なし |
--resume <id|path> | CLIスタートアップ | セッション作成後にあり | 既存セッションを開く。クロスプロジェクトの場合は現在のプロジェクトにフォーク可能 | なし |
--continue | CLIスタートアップ | セッション作成後にあり | ターミナルのブレッドクラムまたは最新セッションを開く。存在しない場合は新規作成 | なし |
エクスポートとダンプ
Section titled “エクスポートとダンプ”/export [outputPath](インタラクティブ)
Section titled “/export [outputPath](インタラクティブ)”フロー:
InputControllerが/export...をCommandController.handleExportCommandにルーティングします。- コマンドはホワイトスペースで分割し、
/exportの後の最初の引数のみをoutputPathとして使用します。 AgentSession.exportToHtml()がexportSessionToHtml(sessionManager, state, { outputPath, themeName })を呼び出します。- 成功時、UIはパスを表示し、ブラウザでファイルを開きます。
動作の詳細:
--copy、clipboard、copy引数は明示的に拒否され、/dumpを使用するよう警告が表示されます。- エクスポートにはセッションのヘッダー/エントリ/リーフと、現在の
systemPromptおよびエージェント状態からのツール説明が埋め込まれます。 - エクスポート中にセッションエントリは追加されません。
注意点:
- 引数の解析はホワイトスペースベース(
text.split(/\s+/))であるため、スペースを含むクォートされたパスはこのコマンドパスでは単一のパスとして保持されません。
--export <inputSessionFile> [outputPath](CLI)
Section titled “--export <inputSessionFile> [outputPath](CLI)”main.ts でのフロー:
- 早期に処理されます(インタラクティブ/セッションスタートアップの前)。
exportFromFile(inputPath, outputPath?)を呼び出します。SessionManager.open(inputPath)がエントリを読み込み、その後HTMLが生成されて書き込まれます。- プロセスは
Exported to: ...と表示して終了します。
動作の詳細:
- 入力ファイルが存在しない場合、
File not found: <path>としてエラーが表示されます。 - このパスは
AgentSessionを作成せず、実行中のセッションを変更しません。
/dump(インタラクティブクリップボードエクスポート)
Section titled “/dump(インタラクティブクリップボードエクスポート)”フロー:
CommandController.handleDumpCommand()がsession.formatSessionAsText()を呼び出します。- 空文字列の場合、
No messages to dump yet.と報告します。 - それ以外の場合、ネイティブの
copyToClipboardでクリップボードにコピーします。
ダンプの内容:
- システムプロンプト
- アクティブなモデル/思考レベル
- ツール定義 + パラメータ
- ユーザー/アシスタントメッセージ
- 思考ブロックとツール呼び出し
- ツール結果と実行ブロック(
excludeFromContextのbash/pythonエントリを除く) - カスタム/フック/ファイルメンション/ブランチサマリー/コンパクションサマリーエントリ
ダンプによるセッション永続化の変更はありません。
/share はインタラクティブ専用で、常に現在のセッションを一時HTMLファイルにエクスポートすることから開始します。
フェーズ1: 一時エクスポート
Section titled “フェーズ1: 一時エクスポート”- 一時ファイルパス:
${os.tmpdir()}/${Snowflake.next()}.html session.exportToHtml(tmpFile)を使用- エクスポートが失敗した場合(特にインメモリセッション)、共有はエラーで終了します。
フェーズ2: カスタム共有ハンドラー(存在する場合)
Section titled “フェーズ2: カスタム共有ハンドラー(存在する場合)”loadCustomShare() は ~/.xcsh/agent で最初に存在する候補をチェックします:
share.tsshare.jsshare.mjs
要件:
- モジュールは
(htmlPath) => Promise<CustomShareResult | string | undefined>関数をデフォルトエクスポートする必要があります。
存在し、有効な場合:
- UIは
Sharing...ローダー状態になります。 - ハンドラー結果の解釈:
- 文字列 => URLとして扱われ、表示されて開かれる
- オブジェクト =>
urlおよび/またはmessageが表示される。urlが開かれる undefined/falsy => 汎用的なSession shared
- 完了後、一時ファイルは削除されます。
重要なフォールバック動作:
- カスタムハンドラーが存在するが読み込みに失敗した場合、コマンドはエラーで終了します。
- カスタムハンドラーが実行されて例外をスローした場合、コマンドはエラーで終了します。
- いずれの失敗ケースでも、GitHub gistへのフォールバックは行われません。
- Gistフォールバックは、カスタム共有スクリプトが存在しない場合にのみ発生します。
フェーズ3: デフォルトgistフォールバック
Section titled “フェーズ3: デフォルトgistフォールバック”カスタム共有ハンドラーが見つからない場合のみ:
gh auth statusを検証します。Creating gist...ローダーを表示します。gh gist create --public=false <tmpFile>を実行します。- gist URLを解析し、gist idを導出し、プレビューURL
https://gistpreview.github.io/?<id>を構築します。 - プレビューURLとgist URLの両方を表示し、プレビューを開きます。
共有でのキャンセル/中断セマンティクス:
- ローダーには、エディターUIを復元して
Share cancelledと報告するonAbortフックがあります。 - このコードパスでは、基盤となる
gh gist createコマンドにアボートシグナルは渡されません。キャンセルはUIレベルで、コマンドが返された後にチェックされます。
/fork は現在のセッションから新しいセッションを作成し、アクティブなセッションのIDを切り替えます。
前提条件と即時ガード
Section titled “前提条件と即時ガード”- エージェントがストリーミング中の場合、
/forkは警告付きで拒否されます。 - 操作前にUIステータス/ローディングインジケーターがクリアされます。
セッションレベルのフロー
Section titled “セッションレベルのフロー”AgentSession.fork():
reason: "fork"でsession_before_switchを発行します(キャンセル可能)。- 保留中の書き込みをフラッシュします。
SessionManager.fork()を呼び出します。- 旧セッション名前空間から新しい名前空間にアーティファクトディレクトリをコピーします(ベストエフォート。ENOENT以外のコピー失敗はログに記録されますが、致命的ではありません)。
agent.sessionIdを更新します。reason: "fork"でsession_switchを発行します。
SessionManager.fork() の動作:
- 永続モードと既存のセッションファイルが必要です。
- 新しいセッションIDと新しいJSONLファイルパスを作成します。
- ヘッダーを以下の内容で書き換えます:
- 新しい
id - 新しいタイムスタンプ
cwdは変更なしparentSessionに前のセッションIDを設定
- 新しい
- 新しいファイル内のヘッダー以外のすべてのエントリは変更なし。
非永続モードの動作
Section titled “非永続モードの動作”- インメモリセッションマネージャーは
fork()からundefinedを返します。 AgentSession.fork()はfalseを返します。- UIは
Fork failed (session not persisted or cancelled)と報告します。
再開とコンティニュー
Section titled “再開とコンティニュー”インタラクティブ /resume
Section titled “インタラクティブ /resume”フロー:
SessionManager.list(currentCwd, currentSessionDir)で取得したセッションセレクターを開きます。- 選択時、
SelectorController.handleResumeSession(sessionPath)がsession.switchSession(sessionPath)を呼び出します。 - UIはチャットとTodoをクリア/再構築し、
Resumed sessionと報告します。
注意:
- このピッカーは現在のセッションディレクトリスコープ内のセッションのみをリストします。
- グローバルなクロスプロジェクト検索は使用しません。
CLI --resume
Section titled “CLI --resume”--resume(値なし)
Section titled “--resume(値なし)”main.tsが現在のcwd/sessionDirのセッションをリストし、ピッカーを開きます。- 選択されたパスは、セッション作成前に
SessionManager.open(selectedPath)で開かれます。
--resume <value>
Section titled “--resume <value>”createSessionManager() の解決順序:
- 値がパスのように見える場合(
/、\、または.jsonl)、直接開きます。 - そうでなければIDプレフィックスとして扱います:
- 現在のスコープを検索(
SessionManager.list(cwd, sessionDir)) - 見つからず、明示的な
sessionDirがない場合、グローバル検索(SessionManager.listAll())
- 現在のスコープを検索(
クロスプロジェクトID一致の動作:
- 一致したセッションのcwdが現在のcwdと異なる場合、CLIは以下を確認します:
Session found in different project ... Fork into current directory? [y/N]
- yesの場合:
SessionManager.forkFrom(match.path, cwd, sessionDir)が新しいローカルフォークファイルを作成します。 - no/非TTYデフォルトの場合: コマンドはエラーになります。
CLI --continue
Section titled “CLI --continue”SessionManager.continueRecent(cwd, sessionDir):
- 現在のcwdのセッションディレクトリを解決します。
- まずターミナルスコープのブレッドクラムを読み取ります。
- 最も最近変更されたセッションファイルにフォールバックします。
- 見つかったセッションを開きます。存在しない場合は新しいセッションを作成します。
これはスタートアップ時のみの動作です。インタラクティブな /continue スラッシュコマンドはありません。
セッション切り替えが実際にランタイム状態を変更する方法
Section titled “セッション切り替えが実際にランタイム状態を変更する方法”AgentSession.switchSession(sessionPath) は、再開系の操作で使用されるランタイム遷移を実行します:
reason: "resume"とtargetSessionFileでsession_before_switchを発行します(キャンセル可能)。- エージェントイベントサブスクリプションを切断し、実行中の作業を中止します。
- キューに入っているステアリング/フォローアップ/次ターンメッセージをクリアします。
- 現在のセッションマネージャーの書き込みをフラッシュします。
sessionManager.setSessionFile(sessionPath)を実行し、agent.sessionIdを更新します。- 読み込まれたエントリからセッションコンテキストを構築します。
reason: "resume"でsession_switchを発行します。- コンテキストからエージェントメッセージを置換します。
- モデルを復元します(現在のレジストリで利用可能な場合)。
- 思考レベルを復元または初期化します。
- エージェントイベントサブスクリプションを再接続します。
switchSession() 自体は新しいセッションファイルを作成しません。
イベント発行とキャンセルポイント
Section titled “イベント発行とキャンセルポイント”切り替え/フォークのライフサイクルフック
Section titled “切り替え/フォークのライフサイクルフック”newSession、fork、switchSession の場合:
- Beforeイベント:
session_before_switch- reason:
new、fork、resume { cancel: true }を返すことでキャンセル可能
- reason:
- Afterイベント:
session_switch- 同じreasonセット
previousSessionFileを含む
ExtensionRunner.emit() は、最初のキャンセルするbeforeイベント結果で早期リターンします。
カスタムツールの onSession 動作
Section titled “カスタムツールの onSession 動作”SDKブリッジが拡張セッションイベントをカスタムツールの onSession コールバックに接続します:
session_switch->onSession({ reason: "switch", previousSessionFile })session_branch->reason: "branch"session_start->reason: "start"session_tree->reason: "tree"session_shutdown->reason: "shutdown"
これらのコールバックは監視用であり、切り替え/フォークをキャンセルしません。
このドキュメントに関連するその他のキャンセルサーフェス
Section titled “このドキュメントに関連するその他のキャンセルサーフェス”/forkはストリーミング中はブロックされます(ユーザーは現在のレスポンスを待つか中止する必要があります)。/resumeセレクターはユーザーがセレクターを閉じることでキャンセルできます。- クロスプロジェクトの
--resume <id>はフォークプロンプトを拒否することでキャンセルできます。 /shareにはgistフローのUIアボートパス(Share cancelled)がありますが、このコードパスではgh gist createに対するプロセスキルセマンティクスは接続されていません。
非永続(インメモリ)セッションの動作
Section titled “非永続(インメモリ)セッションの動作”セッションマネージャーが SessionManager.inMemory()(--no-session)で作成された場合:
- セッションファイルパスは存在しません。
/exportと/shareはCannot export in-memory session to HTMLで失敗します(コマンドエラーUIに伝播)。/forkはSessionManager.fork()が永続性を必要とするため失敗します。/dumpはインメモリのエージェント状態をシリアライズするため、引き続き動作します。--no-sessionが設定されている場合、マネージャー作成が即座にインメモリを返すため、CLI resume/continueセマンティクスはバイパスされます。
既知の実装上の注意点(現在のコード時点)
Section titled “既知の実装上の注意点(現在のコード時点)”SelectorController.handleResumeSession()はsession.switchSession(...)のブール値の結果をチェックしません。フックでキャンセルされた切り替えでも、UI上の「Resumed session」再描画/ステータスパスを通過する可能性があります。/shareのカスタム共有の失敗は、デフォルトのgistフォールバックには降格せず、エラーでコマンドを終了します。/exportの引数トークン化は簡素であり、スペースを含むクォートされたパスを保持しません。