はじめに
AWSのAIエージェント開発ツール「Kiro」を毎日のように使っていると、過去のセッションでどんなやり取りをしたか、後から調べたくなる場面が何度もあります。Claude CodeやGitHub Copilotでは、会話終了時に自動でMarkdownログを吐き出す仕組み(Hook機能)を作って運用していたので、「Kiroでも同じことができるはず」と考えたのが今回の出発点でした。
結論から言うと、Kiroの内部実装はClaude Code / Copilotとはかなり勝手が違い、しかも調査の最中にKiro自体の大型アップデートが入って前提が丸ごと変わる、という展開になりました。この記事では、実際に手を動かして分かった内部構造と、2026年9月時点で「できること・できないこと」を整理します。Kiroはまだ頻繁にアップデートされる発展途上のツールのため、内部構造に関する記述は本記事執筆時点のスナップショットである点にご留意ください。
この記事で分かること
- Kiro内部のセッション保存フォルダ構造(2026年8月時点=「v1形式」)
- アシスタントの応答本文が、なぜ会話ログのJSONに直接入っていないのか
- Hook機能でリアルタイムログ記録を試みて、断念した理由
- 実行ログをキャッシュしながら一括エクスポートするバッチ方式への転換
- Kiroの大型アップデートでセッション保存方式が変わり(「v2形式」)、何が起きたか
- 新しいエクスポート形式(ZIP+JSONL)をMarkdownに変換するスクリプトでの現実的な妥協案
第1章:Hook機能でのリアルタイム記録を試みる
内部フォルダを掘ってみる
Kiroのセッションデータは%APPDATA%\Kiro\User\globalStorage\kiro.kiroagent配下に保存されています。調べてみると、以下のような構造になっていました(2026年8月時点、以下「v1形式」と呼びます)。
globalStorage\kiro.kiroagent\
workspace-sessions\
{Base64エンコードされたワークスペースパス}\
sessions.json # セッション一覧(タイトル・作成日時)
{sessionId}.json # 会話本体(history配列にuser/assistantターン){sessionId}.jsonのhistory[]を見ると、ユーザーの発言はcontentにテキスト・画像とも完全な形で入っています。ところが、アシスタント側のcontentを見ると、中身がすべて"On it."という固定のプレースホルダ文字列でした。
実際のアシスタント応答本文は、history[i].executionIdという別IDを手がかりに、まったく別の巨大な領域(ワークスペースに紐づかないハッシュフォルダ配下、数百MB〜GB規模)を検索しないと見つかりません。しかもこのハッシュフォルダ名やその中の実行ログファイル名は、ワークスペースパスやexecutionIdから単純に逆算できる規則性がなく、実質「全文検索するしかない」構造でした。
タイムスタンプが「次の会話をしないと埋まらない」
会話終了時に発火するagentStopイベントでHookスクリプトを動かし、ユーザー発言とタイムスタンプを記録する仕組みをまず作りました。ところが動かしてみると、直前のターンの## ASSISTANT欄とタイムスタンプが空欄のままで、次の会話をして初めて補完されるという奇妙な挙動に悩まされました。
原因を切り分けるため、次のように対策を積み重ねましたが、いずれも決定打にはなりませんでした。
- 固定時間(数秒〜24秒)の待機を挟む → 直らない
FileSystemWatcherでファイル変更イベントをリアルタイム検知 → Kiroの書き込み方式と噛み合わず検知できない- 短間隔(300ms)のポーリングに戻す → 55秒待っても間に合わないケースが実在
最終的に判明したのは、Kiro内部でのセッション永続化が、会話完了から1分以上遅れることがあるという、そもそも「待てば解決する」類の問題ではなかったという事実でした。Hookのタイムアウトを30秒→60秒→70秒と伸ばしても、それを超える遅延が実際に発生します。
結論として、「直前1ターンだけは次の会話まで反映が遅れる」という制約を仕様として受け入れることにしました。
Hook定義ファイルを直接編集すると、Kiroに存在を忘れられる
もう一つ厄介な事象がありました。.kiro.hookという定義ファイル(JSON形式)を直接編集して設定を変更していたところ、ある時点でKiroのUI(Agent Hooksパネル)からHookそのものが表示されなくなったのです。ファイル自体はディスク上にそのまま存在しているのに、Kiro側の内部インデックスだけが認識を失った状態でした。
ファイルのタイムスタンプを更新しても復活せず、最終的にはKiro自身のHook作成機能(UIの「Create New Hook」に相当する内部ツール)経由で再登録することでしか直りませんでした。この経験から、.kiro.hookファイルの追加・変更は、可能な限りKiro自身の機能経由で行うのが安全だと学びました。
第2章:方針転換 — バッチエクスポート方式へ
一連の調査から見えてきた結論は、「実行ログ領域は数百MB〜GB規模で単調増加し続け、agentStopは会話1回ごとに毎回発火する」という2つの条件が根本的に噛み合わない、というものでした。会話のたびに実行ログ全体をスキャンしていては実用に耐えません。
そこで、Hookでのリアルタイム記録は諦め、**「依頼したときに一括で完全なMarkdownログを生成するスキル」**に方針を切り替えました。ポイントは以下の2つです。
executionId → 応答テキストのマッピングをローカルにキャッシュファイル(JSON)として保存し、2回目以降は新しく増えたファイルだけを差分スキャンする- 初回のみ全件スキャン(当時454ファイル・1.68GBで約43秒)が走るが、以降は数秒で完了する
この方式に切り替えると、20件以上のセッションを一括でエクスポートしても1分かからず完了し、しかも標準のエクスポート機能では長いセッションで発生していた「On it.のまま復元できない」問題も、実行ログを直接読みに行くこの方式ではすべて解消していました。
ログオン時の自動実行(タスクスケジューラが使えなかった話)
この一括エクスポートを毎日自動で走らせたいと考え、Windowsのタスクスケジューラへの登録を試みましたが、schtasks・Register-ScheduledTaskのいずれも組織のグループポリシーでアクセス拒否となり失敗しました。企業ドメイン環境ではよくある制限です。
代替として、Windowsのスタートアップフォルダ(shell:startup)にショートカットを配置し、ログオン時に非表示ウィンドウでスクリプトを実行する方式を採用しました。この処理はローカルファイルの読み書きのみで完結するため、Kiro本体へのネットワーク接続が確立する前(社内PCでモバイル接続を手動で繋ぐような環境)でも問題なく動作することを確認済みです。
マルチルートワークスペースでの出力先問題
複数のフォルダを1つのワークスペースとして開く「マルチルートワークスペース」構成の場合、ログの出力先(Kiroがどのフォルダを「ワークスペースディレクトリ」と認識するか)は、.code-workspaceファイルのfolders配列の先頭に置かれたフォルダで決まることが実証できました(ターミナル用のterminal.integrated.cwd設定は無関係でした)。
本番運用中のGitリポジトリを先頭に置いたままログを出力し続けると、個人用のログファイルが本番リポジトリに紛れ込むリスクがあります。そこで、既存のGitリポジトリとは無関係な「ログ集約専用の空フォルダ」を新規に作り、それをfolders配列の先頭に置く運用に落ち着きました。この構成に切り替える過程では、過去のセッション履歴が一時的にUI上で見えなくなる、実行ログとの紐付けが崩れて「Loading execution...」のまま表示が進まなくなる、といった副作用も経験しましたが、いずれもデータの物理的な損失ではなく、内部の識別子(workspaceDirectory)とUIの紐付けの問題であることを確認しています。
第3章:大型アップデートで前提が丸ごと変わった
苦労してバッチエクスポート方式を安定稼働させた翌朝、Kiroに大型アップデートがかかりました。ログオン時の自動処理は動いていたものの、その日の新しいセッションの内容がどこにも記録されていません。
内部フォルダを再調査したところ、以下が判明しました。
workspace-sessionsフォルダに新しいセッションのデータが一切増えていないstate.vscdb(VSCode系IDE共通のSQLite形式の内部データベース)を確認しても、セッションのタイトルとIDだけで、会話履歴本体は入っていない._migration-{セッションID}.jsonという移行マーカーファイルが存在し、markerVersion: 2という記載がある
つまり、Kiroのセッション保存方式が「v1(ローカルJSONファイル)」から「v2(サーバー側管理)」に切り替わり、会話履歴がローカルのファイルシステム上のどこにも存在しなくなったということです。これまで組み上げてきたHook・バッチエクスポートの仕組みは、いずれもローカルファイルを直接読みに行く前提だったため、この時点で軒並み機能を失いました。
同時に、標準のエクスポート機能自体もダウングレードしました。以前は人間がそのまま読めるMarkdownファイルを出力していたのに対し、v2以降はkiro-session-{ID}.zipという圧縮ファイルをダウンロードする形式に変わっています。ZIPの中身は次の2ファイルです。
kiro-session-{id}.zip
session.json # セッションメタデータ(タイトル・ID・ワークスペースパス等)
messages.jsonl # 全会話データ(1行=1イベントのJSONL形式)messages.jsonlにはuser(ユーザー発言)・assistant(応答テキスト)・tool_call/tool_result(ツール呼び出しと結果)・turn_start/turn_end(ターン境界、executionId付き)といったイベントが時系列で並んでおり、画像はuserイベントのimages[]配列に{data: <生のbase64>, mimeType}という形で格納されています。データとしては十分な情報量がある一方、一般ユーザーがこのJSONLファイルをそのまま開いて内容を追うのは現実的ではありません。
第4章:現実的な妥協案 — ZIP→Markdown変換スクリプト
サーバー側に移ったデータを外部から直接取得することは、非公開API・認証トークンの壁があり現実的ではありません(Kiro本体が使っている内部APIのエンドポイントは公開されておらず、アプリ内部で管理されるトークンを外部プロセスから正規の手段で取得する方法もありません)。
そこで方針を切り替え、**「エクスポートしたZIPを読み込んで、以前と同じMarkdown形式に変換するスクリプト」**を新規に作成しました。旧スクリプトのMarkdown生成ロジック(タイムスタンプの整形、画像の埋め込み記法、段落の改行処理)はそのまま流用でき、新しく実装したのは「ZIPを展開してJSONLをパースし、時系列でUSER/ASSISTANTのターンを組み立てる」変換部分だけです。
kiro-export-zip.ps1 -ZipPath "C:\...\kiro-session-xxxx.zip"
# フォルダ内の全ZIPを一括変換(サブフォルダ含む)
kiro-export-zip.ps1 -InputDir "C:\...\folder" -Recurse
# 変換成功後、ZIPを自動削除する場合
kiro-export-zip.ps1 -InputDir "C:\...\folder" -DeleteZip実際に試すと、以前は長いセッションで応答本文が欠落しがちだった問題も含めて綺麗に復元でき、画像もPNG/JPEGとして正しく保存できることを確認しています。あわせて、v1形式のまま残っているセッション(Migrateしていない過去分)だけを一覧表示するスクリプトも作成しましたが、こちらはv2セッションには対応していません(サーバー側管理のため、ローカルには集計対象となるデータが存在しないためです)。
その後の別アップデートでもローカル永続化が復活していないか確認しましたが、2026年9月上旬時点では変化はありませんでした。
まとめ:2026年9月時点でできること・できないこと
| やりたいこと | 現状(2026年9月時点) |
|---|---|
| 会話終了時にリアルタイムで自動ログ記録 | 不可(v1時代のHook方式も、内部の永続化ラグと仕様変更で維持できなくなった) |
| 過去のv1セッション(アップデート前・未Migrate分)をローカルから読む | 可能(workspace-sessionsのJSONを直接読める) |
| 現在のセッション(v2形式)を自動でMarkdown化 | 不可(会話履歴はサーバー側にのみ存在) |
| 現在のセッションを手動でMarkdown化 | 可能(UIからZIPエクスポート→変換スクリプトでMarkdown化) |
| CLIやAPI経由での一括エクスポート | 不可(公式CLI・公開APIは提供されていない) |
会話履歴がサーバー側管理になったことで、Kiro側の保持期間ポリシーに依存するリスクも生まれています。重要なセッションは気づいたタイミングでZIPエクスポートし、ローカルにMarkdownとして退避しておくのが、現状でできる最も確実な自衛策です。
Kiroはまだ生まれて日の浅いツールで、今回のように内部の保存方式そのものが短期間で変わることがあります。この記事の内容も、次のアップデートで前提が変わる可能性を踏まえた「現時点のスナップショット」として参考にしていただければと思います。
コメント