Skip to content

Stop-hook → /summarize → agent-sdk infinite recursion when no provider key is set (extends #149) #181

Closed

Description

@josephwylie

Summary

On v0.9.1, a user with no provider API key set (and AGENTMEMORY_AUTO_COMPRESS=false — which they believed protected them) hits an infinite recursion loop. The Stop hook POSTs to /agentmemory/summarize, which unconditionally spawns a @anthropic-ai/claude-agent-sdk child session via the agent-sdk provider. That child session inherits the same plugin hooks → its own Stop fires → another child spawns → recursion.

This extends #149: that issue added a stderr warning but no actual guard. The user in this report had AGENTMEMORY_AUTO_COMPRESS=false in ~/.agentmemory/.env (per the warning's intended effect) and still hit the loop because /summarize does not consult AUTO_COMPRESS.

Observed impact

  • ~579 ghost entrypoint: sdk-ts child sessions accumulated in .claude/projects/C--Users-<home>/ over a few minutes
  • Each child burns Claude Pro subscription tokens (SDK inherits CC OAuth when no API key is present)
  • Dashboard unusable — sessions grow faster than it can render
  • User had to kill the MCP server and manually clean up

Root cause (v0.9.1)

  1. src/hooks/stop.ts:28 — Stop hook unconditionally POSTs /agentmemory/summarize with only session_id. No parentage / SDK-child / cwd check.
  2. src/functions/summarize.ts:75-77 — handler always calls provider.summarize(...). AGENTMEMORY_AUTO_COMPRESS is never consulted on this path (it only gates /compress).
  3. src/config.ts:44-98 — detectProvider() with all provider keys empty/unset falls through to return { provider: 'agent-sdk', ... } at L93-L97. AUTO_COMPRESS is only used to emit a stderr warning (L84-L92), not as a gate.
  4. src/providers/agent-sdk.ts:14-33 — calls @anthropic-ai/claude-agent-sdk query() which creates a full CC-style child session that reads ~/.claude/settings.json, registers the same plugin hooks, and fires Stop on exit.
  5. Empty ANTHROPIC_API_KEY= in .env is truthiness-checked at src/config.ts:56 — \"\" is falsy, so it behaves identically to unset.

Minimum repro

  1. Fresh install of the agentmemory CC plugin at v0.9.1
  2. ~/.agentmemory/.env contains ANTHROPIC_API_KEY= (or no key line at all) and AGENTMEMORY_AUTO_COMPRESS=false
  3. npx @agentmemory/agentmemory running (cwd anywhere — home dir amplifies visibility but not required)
  4. Open CC, do anything, exit — Stop fires → loop kicks off
  5. Watch .claude/projects/<server-cwd-encoded>/ fill with entrypoint: sdk-ts sessions

Suggested fixes (any one breaks the loop)

  • Gate the agent-sdk fallback on AGENTMEMORY_AUTO_COMPRESS=true in detectProvider(). If AUTO_COMPRESS is false/unset AND no provider key is set, return a null/no-op provider and have /summarize short-circuit. The current warning suggests this was the intent.
  • Skip the Stop hook when the session is an SDK child. Either pass entrypoint into the hook stdin payload (CC exposes it in the jsonl metadata already) and check for sdk-ts in src/hooks/stop.ts, or set an env marker (AGENTMEMORY_SDK_CHILD=1) when src/providers/agent-sdk.ts spawns query() and honor it in the hook script.
  • Treat empty-string provider keys as unset with a loud warning, and refuse to start in agent-sdk mode without an explicit opt-in flag like AGENTMEMORY_ALLOW_AGENT_SDK=true.

Local workaround (documented here so others find it)

Set a fake ANTHROPIC_API_KEY=sk-ant-fake in ~/.agentmemory/.env. detectProvider() selects the anthropic provider, the real API returns 401, summarize.ts:148 catches and returns {success:false}, and the Stop hook's fetch().catch(() => {}) swallows it. No child session spawned. Confirmed safe as long as FALLBACK_PROVIDERS=agent-sdk is NOT set.

Why this is nastier than #149 suggests

The warning text in #149 implies setting AUTO_COMPRESS=false prevents the problem. It doesn't — it only prevents it on /compress, while /summarize is the actual hot path (fires on every Stop). A user following the warning's implied guidance still gets hit.

Activity

  1. MaiHHConnect commented on Apr 21, 2026

    @MaiHHConnect

    💬 我也在做记忆系统相关的工作:CausaMem,可以来看看,说不定有能借鉴的地方 🙂

  2. rohitg00 commented on Apr 22, 2026

    @rohitg00
    Owner

    @MaiHHConnect stop spamming, please on every issues.

  3. rohitg00 commented on Apr 26, 2026

    @rohitg00
    Owner

    Closing this — fixes from your repro all landed in v0.9.x:

    1. noop default instead of agent-sdk fallback when no provider key is set (src/config.ts). Loud stderr warning explains the trade-off and how to opt in.
    2. Stop hook short-circuits in SDK child context via AGENTMEMORY_SDK_CHILD=1 env marker + entrypoint=sdk-ts check (src/hooks/stop.ts).
    3. agent-sdk provider sets the env marker before spawning query() and restores on finally (src/providers/agent-sdk.ts).
    4. FALLBACK_PROVIDERS=agent-sdk is filtered out unless AGENTMEMORY_ALLOW_AGENT_SDK=true (src/config.ts).

    Regression tests in test/stop-hook-recursion-guard.test.ts cover the env-marker, payload-shape, and provider-gate cases.

    Thanks @josephwylie for the painstaking writeup — the file:line pointers and the impact-vs-#149 comparison were exactly what we needed to ship the fix.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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