Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
3e4db36
Fix Kiro parent-PID watchdog name match (kiro-cli)
realtonyyoung Jul 15, 2026
d81586c
Freeze the watcher idle clock while disconnected
realtonyyoung Jul 15, 2026
03a8fb5
Add staged parent-dead / wedged-watcher recovery
realtonyyoung Jul 15, 2026
8b08266
feat: AgentHookPoster.Spooled + PostOrSpoolAsync spool-on-lapse
realtonyyoung Jul 15, 2026
269ec75
feat: TranscriptSpool — bounded, no-drop, needs-import marker
realtonyyoung Jul 15, 2026
3fb3929
fix: TranscriptSpool no silent drop on I/O failure + Ignored result
realtonyyoung Jul 15, 2026
248e7e1
feat: LifecycleSpoolDrain — global cross-spool ordered drain pass
realtonyyoung Jul 15, 2026
a480a20
feat: spawn-before-post + Spooled for Kiro/OpenCode/Pi/Copilot hooks
realtonyyoung Jul 15, 2026
22a9a4d
fix: throttle spool drain per-prompt; ShouldSpawnAfter drops AuthLapsed
realtonyyoung Jul 15, 2026
6ee892e
feat: Codex spawn-before-post with stdout-first carve-out
realtonyyoung Jul 15, 2026
cebc56e
feat: Antigravity spawn-before-post + Gemini awaited EnsureWatcherRun…
realtonyyoung Jul 15, 2026
fa1dabc
feat: Gemini session-start spawn-before-post + gated spool drain
realtonyyoung Jul 15, 2026
fa058b5
feat: shutdown final-line completion signal (newline/parseable-JSON)
realtonyyoung Jul 15, 2026
1f6cf3e
fix: close final-drain completeness TOCTOU; re-validate at consume time
realtonyyoung Jul 15, 2026
d8b3f06
feat: spool undelivered transcript tail on shutdown-during-outage
realtonyyoung Jul 16, 2026
40b131a
fix: spool tail unconditionally + redact secrets (task-8 review)
realtonyyoung Jul 16, 2026
b2eed14
feat: watcher heartbeat + lock-guarded staleness reap+respawn
realtonyyoung Jul 16, 2026
8b19676
fix: keep heartbeat fresh during connect-retry + purge watcher sideca…
realtonyyoung Jul 16, 2026
07b8914
feat: Kiro live usage-backfill synthetic lines (credits/context%)
realtonyyoung Jul 16, 2026
d76f01d
feat: Cursor live subagent split via hook/backfill path (AI-1151)
realtonyyoung Jul 16, 2026
1d262cd
fix: enforce start-before-stop spool ordering on Cursor live subagent…
realtonyyoung Jul 16, 2026
ad79a63
feat: wire global spool-drain pass into hook entry + daemon-periodic
realtonyyoung Jul 16, 2026
3a89d47
fix: Claude ordering guard must see the .ordered-* backlog namespace
realtonyyoung Jul 16, 2026
e3380a4
test: AI-1357 CLI acceptance tests (spawn-before-post, drain order, h…
realtonyyoung Jul 16, 2026
fc0a493
AI-1358: add import origin marker constant (CLI)
realtonyyoung Jul 16, 2026
3574ca8
AI-1358: shared guarded discovery helper (A4)
realtonyyoung Jul 16, 2026
f34d621
AI-1358: adopt guarded discovery in all import sources (A4)
realtonyyoung Jul 16, 2026
0c9b50c
AI-1358: restore flat discovery scope for Kiro/Gemini (A4 fix)
realtonyyoung Jul 16, 2026
6148afb
AI-1358: OpenCode ended_at null/0 not 1970 (A3)
realtonyyoung Jul 16, 2026
9b295b7
AI-1358: per-vendor ended_at resolvers (A3)
realtonyyoung Jul 16, 2026
96b7bb8
AI-1358: confirm/extend Codex ExtractLastTimestamp shape (A3, open Q2)
realtonyyoung Jul 16, 2026
f2d5db6
AI-1358: Claude/Codex resume end-only reassert + fail-closed tail (A2)
realtonyyoung Jul 16, 2026
05020e9
AI-1358: routed sources send historical-import origin marker (A1)
realtonyyoung Jul 16, 2026
e046974
AI-1358: Cursor import no stale PR + per-cwd repo cache (item 5)
realtonyyoung Jul 16, 2026
f2471d7
AI-1358: Antigravity import usage pass, USAGE-before-end, all classif…
realtonyyoung Jul 16, 2026
4f72566
Merge remote-tracking branch 'origin/main' into harness-ingest-polish
realtonyyoung Jul 16, 2026
70ec7ec
[AI-1383] D3: OpenCode recursive descendant discovery + ledger invali…
realtonyyoung Jul 17, 2026
9634c75
[AI-1383] D3: Gemini recursive descendant discovery with context-carr…
realtonyyoung Jul 17, 2026
f8cd9d3
Merge remote-tracking branch 'origin/main' into impl/ai1383-d3-grandc…
realtonyyoung Jul 17, 2026
1a6cf82
[AI-1383] D3 review fix: re-close the OpenCode root on cancellation-a…
realtonyyoung Jul 17, 2026
85ad88e
[AI-1383] D3 review fix: count and fingerprint the whole omitted desc…
realtonyyoung Jul 17, 2026
a245e64
[AI-1383] D3 review fix: scope MaxCountingNodes to the below-cap coun…
realtonyyoung Jul 17, 2026
c83ab52
[AI-1383] D3 review fix: propagate cancellation that arrives during t…
realtonyyoung Jul 17, 2026
e9c3389
[AI-1383] D3 review fix: stop expanding the below-cap frontier once t…
realtonyyoung Jul 17, 2026
da103fe
[AI-1383] D3 review fix: cap-bound the per-parent below-cap child fetch
realtonyyoung Jul 17, 2026
97a1930
[AI-1383] D3 review fix: sentinel must count eligible rows, not raw rows
realtonyyoung Jul 17, 2026
62fe89f
[AI-1383] Qodo fix: surface unreadable Gemini descendant dirs; strip …
realtonyyoung Jul 17, 2026
b8b1849
[AI-1383] Qodo fix: strip Linear IDs and trim review-narrative commen…
realtonyyoung Jul 17, 2026
9128a46
[AI-1383] Qodo fix: document recursive descendant import in README
realtonyyoung Jul 17, 2026
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
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ kcap import --antigravity # only Antigravity

> **Pi** has no shell hooks, so live capture uses a shipped Pi extension rather than a hooks file: run `kcap plugin install --pi` (or accept the `kcap setup` prompt) to write `~/.pi/agent/extensions/kcap.ts`, which `pi` auto-loads and streams each session live. Because Pi also ships no built-in MCP, the same command installs an MCP-bridge extension (`~/.pi/agent/extensions/kcap-mcp.ts`, opt out `--skip-pi-mcp`) that exposes the kcap MCP servers as native Pi tools, plus a steering block in `~/.pi/agent/AGENTS.md` (opt out `--skip-pi-instructions`). Historical `kcap import --pi` works with or without any of it.

> **OpenCode** likewise has no shell hooks: live capture uses a shipped OpenCode plugin. Run `kcap plugin install --opencode` (or accept the `kcap setup` prompt) to write `~/.config/opencode/plugins/kcap.ts`, which `opencode` auto-loads and streams each session live (`vendor=opencode`). Subagents (the `task` tool / `@agent`) are captured too — the plugin fetches each child session via the SDK and streams it, so it nests under the parent in the trace. Historical `kcap import --opencode` reads OpenCode's SQLite database (`~/.local/share/opencode/opencode.db`) and imports child sessions as subagents, so it backfills sessions from before the plugin was installed.
> **OpenCode** likewise has no shell hooks: live capture uses a shipped OpenCode plugin. Run `kcap plugin install --opencode` (or accept the `kcap setup` prompt) to write `~/.config/opencode/plugins/kcap.ts`, which `opencode` auto-loads and streams each session live (`vendor=opencode`). Subagents (the `task` tool / `@agent`) are captured too — the plugin fetches each child session via the SDK and streams it, so it nests under the parent in the trace. Historical `kcap import --opencode` reads OpenCode's SQLite database (`~/.local/share/opencode/opencode.db`) and imports every transitive descendant session (children, grandchildren, and so on — see [Loading historical sessions](#loading-historical-sessions)), so it backfills sessions from before the plugin was installed.

This backfills your past sessions from `~/.claude/projects/` (Claude), `~/.codex/sessions/` (Codex), `~/.cursor/projects/.../agent-transcripts/` (Cursor), `~/.copilot/session-state/` (Copilot), `~/.gemini/tmp/<project>/chats/` (Gemini), `~/.kiro/sessions/cli/` (Kiro), `~/.pi/agent/sessions/` (Pi), `~/.local/share/opencode/opencode.db` (OpenCode), and `~/.gemini/antigravity/brain/` (Antigravity) so they appear in the dashboard. All agents are discovered automatically — pass `--claude`, `--codex`, `--cursor`, `--copilot`, `--gemini`, `--kiro`, `--pi`, `--opencode`, or `--antigravity` (one or more) to narrow the run. All forms are idempotent — safe to run multiple times.

Expand Down Expand Up @@ -491,7 +491,7 @@ Cursor historical import walks every JSONL transcript under `~/.cursor/projects/

Kiro historical import reads each session's append-only log at `~/.kiro/sessions/cli/{id}.jsonl` (plus the sibling `{id}.json` for cwd / model / title) and posts the lines through `POST /hooks/transcript` — the same lines the live watcher tails, so live and historical ingest converge. Set `KIRO_HOME` to point at a non-default location. Kiro persists no token counts, so imported Kiro sessions show no token usage (by design). Re-imports are idempotent — event ids are deterministic over `(session id, message/tool id, kind)`.

OpenCode historical import reads its SQLite database (`~/.local/share/opencode/opencode.db`, honouring `XDG_DATA_HOME`) and reconstructs the same `{info,parts}` lines the live plugin streams — so live and historical ingest converge on one canonical event stream (`vendor=opencode`). Child sessions are imported as subagents of their parent (`/hooks/subagent-*`), matching the live nesting. Because the server exposes no completeness signal, kcap records each fully-imported session in a local ledger (`~/.cache/kcap/opencode-imported.json`, keyed by server) to skip it on re-run; a session interrupted mid-import is repaired on the next run. Re-imports are idempotent — canonical event ids derive from OpenCode's stable `prt_` part ids.
OpenCode historical import reads its SQLite database (`~/.local/share/opencode/opencode.db`, honouring `XDG_DATA_HOME`) and reconstructs the same `{info,parts}` lines the live plugin streams — so live and historical ingest converge on one canonical event stream (`vendor=opencode`). Unlike live capture, which only nests direct children, historical import walks every transitive descendant session (children, grandchildren, and so on, up to a depth of 8) and imports each one as a direct subagent of the top-level root (`/hooks/subagent-*`) — flattened because a session's stream key can't express deeper nesting. A descendant beyond the depth cap is never silently dropped: import prints a `[kcap] opencode: root <id> descendants_omitted=N (depth cap 8 exceeded)` diagnostic to stderr, appending `(lower bound — counting ceiling hit)` in the rare case where counting the omitted subtree itself hit an internal safety cap before finishing — the count is then a lower bound, not exact. Because the server exposes no completeness signal, kcap records each fully-imported session in a local ledger (`~/.cache/kcap/opencode-imported.json`, keyed by server) to skip it on re-run — the ledger key is a content fingerprint over the whole descendant tree (including omitted ids), so a newly-reachable descendant invalidates a stale entry; a session interrupted mid-import is repaired on the next run. Re-imports are idempotent — canonical event ids derive from OpenCode's stable `prt_` part ids.

Claude (`CLAUDE_CONFIG_DIR`), Codex (`CODEX_HOME`), Gemini (`GEMINI_CLI_HOME`, which names the parent of `.gemini`), OpenCode (`OPENCODE_CONFIG_DIR`), and Pi (`PI_CODING_AGENT_DIR`, which names the `~/.pi/agent` leaf) historical/live paths follow each agent's own config-relocation environment variable when it is set, so a relocated config is discovered automatically.

Expand Down Expand Up @@ -641,7 +641,7 @@ kcap plugin remove --gemini # removes only kcap's entries

Live sessions stream from the chat-recording JSONL Gemini names in each hook's `transcript_path` (`~/.gemini/tmp/<project>/chats/session-*.jsonl`); historical sessions import via `kcap import --gemini`. Sessions resumed with `gemini --resume` reattach to the same recorded session. (Historical import leaves working-directory / repo enrichment empty — Gemini doesn't record the cwd in a machine-readable header; live capture gets it from the hook payload.)

Spawned subagents are captured too: Gemini records each in a nested `chats/<session>/<subId>.jsonl`, and both live capture (a child watcher per subagent) and `kcap import --gemini` discover and stream them, so they nest under the parent session in the trace.
Spawned subagents are captured too: Gemini records each in a nested `chats/<session>/<subId>.jsonl`. Live capture (a child watcher per subagent) discovers direct children only. Historical `kcap import --gemini` goes further, recursively discovering every transitive descendant — a subagent's own subagents, and so on, up to a depth of 8 — under each descendant's own nested dir, and imports them all as direct subagents of the top-level session (flattened, since a session's stream key can't express deeper nesting). A descendant beyond the depth cap is never silently dropped: import prints a `descendants_omitted=N` diagnostic to stderr, appending `(lower bound — counting ceiling hit)` in the rare case where counting the omitted subtree itself hit an internal safety cap before finishing — the count is then a lower bound, not exact.

#### AWS Kiro CLI hooks

Expand Down
9 changes: 6 additions & 3 deletions src/Capacitor.Cli.Core/Gemini/GeminiPaths.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public static string Root(string? home = null, string? geminiCliHome = null) {
/// root is NOT sufficient because <c>~/.gemini</c> is SHARED with Google
/// Antigravity, which stores its state under <c>antigravity/</c> +
/// <c>antigravity-cli/</c> — so an Antigravity-only home would otherwise falsely
/// read as a Gemini install (AI-1158). Require one of the config/recording
/// read as a Gemini install. Require one of the config/recording
/// markers Gemini CLI creates that Antigravity does not: <c>settings.json</c>,
/// <c>projects.json</c>, or the <c>tmp/</c> chat-recording dir. The binary name
/// <c>gemini</c> is too generic to be the only signal, so callers that want a
Expand Down Expand Up @@ -73,9 +73,12 @@ public static string ChatsDir(string projectTmpDir)
/// Nested subagent-recording directory for a parent session:
/// <c>&lt;chats&gt;/&lt;parentSessionId&gt;/</c>. Gemini records each subagent's transcript
/// here as <c>&lt;subId&gt;.jsonl</c> (subId = a fresh dashed UUID — the executor's agent
/// id; nested subagents at any depth stay flat under the top-level session).
/// id). A subagent that itself spawns subagents gets its OWN nested dir the same way —
/// <c>&lt;chats&gt;/&lt;subId&gt;/&lt;grandSubId&gt;.jsonl</c> — so deeper invocations are
/// discovered by recursing into each descendant's own directory, not by assuming a flat
/// layout (see <see cref="GeminiSubagentDiscovery.EnumerateDescendantFiles"/>).
/// <paramref name="parentSessionId"/> is the DASHED form from the parent transcript's
/// header, matching the on-disk directory name. AI-900.
/// header, matching the on-disk directory name.
/// </summary>
public static string SubagentDir(string chatsDir, string parentSessionId)
=> Path.Combine(chatsDir, parentSessionId);
Expand Down
Loading
Loading