Skip to content

feat: Add Beads (bd) integration for persistent agent memory via CLI skill and session hooks #157

@raykao

Description

@raykao

Problem

copilot-bridge agents lose all task context on session restart. The only persistence mechanism is MEMORY.md — a flat, unstructured markdown file with no query capability, no dependency tracking, no concurrency safety, and unbounded growth.

Proposed Solution

Integrate Beads (bd) — a distributed, Dolt-backed graph issue tracker designed for AI agent memory — via a CLI skill file and session lifecycle hooks.


Journey & Key Decisions

Why CLI over MCP

copilot-bridge agents have shell access via the bash tool. The beads-mcp MCP server costs 10–50k tokens per session in tool schema overhead. Beads itself recommends CLI in shell-capable environments. A .agent.md skill file provides the same discoverability at zero token cost.

Why bd backup export-git as the sessionEnd action

Three sync tiers exist: local Dolt (automatic), git branch JSONL backup, full Dolt remote. The git branch tier requires zero new infrastructure — it reuses the existing workspace repo. Right for most single-bot deployments with no extra accounts or services.

Why bd remember must be called at point of discovery, not session end

Dolt commits immediately on every write. Memories stored mid-session survive any ungraceful shutdown. Batching bd remember to session end means an interrupted session loses everything. The pattern: call it inline, like a logger, not like a report.

Why sessionEnd is awaited but sessionStart is non-blocking

sessionEnd fires before destroySession() — the hook must complete (e.g. git backup) before Dolt shuts down. sessionStart fires after the session is already live — awaiting it would delay the bot's first response. Fire-and-forget is correct there.

Dead code discovery: sessionStart/sessionEnd were never wired

During implementation we found that both hook types were fully defined in hooks-loader.ts, mapped in HOOK_TYPE_MAP, and covered by unit tests — but session-manager.ts never called them. Silent no-op for any user who had configured these hooks.

Event ordering bug (found in code review)

Original wiring unsubscribed event listeners before awaiting sessionEnd. During the await window, a concurrent sendMessage() could reuse the cached session with no listeners wired, silently dropping response/usage events. Fix: fire sessionEnd before unsub().

Stale hook cache on /reload and /new (found in code review)

resolveHooks() caches per working directory. Since /reload is designed to pick up config changes including hooks.json, the cache must be invalidated before resolving hooks at these lifecycle points.


Bugs Found During Live Validation

  1. allowWorkspaceHooks belongs under defaults, not config top level. session-manager.ts reads getConfig().defaults.allowWorkspaceHooks — top-level key is silently ignored.

  2. /bin/bash not bash — in nvm-managed Node.js, subprocess PATH may not include bash, causing spawn bash ENOENT. Use absolute path /bin/bash on non-Windows.

  3. cwd in hooks.json is relative to baseDir (the directory containing hooks.json). Setting cwd: '.github/hooks' when hooks.json is already in .github/hooks/ doubles the path → ENOENT. Always use cwd: '.'.

  4. Hook scripts must output valid JSONhooks-loader expects JSON on stdout from all hook scripts. Scripts outputting plain text trigger [WARN] Hook command returned invalid JSON. Fix: redirect bd output to /dev/null and echo '{}' as final line.


Validation Results ✅

Tested end-to-end against a live copilot-bridge instance:

  • sessionEnd hook fired on /reload → new commit on beads-backup git branch confirmed
  • sessionStart hook fired cleanly after session reload → no warnings
  • bd prime recovered all 9 stored memories across session boundary
  • bd backup export-git idempotent — only commits when data changed

Implementation

Phase 1 — Docs and config (PR #159)

  • docs/beads.md — full integration guide: setup, hooks, 3-tier sync, MCP alternative, troubleshooting
  • templates/agents/beads.agent.md — agent skill file with complete bd workflow and point-of-discovery memory discipline
  • templates/agents/AGENTS.md — Beads section added; MEMORY.md kept as fallback
  • config.sample.jsonshell(bd) added to default permissions allow list

Phase 2 — Session hooks wiring (PR #158)

  • Wire sessionStart in createNewSession() and attachSession() (non-blocking)
  • Wire sessionEnd in newSession() and reloadSession() (awaited, before unsub)
  • Invalidate hooks cache on /new and /reload
  • Fix spawn bash ENOENT — use /bin/bash absolute path

Acceptance Criteria

  • Session hooks (sessionStart, sessionEnd) fire at correct lifecycle points
  • Hook scripts execute without errors or warnings in live validation
  • bd backup export-git runs automatically on session end
  • bd prime recovers context automatically on session start
  • Documentation covers setup, gotchas, and 3-tier sync strategy
  • Agent skill file includes point-of-discovery memory discipline
  • Merged to main

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions