Skip to content

feat(agent): stable child-scoped provider identity for RLM subagents, side questions, and auxiliary requests #23

Description

@rynfar

Part of #22 (Meridian reliability umbrella). P0 — the biggest single lever on prompt-cache churn.

Problem

Anthropic-protocol providers have exactly one session-identity channel: metadata.user_id, set only in packages/ai/src/providers/anthropic.ts (buildParams). The Meridian extension fills it from ctx.sessionManager.getSessionId() in before_provider_request. That identity is wrong or missing in four cases:

  1. Inline RLM children stamp the parent's key. _createInlineRlmSubagentRuntime (packages/coding-agent/src/core/agent-session.ts) constructs the child Agent with onPayload: this.agent.onPayload — the root closure from packages/coding-agent/src/core/sdk.ts bound to the root extensionRunnerRef and therefore the root session manager. Every inline child request carries the parent's metadata.user_id. Parent + N children interleaving on one proxy session key means the incoming history never matches what the key last saw, forcing a full fresh-session replay (guaranteed prompt-cache miss, and Meridian's replay drops assistant tool_use blocks — Fresh replay drops every assistant tool_use block, so a thinking-plus-tool-call turn becomes an empty turn rynfar/meridian#888 — losing child task context). Daemon-mode children get their own runner and behave correctly; behavior diverging by mode is itself a bug.
  2. Identity mutates mid-conversation. SessionManager.sessionId changes on fork/branch/load (session-manager.ts newSession, createBranchedSession), so a continuing conversation shows up at the proxy under a new key with old history → lineage=new divergence mid-session (observed in telemetry: lineage=new session=new msgCount=48).
  3. Side questions reuse the parent's key with a forked message array (packages/coding-agent/src/core/side-question.ts reuses parent.onPayload and parent.sessionId) → same key, divergent prefix → churn.
  4. Compaction, branch summarization, and refinement are unkeyed. compaction/compaction.ts, compaction/branch-summarization.ts, and refinement/refinement.ts call completeSimple directly with no onPayload/metadata → they reach the proxy with no session identity at all.

Proposed shape

  • Add a first-class, immutable per-AgentSession identity (survives fork/branch/load; distinct per RLM child and per side question, e.g. <rootId>, <rootId>/child:<childId>, <rootId>/side:<n>), exposed on AgentOptions/AgentLoopConfig and to extension contexts, rather than deriving identity from the mutable SessionManager.sessionId.
  • Make inline RLM children run their payload hooks through the child's extension runner (pass a child-scoped extensionRunnerRef instead of copying the parent's onPayload/onResponse), converging inline and daemon behavior.
  • Thread identity/onPayload through the four unkeyed completeSimple call sites.
  • Keep the extension contract additive: metadata.user_id remains a JSON envelope; a parent linkage field (e.g. parent_session_id) enables proxy-side parent→child cancellation later (Meridian reliability: make concurrent RLM subagents production-safe over Claude Max #22 sequencing).

Acceptance

  • With the Meridian extension active: a parent with 3 concurrent inline RLM children produces 4 distinct stable metadata.user_id values, unchanged across a branch/fork operation, across every request each agent makes (including compaction and side questions carrying their owner's identity or a derived sub-identity — never a sibling's).
  • Focused tests at the seams: inline child runner scoping, identity stability across createBranchedSession, keyed completeSimple call sites.

Activity

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

    pkg:aiAffects packages/aipkg:coding-agentAffects packages/coding-agent

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions