@melaya/runner 1.0.100 → 1.0.102
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/connection.js +20 -2
- package/package.json +1 -1
package/dist/connection.js
CHANGED
|
@@ -889,8 +889,12 @@ export async function connect(opts) {
|
|
|
889
889
|
// watermark so the server doesn't needlessly re-offer a rehydrate (a bare
|
|
890
890
|
// ready would read as watermark 0). No second spawn.
|
|
891
891
|
const live = activeAssistants.get(sid);
|
|
892
|
+
// Report the live host's ACTUAL boot generation (not just its watermark) so a
|
|
893
|
+
// server that restarted (its in-memory session map wiped) can DETECT a host
|
|
894
|
+
// that booted under an older generation and reboot it instead of serving the
|
|
895
|
+
// next turn from stale memory/config. A bare ready hid this.
|
|
892
896
|
if (live) {
|
|
893
|
-
emitEv({ kind: "ready", memoryWatermark: live.memoryWatermark || 0 });
|
|
897
|
+
emitEv({ kind: "ready", memoryWatermark: live.memoryWatermark || 0, generation: live.generation });
|
|
894
898
|
return;
|
|
895
899
|
}
|
|
896
900
|
// A boot is already in flight for this sid (concurrent start / server restart
|
|
@@ -1030,7 +1034,12 @@ export async function connect(opts) {
|
|
|
1030
1034
|
}
|
|
1031
1035
|
});
|
|
1032
1036
|
proc.on("exit", (code) => {
|
|
1033
|
-
|
|
1037
|
+
// Only evict if WE are still the registered host. A config-drift reboot
|
|
1038
|
+
// (e.g. the user added a connector) kills this host and spawns a REPLACEMENT
|
|
1039
|
+
// under the same sid; if this late exit deleted the map entry blindly it
|
|
1040
|
+
// would orphan the newcomer and make the next turn "session_not_found".
|
|
1041
|
+
if (activeAssistants.get(sid) === session)
|
|
1042
|
+
activeAssistants.delete(sid);
|
|
1034
1043
|
const detail = code !== 0 && stderrTail.length ? stderrTail.slice(-12).join("\n") : "";
|
|
1035
1044
|
socket.emit("runner:assistant_event", { sessionId: sid, turnId: "", kind: "session_closed", code, detail });
|
|
1036
1045
|
console.log(chalk.gray(` ■ Assistant session ${sid.slice(0, 10)}… closed (exit ${code})`));
|
|
@@ -1080,6 +1089,15 @@ export async function connect(opts) {
|
|
|
1080
1089
|
socket.emit("runner:assistant_event", { sessionId: payload.sessionId, turnId: payload.turnId, kind: "error", message: "session_not_found" });
|
|
1081
1090
|
return;
|
|
1082
1091
|
}
|
|
1092
|
+
// Generation gate (defense-in-depth): refuse to serve a turn stamped with a
|
|
1093
|
+
// generation that doesn't match this host's boot generation — the server has
|
|
1094
|
+
// moved on (restart / takeover) and this host is stale. session_not_found
|
|
1095
|
+
// triggers the server's clean re-boot path. Backward-compatible: a turn with
|
|
1096
|
+
// no generation (older server) is served as before.
|
|
1097
|
+
if (payload.generation != null && Number(payload.generation) !== s.generation) {
|
|
1098
|
+
socket.emit("runner:assistant_event", { sessionId: payload.sessionId, turnId: payload.turnId, kind: "error", message: "session_not_found" });
|
|
1099
|
+
return;
|
|
1100
|
+
}
|
|
1083
1101
|
s.lastActivity = Date.now();
|
|
1084
1102
|
// Per-turn hitl_mode: the host applies it to os.environ["MEL_ASSISTANT_HITL_MODE"]
|
|
1085
1103
|
// (mirrored to MEL_HITL_MODE) BEFORE running the turn, so a mid-session flip
|