@kybernesis/buzz 0.10.1 → 0.10.2
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/turn.d.ts +10 -1
- package/dist/turn.js +29 -2
- package/package.json +1 -1
package/dist/turn.d.ts
CHANGED
|
@@ -82,8 +82,17 @@ export declare function silentTurnReply(failures: readonly TurnFailure[]): strin
|
|
|
82
82
|
export declare function describeSilentTurn(failures: readonly TurnFailure[]): string;
|
|
83
83
|
/** Convert one inbound Buzz message into an eve-compatible turn. */
|
|
84
84
|
export declare function composeMessage(text: string, attachments: MediaRef[], fetchAttachment: (ref: MediaRef) => Promise<FetchedMedia>, log?: (message: string) => void): Promise<TurnMessage>;
|
|
85
|
+
/**
|
|
86
|
+
* How long reading the backlog of a conversation on record may take before the
|
|
87
|
+
* conversation is treated as gone. An idle session's unread events arrive in
|
|
88
|
+
* well under a second; a session whose durable run died under a runtime
|
|
89
|
+
* upgrade never finishes. On 2026-10-05 a Buzz channel's stored run had failed
|
|
90
|
+
* replay after the eve 0.68 cutover, and every message in that channel sat at
|
|
91
|
+
* "typing" with nothing reaching the agent, for as long as anyone waited.
|
|
92
|
+
*/
|
|
93
|
+
export declare const DEFAULT_UNREAD_DRAIN_TIMEOUT_MS = 20000;
|
|
85
94
|
/** Continue a channel's existing conversation, replacing it only when it is explicitly stale. */
|
|
86
|
-
export declare function answerTurn(client: Client, sessions: SessionStore, channel: string, message: TurnMessage, community: string, log?: (message: string) => void, agentSilenceTimeoutMs?: number, agentWorkTimeoutMs?: number): Promise<TurnOutcome>;
|
|
95
|
+
export declare function answerTurn(client: Client, sessions: SessionStore, channel: string, message: TurnMessage, community: string, log?: (message: string) => void, agentSilenceTimeoutMs?: number, agentWorkTimeoutMs?: number, unreadDrainTimeoutMs?: number): Promise<TurnOutcome>;
|
|
87
96
|
/** What the room is told when eve refuses the turn itself. */
|
|
88
97
|
/**
|
|
89
98
|
* The two answers from eve that mean the stored session cannot carry another
|
package/dist/turn.js
CHANGED
|
@@ -313,12 +313,23 @@ async function sendExisting(responsePromise, streamIndex, timeouts, continueStre
|
|
|
313
313
|
watchdog.dispose();
|
|
314
314
|
}
|
|
315
315
|
}
|
|
316
|
+
/**
|
|
317
|
+
* How long reading the backlog of a conversation on record may take before the
|
|
318
|
+
* conversation is treated as gone. An idle session's unread events arrive in
|
|
319
|
+
* well under a second; a session whose durable run died under a runtime
|
|
320
|
+
* upgrade never finishes. On 2026-10-05 a Buzz channel's stored run had failed
|
|
321
|
+
* replay after the eve 0.68 cutover, and every message in that channel sat at
|
|
322
|
+
* "typing" with nothing reaching the agent, for as long as anyone waited.
|
|
323
|
+
*/
|
|
324
|
+
export const DEFAULT_UNREAD_DRAIN_TIMEOUT_MS = 20_000;
|
|
316
325
|
/** Continue a channel's existing conversation, replacing it only when it is explicitly stale. */
|
|
317
|
-
export async function answerTurn(client, sessions, channel, message, community, log = () => { }, agentSilenceTimeoutMs = DEFAULT_AGENT_SILENCE_TIMEOUT_MS, agentWorkTimeoutMs = DEFAULT_AGENT_WORK_TIMEOUT_MS) {
|
|
326
|
+
export async function answerTurn(client, sessions, channel, message, community, log = () => { }, agentSilenceTimeoutMs = DEFAULT_AGENT_SILENCE_TIMEOUT_MS, agentWorkTimeoutMs = DEFAULT_AGENT_WORK_TIMEOUT_MS, unreadDrainTimeoutMs = DEFAULT_UNREAD_DRAIN_TIMEOUT_MS) {
|
|
318
327
|
const intervalMs = {
|
|
319
328
|
silenceMs: validateAgentSilenceTimeoutMs(agentSilenceTimeoutMs),
|
|
320
329
|
workMs: validateAgentSilenceTimeoutMs(agentWorkTimeoutMs),
|
|
321
330
|
};
|
|
331
|
+
// Never longer than the silence bound, so a caller that tightened silence tightened this too.
|
|
332
|
+
const drainMs = Math.min(validateAgentSilenceTimeoutMs(unreadDrainTimeoutMs), intervalMs.silenceMs);
|
|
322
333
|
const existing = sessions.get(community, channel);
|
|
323
334
|
if (existing) {
|
|
324
335
|
try {
|
|
@@ -330,8 +341,24 @@ export async function answerTurn(client, sessions, channel, message, community,
|
|
|
330
341
|
* `send()` opens its response stream at the position the handle already
|
|
331
342
|
* holds and stops at the FIRST turn boundary it meets. Draining first
|
|
332
343
|
* repairs any cursor that was left behind by a prior disconnected reader.
|
|
344
|
+
* Bounded on its own, tightly: this read is of a backlog, not of work.
|
|
345
|
+
* A backlog that does not finish is not proof of anything by itself —
|
|
346
|
+
* the run behind it may be dead (eve 0.68 cutover, 2026-10-05: a stored
|
|
347
|
+
* run that failed replay streamed nothing, and the channel sat at
|
|
348
|
+
* "typing" for good) or the transport may be having a moment. So the
|
|
349
|
+
* stall is logged and the send goes ahead: the agent's answer to the
|
|
350
|
+
* send decides. 404 / "no longer active" replaces the session below; a
|
|
351
|
+
* real reply continues it; a transport failure preserves it.
|
|
333
352
|
*/
|
|
334
|
-
|
|
353
|
+
let unread = 0;
|
|
354
|
+
try {
|
|
355
|
+
unread = await drainUnread((signal) => session.stream({ follow: false, startIndex: existing.streamIndex, signal }), { silenceMs: drainMs, workMs: drainMs });
|
|
356
|
+
}
|
|
357
|
+
catch (error) {
|
|
358
|
+
if (!(error instanceof AgentSilenceTimeoutError))
|
|
359
|
+
throw error;
|
|
360
|
+
log(`backlog of ${channel.slice(0, 8)} (${existing.id}) did not finish reading in ${drainMs}ms; asking the agent directly`);
|
|
361
|
+
}
|
|
335
362
|
if (unread > 0) {
|
|
336
363
|
log(`caught up on ${unread} unread event(s) in ${channel.slice(0, 8)} before answering`);
|
|
337
364
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kybernesis/buzz",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.2",
|
|
4
4
|
"description": "Put an eve agent in a Buzz workspace as a member: per-speaker verified identity resolved through the control plane, self-service identity linking, presence, typing and seen signals.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|