@perkos/perkos-a2a 0.8.34 → 0.9.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/dist/types.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  /** A2A Protocol types (v0.3.0) */
2
+ export type { AckFrame, AuthErrorFrame, AuthFrame, AuthOkFrame, ChannelJoinFrame, ChatConfig, ChatDeliverFrame, ChatErrorFrame, ChatFrame, ChatIdentity, ChatMessage, ChatMessageInboundFrame, ChatReplyFrame, HistoryChunkFrame, HistoryRequestFrame, PingFrame, PongFrame, TypingFrame, } from "./chat-types.js";
2
3
  export interface AgentCard {
3
4
  name: string;
4
5
  description: string;
@@ -114,6 +115,15 @@ export interface A2APluginConfig {
114
115
  };
115
116
  relay?: RelayConfig;
116
117
  auth?: AuthConfig;
118
+ /**
119
+ * Optional PerkOS-Chat client. When `enabled`, the plugin opens a WS to
120
+ * `chat.perkos.xyz` alongside the A2A relay so the agent can participate
121
+ * in user-facing project chats and 1-on-1 DMs.
122
+ *
123
+ * Separate from the relay because chat traffic has different volume and
124
+ * rate-limit characteristics than task dispatch. See `src/chat-client.ts`.
125
+ */
126
+ chat?: import("./chat-types.js").ChatConfig;
117
127
  }
118
128
  export type RuntimeKind = "openclaw" | "hermes" | "hermes-api" | "none";
119
129
  export interface RuntimeDeliveryConfig {
@@ -129,7 +139,32 @@ export interface RuntimeDeliveryConfig {
129
139
  hermesEndpoint?: string;
130
140
  }
131
141
  /** Wire protocol for relay WebSocket messages */
132
- export type RelayMessageType = "register" | "register_ack" | "task" | "task_response" | "discover" | "discover_response" | "heartbeat" | "heartbeat_ack" | "error";
142
+ export type RelayMessageType = "register" | "register_ack" | "task" | "task_response" | "discover" | "discover_response" | "heartbeat" | "heartbeat_ack" | "webhook_event" | "error";
143
+ /**
144
+ * Payload of a `webhook_event` frame. Transport delivers these when an
145
+ * external system (GitHub, GitLab, Linear, generic) fires a webhook
146
+ * that matches one of this agent's subscriptions. Hooks-first design:
147
+ * the agent does not need to expose its own HTTP surface.
148
+ *
149
+ * `headers` contains only the small set of upstream headers that are
150
+ * useful downstream (delivery id, user-agent). `body` is the parsed
151
+ * JSON body, or `null` when the upstream payload wasn't JSON.
152
+ */
153
+ export interface WebhookEventPayload {
154
+ subscriptionId: string;
155
+ source: "github" | "gitlab" | "linear" | "generic" | string;
156
+ eventType: string;
157
+ headers: {
158
+ delivery?: string;
159
+ userAgent?: string;
160
+ };
161
+ body: unknown;
162
+ }
163
+ /** Convenience helper: narrows a RelayMessage to a webhook_event frame. */
164
+ export declare function isWebhookEvent(msg: RelayMessage): msg is RelayMessage & {
165
+ type: "webhook_event";
166
+ payload: WebhookEventPayload;
167
+ };
133
168
  export interface RelayMessage {
134
169
  type: RelayMessageType;
135
170
  /** Sender agent name */
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/dist/types.js CHANGED
@@ -1,3 +1,6 @@
1
1
  /** A2A Protocol types (v0.3.0) */
2
- export {};
2
+ /** Convenience helper: narrows a RelayMessage to a webhook_event frame. */
3
+ export function isWebhookEvent(msg) {
4
+ return msg.type === "webhook_event";
5
+ }
3
6
  //# sourceMappingURL=types.js.map
package/dist/types.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,kCAAkC"}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,kCAAkC;AA6MlC,2EAA2E;AAC3E,MAAM,UAAU,cAAc,CAC5B,GAAiB;IAEjB,OAAO,GAAG,CAAC,IAAI,KAAK,eAAe,CAAC;AACtC,CAAC"}
@@ -0,0 +1,190 @@
1
+ # PerkOS-Chat client
2
+
3
+ The A2A package ships with a `ChatClient` that connects to PerkOS-Chat
4
+ (`wss://chat.perkos.xyz/chat`) alongside the existing A2A relay. Tasks and
5
+ chat travel on separate sockets — different rate limits, different
6
+ semantics — but both use the same agent identity and the same relay API key
7
+ issued during PerkOS-Transport pairing.
8
+
9
+ Server reference: [PerkOS-Chat](https://github.com/PerkOS-xyz/PerkOS-Chat)
10
+ and `docs/protocol.md` therein.
11
+
12
+ ## Privacy model (C-hybrid)
13
+
14
+ The agent owns the canonical conversation history.
15
+
16
+ - **PerkOS cloud (Firestore)** — metadata only: title, participants,
17
+ `historyHost`, `lastMessageAt`. No bodies, no previews.
18
+ - **This client** — receives every frame addressed to the agent and appends
19
+ it to `<storeRoot>/<convId>/messages.jsonl`. The store is **the** record
20
+ of conversation content for this agent.
21
+ - **History queries** — when a user scrolls back in the MiniApp, the chat
22
+ server forwards the request to the `historyHost` agent. The client
23
+ paginates from the JSONL and responds.
24
+
25
+ Backups are the operator's responsibility. The standard advice: rsync
26
+ `~/.perkos/conversations/` somewhere durable on a schedule.
27
+
28
+ ## Wiring it in — OpenClaw
29
+
30
+ The A2A plugin already wires chat for you when you set `chat.enabled: true`
31
+ in your `openclaw.plugin.json` config:
32
+
33
+ ```json
34
+ {
35
+ "perkos-a2a": {
36
+ "config": {
37
+ "agentName": "apollo",
38
+ "relay": {
39
+ "enabled": true,
40
+ "url": "wss://transport.perkos.xyz/a2a",
41
+ "apiKey": "<your relay key>"
42
+ },
43
+ "chat": {
44
+ "enabled": true,
45
+ "url": "wss://chat.perkos.xyz/chat"
46
+ }
47
+ }
48
+ }
49
+ }
50
+ ```
51
+
52
+ `chat.apiKey` defaults to `relay.apiKey` — one key, two sockets. When an
53
+ inbound message arrives, the plugin enqueues a system event with a marker
54
+ `[PERKOS_CHAT:<convId>]`, wakes the runtime, and the LLM should call the
55
+ `perkos_chat_reply` tool to respond.
56
+
57
+ Two tools are added to the agent's tool surface:
58
+
59
+ - `perkos_chat_reply({ convId, walletAddress, text, replyTo? })`
60
+ - `perkos_chat_history({ convId, before?, limit? })`
61
+
62
+ Run `openclaw perkos-a2a status` (or hit the gateway method
63
+ `perkos-a2a.status`) to verify the chat client is connected.
64
+
65
+ ## Wiring it in — Hermes (via `perkos-a2a-agent` bridge)
66
+
67
+ The standalone bridge binary (`bin/agent.ts`, shipped as `perkos-a2a-agent`)
68
+ multiplexes A2A tasks **and** chat in one process. Run it next to Hermes
69
+ on the same VPS:
70
+
71
+ ```bash
72
+ A2A_AGENT_NAME=apollo \
73
+ A2A_RUNTIME=hermes-api \
74
+ HERMES_API_URL=http://127.0.0.1:8642 \
75
+ A2A_RELAY_URL=wss://transport.perkos.xyz/a2a \
76
+ A2A_RELAY_API_KEY=<your relay key> \
77
+ A2A_CHAT_ENABLED=true \
78
+ A2A_CHAT_URL=wss://chat.perkos.xyz/chat \
79
+ A2A_CHAT_API_KEY=<your relay key> # defaults to A2A_RELAY_API_KEY \
80
+ A2A_CHAT_REPLY_PORT=5060 \
81
+ perkos-a2a-agent
82
+ ```
83
+
84
+ Inbound flow:
85
+
86
+ 1. `chat.perkos.xyz` sends `chat_deliver` over WS to the bridge.
87
+ 2. Bridge appends to local JSONL **and** POSTs the message to Hermes at
88
+ `POST $HERMES_API_URL/v1/responses` with a `[PERKOS_CHAT:<convId>]`
89
+ marker and explicit instructions: *"reply via POST to
90
+ http://127.0.0.1:$A2A_CHAT_REPLY_PORT/chat/reply"*.
91
+ 3. Hermes (via a tool you configure on the Hermes side — `http_request`
92
+ or a custom plugin) POSTs the reply to:
93
+
94
+ ```
95
+ POST http://127.0.0.1:5060/chat/reply
96
+ { "convId": "...", "walletAddress": "0x...", "text": "...", "replyTo": "..." }
97
+ ```
98
+
99
+ 4. The bridge calls `chatClient.sendReply(...)` which appends to the
100
+ JSONL log and broadcasts to other conversation participants via
101
+ `chat.perkos.xyz`.
102
+
103
+ The reply listener is bound to `127.0.0.1` only — never expose it to the
104
+ public internet. The relay API key never leaves the bridge.
105
+
106
+ If Hermes is offline or returns 503, the reply is still appended to the
107
+ local JSONL store, so reconciliation on reconnect is possible later.
108
+
109
+ ## Wiring it in — programmatic
110
+
111
+ If you're not in an OpenClaw plugin context (e.g. a Hermes bridge), use the
112
+ class directly:
113
+
114
+ ```ts
115
+ import { ChatClient } from "@perkos/perkos-a2a";
116
+
117
+ const chat = new ChatClient({
118
+ agentName: "apollo",
119
+ config: {
120
+ enabled: true,
121
+ url: "wss://chat.perkos.xyz/chat",
122
+ apiKey: process.env.PERKOS_RELAY_API_KEY!,
123
+ // storeRoot defaults to ~/.perkos/conversations
124
+ },
125
+ handlers: {
126
+ onChatDeliver: async (frame) => {
127
+ // Hand the message to the agent runtime (Hermes /v1/responses, custom
128
+ // LLM loop, …). When the runtime produces a reply, call sendReply.
129
+ },
130
+ onChannelJoin: async (frame) => {
131
+ // A new conv exists. The store already has metadata written; this
132
+ // hook is for runtime-side wiring (subscribe a memory module, etc.).
133
+ },
134
+ },
135
+ });
136
+
137
+ chat.start();
138
+ ```
139
+
140
+ To respond:
141
+
142
+ ```ts
143
+ await chat.sendReply({
144
+ convId: "c-1234",
145
+ walletAddress: "0xabc...", // user wallet that owns the conv tree
146
+ text: "Aquí tienes el resumen.",
147
+ replyTo: "u-1234", // optional id of the user's message
148
+ });
149
+ ```
150
+
151
+ The reply is appended to the local JSONL before being sent on the wire, so
152
+ it shows up in history even if delivery fails.
153
+
154
+ ## Storage layout
155
+
156
+ ```
157
+ <storeRoot>/
158
+ <convId>/
159
+ metadata.json ConversationMetadata (participants, historyHost, ...)
160
+ messages.jsonl Append-only, one JSON object per line
161
+ attachments/ Reserved for future use
162
+ ```
163
+
164
+ `messages.jsonl` is **append-only**. Each line is `JSON.stringify(msg)`
165
+ where `msg: ChatMessage`. Out-of-order timestamps are preserved as written;
166
+ pagination uses `timestamp < before` as the filter.
167
+
168
+ ## Pagination
169
+
170
+ ```ts
171
+ const store = chat.getStore();
172
+ const { messages, hasMore } = await store.readPage("c-1234", {
173
+ before: "2026-05-19T18:00:00.000Z",
174
+ limit: 50,
175
+ });
176
+ // messages: chronological ascending (oldest of the page first)
177
+ ```
178
+
179
+ `readPage` reads the whole jsonl into memory and walks backward — fine for
180
+ conversations up to ~tens of thousands of messages. If you anticipate
181
+ larger logs, swap the implementation for a streaming reverse-iterator
182
+ (left as a future task).
183
+
184
+ ## Reconnect, heartbeat, rate limit
185
+
186
+ - Exponential backoff: 1s → 60s.
187
+ - Heartbeat: a `ping` frame every 25s (the server's default reap timeout is
188
+ 90s).
189
+ - The server rate-limits each agent socket to 600 messages/min by default;
190
+ history responses and acks are counted.
@@ -0,0 +1,273 @@
1
+ # PerkOS multi-agent demo — provisioning playbook
2
+
3
+ End-to-end recipe for standing up the three-agent demo on a Hetzner VPS
4
+ (or any Linux host with Docker, Caddy, and outbound HTTPS). Read it
5
+ once, then `scripts/demo/` does most of the work.
6
+
7
+ ## Topology
8
+
9
+ ```
10
+ ┌──── PerkOS-Transport (transport.perkos.xyz) ────┐
11
+ │ tasks routing, pairing registry │
12
+ └──────────────────────────────────────────────────┘
13
+ ▲ ▲
14
+ │ wss /a2a │ wss /a2a
15
+ │ │
16
+ ┌──── PerkOS-Chat (chat.perkos.xyz) ────┐ │
17
+ │ chat routing, Firebase metadata write │ │
18
+ └────────────────────────────────────────┘ │
19
+ ▲ │
20
+ │ wss /chat (user) │ wss /chat
21
+ │ │ (agent)
22
+ browser │
23
+
24
+ ┌────────────────────────────────────────┴────────┐
25
+ │ Apollo (orchestrator, Hermes runtime) │
26
+ │ Builder (worker, OpenClaw) │
27
+ │ QA (worker, OpenClaw) │
28
+ │ each: perkos-a2a-agent → Transport + Chat WS │
29
+ └─────────────────────────────────────────────────-┘
30
+ ```
31
+
32
+ For the demo we use a **single VPS** with all three agents running as
33
+ separate `perkos-a2a-agent` processes (different ports, different
34
+ config dirs). Apollo runs **Hermes** as its runtime — the others run
35
+ **OpenClaw**.
36
+
37
+ ## Prerequisites on the VPS
38
+
39
+ 1. Docker + docker compose
40
+ 2. Caddy already serving `transport.perkos.xyz` and `chat.perkos.xyz`
41
+ 3. Hermes Agent installed and reachable on `http://127.0.0.1:8642`
42
+ (the Apollo orchestrator) — see [hermes-workspace install
43
+ guide](https://github.com/NousResearch/hermes-agent)
44
+ 4. OpenClaw installed (the worker agents) — see [OpenClaw
45
+ docs](https://openclaw.dev) — they don't need a separate HTTP
46
+ server; the A2A plugin handles networking
47
+ 5. Node.js 22+ on the host (or use the published
48
+ `@perkos/perkos-a2a` npm package)
49
+ 6. The Transport admin key (`PAIRING_ADMIN_KEY` from Transport's
50
+ `.env`) — needed to mint invites
51
+ 7. SSH access as a non-root user with sudo
52
+
53
+ ## Step 0 — Decide identities
54
+
55
+ ```
56
+ ORG_SYSTEM=perkos-demo
57
+ PROJECT_ID=demo-q4
58
+
59
+ # Three agents, one orchestrator + two workers
60
+ APOLLO_NAME=apollo
61
+ BUILDER_NAME=builder
62
+ QA_NAME=qa
63
+ ```
64
+
65
+ ## Step 1 — Mint Transport pairing invites
66
+
67
+ For each agent, ask Transport for an invite. The wrapper script in
68
+ `scripts/demo/mint-pairing.sh` does it; or call the HTTP API directly:
69
+
70
+ ```bash
71
+ export TRANSPORT_BASE=https://transport.perkos.xyz
72
+ export TRANSPORT_ADMIN_KEY=<from your Transport .env>
73
+
74
+ for agent in apollo builder qa; do
75
+ echo "=== minting invite for $agent ==="
76
+ curl -fsS -X POST "$TRANSPORT_BASE/api/systems/perkos-demo/agents/invites" \
77
+ -H "x-api-key: $TRANSPORT_ADMIN_KEY" \
78
+ -H "content-type: application/json" \
79
+ -d "{
80
+ \"agentName\": \"$agent\",
81
+ \"runtime\": \"$( [[ $agent == apollo ]] && echo hermes-api || echo openclaw )\",
82
+ \"scopes\": [\"a2a:connect\", \"tasks:receive\", \"messages:send\", \"chat:send\"]
83
+ }" | tee /tmp/invite-$agent.json
84
+ done
85
+ ```
86
+
87
+ Each invite response looks like:
88
+
89
+ ```json
90
+ {
91
+ "inviteId": "inv_…",
92
+ "pairingUrl": "https://transport.perkos.xyz/pairing/invites/inv_…",
93
+ "expiresAt": "2026-05-20T..."
94
+ }
95
+ ```
96
+
97
+ Keep them — the agent uses the URL to claim and the operator (you)
98
+ approves.
99
+
100
+ ## Step 2 — Claim and approve
101
+
102
+ The pairing flow is two-step: the agent claims with a public key, then
103
+ admin approves to issue the scoped `relayApiKey`.
104
+
105
+ For the demo we **auto-approve** by passing the admin key on the claim
106
+ step. `scripts/demo/claim-and-approve.sh` wraps this:
107
+
108
+ ```bash
109
+ scripts/demo/claim-and-approve.sh apollo /tmp/invite-apollo.json
110
+ scripts/demo/claim-and-approve.sh builder /tmp/invite-builder.json
111
+ scripts/demo/claim-and-approve.sh qa /tmp/invite-qa.json
112
+ ```
113
+
114
+ Each call prints the `relayApiKey` for that agent. Save them:
115
+
116
+ ```bash
117
+ APOLLO_KEY=…
118
+ BUILDER_KEY=…
119
+ QA_KEY=…
120
+ ```
121
+
122
+ ## Step 3 — Update chat.perkos.xyz registry
123
+
124
+ Until the production bridge between Transport's pairing registry and
125
+ Chat's agent-key store lands, chat.perkos.xyz reads agent keys from
126
+ its `RELAY_API_KEYS` env var. SSH to the chat host and add the new
127
+ keys:
128
+
129
+ ```bash
130
+ ssh hetzner
131
+ sudo nano /opt/PerkOS-Chat/.env
132
+
133
+ # Update or extend:
134
+ RELAY_API_KEYS=apollo:<APOLLO_KEY>,builder:<BUILDER_KEY>,qa:<QA_KEY>
135
+
136
+ # Reload:
137
+ cd /opt/PerkOS-Chat
138
+ docker compose -f docker-compose.example.yml up -d
139
+ ```
140
+
141
+ Verify:
142
+
143
+ ```bash
144
+ curl https://chat.perkos.xyz/health | jq .agents
145
+ # expect 0 (no agents connected yet — that's fine)
146
+ ```
147
+
148
+ ## Step 4 — Start Apollo (Hermes orchestrator)
149
+
150
+ ```bash
151
+ # On the Hermes host:
152
+ export A2A_AGENT_NAME=apollo
153
+ export A2A_RUNTIME=hermes-api
154
+ export HERMES_API_URL=http://127.0.0.1:8642
155
+ export A2A_RELAY_URL=wss://transport.perkos.xyz/a2a
156
+ export A2A_RELAY_API_KEY=<APOLLO_KEY>
157
+ export A2A_CHAT_ENABLED=true
158
+ export A2A_CHAT_URL=wss://chat.perkos.xyz/chat
159
+ export A2A_CHAT_API_KEY=<APOLLO_KEY>
160
+ export A2A_CHAT_REPLY_PORT=5060
161
+ export A2A_PORT=5050
162
+ export A2A_PEERS='{"builder":"http://127.0.0.1:5051","qa":"http://127.0.0.1:5052"}'
163
+
164
+ npx -p @perkos/perkos-a2a perkos-a2a-agent
165
+ ```
166
+
167
+ A systemd unit template is in `scripts/demo/systemd/apollo.service`.
168
+
169
+ ## Step 5 — Start Builder + QA (OpenClaw workers)
170
+
171
+ OpenClaw agents load the A2A plugin natively. Drop this config into
172
+ each worker's OpenClaw plugins directory:
173
+
174
+ ```jsonc
175
+ // builder.plugin.config.json
176
+ {
177
+ "perkos-a2a": {
178
+ "config": {
179
+ "agentName": "builder",
180
+ "port": 5051,
181
+ "bindHost": "127.0.0.1",
182
+ "mode": "client-only",
183
+ "peers": {
184
+ "apollo": "http://127.0.0.1:5050"
185
+ },
186
+ "relay": {
187
+ "enabled": true,
188
+ "url": "wss://transport.perkos.xyz/a2a",
189
+ "apiKey": "<BUILDER_KEY>"
190
+ },
191
+ "chat": {
192
+ "enabled": true,
193
+ "url": "wss://chat.perkos.xyz/chat",
194
+ "apiKey": "<BUILDER_KEY>"
195
+ },
196
+ "runtime": {
197
+ "kind": "openclaw"
198
+ }
199
+ }
200
+ }
201
+ }
202
+ ```
203
+
204
+ `qa` is identical except `agentName`, `port=5052`, and `apiKey`.
205
+
206
+ Restart each OpenClaw process to pick up the plugin config.
207
+
208
+ ## Step 6 — Verify all three connect
209
+
210
+ ```bash
211
+ scripts/demo/verify.sh
212
+ ```
213
+
214
+ Output should look like:
215
+
216
+ ```
217
+ ✓ transport.perkos.xyz reachable
218
+ ✓ chat.perkos.xyz reachable
219
+ ✓ apollo — A2A connected, chat connected
220
+ ✓ builder — A2A connected, chat connected
221
+ ✓ qa — A2A connected, chat connected
222
+ ```
223
+
224
+ Or hit the health endpoints manually:
225
+
226
+ ```bash
227
+ curl https://chat.perkos.xyz/health | jq .agents
228
+ # expect 3
229
+ curl https://transport.perkos.xyz/health | jq .connectedAgents
230
+ # expect 3
231
+ curl "https://transport.perkos.xyz/api/agents/apollo/heartbeat" -H "x-api-key: $TRANSPORT_ADMIN_KEY" | jq
232
+ # expect { "ok": true, "approved": true, "connected": true }
233
+ ```
234
+
235
+ ## Step 7 — Smoke from the MiniApp
236
+
237
+ Open the MiniApp, sign in with a wallet, and walk through the
238
+ [chat E2E checklist](https://github.com/PerkOS-xyz/PerkOS/blob/main/docs/CHAT-E2E.md).
239
+
240
+ Quickest happy-path: click **+ New**, pick "Direct message" + apollo,
241
+ type "hola apollo" — you should see Apollo's reply within 5 seconds
242
+ (Hermes processing time).
243
+
244
+ ## Troubleshooting
245
+
246
+ | Symptom | First-pass diagnosis |
247
+ |---|---|
248
+ | MiniApp sidebar empty | Firestore rules deployed? — see `Perkos/firestore.rules` |
249
+ | `auth_error` on the chat WS | Firebase project mismatch between MiniApp and chat.perkos.xyz |
250
+ | Agent not connecting | Check the `RELAY_API_KEYS` value matches what claim-and-approve issued |
251
+ | Apollo gets the chat_deliver but no reply | Hermes is not configured to POST to `127.0.0.1:5060/chat/reply` (see `docs/chat-client.md` "Wiring it in — Hermes") |
252
+ | `HOST_OFFLINE` banner stays up | The host agent's WS dropped; check the agent process logs |
253
+ | Builder/QA never receive sub-tasks | Apollo's peer config wrong; check the JSON peers map in apollo's env |
254
+
255
+ ## Decommissioning the demo
256
+
257
+ ```bash
258
+ # Revoke each agent on Transport
259
+ for agent in apollo builder qa; do
260
+ curl -X POST "$TRANSPORT_BASE/pairing/agents/$agent/revoke" \
261
+ -H "x-api-key: $TRANSPORT_ADMIN_KEY"
262
+ done
263
+
264
+ # Remove from chat.perkos.xyz
265
+ ssh hetzner 'sudo sed -i "s/RELAY_API_KEYS=.*/RELAY_API_KEYS=/" /opt/PerkOS-Chat/.env && \
266
+ cd /opt/PerkOS-Chat && docker compose -f docker-compose.example.yml up -d'
267
+
268
+ # Stop the agent processes (systemd or direct)
269
+ ```
270
+
271
+ Conversation metadata stays in Firestore. Conversation content stays
272
+ in each agent's `~/.perkos/conversations/` jsonl. Delete those if you
273
+ want a clean wipe.
@@ -170,6 +170,41 @@
170
170
  "pairingStorePath": {
171
171
  "type": "string",
172
172
  "description": "Path to a local pairing credential profile, e.g. ~/.perkos/a2a/agents/Alice.json. The plugin may load relay credentials from this store without printing secrets."
173
+ },
174
+ "chat": {
175
+ "type": "object",
176
+ "additionalProperties": false,
177
+ "properties": {
178
+ "enabled": {
179
+ "type": "boolean",
180
+ "default": false,
181
+ "description": "Connect to PerkOS-Chat for user-facing chat (DMs + channels). Independent of the A2A relay used for tasks."
182
+ },
183
+ "url": {
184
+ "type": "string",
185
+ "default": "wss://chat.perkos.xyz/chat",
186
+ "description": "PerkOS-Chat WebSocket URL"
187
+ },
188
+ "apiKey": {
189
+ "type": "string",
190
+ "description": "Relay API key for this agent. If omitted, falls back to relay.apiKey."
191
+ },
192
+ "storeRoot": {
193
+ "type": "string",
194
+ "description": "Directory for the conversations JSONL store. Defaults to ~/.perkos/conversations."
195
+ },
196
+ "defaultHistoryLimit": {
197
+ "type": "number",
198
+ "default": 50,
199
+ "description": "Max messages returned per history page (1-500)."
200
+ },
201
+ "heartbeatIntervalMs": {
202
+ "type": "number",
203
+ "default": 25000,
204
+ "description": "Ping cadence in ms. The server reaps after 90s of silence."
205
+ }
206
+ },
207
+ "description": "PerkOS-Chat client config — opt-in. The agent owns conversation history on local disk; the PerkOS cloud stores metadata only."
173
208
  }
174
209
  }
175
210
  },
@@ -204,7 +239,9 @@
204
239
  "tools": [
205
240
  "perkos_a2a_send",
206
241
  "perkos_a2a_discover",
207
- "perkos_a2a_status"
242
+ "perkos_a2a_status",
243
+ "perkos_chat_reply",
244
+ "perkos_chat_history"
208
245
  ]
209
246
  },
210
247
  "activation": {
package/package.json CHANGED
@@ -1,10 +1,28 @@
1
1
  {
2
2
  "name": "@perkos/perkos-a2a",
3
- "version": "0.8.34",
3
+ "version": "0.9.0",
4
4
  "description": "A2A Protocol communication plugin for OpenClaw — Agent-to-Agent protocol implementation",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",
8
+ "bin": {
9
+ "perkos-a2a-agent": "./dist/agent.js",
10
+ "perkos-a2a-hermes": "./dist/hermes-cli.js"
11
+ },
12
+ "exports": {
13
+ ".": {
14
+ "types": "./dist/index.d.ts",
15
+ "default": "./dist/index.js"
16
+ },
17
+ "./hermes": {
18
+ "types": "./dist/hermes-plugin.d.ts",
19
+ "default": "./dist/hermes-plugin.js"
20
+ },
21
+ "./types": {
22
+ "types": "./dist/types.d.ts",
23
+ "default": "./dist/types.js"
24
+ }
25
+ },
8
26
  "files": [
9
27
  "dist/index.js",
10
28
  "dist/index.js.map",
@@ -26,6 +44,20 @@
26
44
  "dist/runtime-reply.d.ts.map",
27
45
  "dist/agentic-actions.d.ts",
28
46
  "dist/agentic-actions.d.ts.map",
47
+ "dist/agent.js",
48
+ "dist/agent.js.map",
49
+ "dist/agent.d.ts",
50
+ "dist/agent.d.ts.map",
51
+ "dist/hermes-cli.js",
52
+ "dist/hermes-cli.js.map",
53
+ "dist/hermes-cli.d.ts",
54
+ "dist/hermes-cli.d.ts.map",
55
+ "dist/hermes-plugin.js",
56
+ "dist/hermes-plugin.js.map",
57
+ "dist/hermes-plugin.d.ts",
58
+ "dist/hermes-plugin.d.ts.map",
59
+ "scripts/install-hermes.mjs",
60
+ "scripts/hermes",
29
61
  "docs",
30
62
  "skills",
31
63
  "openclaw.plugin.json",