plugin: benign-reconnect overflow-notice fix (maxAssignedSeq gate), null-id reconnect timer clear; rsync: mkdir before early exit
This commit is contained in:
@@ -6,12 +6,14 @@ set -euo pipefail
|
||||
SRC="${LVMH_PI_AGENT_DIR:-$HOME/.dotfiles/pi/agent}"
|
||||
DEST="$(dirname "$0")/../docker/pi-agent"
|
||||
|
||||
# DEST must exist even without dotfiles: the worker Dockerfile COPYs it
|
||||
# unconditionally (a missing dir would fail the build).
|
||||
mkdir -p "$DEST"
|
||||
|
||||
if [ ! -f "$SRC/settings.json" ]; then
|
||||
echo "rsync-pi-agent: $SRC/settings.json not found — skipping dotfiles sync" >&2
|
||||
exit 0
|
||||
fi
|
||||
|
||||
mkdir -p "$DEST"
|
||||
rsync -a --delete \
|
||||
--exclude 'auth.json' \
|
||||
--exclude 'models.json' \
|
||||
|
||||
+28
-8
@@ -93,6 +93,7 @@ interface SessionSnapshot {
|
||||
|
||||
/** Per-session seq counters survive reconnects and instance rebinds in-process. */
|
||||
const seqCounters: Map<string, number> = new Map();
|
||||
const maxAssignedSeq: Map<string, number> = new Map();
|
||||
const sessionStartTs: Map<string, number> = new Map();
|
||||
|
||||
const logPath: string = path.join(os.homedir(), ".pi", "lvmh-agent.log");
|
||||
@@ -110,6 +111,11 @@ function log(message: unknown): void {
|
||||
function nextSeq(sessionId: string): number {
|
||||
const n: number = (seqCounters.get(sessionId) ?? 0) + 1;
|
||||
seqCounters.set(sessionId, n);
|
||||
// High-water mark of seqs THIS process produced — distinguishes a benign
|
||||
// reconnect (daemon lastSeq <= maxAssigned: frames were delivered by us)
|
||||
// from a foreign high-water after a plugin restart (real gaps).
|
||||
const seen = maxAssignedSeq.get(sessionId) ?? 0;
|
||||
if (n > seen) maxAssignedSeq.set(sessionId, n);
|
||||
return n;
|
||||
}
|
||||
|
||||
@@ -487,19 +493,27 @@ export default function (pi: ExtensionAPI): void {
|
||||
const counter: number = seqCounters.get(currentSessionId) ?? 0;
|
||||
if (counter <= lastSeq) seqCounters.set(currentSessionId, lastSeq);
|
||||
}
|
||||
// Events already covered by the daemon's lastSeq are silently absent
|
||||
// from both replay and queue below — count them so the buffer_overflow
|
||||
// notice reflects every gap, not just cap drops.
|
||||
const covered: number =
|
||||
replayBuf.filter((f) => f.seq <= lastSeq).length +
|
||||
sendQueue.filter((f) => !TRANSIENT_TYPES.has(f.type) && f.seq <= lastSeq)
|
||||
.length;
|
||||
// Gap accounting: only a daemon lastSeq ABOVE anything this process ever
|
||||
// assigned indicates real gaps (plugin restart against a daemon that has
|
||||
// history). lastSeq <= maxAssigned means the daemon acked frames we sent
|
||||
// ourselves — benign reconnect, no notice. replayBuf alone holds every
|
||||
// persisted-kind frame (sendQueue copies are a subset), so counting it
|
||||
// once cannot double-count.
|
||||
const maxAssigned: number =
|
||||
currentSessionId !== null
|
||||
? (maxAssignedSeq.get(currentSessionId) ?? 0)
|
||||
: 0;
|
||||
if (lastSeq > maxAssigned) {
|
||||
const covered: number = replayBuf.filter(
|
||||
(f) => f.seq <= lastSeq,
|
||||
).length;
|
||||
if (covered > 0) {
|
||||
droppedEvents += covered;
|
||||
log(
|
||||
`welcome lastSeq=${lastSeq} covers ${covered} in-flight events; flagging`,
|
||||
`welcome lastSeq=${lastSeq} > maxAssigned=${maxAssigned}: ${covered} replayed frames never acknowledged; flagging`,
|
||||
);
|
||||
}
|
||||
}
|
||||
// Replay persisted events the daemon is missing, before queued live
|
||||
// frames. Persisted-kind frames still sitting in the queue are dropped
|
||||
// here: they are already in replayBuf, so sending both would duplicate
|
||||
@@ -637,6 +651,12 @@ export default function (pi: ExtensionAPI): void {
|
||||
// attributing this session's events to the previous session id.
|
||||
currentSessionId = null;
|
||||
handleDisconnect();
|
||||
try {
|
||||
if (reconnectTimer !== null) clearTimeout(reconnectTimer);
|
||||
} catch {
|
||||
// clearable timers never throw in practice
|
||||
}
|
||||
reconnectTimer = null;
|
||||
return;
|
||||
}
|
||||
const assignedId = process.env[ENV_SESSION_ID];
|
||||
|
||||
Reference in New Issue
Block a user