Skip to main content

sqlite-log-query

Nested system-manual reference for the additive SQLite/`log.sqlite` sidecar over LingTai's JSONL runtime traces. Read it when you need `lingtai-agent log doctor|query|rebuild`, read-only SQL safety and WAL/rebuild caveats, the events/chat_entries/token_entries schema, quick-start snippets, SQL recipes (`tool_call_id` lifecycle, tool result stats and percentiles, spilled/large tool results), gotchas, or the redaction rules. Runtime trace forensics start here; trajectory mining is the sibling `trajectory-mining`.

설치로 이동

소스 정보

저장소
Lingtai-AI/lingtai-kernel
최근 소스 활동
2026년 8월 9일 23:13
감지된 SKILL.md 언어
영어
스타
11
포크
14

설치 방법

기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.

소스 파일 검토

설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.

파일 탐색기
2 개 파일

SKILL.md 표시 중

SKILL.md
소스 지침 · 읽기 전용 미리보기
name
sqlite-log-query
description
Nested system-manual reference for the additive SQLite/`log.sqlite` sidecar over LingTai's JSONL runtime traces. Read it when you need `lingtai-agent log doctor|query|rebuild`, read-only SQL safety and WAL/rebuild caveats, the events/chat_entries/token_entries schema, quick-start snippets, SQL recipes (`tool_call_id` lifecycle, tool result stats and percentiles, spilled/large tool results), gotchas, or the redaction rules. Runtime trace forensics start here; trajectory mining is the sibling `trajectory-mining`.
version
1.4.0
tags
["lingtai","system-manual","sqlite","log.sqlite","runtime-logs","trace","jsonl","daemon","event-log","pitfalls","observability"]
last_changed_at
2026-08-09T00:00:00Z
related_files
["src/lingtai/intrinsic_skills/system-manual/SKILL.md","src/lingtai/intrinsic_skills/system-manual/reference/trajectory-mining/SKILL.md","src/lingtai/intrinsic_skills/system-manual/reference/sqlite-log-query/scripts/event_summary.py"]
maintenance
Tracks the sqlite-log-query topic it documents; update when that integration changes.
# SQLite Log Query LingTai keeps durable runtime traces and token ledgers in JSONL files. The SQLite file at `logs/log.sqlite` is an **additive, rebuildable query index** over those JSONL sources of truth. Use it to answer questions that are painful with `grep`: which event types are hottest, what happened inside daemon runs, what chat-history turn surrounded a failure, whether notification/daemon/context events are storming, or how token usage is distributed across main/soul/daemon sources. ## Start here for log.sqlite (quick start) First-use, copy-pasteable snippets. `log.sqlite` lives at `logs/log.sqlite` under the agent directory (`AGENT_DIR=/path/to/project/.lingtai/agent-name`). Open it read-only and run the three first checks: ```bash sqlite3 -readonly logs/log.sqlite '.tables' sqlite3 -readonly logs/log.sqlite 'pragma table_info(events);' sqlite3 -readonly logs/log.sqlite \ "select type, count(*) as n from events group by type order by n desc limit 20;" ``` Plain `sqlite3 logs/log.sqlite` opens the file read-write; for inspection prefer `-readonly` (or the Python URI form below) so you never accidentally write the sidecar. Equivalent read-only Python: ```python import sqlite3 db_path = "/path/to/.lingtai/<agent>/logs/log.sqlite" conn = sqlite3.connect(f"file:{db_path}?mode=ro", uri=True) for row in conn.execute( "select type, count(*) from events group by type order by count(*) desc limit 20;" ): print(row) ``` The five recipes in **Query recipes** below (event type counts, per-tool result length, percentiles, `tool_call_id` lifecycle, large/spilled results) are directly copy-pasteable. The **Safety contract** and **Gotchas** sections apply to every query. ## Safety contract - **JSONL is authoritative.** `logs/log.sqlite` is derived; deleting it should not delete facts. - **Prefer the CLI.** Use `lingtai-agent log ...` instead of opening the DB for writes yourself. - **Queries are read-only with respect to SQLite database contents.** `log query` accepts read-only `SELECT`, CTE (`WITH ... SELECT`), and `EXPLAIN` statements and opens the sidecar through the kernel read-only inspection path: they never write the main database file (opened `mode=ro` with `PRAGMA query_only=ON`), though SQLite read-support `-wal`/`-shm` sidecar files may still be created or updated. - **Rebuild is offline.** `log rebuild` requires the agent working-directory lock; if the agent is running, stop/sleep/lull/suspend it first as appropriate. - **Runtime SQLite is best effort.** New top-level `logs/events.jsonl` and standard `logs/token_ledger.jsonl` rows are indexed live after the JSONL write succeeds. Chat history, archive, and daemon JSONL sources are indexed into a target agent sidecar by explicit offline rebuild so normal turns and daemon runs do not pay recursive scan or live-rewrite costs. - **Live queries are snapshots.** Runtime writes use SQLite WAL mode. For a complete historical snapshot, stop the agent and run `log rebuild` before querying. - **Never paste secrets.** Logs and chat history can contain URLs, tokens, prompts, and user data — including raw `fields_json`/`entry_json`. Apply the redaction rules below before sharing anything. ## Gotchas - The event-kind column is **`type`**, not `event_type`. - The structured event payload lives in **`fields_json`** (chat rows in `entry_json`); reach into it with `json_extract(fields_json, '$.key')`. - `log.sqlite` is **derived and rebuildable**; JSONL remains authoritative. A missing sidecar is a rebuildable index gap, never lost facts. - Open the sidecar **read-only** for inspection: `sqlite3 -readonly`, the `file:...?mode=ro` URI, or `lingtai-agent log query`. Never write to it directly. - For exact forensic payloads, follow `source_file` / `source_offset` back to the JSONL source. - `fields_json`/`entry_json` can carry URLs, tokens, prompts, and user data — redact before sharing anything. ## Commands Set a variable for the target agent directory: ```bash AGENT_DIR=/path/to/project/.lingtai/agent-name ``` Check whether the sidecar exists and is readable: ```bash lingtai-agent log doctor "$AGENT_DIR" ``` If `doctor` reports `{"status":"missing"...}` or a failing `integrity_check`, rebuild **only while the target agent is stopped/offline**. `doctor` does not detect staleness — a stale but intact sidecar still reports `status: ok`; compare `import_cursors` against source-file mtimes, or just rebuild: ```bash lingtai-agent log rebuild "$AGENT_DIR" ``` `log rebuild` scans the known JSONL trace surfaces under the target agent: - `logs/events.jsonl` → `events` (`source_kind='agent_events'`) - `logs/token_ledger.jsonl` → `token_entries` (`source_kind='agent_token_ledger'`) - `history/chat_history.jsonl` → `chat_entries` (`source_kind='agent_chat'`) - `history/chat_history_archive.jsonl` → `chat_entries` (`source_kind='agent_chat_archive'`) - `daemons/*/logs/events.jsonl` → `events` (`source_kind='daemon_events'`, `run_id=<daemon folder>`) - `daemons/*/logs/token_ledger.jsonl` → `token_entries` (`source_kind='daemon_token_ledger'`, `run_id=<daemon folder>`) - `daemons/*/history/chat_history.jsonl` → `chat_entries` (`source_kind='daemon_chat'`, `run_id=<daemon folder>`) Run a read-only query. The CLI always prints JSON; pipe to `jq .` to pretty-print when it is available: ```bash lingtai-agent log query "$AGENT_DIR" \ 'SELECT id, ts, type, agent_address, substr(fields_json, 1, 240) AS fields FROM events ORDER BY ts DESC LIMIT 20' | jq . ``` ## Schema quick reference `events` indexes top-level agent runtime events and daemon run events: | Column | Meaning | |---|---| | `id` | SQLite row id, not a stable cross-rebuild event identifier | | `ts` | event timestamp as a numeric epoch-like value; ISO strings are parsed when possible | | `type` | event `type` field, or daemon `event` field | | `agent_address` | event `address` field when present | | `agent_name_snapshot` | event `agent_name` field when present | | `fields_json` | the remaining event fields as JSON text | | `source_file` | JSONL file imported from | | `source_offset` | byte offset in the JSONL source; unique with `source_file` | | `source_line` | 1-based JSONL line number | | `source_kind` | `agent_events`, `daemon_events`, or fallback kind | | `scope` | `agent`, `daemon`, or `unknown` | | `run_id` | daemon run folder name for daemon rows | | `inserted_at` | sidecar insertion time | `chat_entries` indexes agent and daemon chat-history JSONL rows: | Column | Meaning | |---|---| | `id` | SQLite row id, not stable across rebuilds | | `ts` | parsed numeric timestamp when a row has `ts`/`timestamp`, else `0` | | `ts_text` | original timestamp text/value as stored in JSONL | | `role` | chat role (`user`, `assistant`, etc.) when present | | `kind` | LingTai daemon user-entry kind (`task`, `tool_results`, `followup`) when present | | `turn` | daemon turn number when present | | `content_text` | best-effort extracted plain text from `text` or content blocks | | `entry_json` | full source chat row as JSON text | | `source_file`, `source_offset`, `source_line` | source JSONL identity | | `source_kind` | `agent_chat`, `agent_chat_archive`, `daemon_chat`, or fallback kind | | `scope` | `agent`, `daemon`, or `unknown` | | `run_id` | daemon run folder name for daemon rows | | `inserted_at` | sidecar insertion time | `token_entries` indexes agent and daemon token-ledger JSONL rows: | Column | Meaning | |---|---| | `id` | SQLite row id, not stable across rebuilds | | `ts` | parsed numeric timestamp when possible | | `ts_text` | original `ts` value from JSONL | | `input_tokens`, `output_tokens`, `thinking_tokens`, `cached_tokens` | token counters from the JSONL ledger row | | `model`, `endpoint` | model/provider endpoint metadata when present | | `source` | ledger source tag such as `main`, `soul`, `daemon`, `tc_wake`, or legacy/null | | `em_id`, `run_id`, `api_call_id` | daemon/run/API attribution when present | | `entry_json` | full source token-ledger row as JSON text | | `source_file`, `source_offset`, `source_line` | source JSONL identity | | `source_kind` | `agent_token_ledger`, `daemon_token_ledger`, or fallback kind | | `scope` | `agent`, `daemon`, or `unknown` | | `inserted_at` | sidecar insertion time | Parent ledgers intentionally include daemon spend rows. If you query both `agent_token_ledger` and `daemon_token_ledger` rows together, avoid double-counting daemon calls that were mirrored into the parent ledger and the daemon-local ledger. Filter by `source_kind`, `source`, `em_id`, or `run_id` according to the report you need. Maintenance tables: - `schema_migrations(version, name, applied_at)` records sidecar schema version. - `import_cursors(source_file, byte_offset, line_no, updated_at)` records the last rebuild/import cursor for each JSONL source. ## Query recipes Recent events: ```sql SELECT id, ts, type, source_kind, run_id, substr(fields_json, 1, 300) AS fields FROM events ORDER BY ts DESC LIMIT 50; ``` Event type counts across agent + daemon events: ```sql SELECT source_kind, type, COUNT(*) AS n, MIN(ts) AS first_ts, MAX(ts) AS last_ts FROM events GROUP BY source_kind, type ORDER BY n DESC LIMIT 50; ``` Per-tool result length aggregation (`$.result` holds the tool output text): ```sql SELECT json_extract(fields_json, '$.tool_name') AS tool, COUNT(*) AS n, CAST(AVG(length(json_extract(fields_json, '$.result'))) AS INT) AS avg_result_len, MAX(length(json_extract(fields_json, '$.result'))) AS max_result_len, SUM(CASE WHEN length(json_extract(fields_json, '$.result')) > 5000 THEN 1 ELSE 0 END) AS over_5000 FROM events WHERE type = 'tool_result' AND json_extract(fields_json, '$.result') IS NOT NULL GROUP BY tool ORDER BY n DESC LIMIT 20; ``` Nearest-rank percentiles of tool result lengths (SQLite window functions): ```sql WITH ranked AS ( SELECT length(json_extract(fields_json, '$.result')) AS result_len, ROW_NUMBER() OVER (ORDER BY length(json_extract(fields_json, '$.result'))) AS rn, COUNT(*) OVER () AS n FROM events WHERE type = 'tool_result' AND json_extract(fields_json, '$.result') IS NOT NULL ) SELECT MAX(CASE WHEN rn <= CAST(n * 0.50 + 0.5 AS INTEGER) THEN result_len END) AS p50, MAX(CASE WHEN rn <= CAST(n * 0.90 + 0.5 AS INTEGER) THEN result_len END) AS p90, MAX(CASE WHEN rn <= CAST(n * 0.95 + 0.5 AS INTEGER) THEN result_len END) AS p95, MAX(CASE WHEN rn <= CAST(n * 0.99 + 0.5 AS INTEGER) THEN result_len END) AS p99, MAX(result_len) AS max_len FROM ranked; ``` Trace one `tool_call_id` lifecycle. A full lifecycle typically spans `tool_call_received` -> `tool_reasoning` -> `tool_call_normalized` -> `tool_call_approved` -> `tool_call` -> `tool_call_dispatch_start` -> `tool_call_dispatch_done` -> `tool_result` -> `tool_result_durable_log_visible` -> `tool_result_model_visible` (daemon runs prefix some of these with `daemon_` and carry `run_id`): ```sql SELECT ts, type, source_kind, run_id, json_extract(fields_json, '$.tool_name') AS tool, substr(fields_json, 1, 160) AS fields FROM events WHERE json_extract(fields_json, '$.tool_call_id') = 'call_00_XXXX' OR json_extract(fields_json, '$.tool_trace_id') = 'call_00_XXXX' ORDER BY ts; ``` Find large/spilled tool results. `tool_result_spilled` rows keep the real size in `$.original_char_count` and point to `$.spill_path`: ```sql SELECT id, ts, type, json_extract(fields_json, '$.tool_name') AS tool, COALESCE(json_extract(fields_json, '$.original_char_count'), length(json_extract(fields_json, '$.result'))) AS result_len, json_extract(fields_json, '$.spill_path') AS spill_path FROM events WHERE type IN ('tool_result', 'tool_result_spilled') AND json_extract(fields_json, '$.result') IS NOT NULL ORDER BY result_len DESC LIMIT 20; ``` Recent chat-history entries: ```sql SELECT id, source_kind, run_id, role, kind, turn, substr(content_text, 1, 400) AS text FROM chat_entries ORDER BY id DESC LIMIT 50; ``` Join daemon tool events with daemon chat rows by `run_id`: ```sql
GitHub에서 보기
이 SKILL.md는 매우 커서 SkillsMP가 여기에는 첫 섹션만 미리 보여줍니다. GitHub에서 보기