@mulmobridge/chat-service 0.1.4 → 0.1.6
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/chat-state.d.ts +9 -1
- package/dist/chat-state.js +7 -1
- package/dist/commands.js +6 -1
- package/dist/index.js +25 -1
- package/dist/types.d.ts +11 -0
- package/package.json +1 -1
package/dist/chat-state.d.ts
CHANGED
|
@@ -11,7 +11,15 @@ export interface ChatStateStore {
|
|
|
11
11
|
getChatState(transportId: string, externalChatId: string): Promise<TransportChatState | null>;
|
|
12
12
|
setChatState(transportId: string, state: TransportChatState): Promise<void>;
|
|
13
13
|
resetChatState(transportId: string, externalChatId: string, roleId: string): Promise<TransportChatState>;
|
|
14
|
-
|
|
14
|
+
/** Repoint the persisted chat state at another session. `roleId` is
|
|
15
|
+
* optional: when omitted, the existing state's roleId is preserved
|
|
16
|
+
* (the old default, kept for HTTP `/connect` callers that only know
|
|
17
|
+
* the session ID). Callers that DO know the target session's role —
|
|
18
|
+
* notably the `/switch` command, which already has `SessionSummary.roleId`
|
|
19
|
+
* from `/sessions` — MUST pass it, otherwise the file-backed state
|
|
20
|
+
* drifts into a stale-role / new-session pair and the next relay's
|
|
21
|
+
* `startChat` uses the mismatched pair (issue #1888). */
|
|
22
|
+
connectSession(transportId: string, externalChatId: string, chatSessionId: string, roleId?: string): Promise<TransportChatState | null>;
|
|
15
23
|
generateSessionId(transportId: string, externalChatId: string): string;
|
|
16
24
|
}
|
|
17
25
|
export declare function createChatStateStore(opts: {
|
package/dist/chat-state.js
CHANGED
|
@@ -57,13 +57,18 @@ export function createChatStateStore(opts) {
|
|
|
57
57
|
});
|
|
58
58
|
return state;
|
|
59
59
|
};
|
|
60
|
-
const connectSession = async (transportId, externalChatId, chatSessionId) => {
|
|
60
|
+
const connectSession = async (transportId, externalChatId, chatSessionId, roleId) => {
|
|
61
61
|
const existing = await getChatState(transportId, externalChatId);
|
|
62
62
|
if (!existing)
|
|
63
63
|
return null;
|
|
64
64
|
const updated = {
|
|
65
65
|
...existing,
|
|
66
66
|
sessionId: chatSessionId,
|
|
67
|
+
// A missing `roleId` arg means "preserve the current role" (that's
|
|
68
|
+
// the HTTP `/connect` route, which doesn't know the target's role);
|
|
69
|
+
// when the caller passes one (`/switch`), take theirs so state and
|
|
70
|
+
// downstream `startChat` agree on which role runs the resumed session.
|
|
71
|
+
...(roleId !== undefined ? { roleId } : {}),
|
|
67
72
|
updatedAt: new Date().toISOString(),
|
|
68
73
|
};
|
|
69
74
|
await setChatState(transportId, updated);
|
|
@@ -71,6 +76,7 @@ export function createChatStateStore(opts) {
|
|
|
71
76
|
transportId,
|
|
72
77
|
externalChatId,
|
|
73
78
|
sessionId: chatSessionId,
|
|
79
|
+
roleId: updated.roleId,
|
|
74
80
|
});
|
|
75
81
|
return updated;
|
|
76
82
|
};
|
package/dist/commands.js
CHANGED
|
@@ -211,7 +211,12 @@ export function createCommandHandler(opts) {
|
|
|
211
211
|
};
|
|
212
212
|
}
|
|
213
213
|
}
|
|
214
|
-
|
|
214
|
+
// Pass `target.roleId` so the persisted state's role tracks the session
|
|
215
|
+
// we just repointed at. Without this, `connectSession` would swap the
|
|
216
|
+
// sessionId but leave the previous role in place, and the next relay's
|
|
217
|
+
// `startChat` would run the resumed session under the wrong role
|
|
218
|
+
// (issue #1888).
|
|
219
|
+
const updated = await connectSession(transportId, chatState.externalChatId, target.id, target.roleId);
|
|
215
220
|
if (!updated) {
|
|
216
221
|
return { reply: "Failed to switch session." };
|
|
217
222
|
}
|
package/dist/index.js
CHANGED
|
@@ -97,7 +97,31 @@ export function createChatService(deps) {
|
|
|
97
97
|
badRequest(res, "chatSessionId is required");
|
|
98
98
|
return;
|
|
99
99
|
}
|
|
100
|
-
|
|
100
|
+
// Resolve the target session's role BEFORE calling connectSession so the
|
|
101
|
+
// persisted state's `roleId` tracks the new session's role — otherwise the
|
|
102
|
+
// next relay's `startChat` would resume the new session under the previous
|
|
103
|
+
// role (#1888 / #1894). Three fallback paths all treated as "preserve
|
|
104
|
+
// existing role":
|
|
105
|
+
// 1. No `getSessionRole` wired at all (backward compat for older hosts).
|
|
106
|
+
// 2. Resolver returns null (unknown / corrupt session metadata).
|
|
107
|
+
// 3. Resolver throws (host bug / timeout / IO error) — catch here so
|
|
108
|
+
// the route can never bubble the failure as a 500 to the API caller
|
|
109
|
+
// (codex review on #1895; the MulmoClaude host's resolver is
|
|
110
|
+
// hardened but the DI contract doesn't require hosts to be).
|
|
111
|
+
let resolvedRole = null;
|
|
112
|
+
if (deps.getSessionRole) {
|
|
113
|
+
try {
|
|
114
|
+
resolvedRole = await deps.getSessionRole(chatSessionId);
|
|
115
|
+
}
|
|
116
|
+
catch (err) {
|
|
117
|
+
logger.warn("chat-service", "getSessionRole threw; falling back to preserving existing role", {
|
|
118
|
+
chatSessionId,
|
|
119
|
+
error: err instanceof Error ? err.message : String(err),
|
|
120
|
+
});
|
|
121
|
+
resolvedRole = null;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
const updated = await store.connectSession(transportId, externalChatId, chatSessionId, resolvedRole ?? undefined);
|
|
101
125
|
if (!updated) {
|
|
102
126
|
notFound(res, "No chat state found for this transport");
|
|
103
127
|
return;
|
package/dist/types.d.ts
CHANGED
|
@@ -122,6 +122,17 @@ export interface ChatServiceDeps {
|
|
|
122
122
|
}>;
|
|
123
123
|
total: number;
|
|
124
124
|
}>;
|
|
125
|
+
/**
|
|
126
|
+
* Resolve the roleId a given session was started with. Used by the HTTP
|
|
127
|
+
* `/connect` route so the persisted bridge state's role tracks the target
|
|
128
|
+
* session's role after a repoint — same drift-fix as bridge `/switch`
|
|
129
|
+
* (issue #1888 / #1894), but for API callers that only supply a session ID.
|
|
130
|
+
* Returns null when the session isn't found OR when its role isn't known;
|
|
131
|
+
* on null the route falls back to the previous "preserve current role"
|
|
132
|
+
* behaviour (safe default). Omit this dep entirely to keep the old
|
|
133
|
+
* session-id-only semantics.
|
|
134
|
+
*/
|
|
135
|
+
getSessionRole?: (sessionId: string) => Promise<string | null>;
|
|
125
136
|
/**
|
|
126
137
|
* Return the skills the bridge command handler should expose. The
|
|
127
138
|
* handler uses the result for two things:
|
package/package.json
CHANGED