@mulmobridge/chat-service 1.3.0 → 1.3.1
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/relay.d.ts +2 -0
- package/dist/relay.js +19 -4
- package/dist/reply-deadline.d.ts +2 -0
- package/dist/reply-deadline.js +9 -0
- package/package.json +1 -1
package/dist/relay.d.ts
CHANGED
|
@@ -34,6 +34,8 @@ export interface RelayDeps {
|
|
|
34
34
|
getRole: (roleId: string) => Role;
|
|
35
35
|
defaultRoleId: string;
|
|
36
36
|
logger: Logger;
|
|
37
|
+
/** Monotonic milliseconds for the reply limit. Defaults to `performance.now()`. */
|
|
38
|
+
now?: () => number;
|
|
37
39
|
}
|
|
38
40
|
export declare function createRelay(deps: RelayDeps): RelayFn;
|
|
39
41
|
export declare function resolveDefaultRole(bridgeOptions: Readonly<Record<string, string | number | boolean>> | undefined, getRole: (roleId: string) => Role, fallbackRoleId: string, logger: Logger, transportId: string): string;
|
package/dist/relay.js
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
// host.
|
|
9
9
|
import { EVENT_TYPES, resolveReplyTimeoutMs } from "@mulmobridge/protocol";
|
|
10
10
|
import { createKeyedSerializer } from "./keyed-serializer.js";
|
|
11
|
+
import { remainingReplyMs } from "./reply-deadline.js";
|
|
11
12
|
// ── Factory ──────────────────────────────────────────────────
|
|
12
13
|
export function createRelay(deps) {
|
|
13
14
|
const serialize = createKeyedSerializer();
|
|
@@ -17,10 +18,19 @@ export function createRelay(deps) {
|
|
|
17
18
|
// sessions, and split the conversation across them (#1878). The
|
|
18
19
|
// JSON pair is an unambiguous composite key for the two ids.
|
|
19
20
|
const key = JSON.stringify([params.transportId, params.externalChatId]);
|
|
20
|
-
|
|
21
|
+
const budget = {
|
|
22
|
+
receivedAtMs: clockOf(deps)(),
|
|
23
|
+
replyTimeoutMs: resolveBridgeReplyTimeout(params.bridgeOptions, deps.logger, params.transportId),
|
|
24
|
+
};
|
|
25
|
+
return serialize.run(key, () => processRelayMessage(deps, params, budget));
|
|
21
26
|
};
|
|
22
27
|
}
|
|
23
|
-
|
|
28
|
+
// Neutral about the cause: a queue behind an earlier message, or slow setup on an idle chat.
|
|
29
|
+
const EXPIRED_BEFORE_START_REPLY = "The request timed out before the agent could start on it. Please send it again.";
|
|
30
|
+
// Monotonic by default, so a wall-clock step between receipt and processing cannot move the limit.
|
|
31
|
+
const clockOf = (deps) => deps.now ?? (() => performance.now());
|
|
32
|
+
const remainingOf = (budget, deps) => remainingReplyMs(budget.receivedAtMs, budget.replyTimeoutMs, clockOf(deps)());
|
|
33
|
+
async function processRelayMessage(deps, params, budget) {
|
|
24
34
|
const { store, handleCommand, startChat, onSessionEvent, getRole, defaultRoleId, logger } = deps;
|
|
25
35
|
const { transportId, externalChatId, attachments, bridgeOptions } = params;
|
|
26
36
|
let { text } = params;
|
|
@@ -60,6 +70,12 @@ async function processRelayMessage(deps, params) {
|
|
|
60
70
|
chatState = commandResult.nextState;
|
|
61
71
|
text = commandResult.forwardAs;
|
|
62
72
|
}
|
|
73
|
+
// Checked here, not on entry: a command above still runs and answers at once, but an agent turn
|
|
74
|
+
// started after the limit would produce a reply nobody is waiting for.
|
|
75
|
+
if (remainingOf(budget, deps) === 0) {
|
|
76
|
+
logger.info("chat-service", "message expired before its turn started", { transportId, externalChatId });
|
|
77
|
+
return { kind: "ok", reply: EXPIRED_BEFORE_START_REPLY };
|
|
78
|
+
}
|
|
63
79
|
const result = await startChat({
|
|
64
80
|
message: text,
|
|
65
81
|
roleId: chatState.roleId,
|
|
@@ -94,8 +110,7 @@ async function processRelayMessage(deps, params) {
|
|
|
94
110
|
};
|
|
95
111
|
}
|
|
96
112
|
try {
|
|
97
|
-
const
|
|
98
|
-
const reply = await collectAgentReply(onSessionEvent, chatState.sessionId, replyTimeoutMs, params.onChunk);
|
|
113
|
+
const reply = await collectAgentReply(onSessionEvent, chatState.sessionId, remainingOf(budget, deps), params.onChunk);
|
|
99
114
|
await store.setChatState(transportId, {
|
|
100
115
|
...chatState,
|
|
101
116
|
updatedAt: new Date().toISOString(),
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// A turn's reply limit is counted from when the relay RECEIVED the message,
|
|
2
|
+
// not from when the turn reached the front of its chat's queue. The bridge
|
|
3
|
+
// client starts its ack timer when it sends, so a limit counted from the start
|
|
4
|
+
// of collection let a queued or slow-to-start turn outlive the client's wait
|
|
5
|
+
// and have its reply dropped (#3312).
|
|
6
|
+
/** Milliseconds of the reply limit still left at `nowMs`; never negative. */
|
|
7
|
+
export function remainingReplyMs(receivedAtMs, replyTimeoutMs, nowMs) {
|
|
8
|
+
return Math.max(0, receivedAtMs + replyTimeoutMs - nowMs);
|
|
9
|
+
}
|
package/package.json
CHANGED