@glyphteck/veyl 0.61.12 → 0.62.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/docs/api.md CHANGED
@@ -435,7 +435,7 @@ await veyl.listen({
435
435
  });
436
436
  ```
437
437
 
438
- `listen` subscribes to the shared live chat-list and transfer-store owners. It emits `ready`, `message`, `message-delete`, `transaction`, and `error` events. Message events carry a stable `chatId`, verified actor/member summaries, and a qualified message target. Compact, incoming-only events and transient chat subscriptions are the defaults: the encrypted chat list acts as a wake index, so a changed chat opens briefly, processes its current and history pages, and releases instead of retaining one listener per chat. Set `compact: false`, `incomingOnly: false`, or `persistentChats: true` only when the caller needs those broader behaviors. A headless viewer may set `read: true, relayReads: true`; the listener then advances each peer read from its existing decrypted batch through a short encrypted live-room lease before emitting the event, while the durable write remains coalesced.
438
+ `listen` subscribes to the shared live chat-list and transfer-store owners. It emits `ready`, `message`, `message-delete`, `transaction`, and `error` events. Message events carry a stable `chatId`, verified actor/member summaries, and a qualified message target. Replies retain `message.replyId`; reaction controls are emitted as `message.type === 'rxn'` with `reactTo` pointing at the original message, so agents can preserve the exact action instead of flattening it into presentation text. Compact, incoming-only events and transient chat subscriptions are the defaults: the encrypted chat list acts as a wake index, so a changed chat opens briefly, processes its current and history pages, and releases instead of retaining one listener per chat. Set `compact: false`, `incomingOnly: false`, or `persistentChats: true` only when the caller needs those broader behaviors. A headless viewer may set `read: true, relayReads: true`; the listener then advances each peer read from its existing decrypted batch through a short encrypted live-room lease before emitting the event, while the durable write remains coalesced.
439
439
 
440
440
  Use `listen` for account-wide agent wakeups. Use `chat.enterById` when an agent is actively inside any notes/direct/group chat; use `chat.enter(peer)` only as a canonical-direct convenience.
441
441
 
@@ -119,6 +119,37 @@ function sendAttachmentToEventChat(client, event, attachment, options) {
119
119
  : client.chat.sendAttachment(event.peer, attachment, options);
120
120
  }
121
121
 
122
+ function eventMessageTarget(event, messageId) {
123
+ const id = String(messageId || '').trim();
124
+ if (!id) return null;
125
+ return {
126
+ chatId: eventChatId(event) || null,
127
+ peer: event.peer || null,
128
+ messageId: id,
129
+ };
130
+ }
131
+
132
+ function sendEchoText(account, client, event) {
133
+ const options = { cid: botActionCid(event, 'echo', account) };
134
+ const replyTarget = eventMessageTarget(event, event.message?.replyId);
135
+ return replyTarget
136
+ ? client.chat.reply(replyTarget, event.message.text || '', options)
137
+ : sendToEventChat(client, event, event.message.text || '', options);
138
+ }
139
+
140
+ function mirrorReaction(account, client, event) {
141
+ const payload = event.message?.payload || {};
142
+ const emoji = String(event.message?.emoji || payload.emoji || '').trim();
143
+ const target = event.reactTo || eventMessageTarget(
144
+ event,
145
+ event.message?.target || payload.target
146
+ );
147
+ if (!emoji || !target?.messageId) return null;
148
+ return client.chat.reactTo(target, emoji, {
149
+ cid: botActionCid(event, 'echo-reaction', account),
150
+ });
151
+ }
152
+
122
153
  function groupEchoWinner(event, echoChatKeys) {
123
154
  const eligible = [...new Set(
124
155
  (event?.chat?.members || [])
@@ -218,9 +249,17 @@ async function mirrorEcho(journal, account, client, event) {
218
249
  event,
219
250
  'echo-text',
220
251
  { replaySafe: true },
221
- () => sendToEventChat(client, event, event.message.text || '', {
222
- cid: botActionCid(event, 'echo', account),
223
- })
252
+ () => sendEchoText(account, client, event)
253
+ );
254
+ }
255
+ if (event.message.type === 'rxn') {
256
+ return runAction(
257
+ journal,
258
+ account,
259
+ event,
260
+ 'echo-reaction',
261
+ { replaySafe: true },
262
+ () => mirrorReaction(account, client, event)
224
263
  );
225
264
  }
226
265
  if (event.message.type === 'req') {
@@ -575,6 +614,16 @@ export function createBotFleetPolicy(options = {}) {
575
614
  },
576
615
  async onEvent({ account, client, event }) {
577
616
  const roles = rolesFor(account);
617
+ if (!baseline && roles.has('live') && event?.type === 'message') {
618
+ const state = liveStates.get(account.profile);
619
+ if (state) {
620
+ await ensureLiveChat(
621
+ account,
622
+ state,
623
+ eventChatId(event)
624
+ );
625
+ }
626
+ }
578
627
  if (checkpoint?.has(account.profile, event)) {
579
628
  return null;
580
629
  }
package/package.json CHANGED
@@ -41,5 +41,5 @@
41
41
  "start": "node src/cli.js",
42
42
  "lint": "eslint src --quiet"
43
43
  },
44
- "version": "0.61.12"
44
+ "version": "0.62.0"
45
45
  }