diff --git a/CLAUDE.md b/CLAUDE.md index d80f5b0cf..355b6065e 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -506,7 +506,7 @@ four numeric segments are required first. Historical 3-segment versions | `CHANGELOG.md` | Top entry header `## [0.31.4.1] - YYYY-MM-DD` plus the "To take advantage of v0.31.4.1" block. | Standard Keep-a-Changelog header. | | `TODOS.md` | Any TODO entries that mention "follow-up from vX.Y.Z.W" use the version of the release that filed them. Update only when filing NEW follow-up TODOs. | Inline `vX.Y.Z.W` references in TODO bodies. | | `CLAUDE.md` | The Key Files section's per-file annotations carry `vX.Y.Z.W (#NNN)` tags noting which release introduced a behavior. Update whenever a wave's annotations get folded in. | Inline `vX.Y.Z.W (#NNN, contributed by @user)` references. | -| `openclaw.plugin.json` | OpenClaw plugin manifest (v0.45.6.0, #4033). Hand-maintained; `test/openclaw-plugin-manifest.test.ts` fails the suite if it drifts from `package.json`. Merges from master auto-resolve it to master's version — re-bump it with the trio. | `"version": "0.45.8.0"` | +| `openclaw.plugin.json` | OpenClaw plugin manifest (v0.45.6.0, #4033). Hand-maintained; `test/openclaw-plugin-manifest.test.ts` fails the suite if it drifts from `package.json`. Merges from master auto-resolve it to master's version — re-bump it with the trio. | `"version": "0.45.10.0"` | | `BOOTSTRAP_FOR_AGENTS.md` | Runbook stamp on line 1. `scripts/check-bootstrap-tag.sh` (in `bun run verify` + CI) fails when it drifts from `VERSION`; refresh it in the same commit as the bump. | `` | | `templates/bootstrap/template-repo/` | Vendored template tree with an embedded version stamp. Auto-derived, but NOT by `bun install`: run `bun run scripts/generate-template-repo.ts --out templates/bootstrap/template-repo` after the bump; `scripts/check-bootstrap-templates.sh` fails CI on drift. | `` in generated files. | diff --git a/INSTALL_FOR_AGENTS.md b/INSTALL_FOR_AGENTS.md index d87b1ed2e..135a3ab20 100644 --- a/INSTALL_FOR_AGENTS.md +++ b/INSTALL_FOR_AGENTS.md @@ -216,6 +216,16 @@ diff against gbrain's bundle when you want upstream improvements. (The legacy `gbrain skillpack install` managed-block model was removed in v0.33 — run `gbrain skillpack migrate-fence` once if upgrading from an older release.) +**If you are Hermes:** register gbrain as your MCP server: + +```bash +printf 'Y\n' | hermes mcp add gbrain --env GBRAIN_HOME=$HOME --connect-timeout 60 --command $(which gbrain) --args serve +``` + +Keep `--args` last (everything after it becomes server argv) and verify with +`hermes mcp test gbrain` — the add exits 0 even on failure. Full reference: +[docs/mcp/HERMES.md](docs/mcp/HERMES.md). + Whether you scaffolded or not, read `skills/RESOLVER.md` (in your workspace, or the bundled copy at `~/gbrain/skills/RESOLVER.md` when running from the cloned repo). It's the skill dispatcher — tells you which skill to read for any task. Save this to your diff --git a/README.md b/README.md index 7e17194cf..e2ea757b9 100644 --- a/README.md +++ b/README.md @@ -170,6 +170,8 @@ GBrain exposes nearly all of its 100+ operations as MCP tools (stdio and HTTP; a - **[Claude Code](docs/mcp/CLAUDE_CODE.md)** — local: one command, `claude mcp add gbrain -- gbrain serve` (zero server, zero tunnel). Remote with just a bearer token: `gbrain connect https://your-host/mcp --token gbrain_xxx` prints a paste-ready block (or `--install` wires it up and smoke-tests the token). - **[Codex](docs/mcp/CODEX.md)** — `gbrain connect https://your-host/mcp --token gbrain_xxx --agent codex` (or `--install`). Codex reads the bearer from `$GBRAIN_REMOTE_TOKEN` at runtime, so the token never lands in Codex config. - **[Cursor / Windsurf / any stdio MCP client](docs/mcp/CLAUDE_CODE.md)** — same shape, add `{"command": "gbrain", "args": ["serve"]}` to your MCP config. +- **[Hermes](docs/mcp/HERMES.md)** — `printf 'Y\n' | hermes mcp add gbrain --env GBRAIN_HOME=$HOME --connect-timeout 60 --command $(which gbrain) --args serve`. Keep `--args` last, and verify with `hermes mcp test gbrain` (the add exits 0 even on failure). +- **[OpenClaw](docs/mcp/OPENCLAW.md)** — the ClawHub bundle plugin registers gbrain automatically (`openclaw.plugin.json` ships in this repo), or add `{"command": "gbrain", "args": ["serve"]}` to `~/.openclaw/config.json`'s `mcpServers`. - **[Claude Desktop (Cowork)](docs/mcp/CLAUDE_DESKTOP.md)** — Settings → Integrations → add the URL of your HTTP server. Remote only; the local `claude_desktop_config.json` does not work for remote servers. - **[Claude Cowork (team plan)](docs/mcp/CLAUDE_COWORK.md)** — org Owner adds the connector under Organization Settings → Connectors. - **[Perplexity Computer](docs/mcp/PERPLEXITY.md)** — `gbrain connect https://your-host/mcp --agent perplexity --oauth --register` mints a least-privilege OAuth client and prints the Issuer/Client ID/Secret to paste into Settings → Connectors (OAuth is the right path for a cloud connector; a bearer token also works for local use). Pro subscription required. diff --git a/TODOS.md b/TODOS.md index 08f8c2d41..cf476c842 100644 --- a/TODOS.md +++ b/TODOS.md @@ -3829,28 +3829,99 @@ After the sweep, both should be fixable and renameable back to plain `*.test.ts` ## claw-test E2E (v0.22.16 follow-ups) -### Hermes runner — `src/core/claw-test/runners/hermes.ts` -**Priority:** P2 - -**What:** Add a Hermes implementation of the `AgentRunner` interface. v1 ships only OpenClaw; v1.1 lands hermes once we have real friction reports from openclaw to validate the contract against. - -**Why:** Cross-agent diff (`gbrain friction diff --base openclaw --compare hermes`) is the highest-leverage next signal. Friction unique to one agent vs common-to-both separates "agent contract bug" from "gbrain bug" automatically. - -**Effort:** S (CC ~30m). Depends on: v1 openclaw runner producing real friction reports first. +### ~~Hermes runner — `src/core/claw-test/runners/hermes.ts`~~ DONE (hermes-harness wave) +Shipped: `HermesRunner` (`hermes -z `, `$HERMES_BIN` > `which hermes`, +`HERMES_HOME` env-allowlist delta) + the full hermes install door +(`test/e2e/install-real-hermes.serial.test.ts`, opt-in-gated) + the label-gated +`hermes-door` CI job in heavy-tests.yml. The cross-agent +`gbrain friction diff --base openclaw --compare hermes` payoff shipped in the +same wave (below). Observed-CLI pins live in `docs/mcp/HERMES-CLI-PIN.md` and +`docs/mcp/HERMES.md`. --- -### Friction analytics suite — `diff` / `trend` / `migration-stub` +### Friction analytics suite — `trend` / `migration-stub` (diff SHIPPED) **Priority:** P2 -**What:** Three new `gbrain friction` subcommands deferred from v1: -- `gbrain friction diff --base --compare ` (cross-agent comparison; ~80 LOC) +**What:** Two remaining `gbrain friction` subcommands deferred from v1 +(`diff` shipped in the hermes-harness wave — see `src/commands/friction.ts`): - `gbrain friction trend [--since ] [--phase ]` (time-series across runs; ~60 LOC) - `gbrain friction migration-stub [--threshold N]` (clusters friction by phase + tokens, emits `skills/migrations/v[N+1].md` stub; ~150 LOC) **Why:** Turns point-in-time reports into a slope. Pairs with the v1.1 public scoreboard. -**Effort:** M (CC ~2h total). +**Effort:** M (CC ~1.5h total). + +--- + +### Promote hermes-door soft probes to hard assertions + build the REAL cron test +**Priority:** P2 + +**What:** Two follow-ups now that the hermes CLI surface is pinned (v0.20.0, +`docs/mcp/HERMES-CLI-PIN.md`): (1) promote the door's logged-evidence probes +(`hermes mcp list` output shape; session-artifact tool-call traces under +`/.hermes/`) to hard assertions once a couple of CI runs confirm their +stability across hermes releases; (2) build the real cron pairing test — the +surface is fully non-interactive (`hermes cron create [--name N] [--no-agent] +[--script PATH] [prompt]` + `hermes cron tick` runs due jobs once +and exits) — create a job that runs `gbrain sync --json`, tick, and assert the +sync actually executed against the run's brain. (A self-skipping probe was +deliberately CUT in review: a test that cannot fail is not coverage.) + +**Why:** INSTALL_FOR_AGENTS.md's recurring-jobs step has zero coverage; the +evidence sweep is the promotion signal the door already logs. + +**Effort:** S-M (CC ~45m). Depends on: first labeled hermes-door CI runs. + +--- + +### Wire the orphaned `voice-agent-install` ScenarioKind +**Priority:** P2 + +**What:** `test/fixtures/claw-test-scenarios/voice-agent-install/` carries the +richest install-assertion template in the repo (60-line expected.json: +filesystem manifest, `.gbrain-source.json` sha256s, resolver rows, PII +blocklist, health probe, tiered soft-fail) but `scenario.json` declares +`kind: "voice-agent-install"`, which `ScenarioKind` rejects — the fixture +cannot load. Extend `ScenarioKind` + `loadScenario` + a `postInstallHook` +implementation so the scenario runs. + +**Why:** Integrations-recipe install coverage (the `gbrain integrations +install` path) has a fully-designed scenario sitting dead. + +**Effort:** M (CC ~1h). Integrations-lane work, deliberately kept out of the +hermes-harness wave. + +--- + +### Cold-install container test — fill the `tests/docker/bootstrap-e2e.sh` placeholder +**Priority:** P3 + +**What:** heavy-tests.yml carries a gated no-op step for +`tests/docker/bootstrap-e2e.sh` (networkless cold-machine container install of +gbrain itself: global install, PATH discovery, migrations). The file doesn't +exist. Write it. + +**Why:** The agent-platform door tests (claude/codex/hermes) all deliberately +run gbrain from the dev tree / compiled binary — none of them proves gbrain's +own cold install. That gap was re-flagged in the hermes-harness wave's outside +review and scoped OUT of that wave on purpose. + +**Effort:** M (CC ~1-2h, docker). + +--- + +### BrainBench hermes adapter +**Priority:** P3 + +**What:** ~50-100 lines in `src/eval/brainbench/adapters/hermes.ts` + an +`ALL_HARNESSES` entry + baseline cells in `evals/brainbench/baselines/main.json`. + +**Why:** Cross-harness memory-conformance coverage for the third platform. +Eval seam (memory conformance), NOT install — kept out of the install wave on +purpose; needs baseline-governance care per the BrainBench gate rules. + +**Effort:** S-M (CC ~1h + baseline runs). --- @@ -3870,7 +3941,7 @@ After the sweep, both should be fixable and renameable back to plain `*.test.ts` ### Real v0.18 SQL dump for upgrade scenario **Priority:** P2 -**What:** The `upgrade-from-v0.18` scenario ships scaffolded — `seed/dump.sql` is missing. The harness gracefully no-ops the seed phase when absent, so the scenario currently behaves like fresh-install. v1.1: generate a real v0.18-shape PGLite dump per the procedure documented in `test/fixtures/claw-test-scenarios/upgrade-from-v0.18/seed/README.md`. +**What:** The `upgrade-from-v0.18` scenario ships scaffolded — `seed/dump.sql` is missing. Both scripted and live runs now FAIL LOUDLY on the missing dump (a silent skip used to init a current database and false-green the "upgrade"), so the shipped scenario is unrunnable until the dump lands. Generate a real v0.18-shape PGLite dump per the procedure documented in `test/fixtures/claw-test-scenarios/upgrade-from-v0.18/seed/README.md`. **Why:** Without a real seed, the scenario doesn't actually exercise the migration chain forward-walk. That's the whole point of the upgrade scenario — proves issue #239/#243/#266/#357 class regressions stay fixed. diff --git a/docs/TESTING.md b/docs/TESTING.md index c8f8319b6..1cd7cef61 100644 --- a/docs/TESTING.md +++ b/docs/TESTING.md @@ -281,7 +281,9 @@ E2E tests live in `test/e2e/` and run against real Postgres+pgvector (require `D - `test/e2e/pglite-cli-exit.serial.test.ts` — real spawned-CLI exit behavior on PGLite (in-memory, no `DATABASE_URL`): read commands (`search`/`get`/`query`) exit 0 promptly; CLI_ONLY `capture` exits clean and frees the single-writer lock; the `#2084` describes pin every swept disconnect site — a failed op exits 1 with the error on stderr, and the dashboard, read-only-timeout, doctor, and `dream --dry-run` paths all exit with no force-exit banner. - `test/e2e/pgbouncer-teardown.test.ts` — PgBouncer TRANSACTION-mode teardown (#2084 / the #1972→#2015→#2084 class). Pins the bug CLASS, not timings: a CLI op against a txn-mode pooled URL exits 0 with intact stdout and does NOT ride the 10s hard-deadline backstop (the `engine.disconnect() did not return` banner is the smoking gun — pre-#2084 it printed on 100% of query-shaped ops). Gated by `GBRAIN_PGBOUNCER_URL` + `GBRAIN_PGBOUNCER_DIRECT_URL` (NOT `DATABASE_URL`) — set automatically by `bun run ci:local`'s `pgbouncer` compose service; skips gracefully elsewhere. Uses a DEDICATED `gbrain_pgbouncer` database so it never races the `gbrain_test` TRUNCATE fixtures. - `test/e2e/volunteer-context-postgres.test.ts` — `volunteer_context` on REAL Postgres (#2095; engine parity beyond the hermetic PGLite unit suite): resolution arms through the actual op handler, the fire-and-forget volunteer-event sink landing rows, the stats join, and the RLS pin that `context_volunteer_events` has ROW LEVEL SECURITY enabled (keeps the v35 auto-RLS event trigger honest for migration-created tables). `DATABASE_URL`-gated. -- `test/e2e/openclaw-reference-compat.test.ts` — `check-resolvable` + `skillpack install` against a minimal AGENTS.md workspace fixture (`test/fixtures/openclaw-reference-minimal/`), regression guard for the OpenClaw deployment shape. +- `test/e2e/openclaw-reference-compat.test.ts` — `check-resolvable` + skillpack install-model against a minimal AGENTS.md workspace fixture (`test/fixtures/openclaw-reference-minimal/`), regression guard for the OpenClaw deployment shape. +- `test/e2e/workspace-generic-compat.test.ts` — always-on (PGLite, no binary): pins the INSTALL_FOR_AGENTS.md "any repo with a workspace" contract against `test/fixtures/generic-agents-workspace/` (Hermes is the motivating consumer): `cwd_walk_up` detection, the `GBRAIN_SKILLS_DIR` override, `check-resolvable` on a root AGENTS.md, and scaffold additivity + refuse-overwrite. The real Hermes-behavior proof is the door suite below. +- `test/e2e/install-real-hermes.serial.test.ts` — the hermes "door": real `hermes` binary + real `hermes mcp add` handshake (110-tool discovery) + a paid `hermes -z` recall turn against a seeded brain. Triple-gated: `GBRAIN_REAL_HERMES_E2E=1` (explicit opt-in — run-e2e.sh scrubs GBRAIN_*, so it can never fire under `bun run test:e2e`) + resolvable binary + non-empty ANTHROPIC key (anthropic-pinned on purpose: a second provider key flips hermes provider-auto into a mis-routed 401). Hermetic HOME + HERMES_HOME with a tripwire on the operator's real config; evidence copies to `GBRAIN_E2E_EVIDENCE_DIR` for CI upload. Venue: heavy-tests.yml (`real-agent-e2e` + `hermes-door` jobs). - `test/e2e/search-swamp.test.ts` — reproduces the source-swamp case. Seeds a curated `originals/talks/article-outline-fat-code` page against two `/chat/` pages stuffed with the same multi-word phrase. Asserts the article wins keyword AND vector ranking, that `detail=high` lets the chat swamp re-surface, and that `source_id` passes through the two-stage CTE intact. PGLite in-memory. - `test/e2e/search-exclude.test.ts` — `test/` + `archive/` pages hidden by default, `include_slug_prefixes` opts back in, caller-supplied `exclude_slug_prefixes` adds to defaults. Both keyword and vector search paths. - `test/e2e/engine-parity.test.ts` — Postgres ↔ PGLite top-result and result-set parity for `searchKeyword` + `searchVector` (Postgres ranks pages then picks best chunk while PGLite returns chunks directly, so the source-boost behavior needs parity coverage). Skips without `DATABASE_URL`. @@ -296,6 +298,7 @@ E2E tests live in `test/e2e/` and run against real Postgres+pgvector (require `D - `test/e2e/think-source-isolation-pglite.test.ts` — PGLite in-memory suite pinning the `think` gather stage's source scope: seeds three sources with cross-source links and embedded takes, then asserts `runGather` under a federated `sourceIds` grant (and under a scalar `sourceId`) keeps every stream — hybrid retrieval, takes keyword + vector (`searchTakes`/`searchTakesVector`), and the `traversePaths` graph walk — inside the grant while still reaching authorized neighboring sources. No `DATABASE_URL` needed. - `test/e2e/skill-brain-first.test.ts` — doctor reports `skill_brain_first` check with structured issues; `--fix --dry-run` previews insertion without writing; `--fix` applies the canonical Convention callout idempotently; `brain_first: exempt` frontmatter resolves the warn; `brain_first_typo` surfaces a paste-ready hint; audit JSONL records `detected` / `resolved` / `fixed` transitions; stable brain emits 0 audit lines/run. - Tier 2 (`test/e2e/skills.test.ts`) requires OpenClaw + API keys, runs nightly in CI. +- `test/e2e/claw-test.test.ts` also covers live mode token-free via shim agents (`OPENCLAW_BIN=`): the success-oracle break path (a do-nothing agent now FAILS), the E0 child-friction merge surviving tempdir cleanup, and the upgrade staging + schema-version-probe regression. - If `.env.testing` doesn't exist in this directory, check sibling worktrees: `find ../ -maxdepth 2 -name .env.testing -print -quit` and copy it here if found. - **Run E2E tests without asking permission.** When you want to verify behavior, there's a relevant E2E test, or you're shipping anything covered by an E2E suite — spin up the test DB, run the tests, tear down. Don't ask, don't propose it, don't defer. The lifecycle is short (~2-30s startup, sub-minute tests, instant teardown) and the gate value is high. Skipping with "DATABASE_URL unset" is silent regression, not caution. diff --git a/docs/architecture/KEY_FILES.md b/docs/architecture/KEY_FILES.md index 1489e34d3..7c09db2f8 100644 --- a/docs/architecture/KEY_FILES.md +++ b/docs/architecture/KEY_FILES.md @@ -322,8 +322,8 @@ per-release `**vX.Y.Z:**` narration — CI enforces this - `src/core/operations-descriptions.ts` — Constants module for tool descriptions. Pinned via `test/operations-descriptions.test.ts`. Houses `GET_RECENT_SALIENCE_DESCRIPTION`, `FIND_ANOMALIES_DESCRIPTION`, `GET_RECENT_TRANSCRIPTS_DESCRIPTION` plus `LIST_PAGES_DESCRIPTION`, `QUERY_DESCRIPTION`, `SEARCH_DESCRIPTION`. Stable surface for the Tier-2 LLM routing eval — extracting them keeps the test from binding to whatever was in `operations.ts` at test-run time. - `src/core/cycle/transcript-discovery.ts` — Pure filesystem walk for synthesize. `discoverTranscripts(opts)` filters `.txt` files by date range, min_chars, and word-boundary regex `excludePatterns` (`medical` matches "medical advice" but NOT "comedical"; power users may pass full regex). `readSingleTranscript(path)` is the `gbrain dream --input ` ad-hoc path. Self-consumption guard: `DREAM_OUTPUT_MARKER_RE` (anchored at frontmatter open `---\n`, optional BOM + CRLF tolerance, scans first 2000 chars for `dream_generated: true` with case-insensitive value and word boundary on `true`) drives `isDreamOutput(content, bypass=false)`. Both functions skip matching files and emit a `[dream] skipped : dream_generated marker` stderr log (no silent skips). `bypassGuard?: boolean` on `DiscoverOpts` and `readSingleTranscript`'s opts disables the guard for the explicit `--unsafe-bypass-dream-guard` escape hatch only — never auto-applied for `--input`. - `src/commands/dream.ts` — `gbrain dream` CLI; thin alias over `runCycle`. Flags: `--dry-run`, `--json`, `--phase `, `--pull`, `--dir `, `--input ` (ad-hoc transcript, implies `--phase synthesize`), `--date YYYY-MM-DD`, `--from --to ` (backfill range), `--unsafe-bypass-dream-guard` (plumbed through `runCycle.synthBypassDreamGuard` → `SynthesizePhaseOpts.bypassDreamGuard` → `discoverTranscripts({bypassGuard})` / `readSingleTranscript({bypassGuard})`; loud stderr warning at synthesize-phase entry; never auto-applied for `--input`). Conflict detection: `--input` + `--date` exits 2. ISO date validation. `--dry-run` runs the Haiku significance verdict but skips Sonnet synthesis (NOT zero LLM calls). Exit 1 on status=failed. `resolveBrainDir` returns `string | null` (order: `--dir` → resolved `--source`'s `local_path` → global `sync.repo_path` → null); a checkout-less postgres/Supabase brain runs DB-only phases (incl. `resolve_symbol_edges`) and skips the 6 filesystem phases with `details.reason: 'no_brain_dir'`; `runDream` owns the only hard error (no checkout AND no engine). When `--source` resolves but has no on-disk checkout, returns null (DB-only) rather than borrowing another source's global `sync.repo_path` (would mix scopes). Pinned by `test/dream-postgres.serial.test.ts`. `--drain [--window ]` for `--phase extract_atoms`: `runDrain()` bypasses the pack-gate and runs the single-hold bounded drain from `src/core/cycle/extract-atoms-drain.ts` under the same `cycleLockIdFor(sourceId)` the routine cycle uses (concurrent autopilot tick defers with `cycle_already_running`), reporting `{extracted, skipped, remaining}`. Exits `EXIT_DRAIN_INCOMPLETE=3` while `remaining > 0`; a null backlog count (count query FAILED) is also exit 3, never a drained success; `LockUnavailableError` → `cycle_already_running` skip (also exit 3). The `extract_atoms_backlog` doctor check (`computeExtractAtomsBacklogCheck`) surfaces the silent pack-gated backlog with the exact `--drain` command; pack-gated cycle skips carry a greppable `pack_gated:true` marker. -- `src/commands/friction.ts` + `src/core/friction.ts` — `gbrain friction {log,render,list,summary}` reporter. Append-only JSONL under `$GBRAIN_HOME/friction/.jsonl`. Schema is a flat extension of `StructuredAgentError`. Render groups by severity → phase, defaults to `--redact` for md output (strips `$HOME`/`$CWD` to placeholders so reports paste safely in PRs). Run-id resolves from `--run-id` > `$GBRAIN_FRICTION_RUN_ID` > `standalone.jsonl`. Skills the claw-test exercises gain a `_friction-protocol.md` callout so agents know when to log friction. -- `src/commands/claw-test.ts` + `src/core/claw-test/` — `gbrain claw-test [--scenario ] [--live --agent openclaw]`. End-to-end "fresh user" friction harness. Two modes: scripted (CI gate, agent-free) and live (real openclaw subprocess, $1–2 in tokens). Sets `GBRAIN_HOME=` for hermeticity and captures gbrain's `--progress-json` events from each child's stderr to verify expected phases ran (`import.files`, `extract.links_fs`, `doctor.db_checks`). Scripted phases: setup → install_brain (`gbrain init --pglite`) → import (`--no-embed`) → query → extract → verify (`gbrain doctor --json`, asserts `status: 'ok'`) → render. Live mode hands `BRIEF.md` from `test/fixtures/claw-test-scenarios//` to the agent runner. Ships with the OpenClaw runner only (`src/core/claw-test/runners/openclaw.ts`, invokes `openclaw agent --local --agent --message `); hermes runner deferred. Transcript capture (`transcript-capture.ts`) uses `fs.createWriteStream` with `'drain'`-event backpressure (256KB-burst child-stall fix). Upgrade scenario seeded via `seed-pglite.ts` SQL replay. +- `src/commands/friction.ts` + `src/core/friction.ts` — `gbrain friction {log,render,list,summary,diff}` reporter. Append-only JSONL under `$GBRAIN_HOME/.gbrain/friction/.jsonl`. Schema is a flat extension of `StructuredAgentError`; every claw-test run opens with a `phase-marker`/`start` meta record carrying `agent` + `scenario` + `harness_schema` (agent-name resolution depends on it). Render groups by severity → phase, defaults to `--redact` for md output (strips `$HOME`/`$CWD` to placeholders so reports paste safely in PRs). `diff --base --compare ` is the cross-agent instrument: exact run-id wins, else agent name resolves to that agent's latest run; identity is `(kind, phase, normalized 80-char message prefix — digit runs collapsed so durations/counts don't split identities)` over `kind ∈ {friction, delight}` as MULTISETS (per-severity counts + totals are the compared attributes: `count_changed` = volume, `severity_changed` = distribution shape via exact integer proportion test, so a delight→friction flip or a 2×error+1×nit → 1×error+2×nit redistribution always surfaces; markers/interrupted feed the compatibility banner, which warns on scenario/version mismatch); output labels sections "unique to " — an instrument, never a blame-attributor. Run-id resolves from `--run-id` > `$GBRAIN_FRICTION_RUN_ID` > `standalone.jsonl`. Skills the claw-test exercises gain a `_friction-protocol.md` callout so agents know when to log friction. +- `src/commands/claw-test.ts` + `src/core/claw-test/` — `gbrain claw-test [--scenario ] [--live --agent ]`. End-to-end "fresh user" friction harness. Two modes: scripted (CI gate, agent-free) and live (real agent subprocess, $1–2 in tokens). Sets `GBRAIN_HOME=` for hermeticity and captures gbrain's `--progress-json` events from each child's stderr to verify expected phases ran (`import.files`, `extract.links_fs`, `doctor.db_checks`). Scripted phases: setup → install_brain (`gbrain init --pglite`) → import (`--no-embed`) → query → extract → verify (`gbrain doctor --json`; top-level status is `healthy|warnings|unhealthy`) → render. Live mode STAGES the scenario before the agent turn (fresh-install: brain pages + AGENTS.md stub + init; upgrade: seed-first via `seed-pglite.ts`, NO init — the migration is the scenario under test), prepends a per-run `gbrain` PATH shim so the BRIEF's bare `gbrain` runs this checkout, hands `BRIEF.md` to the agent runner, then verifies a scenario-declared success ORACLE (`oracle: {query, min_results, files_exist}` in scenario.json; upgrade uses a non-mutating schema-version probe via `readPgliteSchemaVersion` — doctor would auto-migrate and pass a do-nothing agent). Child-side friction merges into the parent's friction file before tempdir cleanup. Two runners ship (`src/core/claw-test/runners/{openclaw,hermes}.ts`): openclaw invokes `openclaw agent --local --agent --message `; hermes invokes `hermes -z ` (`$HERMES_BIN` > `which hermes`; `HERMES_HOME` passthrough is the env-allowlist delta; shared `BASE_ENV_ALLOWLIST` + `validateBinPathEnv` live in `agent-runner.ts`). Live-lane posture: the OPERATOR's configured agent + hermetic brain; the fully hermetic lane is `test/e2e/install-real-hermes.serial.test.ts`. Transcript capture (`transcript-capture.ts`) uses `fs.createWriteStream` with `'drain'`-event backpressure (256KB-burst child-stall fix). - `skills/_friction-protocol.md` — shared cross-cutting convention skill (like `_brain-filing-rules.md`). Tells agents when to call `gbrain friction log` and how to choose a severity. Routes to friction CLI from any skill the claw-test exercises. - `scripts/check-progress-to-stdout.sh` — CI guard against regressing to `\r`-on-stdout progress. Wired into `bun run test` via `scripts/check-progress-to-stdout.sh && bun test` in package.json. - `docs/progress-events.md` — Canonical JSON event schema reference. Additive only. diff --git a/docs/mcp/HERMES-CLI-PIN.md b/docs/mcp/HERMES-CLI-PIN.md new file mode 100644 index 000000000..fb305521e --- /dev/null +++ b/docs/mcp/HERMES-CLI-PIN.md @@ -0,0 +1,112 @@ +# Hermes CLI pin — observed behavior notes (v0.20.0) + +Dev-facing companion to [HERMES.md](HERMES.md): every fact below was OBSERVED +against a real install (2026-08-12), not researched from docs. The claw-test +HermesRunner, the install door e2e, and the heavy-tests hermes-door CI job +assert exactly these shapes — when hermes releases change them, update this +file, the workflow pins, and the affected assertions together. + +## Pin +- **Hermes Agent v0.20.0 (2026.8.3)**, observed against git checkout `3e09adb` at + `~/.hermes/hermes-agent` (an upstream-main commit carrying the same v0.20.0/2026.8.3 + version stamp; CI installs the RELEASE TAG `v2026.8.3` = commit `3c27eb62` — the two + differ by post-release main commits, same declared version. If a CI door run ever + diverges from these notes, re-observe against the tag checkout.) +- Installer sha256: `c118ff31618dc70339049ce71061b8f1351a1c70d9c2a236ed50d8a2550c550d` + (download https://hermes-agent.nousresearch.com/install.sh to a file first; verify; then run) +- Installer flags used: `--skip-setup --non-interactive`; binary lands at `~/.local/bin/hermes` +- Python 3.11.15 via uv + +## HERMES_HOME — HONORED (verified) +Installer (`HERMES_HOME="${HERMES_HOME:-$HOME/.hermes}"`) AND runtime both honor it: +`mcp add`/`mcp list`/`config set` under `HERMES_HOME=` read+write `/config.yaml`, +populate `/{SOUL.md,cron,logs,...}`, and do NOT touch `~/.hermes`. Belt-and-suspenders +(HOME + HERMES_HOME both to tmp) stays in the door test anyway. + +## One-shot (`-z`) +- `hermes -z ""` → **stdout = final text ONLY**; benign notices may appear on stderr + ("Shell cwd was reset to ..."). Verified reply fidelity ("B0-PROBE-OK"). +- Exit codes: 0 = success; **1 = no inference provider configured** (message: "agent failed: + No inference provider configured. Run 'hermes model' ... or set an API key + (OPENROUTER_API_KEY, OPENAI_API_KEY, etc.) in ~/.hermes/.env.") +- `--usage-file PATH` exists; per-call `-m MODEL --provider PROVIDER` exist; also + `--in DIR`, `--ignore-user-config`, `--safe-mode`, `-t TOOLSETS`, `--skills`. + +## Auth + model pin (non-interactive) +- `$HERMES_HOME/.env` with `ANTHROPIC_API_KEY=...` WORKS (verified end-to-end). +- Model pin: `hermes config set model.default anthropic/claude-haiku-4.5` → exit 0, + writes `model.default` into config.yaml. `hermes config get model.default` reads it back. + (`hermes model` is INTERACTIVE-only — never use it in tests/CI.) +- Valid model id format: `anthropic/claude-haiku-4.5` (hermes catalog naming, provider-prefixed). + +## `hermes mcp add` — THE big observed facts +- Shape: `hermes mcp add [--env K=V K2=V2 ...] [--connect-timeout N] --command CMD --args ...` + **`--args` MUST be the last option** — anything after it (incl. a misplaced `--env`) is + swallowed into the server argv. (First rehearsal failed exactly this way.) + **The env flag takes MULTIPLE KEY=VALUE values after ONE flag; REPEATING it REPLACES the + first occurrence** (argparse nargs semantics) — a repeated-flag invocation silently drops + the earlier vars, the handshake fails, and the piped Y then hits the save-anyway prompt → + the entry is saved with `enabled: false`. (First real door run failed exactly this way.) +- Add performs a REAL MCP handshake + tool discovery at add time. Against + `--command bun --args run /src/cli.ts serve` with `--env GBRAIN_HOME=`: + connected, discovered **110 gbrain tools**. +- On success it prompts `Enable all N tools? [Y/n/select]:` — **non-interactive: pipe + `printf 'Y\n'`**. Piping Y saves: `✓ Saved 'gbrain' to /config.yaml (110/110 + tools enabled)`. EOF on the prompt = `Cancelled.`, nothing saved. +- **EXIT CODE IS 0 EVEN ON CONNECTION FAILURE OR CANCEL.** Never assert on `mcp add`'s exit + code. Hard assertions = (a) `config.yaml` contains `mcp_servers.` after the add, + (b) `hermes mcp test ` exits 0. + +## Saved config schema (verbatim shape) +```yaml +_config_version: 34 +mcp_servers: + gbrain: + command: bun + args: + - run + - /abs/path/src/cli.ts + - serve + env: + GBRAIN_HOME: /tmp/gb-xxxx + connect_timeout: 60.0 + enabled: true +``` +(The generated file also contains commented template blocks — security, fallback_model.) + +## Probes +- `hermes mcp list` → table `Name / Transport / Tools / Status`, row shows `gbrain ... ✓ enabled`. +- `hermes mcp test gbrain` → exit 0 + prints the tool list. THE targeted probe for Test 1b. +- `hermes doctor` exists (global health; not a per-server assertion). + +## Cron (for the post-pin F7 TODO — real test is buildable) +`hermes cron create [--name NAME] [--deliver ...] [--repeat N] [--skill S] [--script PATH] +[--no-agent] [--workdir DIR] [--model M] [--provider P] [prompt]` — fully +non-interactive. `hermes cron tick` = run due jobs once and exit. `hermes cron list` exists. + +## CI pin values (heavy-tests.yml `hermes-door` job) +- `HERMES_VERSION: "0.20.0"` +- `HERMES_GIT_TAG: "v2026.8.3"` + `HERMES_GIT_COMMIT: "3c27eb6234bf91b8ceee9e9071591b31e9b148cb"` — + the installer's `--branch`/`--commit` flags pin the cloned PAYLOAD (the sha256 below only + pins the installer script; without the tag+commit the payload would be upstream main). + The flags are asserted, not trusted: post-install the job runs + `git -C ~/.hermes/hermes-agent rev-parse HEAD` and loud-fails on any mismatch, so an + installer that silently ignores unknown flags (or a moved checkout layout) can never + run unpinned upstream code on a runner that later holds secrets. +- `HERMES_INSTALL_SHA256: "c118ff31618dc70339049ce71061b8f1351a1c70d9c2a236ed50d8a2550c550d"` +- Door test asserts `hermes --version` output contains `v$HERMES_VERSION` when the env var is set. +- `hermes --version` output shape: `Hermes Agent v0.20.0 (2026.8.3)` + install dir + python lines. + +## Multi-provider 401 gotcha (door hermeticity) +With `model.default` pinned to `anthropic/*` but a SECOND provider key visible (env or +.env — e.g. `OPENAI_API_KEY`), hermes's provider-auto mis-routes the request and the turn +returns `HTTP 401: Missing Authentication header` as final text with EXIT 0. The door +suite therefore seeds exactly ONE key (anthropic) and scrubs all provider env vars from +hermes children (`hermesChildEnv` in test/helpers/agent-harness.ts) — the seeded +`$HERMES_HOME/.env` is the single auth source. + +## mcp add save-anyway (correction to an earlier note) +A piped `Y` saves the entry EVEN when the handshake failed — the save-anyway prompt +writes it with `enabled: false`. The success discriminators are `enabled: true` in the +saved YAML plus `hermes mcp test ` exit 0 — never the add's exit code, and not the +mere presence of the config entry. diff --git a/docs/mcp/HERMES.md b/docs/mcp/HERMES.md new file mode 100644 index 000000000..6b6723c9b --- /dev/null +++ b/docs/mcp/HERMES.md @@ -0,0 +1,120 @@ +# Connect GBrain to Hermes + +> This page is the MCP-registration reference for Hermes (the NousResearch +> `hermes-agent`). For the full brain install — CLI, engine, skills, dream +> cycle — follow [INSTALL_FOR_AGENTS.md](../../INSTALL_FOR_AGENTS.md) first; +> this page wires the finished brain into Hermes over stdio MCP. + +Hermes spawns `gbrain serve` as a local stdio subprocess. No server, no tunnel, +no token needed. Works with both PGLite and Supabase engines. + +## Register (recommended) + +```bash +printf 'Y\n' | hermes mcp add gbrain --env GBRAIN_HOME=$HOME --connect-timeout 60 --command $(which gbrain) --args serve +``` + +`hermes mcp add` performs a real MCP handshake and tool discovery at add time, +then prompts `Enable all N tools? [Y/n/select]:`. Three gotchas, all observed: + +- **`--args` must be the LAST option.** Everything after it — including a + misplaced `--env` — is swallowed into the server argv. To pass several + environment variables, list them all after ONE `--env` flag + (`--env A=1 B=2`); repeating the flag replaces the earlier values and the + server is saved disabled when its handshake then fails. Put `--env` and + `--connect-timeout` before `--command`, exactly as above. +- **Pipe the `Y` in non-interactive contexts.** EOF on the enable-tools prompt + prints `Cancelled.` and saves nothing. The piped `Y` saves the server with + all tools enabled. +- **The exit code is 0 even on connection failure or cancel.** Never assert on + `mcp add`'s exit status — verify with `hermes mcp list` and + `hermes mcp test gbrain` (below). + +## Direct config (equally supported) + +The add command writes an `mcp_servers` block into `$HERMES_HOME/config.yaml` +(default `~/.hermes/config.yaml`). You can write it yourself instead: + +```yaml +mcp_servers: + gbrain: + command: gbrain + args: + - serve + env: + GBRAIN_HOME: /home/alice-example + connect_timeout: 60.0 + enabled: true +``` + +To remove gbrain, delete this block (or set `enabled: false` to disable +without losing the config). + +## Verify + +```bash +hermes mcp list # table row: gbrain ... ✓ enabled +hermes mcp test gbrain # exits 0 and prints the discovered tool list +``` + +Then one real round-trip: + +```bash +hermes -z "ask my gbrain brain: what did I import most recently?" +``` + +`hermes -z` prints the final answer on stdout (benign notices may appear on +stderr). Inside Hermes, gbrain's tools appear namespaced as +`mcp_gbrain_` (e.g. `mcp_gbrain_search`). + +## Headless auth + model pin + +For cron jobs, CI, or any non-TTY run, Hermes needs a provider key and a +default model configured without the interactive picker: + +- Put the key in `$HERMES_HOME/.env`: + + ```bash + ANTHROPIC_API_KEY=sk-ant-... + # or OPENROUTER_API_KEY / OPENAI_API_KEY + ``` + +- Pin the model non-interactively (`hermes model` is interactive-only — never + use it in scripts or CI): + + ```bash + hermes config set model.default anthropic/claude-haiku-4.5 + hermes config get model.default # reads it back + ``` + +## Pair with cron + +Hermes cron is fully non-interactive, which makes it a natural scheduler for +brain maintenance: + +```bash +hermes cron create --name gbrain-sync '0 */4 * * *' 'Run gbrain sync and report anything unusual' +hermes cron tick # run due jobs once and exit — deterministic testing +hermes cron list +``` + +See [docs/guides/cron-schedule.md](../guides/cron-schedule.md) for the full +brain maintenance protocol (sync, embed, dream cycle). + +## Troubleshooting + +- **`hermes doctor`** — global health check (installation, config, providers). + It's not a per-server assertion; use `hermes mcp test gbrain` for that. +- **`agent failed: No inference provider configured`** (exit 1) — Hermes has + no model key. Set one in `$HERMES_HOME/.env` and pin `model.default` as + above. +- **Relocating Hermes** — both the installer and the runtime honor + `HERMES_HOME`. All state (`config.yaml`, `.env`, `SOUL.md`, cron, logs) + lives under it; the default is `~/.hermes`. Export it consistently or the + gbrain registration lands in a config file the runtime never reads. + +--- + +Documented against **Hermes Agent v0.20.0 (2026.8.3)**. Dev-facing observed-behavior +notes (exact flag semantics, exit-code caveats, CI pin values) live in +[HERMES-CLI-PIN.md](HERMES-CLI-PIN.md). diff --git a/docs/mcp/OPENCLAW.md b/docs/mcp/OPENCLAW.md new file mode 100644 index 000000000..9d20dbbb3 --- /dev/null +++ b/docs/mcp/OPENCLAW.md @@ -0,0 +1,62 @@ +# Connect GBrain to OpenClaw + +> This page is the MCP-registration reference card. For the full brain install +> — CLI, engine, skills, dream cycle — follow +> [INSTALL_FOR_AGENTS.md](../../INSTALL_FOR_AGENTS.md); the README covers the +> bootstrap and connect paths. + +Two supported shapes, both stdio. + +## Option 1: ClawHub bundle plugin + +GBrain ships [`openclaw.plugin.json`](../../openclaw.plugin.json) at the repo +root. Installing the bundle plugin registers the MCP server for you — the +manifest carries an `mcpServers.gbrain` entry (`./bin/gbrain serve`) plus the +bundled skills — and declares the `gbrain-context` context engine. To route +OpenClaw's context-engine slot through gbrain, set: + +``` +plugins.slots.contextEngine = gbrain-context +``` + +## Option 2: Direct `~/.openclaw/config.json` + +The same shape gbrain's own CI uses (see the "Configure OpenClaw MCP" step in +`.github/workflows/e2e.yml`): + +```json +{ + "mcpServers": { + "gbrain": { + "command": "gbrain", + "args": ["serve"], + "env": { + "DATABASE_URL": "postgresql://...", + "GBRAIN_HOME": "/home/alice-example" + } + } + } +} +``` + +The `env` block is optional: a PGLite brain needs no `DATABASE_URL`, and +`GBRAIN_HOME` only matters when the brain home isn't `~/.gbrain`. Append +`"--surface", "verbs"` to `args` for the five-verb memory protocol +([MEMORY_VERBS v1](../protocol/MEMORY_VERBS_v1.md)) instead of the full +operation catalog. + +## Verify + +Start an agent turn and ask it to use the brain: + +``` +Call get_brain_identity, then search my brain for [topic]. +``` + +If the tools respond, the wiring works. `list_skills` shows everything the +brain can do (gated by `mcp.publish_skills` on the host). + +## Remove + +Delete the `mcpServers.gbrain` block from `~/.openclaw/config.json`, or +uninstall the bundle plugin. diff --git a/llms-full.txt b/llms-full.txt index 12da80a55..5c66e2570 100644 --- a/llms-full.txt +++ b/llms-full.txt @@ -661,7 +661,7 @@ four numeric segments are required first. Historical 3-segment versions | `CHANGELOG.md` | Top entry header `## [0.31.4.1] - YYYY-MM-DD` plus the "To take advantage of v0.31.4.1" block. | Standard Keep-a-Changelog header. | | `TODOS.md` | Any TODO entries that mention "follow-up from vX.Y.Z.W" use the version of the release that filed them. Update only when filing NEW follow-up TODOs. | Inline `vX.Y.Z.W` references in TODO bodies. | | `CLAUDE.md` | The Key Files section's per-file annotations carry `vX.Y.Z.W (#NNN)` tags noting which release introduced a behavior. Update whenever a wave's annotations get folded in. | Inline `vX.Y.Z.W (#NNN, contributed by @user)` references. | -| `openclaw.plugin.json` | OpenClaw plugin manifest (v0.45.6.0, #4033). Hand-maintained; `test/openclaw-plugin-manifest.test.ts` fails the suite if it drifts from `package.json`. Merges from master auto-resolve it to master's version — re-bump it with the trio. | `"version": "0.45.8.0"` | +| `openclaw.plugin.json` | OpenClaw plugin manifest (v0.45.6.0, #4033). Hand-maintained; `test/openclaw-plugin-manifest.test.ts` fails the suite if it drifts from `package.json`. Merges from master auto-resolve it to master's version — re-bump it with the trio. | `"version": "0.45.10.0"` | | `BOOTSTRAP_FOR_AGENTS.md` | Runbook stamp on line 1. `scripts/check-bootstrap-tag.sh` (in `bun run verify` + CI) fails when it drifts from `VERSION`; refresh it in the same commit as the bump. | `` | | `templates/bootstrap/template-repo/` | Vendored template tree with an embedded version stamp. Auto-derived, but NOT by `bun install`: run `bun run scripts/generate-template-repo.ts --out templates/bootstrap/template-repo` after the bump; `scripts/check-bootstrap-templates.sh` fails CI on drift. | `` in generated files. | @@ -1232,6 +1232,16 @@ diff against gbrain's bundle when you want upstream improvements. (The legacy `gbrain skillpack install` managed-block model was removed in v0.33 — run `gbrain skillpack migrate-fence` once if upgrading from an older release.) +**If you are Hermes:** register gbrain as your MCP server: + +```bash +printf 'Y\n' | hermes mcp add gbrain --env GBRAIN_HOME=$HOME --connect-timeout 60 --command $(which gbrain) --args serve +``` + +Keep `--args` last (everything after it becomes server argv) and verify with +`hermes mcp test gbrain` — the add exits 0 even on failure. Full reference: +[docs/mcp/HERMES.md](docs/mcp/HERMES.md). + Whether you scaffolded or not, read `skills/RESOLVER.md` (in your workspace, or the bundled copy at `~/gbrain/skills/RESOLVER.md` when running from the cloned repo). It's the skill dispatcher — tells you which skill to read for any task. Save this to your @@ -1751,6 +1761,8 @@ GBrain exposes nearly all of its 100+ operations as MCP tools (stdio and HTTP; a - **[Claude Code](docs/mcp/CLAUDE_CODE.md)** — local: one command, `claude mcp add gbrain -- gbrain serve` (zero server, zero tunnel). Remote with just a bearer token: `gbrain connect https://your-host/mcp --token gbrain_xxx` prints a paste-ready block (or `--install` wires it up and smoke-tests the token). - **[Codex](docs/mcp/CODEX.md)** — `gbrain connect https://your-host/mcp --token gbrain_xxx --agent codex` (or `--install`). Codex reads the bearer from `$GBRAIN_REMOTE_TOKEN` at runtime, so the token never lands in Codex config. - **[Cursor / Windsurf / any stdio MCP client](docs/mcp/CLAUDE_CODE.md)** — same shape, add `{"command": "gbrain", "args": ["serve"]}` to your MCP config. +- **[Hermes](docs/mcp/HERMES.md)** — `printf 'Y\n' | hermes mcp add gbrain --env GBRAIN_HOME=$HOME --connect-timeout 60 --command $(which gbrain) --args serve`. Keep `--args` last, and verify with `hermes mcp test gbrain` (the add exits 0 even on failure). +- **[OpenClaw](docs/mcp/OPENCLAW.md)** — the ClawHub bundle plugin registers gbrain automatically (`openclaw.plugin.json` ships in this repo), or add `{"command": "gbrain", "args": ["serve"]}` to `~/.openclaw/config.json`'s `mcpServers`. - **[Claude Desktop (Cowork)](docs/mcp/CLAUDE_DESKTOP.md)** — Settings → Integrations → add the URL of your HTTP server. Remote only; the local `claude_desktop_config.json` does not work for remote servers. - **[Claude Cowork (team plan)](docs/mcp/CLAUDE_COWORK.md)** — org Owner adds the connector under Organization Settings → Connectors. - **[Perplexity Computer](docs/mcp/PERPLEXITY.md)** — `gbrain connect https://your-host/mcp --agent perplexity --oauth --register` mints a least-privilege OAuth client and prints the Issuer/Client ID/Secret to paste into Settings → Connectors (OAuth is the right path for a cloud connector; a bearer token also works for local use). Pro subscription required.