Skip to content

feat(integrations): add opt-in SAGE persistent memory hooks#202

Open
l33tdawg wants to merge 1 commit into
Intelligent-Internet:mainfrom
l33tdawg:feat/sage-integration
Open

feat(integrations): add opt-in SAGE persistent memory hooks#202
l33tdawg wants to merge 1 commit into
Intelligent-Internet:mainfrom
l33tdawg:feat/sage-integration

Conversation

@l33tdawg
Copy link
Copy Markdown

Summary

Adds an opt-in integration with SAGE, a BFT-consensus persistent memory layer for AI agents, via the existing @hook decorator system. When enabled, each agent turn recalls prior committed memories before the model runs and stores an observation after the turn completes.

Disabled by default (SAGE_ENABLED=false). Framework behavior is unchanged unless the operator sets SAGE_ENABLED=true and installs the optional sage extra.

Why

ii-agent's memory model today is conversation history + per-session file RAG, which is ephemeral across sessions. For long-running personal agents and multi-agent workflows (both of which ii-agent supports via session forking), there's no mechanism to carry learned context or confidence-scored facts between sessions.

SAGE provides that persistence layer with BFT-validated knowledge (confidence decay, corroboration, reflection loop). This PR wires ii-agent's hook system to SAGE without changing any default behavior, so anyone can try the pairing by flipping one env var.

What's in this PR

  • src/ii_agent/integrations/sage/ - new package:
    • client.py - async SageClient wrapper over sage-agent-sdk with is_available(), recall, propose
    • hooks.py - make_sage_hooks() returns (pre_hook, post_hook); post is decorated with run_in_background=True so the agent response isn't held up by SAGE writes
    • registrar.py - register_sage_hooks(agent) wires hooks onto an IIAgent instance
  • src/ii_agent/core/config/sage_config.py - env-driven SageConfig via pydantic-settings (prefix SAGE_)
  • src/ii_agent/agents/agent.py - IIAgent.__post_init__ calls register_sage_hooks(self) inside a try/except (safe no-op when disabled or SDK not installed)
  • docs/sage_integration.md - env vars, registration, fallback semantics
  • pyproject.toml - new optional extra: [project.optional-dependencies].sage = ["sage-agent-sdk>=6.6.1"]
  • src/tests/unit/integrations/test_sage_integration.py - 7 tests, including an end-to-end flow driving the real AsyncSageClient through respx

Env vars

Variable Default Purpose
SAGE_ENABLED false Master switch
SAGE_NODE_URL http://localhost:8090 REST API base URL
SAGE_AGENT_ID unset Display name
SAGE_AGENT_KEY unset Ed25519 seed path
SAGE_DEFAULT_DOMAIN ii-agent Domain tag
SAGE_RECALL_TOP_K 5 Memories per pre-hook
SAGE_PRE_HOOK_TIMEOUT_S 2.0 Pre-hook hard timeout

Safety properties

  • Opt-in only. SAGE_ENABLED=false (default) is a pure no-op. The SDK isn't imported, hooks aren't registered, zero runtime cost.
  • Never blocks the turn. Pre-hook has a hard 2s timeout by default; on timeout or error the turn proceeds with no injected context. Post-hook runs in background.
  • Graceful on SDK missing. The optional extra pip install ii-agent[sage] installs the SDK. Without it, register_sage_hooks is a try/except no-op.
  • No changes to existing behavior unless explicitly enabled.

Test plan

  • python3 -m pytest src/tests/unit/integrations/test_sage_integration.py - 7/7 passing
    • test_register_sage_hooks_noop_when_disabled
    • test_register_sage_hooks_appends_two_hooks_when_enabled
    • test_post_hook_is_marked_run_in_background
    • test_turn_flow_recalls_then_stores (stubbed SDK)
    • test_pre_hook_respects_timeout
    • test_pre_hook_noop_when_disabled
    • test_turn_flow_against_mocked_sdk_http (real SDK + respx wire mock)
  • Smoke run against a live SAGE node (happy to contribute instructions; a local node is docker run ghcr.io/l33tdawg/sage:6.6.2).

Notes

  • The integration is intentionally defensive: every SDK failure path is logged at debug level and swallowed so the agent turn is never blocked by SAGE being slow, down, or misconfigured.
  • The @hook decorator surface in ii-agent is a cleaner integration point than the instrumentation patterns I've used in other frameworks - credit where due, this PR is small because the framework's hook system is the right shape.
  • Happy to iterate on scope, naming, or placement before merge. If an integrations registry pattern emerges later this can be reshaped to fit.

Wire ii-agent into SAGE (Sovereign Agent Governed Experience), a
BFT-consensus memory layer for AI agents, via the @hook decorator.

- Pre-hook: semantic recall before the model runs; bounded by a strict
  timeout (SAGE_PRE_HOOK_TIMEOUT_S, default 2s) so a slow/unreachable
  SAGE node never blocks the agent turn.
- Post-hook: stores a concise turn observation; decorated with
  run_in_background=True so the response returns without waiting for
  BFT consensus.
- Opt-in via SAGE_ENABLED=true; framework runs normally without the
  sage-agent-sdk extra installed.
- Config driven by SAGE_* env vars; no secrets in the source tree.
- Uses the async SDK (AsyncSageClient) with lazy init and graceful
  degradation — any SDK error is logged at debug level and swallowed.
- register_sage_hooks() wired into IIAgent.__post_init__; safe no-op
  when the integration is disabled or the SDK is missing.

Files:
- src/ii_agent/integrations/sage/{__init__,client,hooks,registrar}.py
- src/ii_agent/core/config/sage_config.py
- docs/sage_integration.md
- src/tests/unit/integrations/test_sage_integration.py (7 tests,
  including an integration test driving the real AsyncSageClient
  through respx)
- pyproject.toml: new optional extra "sage" pulling
  sage-agent-sdk>=6.6.1
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant