@etazio/agent-sdk 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,11 @@
1
+ # Changelog
2
+
3
+ ## 0.1.0 – 2026-09-16
4
+
5
+ Erste veröffentlichte Fassung als `@etazio/agent-sdk` (vorher intern `@spacelane/huddle-agent`).
6
+
7
+ - `connect()` / `EtazioAgent` für `agent-protocol/v1`: Handshake, Ack-Auswertung, Reconnect mit derselben `participantId`
8
+ - Typisierte Aufrufe: `say`, `goTo`, `sit`/`stand`, `setState`, `action`, `setTv`, `react`/`wave`, `meetingToken`, `boardOp`
9
+ - Sprachschleife `attachVoice()` mit VAD, Barge-in und OpenAI-Adaptern für STT/TTS (`@livekit/rtc-node` optional)
10
+ - Umbenannt: `HuddleAgent` → `EtazioAgent`, `HuddleAgentError` → `EtazioAgentError`, `HuddleVoice` → `EtazioVoice`
11
+ - Neu: `SDK_VERSION`
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 spacelane – Marcel Wiskow
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,145 @@
1
+ # @etazio/agent-sdk
2
+
3
+ Offizielles SDK für KI-Agenten im [Etazio](https://etazio.de)-Office.
4
+
5
+ Ein Agent ist in Etazio ein Teilnehmer wie jeder Mensch: Er steht im Raum, trägt ein KI-Zeichen,
6
+ läuft über dieselben Wege, schreibt im Chat und kann – wenn man zu ihm hingeht und `E` drückt –
7
+ auch sprechen. Das SDK übernimmt Handshake, Fehlerauswertung und Reconnect; alles Weitere sind
8
+ typisierte Aufrufe.
9
+
10
+ ```bash
11
+ npm install @etazio/agent-sdk
12
+ ```
13
+
14
+ Voraussetzung: Node.js ≥ 20.19. Das Paket ist ESM mit eigenen TypeScript-Typen.
15
+
16
+ ## Agent in 15 Zeilen
17
+
18
+ ```ts
19
+ import { connect } from '@etazio/agent-sdk'
20
+
21
+ const agent = await connect({
22
+ url: process.env.ETAZIO_URL!, // z. B. https://app.etazio.de
23
+ token: process.env.ETAZIO_AGENT_TOKEN!, // hag_…
24
+ client: { name: 'hello-world', version: '1.0.0' },
25
+ })
26
+
27
+ console.log('Scopes:', agent.scopes.join(', '))
28
+ console.log('Im Office:', agent.world?.participants.map((p) => p.name).join(', '))
29
+
30
+ // Jemand spricht den Agenten an: @Erwähnung, Direktnachricht, Hingehen + E, Zone oder Arbeitsplatz
31
+ agent.on('address', async (a) => {
32
+ await agent.say(a.conv, `Hallo ${a.from.displayName}, ich bin da.`)
33
+ })
34
+ ```
35
+
36
+ ```bash
37
+ ETAZIO_URL=https://app.etazio.de ETAZIO_AGENT_TOKEN=hag_… npx tsx agent.ts
38
+ ```
39
+
40
+ ## Token besorgen
41
+
42
+ In Etazio auf der Agenten-Seite des Workspace **Agent anlegen**, Betrieb „Auf eurem eigenen Server“ wählen und die
43
+ nötigen Berechtigungen (Scopes) setzen. Das Token (`hag_…`) wird **nur einmal** angezeigt und kann
44
+ jederzeit rotiert oder per Kill-Switch gesperrt werden. Behandle es wie ein Passwort.
45
+
46
+ ## Aufrufe
47
+
48
+ | Aufruf | Wofür | Scope |
49
+ |---|---|---|
50
+ | `connect(options)` / `new EtazioAgent(options).connect()` | Verbinden und Handshake | `office.read` (+ `world.presence`) |
51
+ | `agent.refresh()` | Frischer Überblick über Personen, Zonen, Objekte | wie oben |
52
+ | `agent.say(conv, text, parentId?)` | Nachricht im Chat, Thread oder DM | `chat.write` |
53
+ | `agent.typing(conv)` / `agent.typingWhile(conv, work)` | Tipp-Anzeige | `chat.write` |
54
+ | `agent.ask(conv, text, block)` | Nachricht mit Bestätigungsknöpfen | `chat.write` |
55
+ | `agent.goTo({ x, z } \| { zoneId } \| { participantId })` | Hingehen (Wegfindung macht der Server) | `world.move` |
56
+ | `agent.sit(seatId)` / `agent.stand()` | Hinsetzen, Aufstehen | `world.seat` |
57
+ | `agent.setState(placementId, state)` | Objektzustand setzen | `object.state` |
58
+ | `agent.action(placementId, action, data?)` | Objektaktion auslösen | `object.action` |
59
+ | `agent.setTv(placementId, tvState)` | Fernseher steuern | `tv.control` |
60
+ | `agent.react(glyph)` / `agent.wave()` | Reaktion, Winken | `world.react` |
61
+ | `agent.meetingToken(target?)` | LiveKit-Token für Audio | `meeting.audio` |
62
+ | `agent.boardOp(envelope)` | Whiteboard bearbeiten | `board.write` |
63
+ | `agent.can(scope)` | Prüfen, ob das Token einen Scope hat | – |
64
+ | `agent.close()` | Verbindung beenden | – |
65
+
66
+ Nützliche Eigenschaften: `agent.hello`, `agent.world`, `agent.scopes`, `agent.participantId`, `agent.connected`.
67
+
68
+ ## Ereignisse
69
+
70
+ ```ts
71
+ agent.on('ready', (hello) => { /* nach jedem (Re-)Connect */ })
72
+ agent.on('address', (a) => { /* Ansprache */ })
73
+ agent.on('chat', (m) => { /* jede sichtbare Chatnachricht (chat.read) */ })
74
+ agent.on('participantJoined', (p) => {})
75
+ agent.on('zone', (z) => {})
76
+ agent.on('throttled', (t) => { /* Budget überschritten – langsamer werden */ })
77
+ agent.on('revoked', (r) => { /* Token gesperrt – kein Reconnect */ })
78
+ ```
79
+
80
+ Außerdem: `voice`, `participantLeft`, `seat`, `objectState`, `objectAction`, `emote`, `disconnect`, `error`.
81
+ Welche Ereignisse ankommen, bestimmen die Scopes des Tokens.
82
+
83
+ ## Fehler
84
+
85
+ Jede Ablehnung wird zu einem `EtazioAgentError` mit `code` – etwa `forbidden` (Scope fehlt),
86
+ `rate_limited`, `not_found`, `timeout`, `unavailable`.
87
+
88
+ ```ts
89
+ import { EtazioAgentError } from '@etazio/agent-sdk'
90
+
91
+ try {
92
+ await agent.goTo({ zoneId: 'meeting-1' })
93
+ } catch (err) {
94
+ if (err instanceof EtazioAgentError && err.code === 'forbidden') {
95
+ console.error('Dem Token fehlt world.move')
96
+ }
97
+ }
98
+ ```
99
+
100
+ ## Reconnect
101
+
102
+ `autoReconnect` ist standardmäßig an. Nach jedem Wiederverbinden meldet sich das SDK mit derselben
103
+ `participantId` zurück, damit Position und Sitzplatz erhalten bleiben, und feuert erneut `ready`.
104
+ Nach `revoked` (Token rotiert, Agent deaktiviert, Kill-Switch) verbindet es sich bewusst nicht neu.
105
+
106
+ ## Sprechen
107
+
108
+ Mit `attachVoice()` hört der Agent zu, sobald ein Mensch neben ihm `E` drückt, und antwortet
109
+ gesprochen – mit Untertitel im Chat. Erkennung (STT) und Stimme (TTS) laufen bei dir; für OpenAI
110
+ liegen fertige Adapter bei. Eigene Anbieter implementieren `SttAdapter` bzw. `TtsAdapter`.
111
+
112
+ ```bash
113
+ npm install @livekit/rtc-node # optional, native Bindings (glibc – kein Alpine)
114
+ ```
115
+
116
+ ```ts
117
+ import { attachVoice, connect, openAiStt, openAiTts } from '@etazio/agent-sdk'
118
+
119
+ const agent = await connect({
120
+ url: process.env.ETAZIO_URL!,
121
+ token: process.env.ETAZIO_AGENT_TOKEN!,
122
+ capabilities: ['chat', 'voice'],
123
+ })
124
+ const speech = { apiKey: process.env.OPENAI_API_KEY!, voice: 'alloy', language: 'de' }
125
+
126
+ attachVoice(agent, {
127
+ stt: openAiStt(speech),
128
+ tts: openAiTts(speech),
129
+ async reply({ text, signal }) {
130
+ // eigenes Modell hier – `signal` weiterreichen, damit Dazwischenreden die Anfrage abbricht
131
+ const antwort = await meinModell(text, { signal })
132
+ return antwort
133
+ },
134
+ })
135
+ ```
136
+
137
+ Nötige Scopes: `meeting.audio`, `voice.listen`, `voice.speak` (plus `chat.write` für Untertitel).
138
+ Der Server entscheidet, wann ein Agent zuhören darf – nur im offenen Gespräch und nur dem Menschen,
139
+ der es eröffnet hat. Was der Mensch sagt, wird nirgends gespeichert.
140
+
141
+ Stellschrauben: `silenceMs` (Standard 700) und `threshold` (0.02).
142
+
143
+ ## Lizenz
144
+
145
+ MIT