SLOPSHOPPER

ruflo-agntcy

AGNTCY/Outshift Internet-of-Cognition runtime integration for Ruflo — SLIM secure transport for cross-host agent coordination, CASA intent-scoped tool…

newcommand
★ 74,184v0.2.3MITupdated 2026-10-07ruvnet/ruflo/plugins/ruflo-agntcy
A shopper browsing a rack in a slop shop
Preview · a replayed session in a sandbox
claude · ~/work/app · ruflo-agntcy
› fix the failing auth test and add an audit log call ⏺ Read(src/auth.ts) ⎿ Read 6 lines ⏺ Update(src/auth.ts) ⎿ Added 2 lines, removed 1 line ⏺ Bash(bun test) ⎿ 3 pass, 1 fail ● Done. refresh now rejects expired claims and logs an audit event. ✻ Worked for 42s · done 4:20 PM › /agntcy-mod ⎿ ruflo-agntcy: /agntcy-mod status ⎿ ruflo-agntcy: /agntcy-mod config ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── › ? for shortcuts
README

ruflo-agntcy

AGNTCY/Outshift Internet-of-Cognition (IOC) runtime integration for Ruflo — the "AGNTCY-identified, SLIM-transported, CASA-authorized" leg of the clean-positioning stack described in ADR-380.

AGNTCY and Outshift define the agent network. MetaHarness builds and evolves the agents. RuFlo executes and coordinates them. Meta LLM governs inference, cost, tenancy, and safety. RuVector supplies local memory and semantic state.

Status: optional, removable augmentation

This plugin follows the ADR-150 precedent, not the ADR-321 hard-dependency exception (see ADR-380 §1). AGNTCY/SLIM/CASA/IOC are early-stage, externally governed protocols (Cisco Outshift-led, Linux Foundation) this project does not control. Concretely:

  • Every AGNTCY-facing package this plugin references (@claude-flow/agntcy and/or a Rust ruflo-agntcy crate) MUST live in optionalDependencies, never in dependencies.
  • Ruflo MUST remain fully operational with every AGNTCY package removed — npm ls --without @claude-flow/agntcy (or equivalent) must still produce a working CLI.
  • Every code path that touches AGNTCY infrastructure MUST catch MODULE_NOT_FOUND / connection-refused and fall back to today's local transport and existing tool-authorization model.
  • This MUST pass a "works without AGNTCY installed" smoke test, mirrored on ADR-150's CI-gated architectural-constraint pattern.

Upstream package status — corrected, and now live

This section originally claimed no AGNTCY/SLIM/Outshift npm package existed under any plausible name. That was wrong — the original check only tried guessed, scoped names and got 404s. The real SLIM package is @agntcy/slim-bindings, and as of 2026-07-31 it is genuinely live-connectable (pinned to the confirmed-working 2.0.0-alpha.5; see ADR-380's own "Update" sections for the full history, including two real upstream bugs found, filed, and resolved along the way). detectAgntcyRuntime() needed zero code changes for this — its existing graceful-degradation design already handled both "package resolves" and "package missing" correctly; only the pinned dependency version changed.

The remaining honest gaps: AGNTCY Identity has no JS/TS SDK (genuinely Go-only, verified), and this plugin's CASA policy compiler is a real, tested implementation but enforcement still lives in the runtime layer per ADR-380 §3, never in this compiler. Nothing here fakes a success it hasn't earned.

What's Included (per ADR-380)

AreaADR-380 sectionScope
SLIM transport§2Opt-in transport switch (ruflo transport use slim) for cross-host/cross-tenant swarm and hive-mind coordination. Local in-process transport stays the default for single-host swarms — zero behavior change, zero new operational cost in the common case.
CASA authorization§3Deterministic, deny-by-default enforcement of a compiled intent envelope (allow/deny/budget_usd/expires_at) in front of every MCP tool call and Agent/Task dispatch. Enforcement never asks an LLM whether an action is permitted — that would defeat the entire point of the gate.
IOC Layer 9 coordination events§4Optional semantic coordination events (Semantic Information Exchange, Cognition and Interoperability, Semantic Alignment Broadcast, Team Formation via Polling) layered on top of — never replacing — Ruflo's own hive-mind_broadcast / hive-mind_consensus / coordination_consensus orchestration.
AGNTCY identity/observability§5OTel span attributes (agent.identity, agent.capability, agent.intent, agent.parent, coordination.episode, authorization.decision, model.route, memory.provenance, evaluation.score, receipt.hash) wired through the existing ruflo-observability plugin rather than a second tracing pipeline. Ruflo owns the two runtime-only attributes (coordination.episode, authorization.decision); the rest are emitted build-time by companion metaharness ADR-240.

Companion ADR

This plugin's runtime half is paired with metaharness repo ADR-240 (build-time half — AGNTCY identity generation, OASF export, semantic observability at manifest time). Neither ADR is complete without the other; see ADR-380's "Companion" note.

Requires

  • ruflo-core plugin (provides the MCP server this plugin's tools/commands attach to)
  • ruflo-observability plugin (span/trace sink for §5's AGNTCY OTel attributes)
  • Optionally, once upstream stabilizes: @claude-flow/agntcy (TS) and/or a Rust ruflo-agntcy crate targeting SLIM (§6)

Architecture Decisions

References

As a mod

AGNTCY also ships as a function-hook mod (ADR-445 pattern; hooks in hooks/, loaded with the plugin). No network, no process, no model call.

  • No guard: this plugin owns no tool a guard could usefully screen, so the mod only reports status.
  • /agntcy-mod: answered locally. /agntcy-mod status, /agntcy-mod config.
  • Status file: .claude-flow/agntcy-mod/status.json (version, updatedMs, counters), written at session start; the console reads it.

Test: claude plugin validate plugins/ruflo-agntcy, claude plugin test plugins/ruflo-agntcy (its vitest suites are *.spec.ts, so the kit collects only tests/mod.test.ts), and bash plugins/ruflo-agntcy/scripts/smoke.sh.

Source 4 files
hooks/register.ts 45 lines
1import type { Hook, Register } from 'claude-code'
2
3import { answer } from './command'
4import { readOptions } from './options'
5import { newStats, STATUS_PATH, statusText, type Stats } from './status'
6import type { ModOptions } from './options'
7
8type Dollar = Parameters<Hook<'session.start'>>[0]
9
10/** Everything one session of the mod keeps: its settings, counters and the project root. */
11type Session = { readonly opts: ModOptions; readonly stats: Stats; root?: string }
12
13async function flush($: Dollar, s: Session): Promise<void> {
14  if (s.root === undefined) return
15  try {
16    await $.fs.write(`${s.root}/${STATUS_PATH}`, statusText(s.stats, s.opts, await $.clock.now()))
17  } catch {
18    /* the status file is a courtesy */
19  }
20}
21
22/**
23 * AGNTCY as a mod (ADR-445 pattern): `/agntcy-mod`, and a status file the console reads.
24 * No network, no process: only the hooks API.
25 */
26export const register: Register = (on, options) => {
27  const s: Session = { opts: readOptions(options), stats: newStats() }
28
29  on('session.start', async ($, e, next) => {
30    const result = await next(e)
31    s.root = (await $.session.root()) as string | undefined
32    s.stats.startedMs = await $.clock.now()
33    try {
34      await $.command.register({ name: 'agntcy-mod', description: 'AGNTCY mod: status, config' })
35    } catch {
36      /* a name taken by another plugin must not stop the mod */
37    }
38    await flush($, s)
39    return result
40  })
41
42  /** `/agntcy-mod` (a markdown command of the plugin cannot be answered by a hook, so the mod owns this name). */
43  on('command.run', { command: 'agntcy-mod' }, async (_$, e) => ({ text: answer(typeof e.args === 'string' ? e.args : '', { opts: s.opts, stats: s.stats }) }))
44}
45
hooks/command.ts 22 lines
1import type { ModOptions } from './options'
2import type { Stats } from './status'
3
4/** `/agntcy-mod` is answered locally and takes no model turn. */
5export type CommandDeps = { readonly opts: ModOptions; readonly stats: Stats }
6
7const HELP = ['/agntcy-mod status', '/agntcy-mod config'].join('\n')
8
9export function answer(args: string, deps: CommandDeps): string {
10  const [verb = ''] = args.trim().split(/\s+/)
11
12  if (verb === '' || verb === 'help') return HELP
13
14  if (verb === 'status') {
15    return `AGNTCY mod active · no tool is guarded`
16  }
17
18  if (verb === 'config') return 'AGNTCY/SLIM/CASA integration is scaffolding (ADR-380): no upstream package is installed, transport is local, CASA enforcement is off. This mod makes no network call to AGNTCY infrastructure.'
19
20  return `Unknown: ${verb}\n${HELP}`
21}
22
hooks/options.ts 19 lines
1import type { PluginOptions } from 'claude-code'
2
3/** The plugin's `userConfig`, validated: a bad value is the default (guard absent). */
4export type ModOptions = {
5  readonly guard: boolean
6}
7
8// BEGIN SHARED FLAG (generated by scripts/sync-mod-screen.mjs; edit plugins/ruflo-agentdb/hooks/options.ts)
9export const flag = (value: unknown, fallback: boolean) =>
10  value === true || value === 'true' || value === 'on' ? true : value === false || value === 'false' || value === 'off' ? false : fallback
11// END SHARED FLAG
12
13export function readOptions(options: PluginOptions | undefined): ModOptions {
14  const o = options ?? {}
15  return {
16    guard: flag(o.guard, false),
17  }
18}
19
hooks/status.ts 12 lines
1/** Counters the mod keeps for the session and writes to `.claude-flow/agntcy-mod/status.json` for the console. */
2export type Stats = { calls: number; blocked: number; startedMs: number }
3
4export const newStats = (): Stats => ({ calls: 0, blocked: 0, startedMs: 0 })
5
6export const STATUS_PATH = '.claude-flow/agntcy-mod/status.json'
7
8/** The file's text; `version` lets the console refuse a shape it does not know. */
9export function statusText(stats: Stats, mode: { guard: boolean }, nowMs: number): string {
10  return `${JSON.stringify({ version: 1, updatedMs: nowMs, mod: 'agntcy', guard: mode.guard, ...stats }, null, 2)}\n`
11}
12