wendkeep 0.68.6 → 0.70.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 +23 -0
- package/README.en.md +7 -3
- package/README.md +7 -3
- package/docs/en/commands/changes-and-verification.md +4 -0
- package/docs/en/commands/costs-and-observability.md +11 -2
- package/docs/en/commands/memory.md +10 -1
- package/docs/en/commands/observer.md +104 -0
- package/docs/pt-BR/commands/changes-and-verification.md +4 -0
- package/docs/pt-BR/commands/costs-and-observability.md +12 -2
- package/docs/pt-BR/commands/memory.md +10 -1
- package/docs/pt-BR/commands/observer.md +105 -0
- package/hooks/brain-core.mjs +46 -2
- package/hooks/brain-inject.mjs +3 -3
- package/hooks/harness-doctor.mjs +21 -7
- package/hooks/observer-publish.mjs +21 -0
- package/hooks/pricing.json +10 -1
- package/hooks/session-ensure.mjs +23 -0
- package/hooks/session-identity.mjs +4 -2
- package/hooks/session-stop.mjs +17 -0
- package/hooks/token-usage.mjs +13 -0
- package/hooks/vault-health.mjs +13 -0
- package/package.json +3 -3
- package/packages/cli/src/index.mjs +9 -2
- package/packages/integrations/src/host-hooks.mjs +4 -0
- package/packages/vault/src/memory-handoff.mjs +75 -9
- package/packages/vault/src/memory-store.mjs +34 -6
- package/packages/vault/src/validate-core.mjs +29 -15
- package/packages/vault/src/validate-memory.mjs +209 -6
- package/src/doctor.mjs +4 -0
- package/src/memory.mjs +4 -1
- package/src/observer-publish.mjs +122 -0
- package/src/observer-server.mjs +203 -0
- package/src/observer-snapshot.mjs +153 -0
- package/src/observer-store.mjs +155 -0
- package/src/observer.mjs +108 -0
- package/src/taxonomy.mjs +2 -0
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,29 @@ 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.70.0] — 2026-08-17
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- **Observer local multi-projeto.** O WendKeep agora expõe snapshots sanitizados, ingestão HTTP
|
|
12
|
+
loopback, índice append-only reconstruível, CLI `observer`, outbox fail-open nos hooks e um
|
|
13
|
+
compose Docker que persiste somente a projeção; os vaults locais continuam sendo a autoridade.
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
- **Modelos research preview agora são distinguidos de modelos desconhecidos.** O Spark é
|
|
18
|
+
reconhecido sem preço inventado, o `doctor` mantém o custo não estimado e a cobertura de
|
|
19
|
+
preços passa a ser validada por sensor crítico.
|
|
20
|
+
|
|
21
|
+
## [0.69.0] — 2026-08-16
|
|
22
|
+
|
|
23
|
+
### Added
|
|
24
|
+
|
|
25
|
+
- **A Shared Project Memory v2 preserva handoffs estruturados entre providers.** Stop publica
|
|
26
|
+
objetivo, entrega, restrições, decisões, próximas ações, bloqueios e riscos com `work_session_id`;
|
|
27
|
+
CORE continua manual com cap 40/alerta 35, e status/validate-memory diagnosticam cobertura
|
|
28
|
+
semântica sem expor valores privados.
|
|
29
|
+
|
|
7
30
|
## [0.68.6] — 2026-08-16
|
|
8
31
|
|
|
9
32
|
### Fixed
|
package/README.en.md
CHANGED
|
@@ -85,9 +85,10 @@ Decisions, dead ends, the reason you chose X over Y — gone next session. The p
|
|
|
85
85
|
| **Capture** — every turn, on disk | `SessionStart` / `Stop` hooks write each session to a dated Markdown note: prompts, iterations, files touched, wikilinks. |
|
|
86
86
|
| **Derive** — decisions, bugs, learnings | Pulled from the transcript into their own notes, backlinked to the session. Your history becomes navigable, not archival. |
|
|
87
87
|
| **Recall** — injected back | Canonical `CORE` + operational `SHARED_MEMORY`, with explicit budgets, are injected on `startup`, `/clear`, and `/compact`; `DIGEST` remains a deep-recall index and legacy fallback. |
|
|
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. |
|
|
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
92
|
|
|
92
93
|
## Requirements
|
|
93
94
|
|
|
@@ -110,7 +111,7 @@ npx wendkeep init
|
|
|
110
111
|
|
|
111
112
|
1. Create the vault folder taxonomy and a templated `README.md` (default vault: `<project>/.<project-name>-vault`, e.g. `.MyApp-vault`; override with `--vault`).
|
|
112
113
|
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.
|
|
113
|
-
3. Wire the Codex hooks in **`.codex/hooks.json`** —
|
|
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**.
|
|
114
115
|
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`.)
|
|
115
116
|
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:
|
|
116
117
|
- **`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`.
|
|
@@ -235,6 +236,7 @@ The README is the map; the guides provide syntax, options, exit codes, examples,
|
|
|
235
236
|
| **Notes and knowledge** | BUG/APR/ADR, repairs, renumbering, lessons, and dashboard | [Notes and knowledge](https://github.com/rogersialves/wendkeep/blob/main/docs/en/commands/notes-and-knowledge.md) |
|
|
236
237
|
| **Costs and observability** | safe dry-run, tri-state, aggregation, trends, and historical rebuild | [Costs and observability](https://github.com/rogersialves/wendkeep/blob/main/docs/en/commands/costs-and-observability.md) |
|
|
237
238
|
| **Maintenance and diagnostics** | doctor, frontier/manifest freshness, drift, version, and help | [Maintenance and diagnostics](https://github.com/rogersialves/wendkeep/blob/main/docs/en/commands/maintenance-and-diagnostics.md) |
|
|
239
|
+
| **Local Observer** | `observer serve`, registration, snapshots, outbox, and multi-project index | [Local Observer](https://github.com/rogersialves/wendkeep/blob/main/docs/en/commands/observer.md) |
|
|
238
240
|
|
|
239
241
|
Operations that deserve step-by-step guidance: [verify and exits 0/1/2](https://github.com/rogersialves/wendkeep/blob/main/docs/en/commands/verify.md),
|
|
240
242
|
[legacy-memory migration](https://github.com/rogersialves/wendkeep/blob/main/docs/en/commands/memory-migration.md), and
|
|
@@ -357,7 +359,9 @@ Codex uses `session_id`/`turn_id` plus transcript order, with no artificial caus
|
|
|
357
359
|
|
|
358
360
|
### Injection and budgets
|
|
359
361
|
|
|
360
|
-
`brain-inject` delivers the same revision/hash on `startup`, `/clear`, and `/compact` `SessionStart` events, always placing CORE and SHARED before change context. The full envelope is capped at 24 KiB;
|
|
362
|
+
`CORE.md` is the only manual layer: it accepts up to 40 lines, warns from 35, keeps a 4 KiB ceiling, and caps each line at 320 characters. `SHARED_MEMORY.md` is generated exclusively by the projector and ledger; never edit it to repair state. `brain-inject` delivers the same revision/hash on `startup`, `/clear`, and `/compact` `SessionStart` events, always placing CORE and SHARED before change context. The full envelope is capped at 24 KiB; SHARED reserves up to 6 KiB. Under pressure, lessons are removed first, then non-current changes. CORE and SHARED are never prefix-sliced: a missing, invalid, or over-budget layer becomes a visible, repairable `<wk_memory_error>`.
|
|
363
|
+
|
|
364
|
+
`memory status --gate` and `validate-memory --vault` also check semantic coverage: they report a code, counts, and active/projected/missing keys. An empty v2 bundle is neutral; a missing projectable event, placeholders as the only content, or an unresolved decision link becomes an explicit degraded/blocking diagnosis without printing memory values.
|
|
361
365
|
|
|
362
366
|
`DIGEST.md` is no longer the operational handoff: it remains the `/brain-recall` bridge and legacy-vault fallback. A vault without SHARED receives CORE+DIGEST with a deprecation warning; migrate during the compatibility window:
|
|
363
367
|
|
package/README.md
CHANGED
|
@@ -85,9 +85,10 @@ Decisions, dead ends, the reason you chose X over Y — gone next session. The p
|
|
|
85
85
|
| **Capture** — every turn, on disk | `SessionStart` / `Stop` hooks write each session to a dated Markdown note: prompts, iterations, files touched, wikilinks. |
|
|
86
86
|
| **Derive** — decisions, bugs, learnings | Pulled from the transcript into their own notes, backlinked to the session. Your history becomes navigable, not archival. |
|
|
87
87
|
| **Recall** — injected back | Canonical `CORE` + operational `SHARED_MEMORY`, with explicit budgets, are injected on `startup`, `/clear`, and `/compact`; `DIGEST` remains a deep-recall index and legacy fallback. |
|
|
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. |
|
|
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
92
|
|
|
92
93
|
## Requirements
|
|
93
94
|
|
|
@@ -110,7 +111,7 @@ npx wendkeep init
|
|
|
110
111
|
|
|
111
112
|
1. Create the vault folder taxonomy and a templated `README.md` (default vault: `<project>/.<project-name>-vault`, e.g. `.MyApp-vault`; override with `--vault`).
|
|
112
113
|
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.
|
|
113
|
-
3. Wire the Codex hooks in **`.codex/hooks.json`** —
|
|
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**.
|
|
114
115
|
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`.)
|
|
115
116
|
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:
|
|
116
117
|
- **`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`.
|
|
@@ -235,6 +236,7 @@ The README is the map; the guides provide syntax, options, exit codes, examples,
|
|
|
235
236
|
| **Notes and knowledge** | BUG/APR/ADR, repairs, renumbering, lessons, and dashboard | [Notes and knowledge](https://github.com/rogersialves/wendkeep/blob/main/docs/en/commands/notes-and-knowledge.md) |
|
|
236
237
|
| **Costs and observability** | safe dry-run, tri-state, aggregation, trends, and historical rebuild | [Costs and observability](https://github.com/rogersialves/wendkeep/blob/main/docs/en/commands/costs-and-observability.md) |
|
|
237
238
|
| **Maintenance and diagnostics** | doctor, frontier/manifest freshness, drift, version, and help | [Maintenance and diagnostics](https://github.com/rogersialves/wendkeep/blob/main/docs/en/commands/maintenance-and-diagnostics.md) |
|
|
239
|
+
| **Local Observer** | `observer serve`, registration, snapshots, outbox, and multi-project index | [Local Observer](https://github.com/rogersialves/wendkeep/blob/main/docs/en/commands/observer.md) |
|
|
238
240
|
|
|
239
241
|
Operations that deserve step-by-step guidance: [verify and exits 0/1/2](https://github.com/rogersialves/wendkeep/blob/main/docs/en/commands/verify.md),
|
|
240
242
|
[legacy-memory migration](https://github.com/rogersialves/wendkeep/blob/main/docs/en/commands/memory-migration.md), and
|
|
@@ -357,7 +359,9 @@ Codex uses `session_id`/`turn_id` plus transcript order, with no artificial caus
|
|
|
357
359
|
|
|
358
360
|
### Injection and budgets
|
|
359
361
|
|
|
360
|
-
`brain-inject` delivers the same revision/hash on `startup`, `/clear`, and `/compact` `SessionStart` events, always placing CORE and SHARED before change context. The full envelope is capped at 24 KiB;
|
|
362
|
+
`CORE.md` is the only manual layer: it accepts up to 40 lines, warns from 35, keeps a 4 KiB ceiling, and caps each line at 320 characters. `SHARED_MEMORY.md` is generated exclusively by the projector and ledger; never edit it to repair state. `brain-inject` delivers the same revision/hash on `startup`, `/clear`, and `/compact` `SessionStart` events, always placing CORE and SHARED before change context. The full envelope is capped at 24 KiB; SHARED reserves up to 6 KiB. Under pressure, lessons are removed first, then non-current changes. CORE and SHARED are never prefix-sliced: a missing, invalid, or over-budget layer becomes a visible, repairable `<wk_memory_error>`.
|
|
363
|
+
|
|
364
|
+
`memory status --gate` and `validate-memory --vault` also check semantic coverage: they report a code, counts, and active/projected/missing keys. An empty v2 bundle is neutral; a missing projectable event, placeholders as the only content, or an unresolved decision link becomes an explicit degraded/blocking diagnosis without printing memory values.
|
|
361
365
|
|
|
362
366
|
`DIGEST.md` is no longer the operational handoff: it remains the `/brain-recall` bridge and legacy-vault fallback. A vault without SHARED receives CORE+DIGEST with a deprecation warning; migrate during the compatibility window:
|
|
363
367
|
|
|
@@ -77,6 +77,10 @@ tool runs, it compares the session, project, Git root, remote, branch, and workt
|
|
|
77
77
|
recorded in `SESSION_REGISTRY.json`. Missing, ambiguous, concurrent, or cross-project targets are
|
|
78
78
|
blocked before the tool.
|
|
79
79
|
|
|
80
|
+
The [local Observer](observer.md) is a read-only observability projection: the vault and change
|
|
81
|
+
remain local authorities. Observer queries do not complete, archive, repair, or promote state in a
|
|
82
|
+
vault.
|
|
83
|
+
|
|
80
84
|
Codex blocks with `permissionDecision: "deny"`; `ask` is not a valid `PreToolUse` decision.
|
|
81
85
|
`commit`, `push`, `pull`, `merge`, `publish`, and destructive operations remain separate capabilities,
|
|
82
86
|
including when one command contains multiple actions. Switching projects requires a new explicit
|
|
@@ -19,7 +19,8 @@ projects whose registries are mixed.
|
|
|
19
19
|
|
|
20
20
|
## Prerequisites
|
|
21
21
|
|
|
22
|
-
A consistent registry,
|
|
22
|
+
A consistent registry, a price table for models with published rates, and transcript access for
|
|
23
|
+
rebuilt sessions. A research preview without a final rate must remain explicitly unestimated.
|
|
23
24
|
|
|
24
25
|
## Syntax
|
|
25
26
|
|
|
@@ -64,9 +65,17 @@ returns `complete`, `none`, or `degraded`, plus a frontier, manifest, and saniti
|
|
|
64
65
|
Run and review the dry-run before repeating the same command with `--apply`; a semantically
|
|
65
66
|
identical second apply preserves the note, checkpoint, report, and mtime.
|
|
66
67
|
|
|
68
|
+
## Models without a final rate
|
|
69
|
+
|
|
70
|
+
`doctor` separates a model that is truly missing from the table from a known
|
|
71
|
+
`research-preview` model. The former remains actionable and asks for an update to
|
|
72
|
+
`hooks/pricing.json`; the latter is reported as **final rate not published** and **cost not
|
|
73
|
+
estimated**. Do not copy another model's price into the gap.
|
|
74
|
+
|
|
67
75
|
## Common errors and diagnosis
|
|
68
76
|
|
|
69
|
-
-
|
|
77
|
+
- Unknown model without a price: update the table before accepting totals.
|
|
78
|
+
- Research preview without a final rate: keep the cost unestimated; do not invent a rate.
|
|
70
79
|
- Wrong-provider costs: validate the session identity chain.
|
|
71
80
|
- Missing transcript: do not estimate silently; keep the gap visible.
|
|
72
81
|
- Duplicated parent/subagent/fork totals: verify registry relationships and deduplication.
|
|
@@ -7,6 +7,10 @@
|
|
|
7
7
|
Inspect and curate CORE, SHARED, ledger, outbox, attempts, and candidates without confusing
|
|
8
8
|
canonical authorship with generated operational state.
|
|
9
9
|
|
|
10
|
+
`CORE.md` is the only manual, canonical layer: it accepts up to 40 lines, warns from 35,
|
|
11
|
+
keeps a 4 KiB ceiling, and caps each line at 320 characters. `SHARED_MEMORY.md` is generated
|
|
12
|
+
from the ledger and must not be hand-edited.
|
|
13
|
+
|
|
10
14
|
## When to use
|
|
11
15
|
|
|
12
16
|
Use in CI, before verify/archive, after doctor warnings, or when deciding candidates.
|
|
@@ -115,7 +119,12 @@ npx wendkeep validate-memory --vault <v2-vault>
|
|
|
115
119
|
different, incomplete, or ambiguous identity keeps the candidate queued for curation. `memory
|
|
116
120
|
repair` compares the old and current replay and migrates checkpoint+mirror only with exact
|
|
117
121
|
identity, backup, audit, and CAS; it does not reorder, rewrite, or append a ledger event.
|
|
118
|
-
- `validate-memory <CORE.md>` checks the
|
|
122
|
+
- `validate-memory <CORE.md>` checks the hard 40-line cap, warns from 35, enforces 4 KiB and
|
|
123
|
+
320 characters per line, and checks required sections and secrets/PII.
|
|
124
|
+
- `validate-memory --vault` also compares semantic ledger coverage with SHARED and prints a code,
|
|
125
|
+
counts, and active/projected/missing keys. An empty v2 bundle is neutral; a missing projectable
|
|
126
|
+
event, exclusive placeholders, or a dead decision link produces a degraded/blocking diagnosis
|
|
127
|
+
without exposing values.
|
|
119
128
|
- `validate-memory --vault` requires a complete v2 bundle and is not the legacy-vault gate.
|
|
120
129
|
- For `recover-attempt`, exit `0` means a valid dry run/apply, including `unchanged`; exit `1`
|
|
121
130
|
means a precondition, authority, CAS, topology, or lock check failed; exit `2` means a missing
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# Local Observer
|
|
2
|
+
|
|
3
|
+
**English** · [Português](../../pt-BR/commands/observer.md)
|
|
4
|
+
|
|
5
|
+
## Purpose
|
|
6
|
+
|
|
7
|
+
The Observer consolidates observability for multiple WendKeep projects in a local service without
|
|
8
|
+
copying or taking ownership of their vaults.
|
|
9
|
+
|
|
10
|
+
## When to use
|
|
11
|
+
|
|
12
|
+
Use it to query summarized changes, sessions, tasks, and health across projects through one local
|
|
13
|
+
projection while each vault remains authoritative.
|
|
14
|
+
|
|
15
|
+
## When not to use
|
|
16
|
+
|
|
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.
|
|
19
|
+
|
|
20
|
+
## Prerequisites
|
|
21
|
+
|
|
22
|
+
Initialize projects with WendKeep, explicitly register each project, and set a local token before
|
|
23
|
+
starting the HTTP server.
|
|
24
|
+
|
|
25
|
+
## Syntax
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npx wendkeep observer status --data-dir <directory> --json
|
|
29
|
+
npx wendkeep observer register --project <project> --vault <vault> --data-dir <directory>
|
|
30
|
+
npx wendkeep observer publish --project <project> --vault <vault> --data-dir <directory>
|
|
31
|
+
npx wendkeep observer serve --host 127.0.0.1 --port 8787 --data-dir <directory>
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Options and exit codes
|
|
35
|
+
|
|
36
|
+
- `--data-dir` selects the local event and index directory; the default is
|
|
37
|
+
`WENDKEEP_OBSERVER_DATA_DIR` or `~/.wendkeep-observer`.
|
|
38
|
+
- `--project` and `--vault` identify a project only for `register` and `publish`.
|
|
39
|
+
- `--host` accepts only `127.0.0.1`, `localhost`, or `::1`; other hosts are rejected before
|
|
40
|
+
listening.
|
|
41
|
+
- `--token` or `WENDKEEP_OBSERVER_TOKEN` protects `/v1`; `GET /healthz` exposes no project data.
|
|
42
|
+
- Exit `0` means success; exit `1` means configuration or operation failure; the publisher hook
|
|
43
|
+
also returns `0` when the Observer is unavailable.
|
|
44
|
+
|
|
45
|
+
## Examples
|
|
46
|
+
|
|
47
|
+
```powershell
|
|
48
|
+
$env:WENDKEEP_OBSERVER_TOKEN = '<local-token>'
|
|
49
|
+
npx wendkeep observer register --project C:\GitHub\WendKeep --vault C:\GitHub\WendKeep\.WendKeep-vault --data-dir C:\WendKeepObserver
|
|
50
|
+
npx wendkeep observer serve --host 127.0.0.1 --port 8787 --data-dir C:\WendKeepObserver
|
|
51
|
+
$env:WENDKEEP_OBSERVER_URL = 'http://127.0.0.1:8787'
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
For local Docker:
|
|
55
|
+
|
|
56
|
+
```powershell
|
|
57
|
+
$env:WENDKEEP_OBSERVER_TOKEN = '<local-token>'
|
|
58
|
+
docker compose -f docker/wendkeep-observer/compose.yaml up -d --build
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Expected result
|
|
62
|
+
|
|
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`.
|
|
67
|
+
|
|
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.
|
|
71
|
+
|
|
72
|
+
## Common errors and diagnosis
|
|
73
|
+
|
|
74
|
+
- `project_not_registered`: run `observer register` before publishing.
|
|
75
|
+
- `unauthorized`: check `Authorization: Bearer <token>` and `WENDKEEP_OBSERVER_TOKEN`.
|
|
76
|
+
- `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.
|
|
81
|
+
|
|
82
|
+
## Next steps
|
|
83
|
+
|
|
84
|
+
Read the `local-observer` change for the `OBS-1` through `OBS-8` contract. Do not remove the Docker
|
|
85
|
+
volume with `docker compose down -v` during normal operation because it deletes the local
|
|
86
|
+
projection.
|
|
87
|
+
|
|
88
|
+
## Data authority
|
|
89
|
+
|
|
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.
|
|
93
|
+
|
|
94
|
+
## Minimal API
|
|
95
|
+
|
|
96
|
+
- `GET /healthz` — availability without project data.
|
|
97
|
+
- `GET /v1/projects` — projects with an accepted snapshot.
|
|
98
|
+
- `GET /v1/projects/:project_id` — the latest project snapshot.
|
|
99
|
+
- `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.
|
|
@@ -77,6 +77,10 @@ uma ferramenta de escrita suportada, ele compara sessão, projeto, raiz Git, rem
|
|
|
77
77
|
worktree com a lease registrada no `SESSION_REGISTRY.json`. Um alvo ausente, ambíguo, concorrente ou
|
|
78
78
|
fora do projeto é bloqueado antes da ferramenta.
|
|
79
79
|
|
|
80
|
+
O [Observer local](observer.md) é uma projeção read-only da observabilidade: o vault e a change
|
|
81
|
+
continuam autoridades locais. Consultas do Observer não concluem, arquivam, reparam ou promovem
|
|
82
|
+
estado no vault.
|
|
83
|
+
|
|
80
84
|
No Codex o bloqueio usa `permissionDecision: "deny"`; `ask` não é uma decisão válida de
|
|
81
85
|
`PreToolUse`. `commit`, `push`, `pull`, `merge`, `publish` e operações destrutivas continuam
|
|
82
86
|
capacidades separadas, inclusive quando um comando contém mais de uma ação. A troca de projeto
|
|
@@ -19,7 +19,9 @@ projetos com registries misturados.
|
|
|
19
19
|
|
|
20
20
|
## Pré-requisitos
|
|
21
21
|
|
|
22
|
-
Registry consistente, tabela de preços
|
|
22
|
+
Registry consistente, tabela de preços para os modelos com tarifa publicada e acesso aos transcripts
|
|
23
|
+
das sessões reconstruídas. Um research preview sem tarifa final deve permanecer explicitamente
|
|
24
|
+
como custo não estimado.
|
|
23
25
|
|
|
24
26
|
## Sintaxe
|
|
25
27
|
|
|
@@ -64,9 +66,17 @@ tri-state devolve `complete`, `none` ou `degraded`, mais frontier, manifest e di
|
|
|
64
66
|
sanitizados. Rode e revise o dry-run antes de repetir o mesmo comando com `--apply`; uma segunda
|
|
65
67
|
aplicação semanticamente idêntica preserva nota, checkpoint, relatório e mtime.
|
|
66
68
|
|
|
69
|
+
## Modelos sem tarifa final
|
|
70
|
+
|
|
71
|
+
O `doctor` separa um modelo realmente ausente da tabela de um modelo conhecido em
|
|
72
|
+
`research-preview`. O primeiro continua acionável e pede atualização de
|
|
73
|
+
`hooks/pricing.json`; o segundo aparece como **tarifa final não publicada** e **custo não
|
|
74
|
+
estimado**. Não copie o preço de outro modelo para preencher essa lacuna.
|
|
75
|
+
|
|
67
76
|
## Erros comuns e diagnóstico
|
|
68
77
|
|
|
69
|
-
- Modelo sem preço: atualize a tabela antes de aceitar o total.
|
|
78
|
+
- Modelo desconhecido sem preço: atualize a tabela antes de aceitar o total.
|
|
79
|
+
- Research preview sem tarifa final: mantenha o custo não estimado; não invente uma tarifa.
|
|
70
80
|
- Custos de provider errado: valide a cadeia de identidade da sessão.
|
|
71
81
|
- Transcript ausente: não estime silenciosamente; mantenha a lacuna visível.
|
|
72
82
|
- Total duplicado por subagent/fork: confirme relação pai/subagent e deduplicação do registry.
|
|
@@ -7,6 +7,10 @@
|
|
|
7
7
|
Inspecionar e curar CORE, SHARED, ledger, outbox, attempts e candidates sem confundir autoria
|
|
8
8
|
canônica com estado operacional gerado.
|
|
9
9
|
|
|
10
|
+
`CORE.md` é a única camada manual e canônica: aceita até 40 linhas, alerta a partir de 35,
|
|
11
|
+
mantém o teto de 4 KiB e limita cada linha a 320 caracteres. `SHARED_MEMORY.md` é uma projeção
|
|
12
|
+
gerada pelo ledger e não deve ser editado à mão.
|
|
13
|
+
|
|
10
14
|
## Quando usar
|
|
11
15
|
|
|
12
16
|
Use no CI, antes de verify/archive, diante de avisos do doctor ou para decidir candidates.
|
|
@@ -113,7 +117,12 @@ npx wendkeep validate-memory --vault <cofre-v2>
|
|
|
113
117
|
divergente, incompleta ou ambígua mantém o candidate para curadoria. `memory repair` compara o
|
|
114
118
|
replay anterior e o atual e só migra checkpoint+espelho com identidade exata, backup, audit e
|
|
115
119
|
CAS; ele não reordena, reescreve nem acrescenta evento ao ledger.
|
|
116
|
-
- `validate-memory <CORE.md>` valida cap de
|
|
120
|
+
- `validate-memory <CORE.md>` valida cap rígido de 40 linhas, alerta em 35, 4 KiB, 320 caracteres
|
|
121
|
+
por linha, seções e segredos/PII.
|
|
122
|
+
- `validate-memory --vault` também compara a cobertura semântica do ledger com SHARED e imprime
|
|
123
|
+
código, contagens e chaves ativas/projetadas/ausentes. Bundle v2 vazio é neutro; evento
|
|
124
|
+
projetável ausente, placeholders exclusivos ou link de decisão morto geram diagnóstico
|
|
125
|
+
degradado/bloqueante sem expor valores.
|
|
117
126
|
- `validate-memory --vault` exige bundle v2 completo; não é o gate correto para vault legado.
|
|
118
127
|
- Para `recover-attempt`, exit `0` indica dry-run/apply válido, inclusive `unchanged`; exit `1`
|
|
119
128
|
indica falha de pré-condição, autoridade, CAS, topologia ou lock; exit `2` indica
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# Observer local
|
|
2
|
+
|
|
3
|
+
**PT-BR** · [English](../../en/commands/observer.md)
|
|
4
|
+
|
|
5
|
+
## Objetivo
|
|
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.
|
|
9
|
+
|
|
10
|
+
## Quando usar
|
|
11
|
+
|
|
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
|
+
|
|
15
|
+
## Quando não usar
|
|
16
|
+
|
|
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.
|
|
19
|
+
|
|
20
|
+
## Pré-requisitos
|
|
21
|
+
|
|
22
|
+
Tenha os projetos inicializados com WendKeep, registre explicitamente cada projeto e defina um
|
|
23
|
+
token local antes de iniciar o servidor HTTP.
|
|
24
|
+
|
|
25
|
+
## Sintaxe
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npx wendkeep observer status --data-dir <diretório> --json
|
|
29
|
+
npx wendkeep observer register --project <projeto> --vault <vault> --data-dir <diretório>
|
|
30
|
+
npx wendkeep observer publish --project <projeto> --vault <vault> --data-dir <diretório>
|
|
31
|
+
npx wendkeep observer serve --host 127.0.0.1 --port 8787 --data-dir <diretório>
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Opções e códigos de saída
|
|
35
|
+
|
|
36
|
+
- `--data-dir` escolhe o diretório local de eventos e índice; o padrão é
|
|
37
|
+
`WENDKEEP_OBSERVER_DATA_DIR` ou `~/.wendkeep-observer`.
|
|
38
|
+
- `--project` e `--vault` identificam o projeto somente nos comandos `register` e `publish`.
|
|
39
|
+
- `--host` aceita somente `127.0.0.1`, `localhost` ou `::1`; outros hosts são recusados antes do
|
|
40
|
+
listen.
|
|
41
|
+
- `--token` ou `WENDKEEP_OBSERVER_TOKEN` protege as rotas `/v1`; `GET /healthz` permanece sem
|
|
42
|
+
dados de projeto.
|
|
43
|
+
- Exit `0` indica sucesso; exit `1` indica falha de configuração ou operação; o hook publisher
|
|
44
|
+
também retorna `0` quando o Observer está indisponível.
|
|
45
|
+
|
|
46
|
+
## Exemplos
|
|
47
|
+
|
|
48
|
+
```powershell
|
|
49
|
+
$env:WENDKEEP_OBSERVER_TOKEN = '<token-local>'
|
|
50
|
+
npx wendkeep observer register --project C:\GitHub\WendKeep --vault C:\GitHub\WendKeep\.WendKeep-vault --data-dir C:\WendKeepObserver
|
|
51
|
+
npx wendkeep observer serve --host 127.0.0.1 --port 8787 --data-dir C:\WendKeepObserver
|
|
52
|
+
$env:WENDKEEP_OBSERVER_URL = 'http://127.0.0.1:8787'
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Para Docker local:
|
|
56
|
+
|
|
57
|
+
```powershell
|
|
58
|
+
$env:WENDKEEP_OBSERVER_TOKEN = '<token-local>'
|
|
59
|
+
docker compose -f docker/wendkeep-observer/compose.yaml up -d --build
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Resultado esperado
|
|
63
|
+
|
|
64
|
+
`register` grava apenas `project_id`, nome, versão e data de registro. `publish` lê 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`.
|
|
68
|
+
|
|
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.
|
|
73
|
+
|
|
74
|
+
## Erros comuns e diagnóstico
|
|
75
|
+
|
|
76
|
+
- `project_not_registered`: rode `observer register` antes de publicar.
|
|
77
|
+
- `unauthorized`: confira `Authorization: Bearer <token>` e `WENDKEEP_OBSERVER_TOKEN`.
|
|
78
|
+
- `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 lê conteúdo bruto, caminhos, transcripts ou memória; rejeições desse tipo são
|
|
82
|
+
esperadas e devem ser investigadas na origem do snapshot.
|
|
83
|
+
|
|
84
|
+
## Próximos passos
|
|
85
|
+
|
|
86
|
+
Leia a change `local-observer` para o contrato `OBS-1` a `OBS-8`. O volume Docker não deve ser
|
|
87
|
+
removido com `docker compose down -v` durante a operação normal, pois isso apaga a projeção local.
|
|
88
|
+
|
|
89
|
+
## Autoridade dos dados
|
|
90
|
+
|
|
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.
|
|
94
|
+
|
|
95
|
+
## API mínima
|
|
96
|
+
|
|
97
|
+
- `GET /healthz` — disponibilidade sem dados de projeto.
|
|
98
|
+
- `GET /v1/projects` — projetos com snapshot aceito.
|
|
99
|
+
- `GET /v1/projects/:project_id` — último snapshot do projeto.
|
|
100
|
+
- `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.
|
package/hooks/brain-core.mjs
CHANGED
|
@@ -4,6 +4,7 @@ import { existsSync, readdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
|
4
4
|
import { basename, join } from 'node:path';
|
|
5
5
|
import { ensureDir, stripYamlQuotes, toVaultRelative } from './obsidian-common.mjs';
|
|
6
6
|
import { getLocale } from './locale.mjs';
|
|
7
|
+
import { sanitizeMemoryText } from './memory-schema.mjs';
|
|
7
8
|
|
|
8
9
|
export function brainDir(vaultBase) {
|
|
9
10
|
return join(vaultBase, '.brain');
|
|
@@ -104,6 +105,41 @@ function adrNumber(path) {
|
|
|
104
105
|
return m ? Number(m[1]) : -1;
|
|
105
106
|
}
|
|
106
107
|
|
|
108
|
+
function decisionTitle(content, path) {
|
|
109
|
+
const frontmatter = parseFrontmatter(content);
|
|
110
|
+
const heading = content.match(/^#\s+(.+?)\s*$/m)?.[1] || '';
|
|
111
|
+
const raw = frontmatter.title || frontmatter.name || heading;
|
|
112
|
+
if (!raw) return '';
|
|
113
|
+
const title = sanitizeMemoryText(String(raw)
|
|
114
|
+
.replace(/^ADR-\d+\s*(?:[-:—]\s*)?/i, '')
|
|
115
|
+
.replace(/\s+/g, ' ')
|
|
116
|
+
.trim());
|
|
117
|
+
return title.slice(0, 180).trim();
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function decisionNotes(vaultBase) {
|
|
121
|
+
const byPath = new Map();
|
|
122
|
+
const byBasename = new Map();
|
|
123
|
+
const decisionsDir = join(vaultBase, getLocale(vaultBase).folders.decisions);
|
|
124
|
+
for (const filePath of walkMd(decisionsDir)) {
|
|
125
|
+
let content;
|
|
126
|
+
try { content = readFileSync(filePath, 'utf8'); } catch { continue; }
|
|
127
|
+
const rel = toVaultRelative(vaultBase, filePath).replace(/\.md$/i, '');
|
|
128
|
+
const note = { path: rel, title: decisionTitle(content, rel) };
|
|
129
|
+
byPath.set(rel, note);
|
|
130
|
+
const key = basename(rel);
|
|
131
|
+
if (!byBasename.has(key)) byBasename.set(key, note);
|
|
132
|
+
}
|
|
133
|
+
return { byPath, byBasename };
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function resolveDecisionTarget(target, notes) {
|
|
137
|
+
const normalized = String(target || '').replace(/\.md$/i, '').trim();
|
|
138
|
+
if (!normalized || normalized.includes('...') || normalized.includes('…')) return null;
|
|
139
|
+
const note = notes.byPath.get(normalized) || notes.byBasename.get(basename(normalized));
|
|
140
|
+
return note?.title ? note : null;
|
|
141
|
+
}
|
|
142
|
+
|
|
107
143
|
// Destila index.jsonl em .brain/DIGEST.md (camada quente, determinístico, 0 token LLM).
|
|
108
144
|
// Cap por construção: 1 header + 13 itens (5/4/2/2) + 1 pointer = máx 15 linhas.
|
|
109
145
|
export function buildBrainDigest(vaultBase, rows = null) {
|
|
@@ -139,13 +175,21 @@ export function buildBrainDigest(vaultBase, rows = null) {
|
|
|
139
175
|
};
|
|
140
176
|
const pickLive = (kind, max) => pick(kind, max * 4).filter(resolves).slice(0, max);
|
|
141
177
|
|
|
142
|
-
const
|
|
178
|
+
const notes = decisionNotes(vaultBase);
|
|
179
|
+
const decisions = [];
|
|
180
|
+
for (const target of pick('decisions', DIGEST_CAPS.decisions * 4)) {
|
|
181
|
+
const resolved = resolveDecisionTarget(target, notes);
|
|
182
|
+
if (!resolved || decisions.some((item) => item.path === resolved.path)) continue;
|
|
183
|
+
decisions.push(resolved);
|
|
184
|
+
if (decisions.length >= DIGEST_CAPS.decisions) break;
|
|
185
|
+
}
|
|
186
|
+
decisions.sort((a, b) => adrNumber(b.path) - adrNumber(a.path));
|
|
143
187
|
const sessions = byDateDesc.slice(0, DIGEST_CAPS.sessions);
|
|
144
188
|
const bugs = pickLive('bugs', DIGEST_CAPS.bugs);
|
|
145
189
|
const learnings = pickLive('learnings', DIGEST_CAPS.learnings);
|
|
146
190
|
|
|
147
191
|
const lines = ['<!-- AUTO-GERADO por brain-core.mjs (0 token LLM). NÃO editar. Rebuild: node .agent/hooks/brain-reindex.mjs -->'];
|
|
148
|
-
for (const d of decisions) lines.push(`- Decisão: [[${d}]]`);
|
|
192
|
+
for (const d of decisions) lines.push(`- Decisão: [[${d.path}]] — ${d.title}`);
|
|
149
193
|
for (const s of sessions) lines.push(`- Sessão ${s.date} (${s.provider || '?'}): ${s.summary || s.file} → [[${String(s.file || '').replace(/\.md$/, '')}]]`);
|
|
150
194
|
for (const b of bugs) lines.push(`- Bug: [[${b}]]`);
|
|
151
195
|
for (const l of learnings) lines.push(`- Aprendizado: [[${l}]]`);
|
package/hooks/brain-inject.mjs
CHANGED
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
resolveHookOperatingProfile,
|
|
20
20
|
} from './operating-profile-runtime.mjs';
|
|
21
21
|
import { assertVaultPathSafe } from './vault-path-safety.mjs';
|
|
22
|
-
import { validateCore } from '../src/validate-core.mjs';
|
|
22
|
+
import { CORE_LIMITS, validateCore } from '../src/validate-core.mjs';
|
|
23
23
|
|
|
24
24
|
// The process ROUTER — the enforcement layer. The wk-* skills are passive files; without a
|
|
25
25
|
// standing instruction the model plans in chat, leaves the change scaffold raw and forces the
|
|
@@ -52,8 +52,8 @@ function processRouter(localeId) {
|
|
|
52
52
|
|
|
53
53
|
const INJECTION_LIMITS = Object.freeze({
|
|
54
54
|
totalBytes: 24 * 1024,
|
|
55
|
-
lineChars:
|
|
56
|
-
coreBytes:
|
|
55
|
+
lineChars: CORE_LIMITS.lineChars,
|
|
56
|
+
coreBytes: CORE_LIMITS.bytes,
|
|
57
57
|
sharedBytes: 6 * 1024,
|
|
58
58
|
attentionBytes: 1024,
|
|
59
59
|
recallBytes: 512,
|
package/hooks/harness-doctor.mjs
CHANGED
|
@@ -212,9 +212,11 @@ export function checkStackedFrontmatter(vaultBase) {
|
|
|
212
212
|
}
|
|
213
213
|
|
|
214
214
|
// Um modelo fora de `pricing.json` faz `priceForModel` devolver null e a parcela dele do custo
|
|
215
|
-
// virar zero — sem erro, sem aviso.
|
|
216
|
-
//
|
|
217
|
-
//
|
|
215
|
+
// virar zero — sem erro, sem aviso. Modelos conhecidos sem tarifa final (por exemplo, um
|
|
216
|
+
// research preview) ficam na tabela com status explícito e são reportados separadamente: isso
|
|
217
|
+
// evita tanto o silêncio quanto a sugestão de inventar um preço. A checagem é sobre o vault, não
|
|
218
|
+
// sobre o caminho de cálculo: o cálculo roda em hook a cada turno, onde avisar viraria ruído e
|
|
219
|
+
// lançar derrubaria a captura da sessão.
|
|
218
220
|
//
|
|
219
221
|
// Cada modelo citado na nota é consultado direto em `priceForModel` — NÃO se infere pelo
|
|
220
222
|
// sintoma "custo zerado". Numa sessão multi-modelo (`modelo: "claude-opus-4.8 + claude-opus-5"`)
|
|
@@ -222,6 +224,7 @@ export function checkStackedFrontmatter(vaultBase) {
|
|
|
222
224
|
// motivou esta change, a nota fecha com $415 e a fatia do Opus 5 é a única zerada.
|
|
223
225
|
export function checkUnpricedModels(vaultBase) {
|
|
224
226
|
const counts = new Map();
|
|
227
|
+
const researchPreview = new Map();
|
|
225
228
|
|
|
226
229
|
const modelsOf = (frontmatter) => {
|
|
227
230
|
// `modelos:` é a lista canônica; `modelo:` é o rótulo agregado (junta com " + ").
|
|
@@ -247,23 +250,34 @@ export function checkUnpricedModels(vaultBase) {
|
|
|
247
250
|
for (const raw of modelsOf(fm[1])) {
|
|
248
251
|
const model = raw.trim().replace(/^["']|["']$/g, '');
|
|
249
252
|
if (!model || model === 'unknown') continue;
|
|
250
|
-
|
|
251
|
-
|
|
253
|
+
const price = priceForModel(model);
|
|
254
|
+
if (price?.pricingStatus === 'research-preview') {
|
|
255
|
+
researchPreview.set(model, (researchPreview.get(model) || 0) + 1);
|
|
256
|
+
} else if (!price) {
|
|
257
|
+
counts.set(model, (counts.get(model) || 0) + 1);
|
|
258
|
+
}
|
|
252
259
|
}
|
|
253
260
|
}
|
|
254
261
|
};
|
|
255
262
|
|
|
256
263
|
walk(join(vaultBase, '02-Sessões'));
|
|
257
|
-
return {
|
|
264
|
+
return {
|
|
265
|
+
models: [...counts].map(([model, notes]) => ({ model, notes })),
|
|
266
|
+
researchPreview: [...researchPreview].map(([model, notes]) => ({ model, notes })),
|
|
267
|
+
};
|
|
258
268
|
}
|
|
259
269
|
|
|
260
270
|
export function renderUnpricedModelLines(unpriced) {
|
|
271
|
+
const preview = unpriced.researchPreview || [];
|
|
261
272
|
const lines = [`[preços] ${unpriced.models.length} modelo(s) sem preço na tabela`];
|
|
262
273
|
for (const { model, notes } of unpriced.models) {
|
|
263
274
|
lines.push(` ✗ ${model} (${notes} nota(s) com custo zerado)`);
|
|
264
275
|
}
|
|
265
276
|
if (unpriced.models.length) lines.push(' → adicione o modelo em hooks/pricing.json');
|
|
266
|
-
else lines.push(' tabela de preços completa ✓');
|
|
277
|
+
else if (!preview.length) lines.push(' tabela de preços completa ✓');
|
|
278
|
+
for (const { model, notes } of preview) {
|
|
279
|
+
lines.push(` ! ${model} (${notes} nota(s)): research preview sem tarifa final; custo não estimado`);
|
|
280
|
+
}
|
|
267
281
|
return lines;
|
|
268
282
|
}
|
|
269
283
|
|