@etazio/agent-sdk
v0.2.0
Published
Offizielles SDK für KI-Agenten im Etazio-Office – Handshake, Reconnect, typisierte Aufrufe und Sprachschleife (agent-protocol/v1.1).
Maintainers
Readme
@etazio/agent-sdk
Offizielles SDK für KI-Agenten im Etazio-Office.
Ein Agent ist in Etazio ein Teilnehmer wie jeder Mensch: Er steht im Raum, trägt ein KI-Zeichen,
läuft über dieselben Wege, schreibt im Chat und kann – wenn man zu ihm hingeht und E drückt –
auch sprechen. Das SDK übernimmt Handshake, Fehlerauswertung und Reconnect; alles Weitere sind
typisierte Aufrufe.
npm install @etazio/agent-sdkVoraussetzung: Node.js ≥ 20.19. Das Paket ist ESM mit eigenen TypeScript-Typen.
Agent in 15 Zeilen
import { connect } from '@etazio/agent-sdk'
const agent = await connect({
url: process.env.ETAZIO_URL!, // z. B. https://app.etazio.de
token: process.env.ETAZIO_AGENT_TOKEN!, // hag_…
client: { name: 'hello-world', version: '1.0.0' },
})
console.log('Scopes:', agent.scopes.join(', '))
console.log('Im Office:', agent.world?.participants.map((p) => p.name).join(', '))
// Jemand spricht den Agenten an: @Erwähnung, Direktnachricht, Hingehen + E, Zone oder Arbeitsplatz
agent.on('address', async (a) => {
await agent.say(a.conv, `Hallo ${a.from.displayName}, ich bin da.`)
})ETAZIO_URL=https://app.etazio.de ETAZIO_AGENT_TOKEN=hag_… npx tsx agent.tsToken besorgen
In Etazio auf der Agenten-Seite des Workspace Agent anlegen, Betrieb „Auf eurem eigenen Server“ wählen und die
nötigen Berechtigungen (Scopes) setzen. Das Token (hag_…) wird nur einmal angezeigt und kann
jederzeit rotiert oder per Kill-Switch gesperrt werden. Behandle es wie ein Passwort.
Aufrufe
| Aufruf | Wofür | Scope |
|---|---|---|
| connect(options) / new EtazioAgent(options).connect() | Verbinden und Handshake | office.read (+ world.presence) |
| agent.refresh() | Frischer Überblick über Personen, Zonen, Objekte | wie oben |
| agent.say(conv, text, parentId?) | Nachricht im Chat, Thread oder DM | chat.write |
| agent.typing(conv) / agent.typingWhile(conv, work) | Tipp-Anzeige | chat.write |
| agent.ask(conv, text, block) | Nachricht mit Bestätigungsknöpfen | chat.write |
| agent.goTo({ x, z } \| { zoneId } \| { participantId }) | Hingehen (Wegfindung macht der Server) | world.move |
| agent.sit(seatId) / agent.stand() | Hinsetzen, Aufstehen | world.seat |
| agent.setState(placementId, state) | Objektzustand setzen | object.state |
| agent.action(placementId, action, data?) | Objektaktion auslösen | object.action |
| agent.setTv(placementId, tvState) | Fernseher steuern | tv.control |
| agent.react(glyph) / agent.wave() | Reaktion, Winken | world.react |
| agent.meetingToken(target?) | LiveKit-Token für Audio | meeting.audio |
| agent.boardOp(envelope) | Whiteboard bearbeiten | board.write |
| agent.can(scope) | Prüfen, ob das Token einen Scope hat | – |
| agent.close() | Verbindung beenden | – |
Nützliche Eigenschaften: agent.hello, agent.world, agent.scopes, agent.participantId, agent.connected.
Ereignisse
agent.on('ready', (hello) => { /* nach jedem (Re-)Connect */ })
agent.on('address', (a) => { /* Ansprache */ })
agent.on('chat', (m) => { /* jede sichtbare Chatnachricht (chat.read) */ })
agent.on('participantJoined', (p) => {})
agent.on('zone', (z) => {})
agent.on('throttled', (t) => { /* Budget überschritten – langsamer werden */ })
agent.on('revoked', (r) => { /* Token gesperrt – kein Reconnect */ })Außerdem: voice, participantLeft, seat, objectState, objectAction, emote, disconnect, error.
Welche Ereignisse ankommen, bestimmen die Scopes des Tokens.
Fehler
Jede Ablehnung wird zu einem EtazioAgentError mit code – etwa forbidden (Scope fehlt),
rate_limited, not_found, timeout, unavailable.
import { EtazioAgentError } from '@etazio/agent-sdk'
try {
await agent.goTo({ zoneId: 'meeting-1' })
} catch (err) {
if (err instanceof EtazioAgentError && err.code === 'forbidden') {
console.error('Dem Token fehlt world.move')
}
}Reconnect
autoReconnect ist standardmäßig an. Nach jedem Wiederverbinden meldet sich das SDK mit derselben
participantId zurück, damit Position und Sitzplatz erhalten bleiben, und feuert erneut ready.
Nach revoked (Token rotiert, Agent deaktiviert, Kill-Switch) verbindet es sich bewusst nicht neu.
Sprechen
Mit attachVoice() hört der Agent zu, sobald ein Mensch neben ihm E drückt, und antwortet
gesprochen – mit Untertitel im Chat. Erkennung (STT) und Stimme (TTS) laufen bei dir; für OpenAI
liegen fertige Adapter bei. Eigene Anbieter implementieren SttAdapter bzw. TtsAdapter.
npm install @livekit/rtc-node # optional, native Bindings (glibc – kein Alpine)import { attachVoice, connect, openAiStt, openAiTts } from '@etazio/agent-sdk'
const agent = await connect({
url: process.env.ETAZIO_URL!,
token: process.env.ETAZIO_AGENT_TOKEN!,
capabilities: ['chat', 'voice'],
})
const speech = { apiKey: process.env.OPENAI_API_KEY!, voice: 'alloy', language: 'de' }
attachVoice(agent, {
stt: openAiStt(speech),
tts: openAiTts(speech),
async reply({ text, signal }) {
// eigenes Modell hier – `signal` weiterreichen, damit Dazwischenreden die Anfrage abbricht
const antwort = await meinModell(text, { signal })
return antwort
},
})Nötige Scopes: meeting.audio, voice.listen, voice.speak (plus chat.write für Untertitel).
Der Server entscheidet, wann ein Agent zuhören darf – nur im offenen Gespräch und nur dem Menschen,
der es eröffnet hat. Was der Mensch sagt, wird nirgends gespeichert.
Stellschrauben: silenceMs (Standard 700) und threshold (0.02).
Lizenz
MIT
Routinen (ab 0.2, Protokoll v1.1)
Der Server stellt fällige Routinen als eigenes Ereignis zu – nie als address:
agent.onRoutine(async (run) => {
await agent.setStatus(`${run.name}…`)
const text = `Guten Morgen! (${run.instructions})`
if (!run.dryRun) await agent.say(run.conv, text) // nur ins konfigurierte Ziel schreiben
await agent.setStatus(null)
return { status: 'done', toolCalls: [], preview: run.dryRun ? text : null }
})Der Rückgabewert wird als agent:run:report gemeldet, eine Ausnahme als failed. Details, Regeln
(Trockenlauf, Vorab-Freigaben, Bestätigungs-Karten) und Grenzen: docs/agent-protocol.md §5b.
