8gent Code.Docs
Device Kit (8DK), Preview

Quickstart

Define a device, pair it, allow one capability and call it as a tool.

Runs inside the 8gent-code repository only

@8gent/8dk is a private workspace package, merged to 8gent-code main but not yet in a release. It is not on npm, and it imports the permission engine from its sibling package, so it runs inside a checkout of main. Nothing here pairs a device with a running vessel: that needs slice 2 (#3363).

Get the code

git clone https://github.com/8gi-foundation/8gent-code.git
cd 8gent-code
bun install
bun test packages/8dk

Define a device

A desk lamp with one thing it can read:

import { defineDevice } from "@8gent/8dk";

const lamp = defineDevice(
  {
    id: "desk-lamp", name: "Desk lamp", kind: "lamp", version: "0.1.0",
    capabilities: [{ name: "read_light", kind: "sensor", description: "Is the lamp on, and how bright." }],
  },
  { read_light: () => ({ on: true, brightness: 60 }) },
);

defineDevice takes a manifest and exactly one handler per capability. It checks both and returns a frozen definition. A bad id, a missing handler or a handler for a capability that does not exist throws a ManifestError.

Connect it to the vessel side

The vessel and the device talk in JSON frames. CallCorrelator is the vessel end, serveDevice the device end. Here they are wired in one process; the /device WebSocket route that carries them between machines is slice 2.

import { CallCorrelator, parseDeviceFrame, serveDevice } from "@8gent/8dk";

const link = new CallCorrelator((frame) => deviceSide(frame));
const deviceSide = serveDevice(lamp, (frame) => {
  const parsed = parseDeviceFrame(JSON.stringify(frame));
  if (parsed) link.handle(parsed);
});

Pair, allow, call

import { DeviceRegistry, DeviceToolAdapter, deviceToolName } from "@8gent/8dk";

const registry = new DeviceRegistry();
const paired = await registry.pair(lamp.manifest, {
  showCode: (code) => console.log("device shows", code),
  consent: async (req) => ({ approved: true }), // demo only: a real prompt shows req.code and asks the person. Never approve without asking.
});

const adapter = new DeviceToolAdapter(registry, (id) => (id === "desk-lamp" ? link : undefined));
const tool = deviceToolName("desk-lamp", "read_light"); // "device__desk_lamp__read_light"

adapter.toolDefinitions();            // []: paired, but nothing allowed yet
await adapter.execute(tool, {});      // { ok: false, reason: '[8dk-deny] capability "read_light" on "desk-lamp" is not granted' }

registry.grant("desk-lamp", "read_light");
await adapter.execute(tool, {}, { agentId: "main", sessionId: "s1" });
// { ok: true, result: { on: true, brightness: 60 } }

The approval returned no grant list, so the lamp paired with nothing allowed. That is the default. The person allows a capability at pairing (grant: ["read_light"]) or later with registry.grant.

Next: The manifest.