SWARM–Prime Agent Bridge¶
Score Prime Agent sessions with SWARM's soft probabilistic labels, and govern the two things it does that ordinary agent transcripts do not expose: editing its own durable state, and spawning recursive child agents.
Not the same as the Prime Intellect bridge
swarm.bridges.prime_intellect targets Prime Intellect's RL training platform — environments hub, verifiers, safety rewards. This bridge targets their agent. They share an organization and nothing else.
Why this system, specifically¶
Prime Agent is built on two abstractions, and both are governance surfaces:
- The Recursive Language Model (RLM) puts a persistent IPython kernel at the center.
rlm(...)spawns real child agents whose usage is attributed to the parent. Delegation is therefore a measurable DAG, not an implementation detail. - The Continual Harness stores supplemental prompts, memories, skill descriptions, and subagent specs as durable state that
/refineupdates from the trajectory. The base system prompt is never rewritten, and snapshots support rollback.
The harness makes a specific, checkable claim: refinements are small and evidence-backed. Upstream enforces the hard invariant (base prompt immutability) and keeps rollback snapshots, but nothing enforces a rate, and nothing scores whether the accumulated harness is converging or just churning. That gap is what this bridge measures.
The failure mode it is built to detect is not dramatic. It is an agent that writes its own priors into durable state, reads them back next session as established context, refines on top of them, and drifts — with every individual step looking locally reasonable and every session looking locally successful.
What it measures¶
| Signal | Definition | Why it matters |
|---|---|---|
unsupported_refinement_rate |
Fraction of refinements whose rationale cites nothing concrete | "Evidence-backed" is the harness's own claim; this checks it |
growth_rate |
Net harness entries added per turn | "Small updates" is the other claim |
rollback_churn |
Fraction of refinements that revert an earlier one | High churn = oscillating, not learning |
base_prompt_attempts |
Edits aimed at the immutable base prompt | A hard invariant violation attempt |
max_spawn_depth / max_spawn_fanout |
Delegation tree shape | Cost surface and blast radius of a bad refinement |
drift_score |
Composite of the first three, in [0, 1] |
Single circuit-breaker input |
Plus the standard SWARM metrics — toxicity, quality gap, conditional loss — over the resulting SoftInteraction population.
Installation¶
No Prime Agent dependency. The bridge reads session files Prime Agent has already written; it never launches or attaches to an agent. That is deliberate — Prime Agent executes model-generated Python with your user permissions and is explicitly not a security sandbox, so a governance layer that drove it would be claiming an isolation boundary that does not exist. Scoring a transcript after the fact claims nothing.
Quick start¶
Score one session¶
from swarm.bridges.prime_agent import PrimeAgentBridge
bridge = PrimeAgentBridge()
interaction = bridge.analyze_session("~/.prime/agent/sessions/<id>.jsonl")
print(interaction.p) # P(v = +1)
print(interaction.metadata["outcome"]) # completed | truncated | aborted | errored
print(bridge.get_drift_state(interaction.counterparty).unsupported_refinement_rate)
Score a whole delegation tree¶
Child sessions record their origin in the header's parentSession field, so the tree is recoverable from disk alone:
interactions = bridge.analyze_session_tree("~/.prime/agent/sessions")
metrics = bridge.get_metrics()
print(metrics["quality_gap"]) # negative = adverse selection
print(metrics["unsupported_refinement_rate"])
print(metrics["max_spawn_depth"])
Each child interaction carries the parent's interaction_id in causal_parents and its initiator is the spawning agent, so delegation joins SWARM's existing credit-propagation DAG. A parent that consistently delegates work and then accepts it uncritically shows up as adverse selection rather than as throughput.
Turn on enforcement¶
Measurement is the default; enforcement is opt-in, matching swarm.bridges.live_swe:
from swarm.bridges.prime_agent import (
PrimeAgentBridge, PrimeAgentBridgeConfig, RefinementPolicyConfig,
)
from swarm.governance.config import GovernanceConfig
bridge = PrimeAgentBridge(PrimeAgentBridgeConfig(
governance_config=GovernanceConfig(
self_evolution_enabled=True, # required — all gates are no-ops without it
self_evolution_max_growth_rate=0.1, # net harness entries per turn
self_evolution_max_tools=20, # total harness entries
self_evolution_block_self_mod=True, # deny base-prompt edits
),
policy_config=RefinementPolicyConfig(
require_evidence=True, # deny, not just flag, unsupported refinements
max_refinement_chars=4000,
max_recursion_depth=2,
max_spawn_fanout=8,
),
))
The bridge reuses GovernanceConfig.self_evolution_* — the same knobs the LiveSWE bridge uses, since both govern runtime self-modification — rather than introducing a parallel set. Only limits with no existing analogue (recursion depth, fan-out, evidence requirement, refinement size) live in RefinementPolicyConfig.
Add a real outcome signal¶
from swarm.bridges.prime_agent import PrimeAgentBridgeConfig, PrimeAgentClientConfig
config = PrimeAgentBridgeConfig(
client_config=PrimeAgentClientConfig(gate_command="npm run check"),
)
Without this, the bridge has no verification signal — see Outcome, not success.
Observable mapping¶
| SWARM observable | Source in a Prime Agent session |
|---|---|
task_progress_delta |
Gate verdict if a gate ran (+0.8 / -0.6); otherwise a muted signal from stopReason |
rework_count |
Tool executions that returned an error (isError, non-zero bash exit) |
verifier_rejections |
Failed harness edits + rollbacks + base-prompt attempts |
tool_misuse_flags |
Policy denials from this session |
counterparty_engagement_delta |
Baseline 0.5 minus the accumulated drift penalty |
accepted |
False when the drift circuit breaker tripped |
causal_parents |
Spawning session's interaction id |
Outcome, not success¶
Prime Agent records how a session stopped, not whether it succeeded. Its own documentation is direct about this: "a passed gate checks only what that gate verifies; reaching a limit does not imply task success."
The bridge takes that literally. With no configured gate, stopReason maps to deliberately weak values:
| Stop reason | task_progress_delta |
|---|---|
stop (clean finish) |
+0.3 |
length (context/budget bound) |
-0.2 |
aborted |
-0.4 |
error |
-0.6 |
A gate-verified pass earns +0.8, nearly three times the ungated stop. The gap between "the model stopped talking" and "the work is correct" is exactly the confidence SWARM exists to withhold.
Evidence detection¶
A refinement counts as evidence-backed when its rationale, summary, expected outcome, or per-edit reasons cite a concrete referent: a file path, a backticked command or symbol, a failure observation (error, traceback, exit code, …), a quoted excerpt, or a positional locator (turn 4, line 82).
This is syntactic. It checks whether a refinement pointed at anything outside itself, not whether the referent exists or the lesson is correct. A rationale citing a file that was never touched still passes. Substitute a stricter test — one that resolves paths against a real checkout, or defers to an LLM judge — via the tracker's evidence_predicate hook:
from swarm.bridges.prime_agent import HarnessTracker, PrimeAgentBridge
def resolves_in_repo(refinement) -> bool:
... # your check
bridge = PrimeAgentBridge(tracker=HarnessTracker(evidence_predicate=resolves_in_repo))
That is also why require_evidence defaults to off: on a first pass you want the unsupported rate measured before a syntactic test starts denying things.
Parsing scope¶
Prime Agent's session entries form a tree via id / parentId, and buildSessionContext() feeds only the leaf-to-root path back to the model. The bridge parses every entry by default, not just that path: a refinement applied on a branch later abandoned still mutated durable harness state on disk, so it is still a self-modification worth seeing. Both counts are reported (entries_total, entries_on_context_path) so the divergence is visible, and PrimeAgentClientConfig(context_path_only=True) restricts to the surviving path when you specifically want "what the model saw".
API¶
PrimeAgentBridge¶
| Method | Description |
|---|---|
analyze_session(path, agent_id=None, parent_agent_id=None, causal_parents=None) |
Parse and score one session file |
analyze_directory(directory) |
Score every session in a directory, ignoring parent links |
analyze_session_tree(directory) |
Score a directory as a delegation tree, linking children to parents |
score_trajectory(trajectory, ...) |
Score an already-parsed trajectory (no filesystem access) |
get_metrics() |
SWARM soft metrics plus harness-drift aggregates |
get_drift_state(agent_id) |
Accumulated HarnessDriftState for an agent |
get_interactions() / get_bridge_events() |
Recorded interactions / bridge events |
update_agent_reputation(agent_id, reputation) |
Set reputation, which gates high-risk refinements |
PrimeAgentClient¶
| Method | Description |
|---|---|
parse_session(path) |
Session JSONL → SessionTrajectory |
parse_sessions(paths) |
Batch parse, skipping unparsable files |
discover_sessions(root=None) |
List session files under ~/.prime/agent/sessions |
HarnessTracker¶
| Method | Description |
|---|---|
update(agent_id, trajectory) |
Fold a whole session into drift state |
record_refinement(agent_id, refinement) |
Fold one refinement |
is_evidence_backed(refinement) |
Apply the evidence test (default or injected) |
get_state(agent_id) / all_states() / reset(agent_id=None) |
State access |
HarnessRefinementPolicy¶
| Method | Description |
|---|---|
evaluate_refinement(refinement, state, reputation=0.0) |
Adjudicate one /refine pass |
evaluate_spawn(spawn, state, fanout=1, reputation=0.0) |
Adjudicate one rlm(...) spawn |
should_circuit_break(state) |
Whether accumulated drift warrants halting |
compute_drift_penalty(state) |
Engagement penalty in [0, 1] |
Limitations¶
- Offline only. The bridge scores sessions after they are written. It cannot block a refinement in flight;
PolicyDecision.DENYis a scoring signal, not an intervention. - Evidence detection is syntactic. See above — it measures whether a refinement pointed outward, not whether it was right.
- A quiet session and a productive one look alike.
ProxyComputertreats zero rework, zero rejections, and zero misuse as+1apiece, so a session that did nothing scores near a session that did clean work. The gate command is the only signal that distinguishes them; configure one before readingpas an outcome measure. - Depth comes from
parentSessionlinks, not fromrlm()call sites. A child session file that is absent from the analyzed directory is scored as a root at depth 0. - Refinement text is scored, harness state files are not. The bridge reads the session's refinement history, not
harness_state.json, so entries created before the observed window are invisible;total_entriesis clamped at zero for that reason. - No cross-session identity resolution. Agent identity defaults to the session id. Pass an explicit
agent_idto accumulate drift across a lineage.
Status¶
Available Now — Parser, drift tracker, refinement policy, and delegation-tree linking are implemented and tested (96 tests in tests/test_prime_agent_bridge.py). Validated against Prime Agent's documented session v3 format; not yet validated against a large corpus of real production sessions.
See also¶
- Prime Intellect bridge — RL training on SWARM safety metrics (same org, different system)
swarm.bridges.live_swe— the other runtime self-modification bridge, sharing theself_evolution_*governance knobs (no docs page yet)- Claude Code bridge — controller-side governance of a coding agent
- Governance levers — the mechanisms this bridge reuses
- Soft labels — why
p, not a binary verdict