wendkeep 0.70.0 → 0.72.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,63 @@ All notable changes to **wendkeep** are documented here. Format based on
4
4
  [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); this project follows
5
5
  [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## [0.72.0] — 2026-08-17
8
+
9
+ ### Added
10
+
11
+ - **Observer SQL authority.** O volume Docker agora usa `/data/observer.sqlite` como
12
+ autoridade única para documentos, sessões, agentes, uso, chamadas LLM e transcripts
13
+ comprimidos; as migrações SQL são versionadas e idempotentes.
14
+ - **Ingestão resiliente.** Hooks de sessão e subagentes enviam eventos idempotentes ao
15
+ Observer, preservam custos registrados e usam outbox local quando o container está
16
+ indisponível.
17
+ - **Dashboard de Consumo.** Cada projeto ganhou resumo de tokens/custos, filtros,
18
+ hierarquia agente/subagente/modelo, tendência diária, chamadas e leitura de transcript.
19
+ - **Migração sem perda.** Conteúdo legado do volume, `MEMORY_EVENTS.jsonl`, frontmatter de
20
+ custo e históricos sem transcript são importados sem apagar as fontes existentes;
21
+ históricos incompletos são marcados como `summary_only`; divergências entre frontmatter e
22
+ ledger ficam em linhas explícitas de reconciliação e `session_id` duplicado é desambiguado
23
+ por arquivo.
24
+
25
+ ### Changed
26
+
27
+ - Markdown deixou de ser autoridade operacional no container. Ele permanece armazenado como
28
+ conteúdo documental no SQLite e só é materializado por exportação explícita.
29
+ - O transporte divide lotes por quantidade e tamanho, reconhece retries do hash legado e
30
+ preserva transcripts grandes dentro do limite HTTP do Observer.
31
+ - Lotes SQL agora usam gzip no transporte e são expandidos com limite controlado no Observer,
32
+ permitindo importar transcripts históricos que excedem 64 MB em JSON puro sem aumentar
33
+ indiscriminadamente o limite de requisição.
34
+
35
+ ## [0.71.1] — 2026-08-17
36
+
37
+ ### Added
38
+
39
+ - **Observer memory authority.** O container local agora recebe a cópia integral das sessões,
40
+ decisões, bugs, aprendizados, specs, changes, CORE, DIGEST, SHARED_MEMORY e estado do brain.
41
+ A API oferece árvore, leitura completa, busca por corpo, sincronização, modos
42
+ `mirror`/`container-read`/`container-authority` e exportação read-only.
43
+ - **Workspace de memória no navegador.** O painel local navega por projeto, sessões, memória,
44
+ changes, sincronização e leitor Markdown completo, sem formulário ou token no loopback.
45
+ - **Importação do WendKeep.** `observer memory import` registra a carga inicial e informa paridade
46
+ por arquivo e hash; o volume Docker permanece preservado em rebuild e reinício.
47
+
48
+ ### Fixed
49
+
50
+ - **Observer local abre diretamente.** O dashboard não exige mais token, formulário ou
51
+ `Authorization` para consultar a projeção no loopback; o compose também inicia sem variável de
52
+ token obrigatória.
53
+
54
+ ## [0.71.0] — 2026-08-17
55
+
56
+ ### Added
57
+
58
+ - **Painel web local do Observer.** O servidor agora entrega uma interface read-only no navegador
59
+ para consultar projetos, saúde, sessões, changes e snapshots sanitizados da última captura. A interface é empacotada no
60
+ npm e na imagem Docker, exige o token local para consultar `/v1`, mantém o token somente no
61
+ `sessionStorage` da aba e
62
+ não amplia o contrato de dados sanitizados.
63
+
7
64
  ## [0.70.0] — 2026-08-17
8
65
 
9
66
  ### Added
package/README.en.md CHANGED
@@ -78,7 +78,7 @@ npx wendkeep import # backfill past Claude + Codex sess
78
78
 
79
79
  ## The problem: the context dies when the window closes
80
80
 
81
- Decisions, dead ends, the reason you chose X over Y — gone next session. The pieces to fix that exist but are scattered (qmd‑sessions, memsearch, Nexus, hand‑written hooks). wendkeep ships them as one turnkey package that writes into a knowledge graph **inside the Obsidian vault you already use** — no manual setup, no snapshot to keep in sync.
81
+ Decisions, dead ends, the reason you chose X over Y — gone next session. The pieces to fix that exist but are scattered (qmd‑sessions, memsearch, Nexus, hand‑written hooks). wendkeep ships local capture plus an optional Docker Observer that keeps complete memory browsable without depending on Obsidian.
82
82
 
83
83
  | | |
84
84
  |---|---|
@@ -88,7 +88,11 @@ Decisions, dead ends, the reason you chose X over Y — gone next session. The p
88
88
  | **Cost** — what it all cost | Per‑model, cache‑aware token pricing per session — plus `cost --trend` with a run‑rate projection across the whole vault; research previews without a final rate remain unestimated. |
89
89
  | **Multi‑agent** — one vault, both agents | `init` wires the session hooks into `.claude/settings.json` *and* `.codex/hooks.json`, and every note is tagged with the agent that wrote it: Claude Code is detected from its environment, anything else is recorded as Codex. One shared graph, whichever agent you are in. |
90
90
  | **Local‑first** — no cloud, no account | Everything is plain Markdown on your disk. An optional MCP server (`@bitbonsai/mcpvault`) lets the agent read/write the vault. |
91
- | **Local Observer** — many projects, one view | `wendkeep observer` keeps a local HTTP index of sanitized, idempotent snapshots; vaults remain local, the container does not mount them, and unavailable hooks use an outbox without blocking the session. |
91
+ | **Local Observer** — many projects, one view | `wendkeep observer` keeps documents, sessions, agents, tokens, costs, calls, and transcripts in the Docker volume SQLite database; every project gets a **Consumption** tab in the read-only dashboard at `http://127.0.0.1:8787/`, loopback has no token, and unavailable hooks use a gzip outbox without blocking the session, including large transcripts. |
92
+
93
+ During historical migration, the Observer preserves differences between frontmatter totals and the
94
+ ledger as explicit reconciliation rows, and disambiguates duplicate `session_id` values per file
95
+ without inventing calls.
92
96
 
93
97
  ## Requirements
94
98
 
@@ -111,7 +115,7 @@ npx wendkeep init
111
115
 
112
116
  1. Create the vault folder taxonomy and a templated `README.md` (default vault: `<project>/.<project-name>-vault`, e.g. `.MyApp-vault`; override with `--vault`).
113
117
  2. Write a provider-neutral **`.wendkeep.json`** binding at the project root and a matching `.brain/PROJECT.json` marker in the vault, then merge the session hooks into **`.claude/settings.json`**. The binding is provider-neutral by design: any agent resolves the same vault from its session `cwd`, with no machine-global environment variable. Older registrations already in `.claude/settings.json` are adopted automatically.
114
- 3. Wire the Codex hooks in **`.codex/hooks.json`** — ten compatible entries: `brain-inject` + `session-start` + `observer-publish` on `SessionStart`, `session-ensure` + `change-context` on `UserPromptSubmit`, `session-stop` + `observer-publish` + `change-nag` on `Stop`, `subagent-stop` on `SubagentStop`, and `change-guard` on `PreToolUse` for `Bash`, `exec_command`, `apply_patch`, and mutable MCP tools, always in the `npx wendkeep hook <name>` form. `observer-publish` only publishes a sanitized, fail-open projection; it does not replace the local lifecycle. The guard accepts object, raw-string, and argv Codex payloads; before a mutation it compares the session with the project, Git root, remote, branch, and worktree, denying missing or divergent targets. The other four stay out because Codex offers no equivalent payload, tool, or event: `change-warn` (no reliable `tool_input.file_path`), `plan-capture` (no `ExitPlanMode`), `decision-capture` (`AskUserQuestion` is Claude-only), and `task-log` (`TaskCompleted` is not in Codex's event enum). Codex scope blocks use `permissionDecision: "deny"`; `ask` is never emitted in `PreToolUse`. The merge remains non-destructive, preserves third-party hooks, and migrates legacy `timeout` to `timeoutSec`. **Codex enumerates every hook as untrusted and runs none until you approve the “Hooks need review” prompt at startup — `init` cannot pre-approve them**.
118
+ 3. Wire the Codex hooks in **`.codex/hooks.json`** — eleven compatible entries: `brain-inject` + `session-start` + `observer-publish` on `SessionStart`, `session-ensure` + `change-context` on `UserPromptSubmit`, `session-stop` + `observer-publish` + `change-nag` on `Stop`, `subagent-stop` + `observer-publish` on `SubagentStop`, and `change-guard` on `PreToolUse` for `Bash`, `exec_command`, `apply_patch`, and mutable MCP tools, always in the `npx wendkeep hook <name>` form. `observer-publish` keeps the sanitized index projection and also sends the local SQL authority with documents, consumption, and transcripts; it does not replace the local lifecycle. The guard accepts object, raw-string, and argv Codex payloads; before a mutation it compares the session with the project, Git root, remote, branch, and worktree, denying missing or divergent targets. The other four stay out because Codex offers no equivalent payload, tool, or event: `change-warn` (no reliable `tool_input.file_path`), `plan-capture` (no `ExitPlanMode`), `decision-capture` (`AskUserQuestion` is Claude-only), and `task-log` (`TaskCompleted` is not in Codex's event enum). Codex scope blocks use `permissionDecision: "deny"`; `ask` is never emitted in `PreToolUse`. The merge remains non-destructive, preserves third-party hooks, and migrates legacy `timeout` to `timeoutSec`. **Codex enumerates every hook as untrusted and runs none until you approve the “Hooks need review” prompt at startup — `init` cannot pre-approve them**.
115
119
  4. Add the **`wendkeep-vault`** MCP server to `.mcp.json` so the agent can read/write the vault. Skip with `--no-mcp` — e.g. when the agent already has a vault MCP. (`--no-mcp` skips *only wendkeep's own* MCP; companion MCPs still follow `--companions`.)
116
120
  5. Offer to pin **companion** plugins/MCP (multi-choice; **none** pre-checked — wendkeep is a neutral harness and presumes no third-party plugin). Each is wired the most agent-agnostic way it supports:
117
121
  - **`context-mode`** — context optimizer + FTS5 memory, wired as a Claude Code plugin. It ships its own MCP server, so wendkeep deliberately adds no `.mcp.json` entry (registering both cold-started two servers at once). On non-Claude agents, add the MCP by hand: `npx -y context-mode`.
package/README.md CHANGED
@@ -78,7 +78,7 @@ npx wendkeep import # backfill past Claude + Codex sess
78
78
 
79
79
  ## The problem: the context dies when the window closes
80
80
 
81
- Decisions, dead ends, the reason you chose X over Y — gone next session. The pieces to fix that exist but are scattered (qmd‑sessions, memsearch, Nexus, hand‑written hooks). wendkeep ships them as one turnkey package that writes into a knowledge graph **inside the Obsidian vault you already use** — no manual setup, no snapshot to keep in sync.
81
+ Decisions, dead ends, the reason you chose X over Y — gone next session. The pieces to fix that exist but are scattered (qmd‑sessions, memsearch, Nexus, hand‑written hooks). wendkeep ships local capture plus an optional Docker Observer that keeps complete memory browsable without depending on Obsidian.
82
82
 
83
83
  | | |
84
84
  |---|---|
@@ -88,7 +88,11 @@ Decisions, dead ends, the reason you chose X over Y — gone next session. The p
88
88
  | **Cost** — what it all cost | Per‑model, cache‑aware token pricing per session — plus `cost --trend` with a run‑rate projection across the whole vault; research previews without a final rate remain unestimated. |
89
89
  | **Multi‑agent** — one vault, both agents | `init` wires the session hooks into `.claude/settings.json` *and* `.codex/hooks.json`, and every note is tagged with the agent that wrote it: Claude Code is detected from its environment, anything else is recorded as Codex. One shared graph, whichever agent you are in. |
90
90
  | **Local‑first** — no cloud, no account | Everything is plain Markdown on your disk. An optional MCP server (`@bitbonsai/mcpvault`) lets the agent read/write the vault. |
91
- | **Local Observer** — many projects, one view | `wendkeep observer` keeps a local HTTP index of sanitized, idempotent snapshots; vaults remain local, the container does not mount them, and unavailable hooks use an outbox without blocking the session. |
91
+ | **Local Observer** — many projects, one view | `wendkeep observer` keeps documents, sessions, agents, tokens, costs, calls, and transcripts in the Docker volume SQLite database; every project gets a **Consumption** tab in the read-only dashboard at `http://127.0.0.1:8787/`, loopback has no token, and unavailable hooks use a gzip outbox without blocking the session, including large transcripts. |
92
+
93
+ During historical migration, the Observer preserves differences between frontmatter totals and the
94
+ ledger as explicit reconciliation rows, and disambiguates duplicate `session_id` values per file
95
+ without inventing calls.
92
96
 
93
97
  ## Requirements
94
98
 
@@ -111,7 +115,7 @@ npx wendkeep init
111
115
 
112
116
  1. Create the vault folder taxonomy and a templated `README.md` (default vault: `<project>/.<project-name>-vault`, e.g. `.MyApp-vault`; override with `--vault`).
113
117
  2. Write a provider-neutral **`.wendkeep.json`** binding at the project root and a matching `.brain/PROJECT.json` marker in the vault, then merge the session hooks into **`.claude/settings.json`**. The binding is provider-neutral by design: any agent resolves the same vault from its session `cwd`, with no machine-global environment variable. Older registrations already in `.claude/settings.json` are adopted automatically.
114
- 3. Wire the Codex hooks in **`.codex/hooks.json`** — ten compatible entries: `brain-inject` + `session-start` + `observer-publish` on `SessionStart`, `session-ensure` + `change-context` on `UserPromptSubmit`, `session-stop` + `observer-publish` + `change-nag` on `Stop`, `subagent-stop` on `SubagentStop`, and `change-guard` on `PreToolUse` for `Bash`, `exec_command`, `apply_patch`, and mutable MCP tools, always in the `npx wendkeep hook <name>` form. `observer-publish` only publishes a sanitized, fail-open projection; it does not replace the local lifecycle. The guard accepts object, raw-string, and argv Codex payloads; before a mutation it compares the session with the project, Git root, remote, branch, and worktree, denying missing or divergent targets. The other four stay out because Codex offers no equivalent payload, tool, or event: `change-warn` (no reliable `tool_input.file_path`), `plan-capture` (no `ExitPlanMode`), `decision-capture` (`AskUserQuestion` is Claude-only), and `task-log` (`TaskCompleted` is not in Codex's event enum). Codex scope blocks use `permissionDecision: "deny"`; `ask` is never emitted in `PreToolUse`. The merge remains non-destructive, preserves third-party hooks, and migrates legacy `timeout` to `timeoutSec`. **Codex enumerates every hook as untrusted and runs none until you approve the “Hooks need review” prompt at startup — `init` cannot pre-approve them**.
118
+ 3. Wire the Codex hooks in **`.codex/hooks.json`** — eleven compatible entries: `brain-inject` + `session-start` + `observer-publish` on `SessionStart`, `session-ensure` + `change-context` on `UserPromptSubmit`, `session-stop` + `observer-publish` + `change-nag` on `Stop`, `subagent-stop` + `observer-publish` on `SubagentStop`, and `change-guard` on `PreToolUse` for `Bash`, `exec_command`, `apply_patch`, and mutable MCP tools, always in the `npx wendkeep hook <name>` form. `observer-publish` keeps the sanitized index projection and also sends the local SQL authority with documents, consumption, and transcripts; it does not replace the local lifecycle. The guard accepts object, raw-string, and argv Codex payloads; before a mutation it compares the session with the project, Git root, remote, branch, and worktree, denying missing or divergent targets. The other four stay out because Codex offers no equivalent payload, tool, or event: `change-warn` (no reliable `tool_input.file_path`), `plan-capture` (no `ExitPlanMode`), `decision-capture` (`AskUserQuestion` is Claude-only), and `task-log` (`TaskCompleted` is not in Codex's event enum). Codex scope blocks use `permissionDecision: "deny"`; `ask` is never emitted in `PreToolUse`. The merge remains non-destructive, preserves third-party hooks, and migrates legacy `timeout` to `timeoutSec`. **Codex enumerates every hook as untrusted and runs none until you approve the “Hooks need review” prompt at startup — `init` cannot pre-approve them**.
115
119
  4. Add the **`wendkeep-vault`** MCP server to `.mcp.json` so the agent can read/write the vault. Skip with `--no-mcp` — e.g. when the agent already has a vault MCP. (`--no-mcp` skips *only wendkeep's own* MCP; companion MCPs still follow `--companions`.)
116
120
  5. Offer to pin **companion** plugins/MCP (multi-choice; **none** pre-checked — wendkeep is a neutral harness and presumes no third-party plugin). Each is wired the most agent-agnostic way it supports:
117
121
  - **`context-mode`** — context optimizer + FTS5 memory, wired as a Claude Code plugin. It ships its own MCP server, so wendkeep deliberately adds no `.mcp.json` entry (registering both cold-started two servers at once). On non-Claude agents, add the MCP by hand: `npx -y context-mode`.
@@ -4,23 +4,28 @@
4
4
 
5
5
  ## Purpose
6
6
 
7
- The Observer consolidates observability for multiple WendKeep projects in a local service without
8
- copying or taking ownership of their vaults.
7
+ The Observer consolidates observability for multiple WendKeep projects in a local service. The
8
+ Docker volume keeps `/data/observer.sqlite` as the single authority for documents, sessions,
9
+ agents, usage, calls, and complete transcripts. The content can be browsed and searched in the
10
+ container without depending on Obsidian for queries.
9
11
 
10
12
  ## When to use
11
13
 
12
- Use it to query summarized changes, sessions, tasks, and health across projects through one local
13
- projection while each vault remains authoritative.
14
+ Use it to query changes, sessions, decisions, bugs, learnings, specs, brain documents, per-agent
15
+ and per-model consumption, and health across projects through one local memory. During the
16
+ transition, the vault and legacy Markdown files remain a recovery copy; the Observer is
17
+ authoritative for queries made through its container.
14
18
 
15
19
  ## When not to use
16
20
 
17
- Do not use the Observer to edit, complete, or archive changes, curate memory, store transcripts,
18
- or replace local hooks. Do not expose it to the network in this version.
21
+ Do not use the Observer to edit, complete, or archive changes, curate memory, automatically export
22
+ the authority back to Markdown, or expose the service to the network. Edits still go through local
23
+ WendKeep hooks.
19
24
 
20
25
  ## Prerequisites
21
26
 
22
- Initialize projects with WendKeep, explicitly register each project, and set a local token before
23
- starting the HTTP server.
27
+ Initialize projects with WendKeep and explicitly register each project before starting the HTTP
28
+ server. The default local mode has no token to configure.
24
29
 
25
30
  ## Syntax
26
31
 
@@ -28,6 +33,7 @@ starting the HTTP server.
28
33
  npx wendkeep observer status --data-dir <directory> --json
29
34
  npx wendkeep observer register --project <project> --vault <vault> --data-dir <directory>
30
35
  npx wendkeep observer publish --project <project> --vault <vault> --data-dir <directory>
36
+ npx wendkeep observer memory import --project <project> --vault <vault> --url http://127.0.0.1:8787 --json
31
37
  npx wendkeep observer serve --host 127.0.0.1 --port 8787 --data-dir <directory>
32
38
  ```
33
39
 
@@ -35,17 +41,17 @@ npx wendkeep observer serve --host 127.0.0.1 --port 8787 --data-dir <directory>
35
41
 
36
42
  - `--data-dir` selects the local event and index directory; the default is
37
43
  `WENDKEEP_OBSERVER_DATA_DIR` or `~/.wendkeep-observer`.
38
- - `--project` and `--vault` identify a project only for `register` and `publish`.
44
+ - `--project` and `--vault` identify a project for `register`, `publish`, and `memory import`.
39
45
  - `--host` accepts only `127.0.0.1`, `localhost`, or `::1`; other hosts are rejected before
40
46
  listening.
41
- - `--token` or `WENDKEEP_OBSERVER_TOKEN` protects `/v1`; `GET /healthz` exposes no project data.
47
+ - `/v1` is open in the default local mode; keep `--host 127.0.0.1` and do not publish the port on
48
+ a network address.
42
49
  - Exit `0` means success; exit `1` means configuration or operation failure; the publisher hook
43
50
  also returns `0` when the Observer is unavailable.
44
51
 
45
52
  ## Examples
46
53
 
47
54
  ```powershell
48
- $env:WENDKEEP_OBSERVER_TOKEN = '<local-token>'
49
55
  npx wendkeep observer register --project C:\GitHub\WendKeep --vault C:\GitHub\WendKeep\.WendKeep-vault --data-dir C:\WendKeepObserver
50
56
  npx wendkeep observer serve --host 127.0.0.1 --port 8787 --data-dir C:\WendKeepObserver
51
57
  $env:WENDKEEP_OBSERVER_URL = 'http://127.0.0.1:8787'
@@ -54,30 +60,53 @@ $env:WENDKEEP_OBSERVER_URL = 'http://127.0.0.1:8787'
54
60
  For local Docker:
55
61
 
56
62
  ```powershell
57
- $env:WENDKEEP_OBSERVER_TOKEN = '<local-token>'
58
63
  docker compose -f docker/wendkeep-observer/compose.yaml up -d --build
59
64
  ```
60
65
 
61
- ## Expected result
66
+ ## Local web dashboard
67
+
68
+ With the server running, open [http://127.0.0.1:8787/](http://127.0.0.1:8787/) in a browser. The
69
+ dashboard is served by the same process and opens directly, without a form or token. Keep the port
70
+ bound to the computer loopback; do not expose this address on a network interface.
71
+
72
+ The dashboard shows the multi-project list, version, health, latest session, active change, change
73
+ count, and last capture time. Opening a project exposes Overview, Consumption, Sessions, Memory,
74
+ Changes, and Sync screens. Consumption shows total cost, token categories, primary agents,
75
+ subagents, providers, models, daily trend, historical coverage, and calls with prompt, response,
76
+ and complete transcript. Loading, empty, unavailable-server, conflict, no-pricing, and stale-data
77
+ states are visible, with manual refresh and an automatic 15-second refresh.
62
78
 
63
- `register` stores only `project_id`, name, version, and registration time. `publish` reads the
64
- local vault, produces a sanitized snapshot, and sends an idempotent event. The container stores
65
- only `EVENTS.jsonl` and `INDEX.json` in the `observer-data` volume; it does not mount `C:\GitHub`
66
- or any `.WendKeep-vault`.
79
+ If the browser shows the shell but the list fails, check the service health at
80
+ `http://127.0.0.1:8787/healthz` and confirm that the container is running.
67
81
 
68
- `init` projects `observer-publish` into `SessionStart` and `Stop` after the primary hooks. Without
69
- `WENDKEEP_OBSERVER_URL`, the hook is a no-op. When the server is stopped, it writes to
70
- `.brain/observer-outbox/` and does not block the session; a later run retries pending events.
82
+ ## Expected result
83
+
84
+ `register` stores `project_id`, name, version, and registration time. `publish` reads the local
85
+ vault, produces the snapshot, and sends idempotent events to SQLite containing the complete content
86
+ of sessions, decisions, bugs, learnings, specs, changes, CORE, DIGEST, SHARED_MEMORY, brain state,
87
+ agent sessions, cost rollups, calls, and transcripts. The container stores everything in
88
+ `/data/observer.sqlite`; it does not mount `C:\GitHub` or any `.WendKeep-vault`. Markdown is only
89
+ the text held in SQL and is recreated as files only by an explicit read-only export.
90
+ `memory import` performs the initial load and returns file/hash parity. During migration, the
91
+ cost/token total recorded in frontmatter is preserved through an explicit reconciliation row when
92
+ the detailed ledger does not add up; that row does not invent calls. Historical sessions sharing
93
+ one `session_id` receive a canonical per-file identity so one rollup cannot overwrite the other.
94
+
95
+ `init` projects `observer-publish` into `SessionStart`, `Stop`, and `SubagentStop` after the primary
96
+ hooks. When the server is unavailable, it writes snapshots to `.brain/observer-outbox/` and SQL
97
+ events to `.brain/observer-sql-outbox/` without blocking the session; a later run retries the
98
+ batches. SQL batches use gzip so complete transcripts larger than 64 MB as plain JSON remain within
99
+ the transport limit; the Observer decompresses and validates the body before ingesting it. The
100
+ outbox is temporary transport, not authority.
71
101
 
72
102
  ## Common errors and diagnosis
73
103
 
74
104
  - `project_not_registered`: run `observer register` before publishing.
75
- - `unauthorized`: check `Authorization: Bearer <token>` and `WENDKEEP_OBSERVER_TOKEN`.
76
105
  - `host loopback`: replace `0.0.0.0` or a LAN address with `127.0.0.1`.
77
- - Pending outbox: the service was unavailable; preserve `.brain/observer-outbox/` and rerun the
78
- publisher. Do not delete events manually.
79
- - The Observer does not read raw content, paths, transcripts, or memory; such rejections are
80
- expected and should be investigated at the snapshot source.
106
+ - Pending outbox: the service was unavailable; preserve `.brain/observer-outbox/` and
107
+ `.brain/observer-sql-outbox/`, then rerun the publisher. Do not delete events manually.
108
+ - If memory or usage is incomplete, check the Sync screen, preserve the outbox, and run
109
+ `observer memory import` to rebuild the load from the vault.
81
110
 
82
111
  ## Next steps
83
112
 
@@ -87,18 +116,34 @@ projection.
87
116
 
88
117
  ## Data authority
89
118
 
90
- Each project vault remains authoritative for sessions, changes, tasks, memory, and evidence. The
91
- Observer is a read-only, rebuildable projection; its queries do not complete, archive, repair, or
92
- promote state in a vault.
119
+ The container SQLite database is canonical for Observer queries and stores the complete published
120
+ content. The vault and any legacy `/data/memory` remain preserved during migration as a transition
121
+ and recovery copy; hooks do not update container Markdown after cutover. Observer screens do not
122
+ complete, archive, repair, or promote state.
93
123
 
94
124
  ## Minimal API
95
125
 
96
- - `GET /healthz` — availability without project data.
97
- - `GET /v1/projects` — projects with an accepted snapshot.
126
+ - `GET /healthz` — availability, SQLite migration version, and legacy migration state.
127
+ - `GET /v1/projects` — projects registered in SQLite, with a snapshot when available.
98
128
  - `GET /v1/projects/:project_id` — the latest project snapshot.
99
129
  - `GET /v1/projects/:project_id/changes` — change summary from the snapshot.
100
- - `PUT /v1/projects/:project_id` — authenticated explicit registration.
101
- - `POST /v1/projects/:project_id/snapshot` — authenticated idempotent ingestion.
102
-
103
- The `/v1` routes reject oversized bodies and never accept vault paths, transcripts, secrets, or raw
104
- memory content.
130
+ - `PUT /v1/projects/:project_id` — explicit local registration.
131
+ - `POST /v1/projects/:project_id/snapshot` — idempotent local ingestion.
132
+ - `POST /v1/projects/:project_id/ingest` — idempotent batches of documents, sessions, agents,
133
+ rollups, calls, and transcripts.
134
+ - `GET /v1/projects/:project_id/memory/tree` — document tree and metadata.
135
+ - `GET /v1/projects/:project_id/memory/document?path=...` — complete Markdown content.
136
+ - `GET /v1/projects/:project_id/memory/search?q=...` — path and body search.
137
+ - `GET /v1/projects/:project_id/sync` — mode, counts, conflicts, and latest event.
138
+ - `PUT /v1/projects/:project_id/sync` — compatibility configuration; SQL remains authoritative.
139
+ - `GET /v1/projects/:project_id/memory/export` — read-only export with complete content.
140
+ - `POST /v1/projects/:project_id/memory/events` — idempotent batch ingestion.
141
+ - `GET /v1/projects/:project_id/usage/summary` — filterable totals by period, change, session,
142
+ agent, provider, model, and role.
143
+ - `GET /v1/projects/:project_id/usage/breakdown` — agent, subagent, and model hierarchy.
144
+ - `GET /v1/projects/:project_id/usage/calls` — individual calls with prompt and response.
145
+ - `GET /v1/projects/:project_id/transcripts/:transcript_id` — compressed transcript validated by hash.
146
+
147
+ The `/v1` routes reject transported or expanded bodies above their limits and validate project,
148
+ path, revision, hash, idempotency, and isolation before writing to SQLite. Use `memory/export` for
149
+ a Markdown copy; it does not alter SQL authority.
@@ -4,23 +4,28 @@
4
4
 
5
5
  ## Objetivo
6
6
 
7
- O Observer consolida a observabilidade de vários projetos WendKeep em um serviço local, sem
8
- copiar ou assumir a propriedade dos vaults.
7
+ O Observer consolida a observabilidade de vários projetos WendKeep em um serviço local. O volume
8
+ Docker mantém o SQLite `/data/observer.sqlite` como autoridade única para documentos, sessões,
9
+ agentes, uso, chamadas e transcripts completos. O conteúdo fica disponível para navegação e busca
10
+ no próprio container, sem depender do Obsidian para consulta.
9
11
 
10
12
  ## Quando usar
11
13
 
12
- Use para consultar changes, sessões, tarefas resumidas e saúde de vários projetos em uma única
13
- projeção local, mantendo cada vault como fonte oficial.
14
+ Use para consultar changes, sessões, decisões, bugs, aprendizados, specs, documentos do brain,
15
+ consumo por agente/modelo e saúde de vários projetos em uma única memória local. Durante a
16
+ transição, o vault e os arquivos Markdown legados continuam preservados como cópia de origem para
17
+ recuperação; o Observer é a autoridade de consulta do seu container.
14
18
 
15
19
  ## Quando não usar
16
20
 
17
- Não use o Observer para editar, concluir ou arquivar changes, curar memória, armazenar transcripts
18
- ou substituir os hooks locais. Não o exponha na rede nesta versão.
21
+ Não use o Observer para editar, concluir ou arquivar changes, curar memória, exportar a autoridade
22
+ de volta para Markdown automaticamente ou expor o serviço na rede. As edições continuam passando
23
+ pelos hooks e pelo WendKeep local.
19
24
 
20
25
  ## Pré-requisitos
21
26
 
22
- Tenha os projetos inicializados com WendKeep, registre explicitamente cada projeto e defina um
23
- token local antes de iniciar o servidor HTTP.
27
+ Tenha os projetos inicializados com WendKeep e registre explicitamente cada projeto antes de
28
+ iniciar o servidor HTTP. No modo local padrão não token para configurar.
24
29
 
25
30
  ## Sintaxe
26
31
 
@@ -28,6 +33,7 @@ token local antes de iniciar o servidor HTTP.
28
33
  npx wendkeep observer status --data-dir <diretório> --json
29
34
  npx wendkeep observer register --project <projeto> --vault <vault> --data-dir <diretório>
30
35
  npx wendkeep observer publish --project <projeto> --vault <vault> --data-dir <diretório>
36
+ npx wendkeep observer memory import --project <projeto> --vault <vault> --url http://127.0.0.1:8787 --json
31
37
  npx wendkeep observer serve --host 127.0.0.1 --port 8787 --data-dir <diretório>
32
38
  ```
33
39
 
@@ -35,18 +41,17 @@ npx wendkeep observer serve --host 127.0.0.1 --port 8787 --data-dir <diretório>
35
41
 
36
42
  - `--data-dir` escolhe o diretório local de eventos e índice; o padrão é
37
43
  `WENDKEEP_OBSERVER_DATA_DIR` ou `~/.wendkeep-observer`.
38
- - `--project` e `--vault` identificam o projeto somente nos comandos `register` e `publish`.
44
+ - `--project` e `--vault` identificam o projeto nos comandos `register`, `publish` e `memory import`.
39
45
  - `--host` aceita somente `127.0.0.1`, `localhost` ou `::1`; outros hosts são recusados antes do
40
46
  listen.
41
- - `--token` ou `WENDKEEP_OBSERVER_TOKEN` protege as rotas `/v1`; `GET /healthz` permanece sem
42
- dados de projeto.
47
+ - as rotas `/v1` ficam abertas no modo local padrão; mantenha `--host 127.0.0.1` e não publique a
48
+ porta em um endereço de rede.
43
49
  - Exit `0` indica sucesso; exit `1` indica falha de configuração ou operação; o hook publisher
44
50
  também retorna `0` quando o Observer está indisponível.
45
51
 
46
52
  ## Exemplos
47
53
 
48
54
  ```powershell
49
- $env:WENDKEEP_OBSERVER_TOKEN = '<token-local>'
50
55
  npx wendkeep observer register --project C:\GitHub\WendKeep --vault C:\GitHub\WendKeep\.WendKeep-vault --data-dir C:\WendKeepObserver
51
56
  npx wendkeep observer serve --host 127.0.0.1 --port 8787 --data-dir C:\WendKeepObserver
52
57
  $env:WENDKEEP_OBSERVER_URL = 'http://127.0.0.1:8787'
@@ -55,31 +60,55 @@ $env:WENDKEEP_OBSERVER_URL = 'http://127.0.0.1:8787'
55
60
  Para Docker local:
56
61
 
57
62
  ```powershell
58
- $env:WENDKEEP_OBSERVER_TOKEN = '<token-local>'
59
63
  docker compose -f docker/wendkeep-observer/compose.yaml up -d --build
60
64
  ```
61
65
 
62
- ## Resultado esperado
66
+ ## Painel web local
67
+
68
+ Com o servidor em execução, abra [http://127.0.0.1:8787/](http://127.0.0.1:8787/) no navegador.
69
+ O painel é servido pelo mesmo processo e abre diretamente, sem formulário ou token. A porta fica
70
+ presa ao loopback do computador; não coloque o endereço em uma interface de rede.
71
+
72
+ O painel mostra a lista multi-projeto, versão, saúde, sessão mais recente, change ativa, contagem
73
+ de changes e data da última captura. Ao abrir um projeto, o workspace oferece Overview, Consumo,
74
+ Sessões, Memória, Changes e Sincronização. A aba Consumo mostra custo total, tokens por categoria,
75
+ agentes principais, subagentes, provedores, modelos, tendência diária, cobertura histórica e
76
+ chamadas com prompt, resposta e transcript completo. Os estados de carregamento, vazio, servidor
77
+ indisponível, conflito, modelo sem tarifa e dados desatualizados ficam visíveis, e a atualização
78
+ pode ser manual ou automática a cada 15 segundos.
63
79
 
64
- `register` grava apenas `project_id`, nome, versão e data de registro. `publish` o vault local,
65
- produz um snapshot sanitizado e envia um evento idempotente. O container mantém somente
66
- `EVENTS.jsonl` e `INDEX.json` no volume `observer-data`; não monta `C:\GitHub` nem qualquer
67
- `.WendKeep-vault`.
80
+ Se o navegador mostrar a tela mas a lista falhar, confirme a saúde em
81
+ `http://127.0.0.1:8787/healthz` e verifique se o container está em execução.
68
82
 
69
- O `init` projeta `observer-publish` para `SessionStart` e `Stop` depois dos hooks principais. Sem
70
- `WENDKEEP_OBSERVER_URL`, o hook é no-op. Com o servidor parado, ele grava em
71
- `.brain/observer-outbox/` e não bloqueia a sessão; uma execução posterior tenta reenviar eventos
72
- pendentes.
83
+ ## Resultado esperado
84
+
85
+ `register` grava `project_id`, nome, versão e data de registro. `publish` o vault local,
86
+ produz o snapshot e envia eventos idempotentes para o SQLite com o conteúdo integral das sessões,
87
+ decisões, bugs, aprendizados, specs, changes, CORE, DIGEST, SHARED_MEMORY, estado do brain,
88
+ sessões de agentes, rollups de custo, chamadas e transcripts. O container grava tudo em
89
+ `/data/observer.sqlite`; não monta `C:\GitHub` nem qualquer `.WendKeep-vault`. Markdown é aceito
90
+ somente como conteúdo de uma coluna SQL e volta a existir como arquivo apenas pela exportação
91
+ read-only sob demanda. `memory import` faz a carga inicial e retorna a paridade por arquivo e hash.
92
+ Na migração, o total de custo/token registrado no frontmatter é preservado por uma linha de
93
+ reconciliação quando o ledger detalhado não fecha com ele; essa linha não inventa chamadas.
94
+ Sessões históricas com o mesmo `session_id` recebem uma identidade canônica por arquivo para
95
+ evitar que um rollup sobrescreva o outro.
96
+
97
+ O `init` projeta `observer-publish` para `SessionStart`, `Stop` e `SubagentStop` depois dos hooks
98
+ principais. Sem servidor disponível, ele grava snapshots em `.brain/observer-outbox/` e eventos
99
+ SQL em `.brain/observer-sql-outbox/`, sem bloquear a sessão; uma execução posterior tenta
100
+ reenviar os lotes. Os lotes SQL são enviados com gzip para que transcripts completos maiores que
101
+ 64 MB em JSON puro continuem dentro do limite do transporte; o Observer descomprime e valida o
102
+ corpo antes de ingerir. O outbox é transporte temporário, não autoridade.
73
103
 
74
104
  ## Erros comuns e diagnóstico
75
105
 
76
106
  - `project_not_registered`: rode `observer register` antes de publicar.
77
- - `unauthorized`: confira `Authorization: Bearer <token>` e `WENDKEEP_OBSERVER_TOKEN`.
78
107
  - `host loopback`: troque `0.0.0.0` ou endereço LAN por `127.0.0.1`.
79
- - Outbox pendente: o serviço estava indisponível; preserve `.brain/observer-outbox/` e repita o
80
- publisher. Não apague eventos manualmente.
81
- - O Observer não conteúdo bruto, caminhos, transcripts ou memória; rejeições desse tipo são
82
- esperadas e devem ser investigadas na origem do snapshot.
108
+ - Outbox pendente: o serviço estava indisponível; preserve `.brain/observer-outbox/` e
109
+ `.brain/observer-sql-outbox/` e repita o publisher. Não apague eventos manualmente.
110
+ - Se a memória ou o consumo ficarem incompletos, verifique a tela Sincronização, preserve o
111
+ outbox e rode `observer memory import` para reconstruir a carga a partir do vault.
83
112
 
84
113
  ## Próximos passos
85
114
 
@@ -88,18 +117,34 @@ removido com `docker compose down -v` durante a operação normal, pois isso apa
88
117
 
89
118
  ## Autoridade dos dados
90
119
 
91
- O vault de cada projeto continua sendo a fonte oficial de sessões, changes, tarefas, memória e
92
- evidências. O Observer é uma projeção read-only reconstruível; suas consultas não concluem,
93
- arquivam, reparam ou promovem estado no vault.
120
+ O SQLite do container é a memória canônica para consultas do Observer e guarda o conteúdo integral
121
+ publicado. O vault e qualquer `/data/memory` legado continuam preservados durante a migração como
122
+ cópia de transição e fonte de recuperação; os hooks não atualizam Markdown no container depois do
123
+ corte. As telas do Observer não concluem, arquivam, reparam ou promovem estado.
94
124
 
95
125
  ## API mínima
96
126
 
97
- - `GET /healthz` — disponibilidade sem dados de projeto.
98
- - `GET /v1/projects` — projetos com snapshot aceito.
127
+ - `GET /healthz` — disponibilidade, versão das migrações SQLite e estado da migração legada.
128
+ - `GET /v1/projects` — projetos registrados no SQLite, com snapshot quando houver.
99
129
  - `GET /v1/projects/:project_id` — último snapshot do projeto.
100
130
  - `GET /v1/projects/:project_id/changes` — resumo das changes do snapshot.
101
- - `PUT /v1/projects/:project_id` — registro explícito autenticado.
102
- - `POST /v1/projects/:project_id/snapshot` — ingestão autenticada e idempotente.
103
-
104
- As rotas `/v1` rejeitam corpo acima do limite e nunca aceitam caminho de vault, transcript, segredo
105
- ou conteúdo bruto de memória.
131
+ - `PUT /v1/projects/:project_id` — registro explícito local.
132
+ - `POST /v1/projects/:project_id/snapshot` — ingestão local idempotente.
133
+ - `POST /v1/projects/:project_id/ingest` — lote idempotente de documentos, sessões, agentes, rollups,
134
+ chamadas e transcripts.
135
+ - `GET /v1/projects/:project_id/memory/tree` árvore e metadados dos documentos.
136
+ - `GET /v1/projects/:project_id/memory/document?path=...` — conteúdo Markdown integral.
137
+ - `GET /v1/projects/:project_id/memory/search?q=...` — busca no caminho e no corpo.
138
+ - `GET /v1/projects/:project_id/sync` — modo, contagem, conflitos e último evento.
139
+ - `PUT /v1/projects/:project_id/sync` — compatibilidade de configuração; a autoridade continua SQL.
140
+ - `GET /v1/projects/:project_id/memory/export` — exportação read-only com conteúdo completo.
141
+ - `POST /v1/projects/:project_id/memory/events` — ingestão idempotente em lote.
142
+ - `GET /v1/projects/:project_id/usage/summary` — totais filtráveis por período, change, sessão,
143
+ agente, provedor, modelo e papel.
144
+ - `GET /v1/projects/:project_id/usage/breakdown` — hierarquia de agentes, subagentes e modelos.
145
+ - `GET /v1/projects/:project_id/usage/calls` — chamadas individuais com prompt e resposta.
146
+ - `GET /v1/projects/:project_id/transcripts/:transcript_id` — transcript comprimido, validado por hash.
147
+
148
+ As rotas `/v1` rejeitam corpo transportado ou expandido acima do limite e validam projeto, caminho,
149
+ revisão, hash, idempotência e isolamento antes de gravar o conteúdo no SQLite. Para preservar uma
150
+ cópia Markdown, use a rota `memory/export`; ela não altera a autoridade SQL.
@@ -9,6 +9,7 @@ async function main() {
9
9
  const result = await publishObserverSnapshot({
10
10
  vaultBase: resolved.base,
11
11
  projectRoot: resolved.projectRoot,
12
+ input,
12
13
  });
13
14
  if (!result.ok && result.error) debugLog('Observer publish fail-open:', result.error);
14
15
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wendkeep",
3
- "version": "0.70.0",
3
+ "version": "0.72.0",
4
4
  "description": "Vault-first persistent memory for AI coding agents, with an optional profile-aware governance runtime: OFF, FLOW, GUIDE, GOVERN, or ASSURE. Local-first and agent-agnostic (Claude Code, Codex, Cursor…).",
5
5
  "type": "module",
6
6
  "workspaces": [
@@ -30,6 +30,7 @@
30
30
  "hooks",
31
31
  "packages",
32
32
  "schema",
33
+ "web/observer",
33
34
  "docs/pt-BR/commands/*.md",
34
35
  "docs/en/commands/*.md",
35
36
  "README.md",
@@ -40,7 +41,7 @@
40
41
  "node": ">=18"
41
42
  },
42
43
  "scripts": {
43
- "check": "node --check scripts/release.mjs && node --check scripts/release-plan.mjs && node --check bin/wendkeep.mjs && node --check packages/cli/src/index.mjs && node --check src/init.mjs && node --check src/doctor.mjs && node --check src/project-vault.mjs && node --check src/observer-snapshot.mjs && node --check src/observer-store.mjs && node --check src/observer-server.mjs && node --check src/observer.mjs && node --check src/observer-publish.mjs && node --check src/operating-profile.mjs && node --check src/profile.mjs && node --check src/flow.mjs && node --check hooks/observer-publish.mjs && node --check hooks/operating-profile-runtime.mjs && node --check hooks/operating-profile-task-store.mjs && node --check hooks/flow-core.mjs && node --check hooks/flow-protected-policy.mjs && node --check hooks/git-snapshot.mjs && node --check hooks/vault-path-safety.mjs && node --check hooks/vault-runtime-store.mjs && node --check packages/harness/src/index.mjs && node --check packages/harness/src/flow-store.mjs && node --check packages/harness/src/operating-profile.mjs && node --check packages/harness/src/sensors-core.mjs && node --check packages/integrations/src/host-hooks.mjs && node --check packages/integrations/src/hook-envelope.mjs && node --check packages/integrations/src/prompt-content.mjs && node --check packages/integrations/src/transcript-usage.mjs && node --check packages/integrations/src/transcripts.mjs && node --check packages/integrations/src/session-identity.mjs && node --check packages/integrations/src/index.mjs && node --check packages/mcp/src/config.mjs && node --check packages/mcp/src/index.mjs && node --check packages/vault/src/index.mjs && node --check packages/vault/src/project-vault.mjs && node --check packages/vault/src/vault-path-safety.mjs && node --check packages/vault/src/locale.mjs && node --check packages/vault/src/memory-schema.mjs && node --check packages/vault/src/memory-mode.mjs && node --check packages/vault/src/memory-handoff.mjs && node --check packages/vault/src/memory-store.mjs && node --check packages/vault/src/validate-core.mjs && node --check packages/vault/src/validate-memory.mjs",
44
+ "check": "node --check scripts/release.mjs && node --check scripts/release-plan.mjs && node --check bin/wendkeep.mjs && node --check packages/cli/src/index.mjs && node --check src/init.mjs && node --check src/doctor.mjs && node --check src/project-vault.mjs && node --check src/observer-snapshot.mjs && node --check src/observer-store.mjs && node --check src/observer-memory.mjs && node --check src/observer-memory-publish.mjs && node --check src/observer-sql-store.mjs && node --check src/observer-sql-migrate.mjs && node --check src/observer-sql-publish.mjs && node --check src/observer-transcript-store.mjs && node --check src/observer-server.mjs && node --check src/observer.mjs && node --check src/observer-publish.mjs && node --check src/operating-profile.mjs && node --check src/profile.mjs && node --check src/flow.mjs && node --check web/observer/app.mjs && node --check hooks/observer-publish.mjs && node --check hooks/operating-profile-runtime.mjs && node --check hooks/operating-profile-task-store.mjs && node --check hooks/flow-core.mjs && node --check hooks/flow-protected-policy.mjs && node --check hooks/git-snapshot.mjs && node --check hooks/vault-path-safety.mjs && node --check hooks/vault-runtime-store.mjs && node --check packages/harness/src/index.mjs && node --check packages/harness/src/flow-store.mjs && node --check packages/harness/src/operating-profile.mjs && node --check packages/harness/src/sensors-core.mjs && node --check packages/integrations/src/host-hooks.mjs && node --check packages/integrations/src/hook-envelope.mjs && node --check packages/integrations/src/prompt-content.mjs && node --check packages/integrations/src/transcript-usage.mjs && node --check packages/integrations/src/transcripts.mjs && node --check packages/integrations/src/session-identity.mjs && node --check packages/integrations/src/index.mjs && node --check packages/mcp/src/config.mjs && node --check packages/mcp/src/index.mjs && node --check packages/vault/src/index.mjs && node --check packages/vault/src/project-vault.mjs && node --check packages/vault/src/vault-path-safety.mjs && node --check packages/vault/src/locale.mjs && node --check packages/vault/src/memory-schema.mjs && node --check packages/vault/src/memory-mode.mjs && node --check packages/vault/src/memory-handoff.mjs && node --check packages/vault/src/memory-store.mjs && node --check packages/vault/src/validate-core.mjs && node --check packages/vault/src/validate-memory.mjs",
44
45
  "test": "node --test --test-concurrency=2",
45
46
  "release": "node scripts/release.mjs",
46
47
  "release:dry": "node scripts/release.mjs --dry-run",
@@ -24,6 +24,8 @@ export const SESSION_HOOKS = [
24
24
  { event: 'PostToolUse', matcher: 'AskUserQuestion', name: 'decision-capture', timeout: 15, statusMessage: 'wendkeep: recording decision' },
25
25
  // Refresh subagent/workflow telemetry as each subagent finishes (resilient to a missed Stop).
26
26
  { event: 'SubagentStop', matcher: null, name: 'subagent-stop', timeout: 20, codex: true, statusMessage: 'wendkeep: subagent telemetry' },
27
+ // Publish the SQL observer projection after the subagent telemetry is settled.
28
+ { event: 'SubagentStop', matcher: null, name: 'observer-publish', timeout: 5, order: 20, codex: true, statusMessage: 'wendkeep: publishing local observer usage' },
27
29
  // Log plan/task progress into the active session note when a task is marked complete.
28
30
  // codex: TaskCompleted is not in Codex's hook event enum.
29
31
  { event: 'TaskCompleted', matcher: null, name: 'task-log', timeout: 10, statusMessage: 'wendkeep: plan progress' },