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

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.
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:
@claude-flow/agntcy and/or a Rust ruflo-agntcy crate) MUST live in optionalDependencies, never in dependencies.npm ls --without @claude-flow/agntcy (or equivalent) must still produce a working CLI.MODULE_NOT_FOUND / connection-refused and fall back to today's local transport and existing tool-authorization model.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.
| Area | ADR-380 section | Scope |
|---|---|---|
| SLIM transport | §2 | Opt-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 | §3 | Deterministic, 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 | §4 | Optional 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 | §5 | OTel 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. |
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.
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)@claude-flow/agntcy (TS) and/or a Rust ruflo-agntcy crate targeting SLIM (§6)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.
/agntcy-mod: answered locally. /agntcy-mod status, /agntcy-mod config..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.
hooks/register.ts 45 lines1import 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}
45hooks/command.ts 22 lines1import 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}
22hooks/options.ts 19 lines1import 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}
19hooks/status.ts 12 lines1/** 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