Problem
ctx search --refresh strict --provider kilo and ctx import --provider kilo fail with:
import kilo source ~/.local/share/kilo/kilo.db failed with 1 failure(s);
first failure: line 0: Kilo SQLite database contained no real conversational message rows
This blocks every Kilo session from being indexed, regardless of message size.
Root cause
The Kilo Code VSCode extension stores its history in ~/.local/share/kilo/kilo.db. ctx reads it through KiloSqliteAdapter → normalize_opencode_sqlite with OPENCODE_KILO_DIALECT (see crates/ctx-history-capture/src/provider/adapter_impls/sqlite.rs:143 and crates/ctx-history-capture/src/provider/providers/opencode.rs:55). The opencode adapter runs every fetched row through opencode_data_has_real_message_content, which requires the row's data JSON to have a top-level text, content, or message key (crates/ctx-history-capture/src/provider/providers/opencode.rs:708):
data.get("text")
.or_else(|| data.get("content"))
.or_else(|| data.pointer("/message/role"))
Kilo's message schema does not use any of those keys. Representative message shapes from a real kilo.db:
- user message:
{"role":"user","time":{...},"summary":{"diffs":[...]},"agent":"code","model":{...},"editorContext":{...}}
- assistant message:
{"role":"assistant","time":{...},"parentID":...,"modelID":...,"providerID":...,"mode":...,"agent":...,"path":...,"cost":...,"tokens":...,"finish":...}
Top-level keys for both shapes are role / time / agent / model (or modelID) / path / cost / tokens / finish / summary / editorContext. No text, content, or message. As a result opencode_data_has_real_message_content returns false for every kilo row, the adapter concludes "no real conversational message rows", and the source import fails.
(Note: Kilo Code is the VSCode extension itself; z.ai is the coding-plan / model provider behind it, not the extension vendor. The kilo.db schema is owned by the extension.)
This is independent of the oversized-row handling that #88 / #89 recently fixed — kilo was already failing on schema grounds before the oversized path could matter.
Reproduction
- Install the Kilo Code VSCode extension locally and have at least one conversation.
- Run
ctx sources --provider kilo → source is detected and reported as available.
- Run
ctx search "anything" --refresh strict --provider kilo → fails with the error above.
- Run
ctx import --provider kilo --json → failed=1, failures[0].error contains "no real conversational message rows".
Expected behaviour
Kilo messages should import analogously to opencode messages: each message becomes an event keyed by role (user/assistant/...), with payload body assembled from the fields kilo actually uses (summary.diffs for user turns, path/mode/agent for assistant turns, etc.). At minimum the role / time / agent / model fields that exist on every kilo row are enough to produce a searchable event.
Proposed direction
Two reasonable options, both contained to crates/ctx-history-capture/src/provider/providers/opencode.rs:
- Extend
opencode_data_has_real_message_content / opencode_event_text to recognize kilo's field set when dialect.provider == CaptureProvider::Kilo (or via a dialect flag). Smallest change, keeps the shared opencode pipeline intact.
- Add a dedicated
normalize_kilo_sqlite mirroring the pattern of the 13 existing per-provider adapters (crates/ctx-history-capture/src/provider/providers/{crush,firebender,goose,...}.rs). Cleaner separation but more code.
The KiloSqliteAdapter is already plumbed (provider, source_format, dialect struct all exist), so either option is mostly local work — no schema migration, no SDK changes.
Validation
A regression test along the lines of native_opencode_imports_read_only_sqlite (crates/ctx-history-capture/src/tests/native_sqlite.rs:5) but using a kilo-shaped fixture (rows with role / time.created / summary.diffs instead of opencode's text / content) would lock the fix in. The existing write_opencode_smoke_db fixture helper can serve as the template.
Related
Problem
ctx search --refresh strict --provider kiloandctx import --provider kilofail with:This blocks every Kilo session from being indexed, regardless of message size.
Root cause
The Kilo Code VSCode extension stores its history in
~/.local/share/kilo/kilo.db. ctx reads it throughKiloSqliteAdapter→normalize_opencode_sqlitewithOPENCODE_KILO_DIALECT(seecrates/ctx-history-capture/src/provider/adapter_impls/sqlite.rs:143andcrates/ctx-history-capture/src/provider/providers/opencode.rs:55). The opencode adapter runs every fetched row throughopencode_data_has_real_message_content, which requires the row'sdataJSON to have a top-leveltext,content, ormessagekey (crates/ctx-history-capture/src/provider/providers/opencode.rs:708):Kilo's message schema does not use any of those keys. Representative message shapes from a real
kilo.db:{"role":"user","time":{...},"summary":{"diffs":[...]},"agent":"code","model":{...},"editorContext":{...}}{"role":"assistant","time":{...},"parentID":...,"modelID":...,"providerID":...,"mode":...,"agent":...,"path":...,"cost":...,"tokens":...,"finish":...}Top-level keys for both shapes are
role/time/agent/model(ormodelID) /path/cost/tokens/finish/summary/editorContext. Notext,content, ormessage. As a resultopencode_data_has_real_message_contentreturnsfalsefor every kilo row, the adapter concludes "no real conversational message rows", and the source import fails.(Note: Kilo Code is the VSCode extension itself; z.ai is the coding-plan / model provider behind it, not the extension vendor. The
kilo.dbschema is owned by the extension.)This is independent of the oversized-row handling that #88 / #89 recently fixed — kilo was already failing on schema grounds before the oversized path could matter.
Reproduction
ctx sources --provider kilo→ source is detected and reported as available.ctx search "anything" --refresh strict --provider kilo→ fails with the error above.ctx import --provider kilo --json→failed=1,failures[0].errorcontains "no real conversational message rows".Expected behaviour
Kilo messages should import analogously to opencode messages: each message becomes an event keyed by role (
user/assistant/...), with payload body assembled from the fields kilo actually uses (summary.diffsfor user turns,path/mode/agentfor assistant turns, etc.). At minimum the role / time / agent / model fields that exist on every kilo row are enough to produce a searchable event.Proposed direction
Two reasonable options, both contained to
crates/ctx-history-capture/src/provider/providers/opencode.rs:opencode_data_has_real_message_content/opencode_event_textto recognize kilo's field set whendialect.provider == CaptureProvider::Kilo(or via a dialect flag). Smallest change, keeps the shared opencode pipeline intact.normalize_kilo_sqlitemirroring the pattern of the 13 existing per-provider adapters (crates/ctx-history-capture/src/provider/providers/{crush,firebender,goose,...}.rs). Cleaner separation but more code.The
KiloSqliteAdapteris already plumbed (provider, source_format, dialect struct all exist), so either option is mostly local work — no schema migration, no SDK changes.Validation
A regression test along the lines of
native_opencode_imports_read_only_sqlite(crates/ctx-history-capture/src/tests/native_sqlite.rs:5) but using a kilo-shaped fixture (rows withrole/time.created/summary.diffsinstead of opencode'stext/content) would lock the fix in. The existingwrite_opencode_smoke_dbfixture helper can serve as the template.Related
SQLITE_TOOBIGabort for kilo's giant diff messages but does not address this schema gap.