ScuttleBot
fix: codex-relay session discovery — match closest timestamp to startedAt When multiple Codex sessions share a CWD, pick the session whose first-entry timestamp is closest to the relay's startedAt, not just the newest. This prevents all relays from latching onto the same file.
Commit
8134f34d1fa44d33de7fa155d3c2ed5dd179d56b8fa1577235253eafad0b8cb7
Parent
a053aa67a2c7504…
1 file changed
+21
-9
+21
-9
| --- cmd/codex-relay/main.go | ||
| +++ cmd/codex-relay/main.go | ||
| @@ -1136,13 +1136,12 @@ | ||
| 1136 | 1136 | } |
| 1137 | 1137 | return match, nil |
| 1138 | 1138 | } |
| 1139 | 1139 | |
| 1140 | 1140 | // findLatestSessionPath finds the .jsonl file in root whose first entry |
| 1141 | -// timestamp is closest to (but after) notBefore — this ensures each relay | |
| 1142 | -// latches onto its own subprocess's session rather than whichever session | |
| 1143 | -// happens to have the latest timestamp when multiple sessions share a CWD. | |
| 1141 | +// timestamp is closest to notBefore — this ensures each relay latches onto | |
| 1142 | +// its own subprocess's session when multiple sessions share a CWD. | |
| 1144 | 1143 | func findLatestSessionPath(root, target string, notBefore time.Time) (string, error) { |
| 1145 | 1144 | type candidate struct { |
| 1146 | 1145 | path string |
| 1147 | 1146 | ts time.Time |
| 1148 | 1147 | } |
| @@ -1169,16 +1168,29 @@ | ||
| 1169 | 1168 | return "", err |
| 1170 | 1169 | } |
| 1171 | 1170 | if len(candidates) == 0 { |
| 1172 | 1171 | return "", os.ErrNotExist |
| 1173 | 1172 | } |
| 1174 | - // Sort newest first — the session that started most recently | |
| 1175 | - // (closest to our relay's startedAt) is most likely ours. | |
| 1176 | - sort.Slice(candidates, func(i, j int) bool { | |
| 1177 | - return candidates[i].ts.After(candidates[j].ts) | |
| 1178 | - }) | |
| 1179 | - return candidates[0].path, nil | |
| 1173 | + // Pick the session whose start time is closest to notBefore. | |
| 1174 | + // When multiple relays start near-simultaneously, each relay's | |
| 1175 | + // notBefore is slightly different, so each matches its own session. | |
| 1176 | + best := 0 | |
| 1177 | + bestDelta := candidates[0].ts.Sub(notBefore) | |
| 1178 | + for i := 1; i < len(candidates); i++ { | |
| 1179 | + delta := candidates[i].ts.Sub(notBefore) | |
| 1180 | + if delta < 0 { | |
| 1181 | + delta = -delta | |
| 1182 | + } | |
| 1183 | + if bestDelta < 0 { | |
| 1184 | + bestDelta = -bestDelta | |
| 1185 | + } | |
| 1186 | + if delta < bestDelta { | |
| 1187 | + best = i | |
| 1188 | + bestDelta = candidates[i].ts.Sub(notBefore) | |
| 1189 | + } | |
| 1190 | + } | |
| 1191 | + return candidates[best].path, nil | |
| 1180 | 1192 | } |
| 1181 | 1193 | |
| 1182 | 1194 | func readSessionMeta(path string) (sessionMetaPayload, time.Time, error) { |
| 1183 | 1195 | file, err := os.Open(path) |
| 1184 | 1196 | if err != nil { |
| 1185 | 1197 |
| --- cmd/codex-relay/main.go | |
| +++ cmd/codex-relay/main.go | |
| @@ -1136,13 +1136,12 @@ | |
| 1136 | } |
| 1137 | return match, nil |
| 1138 | } |
| 1139 | |
| 1140 | // findLatestSessionPath finds the .jsonl file in root whose first entry |
| 1141 | // timestamp is closest to (but after) notBefore — this ensures each relay |
| 1142 | // latches onto its own subprocess's session rather than whichever session |
| 1143 | // happens to have the latest timestamp when multiple sessions share a CWD. |
| 1144 | func findLatestSessionPath(root, target string, notBefore time.Time) (string, error) { |
| 1145 | type candidate struct { |
| 1146 | path string |
| 1147 | ts time.Time |
| 1148 | } |
| @@ -1169,16 +1168,29 @@ | |
| 1169 | return "", err |
| 1170 | } |
| 1171 | if len(candidates) == 0 { |
| 1172 | return "", os.ErrNotExist |
| 1173 | } |
| 1174 | // Sort newest first — the session that started most recently |
| 1175 | // (closest to our relay's startedAt) is most likely ours. |
| 1176 | sort.Slice(candidates, func(i, j int) bool { |
| 1177 | return candidates[i].ts.After(candidates[j].ts) |
| 1178 | }) |
| 1179 | return candidates[0].path, nil |
| 1180 | } |
| 1181 | |
| 1182 | func readSessionMeta(path string) (sessionMetaPayload, time.Time, error) { |
| 1183 | file, err := os.Open(path) |
| 1184 | if err != nil { |
| 1185 |
| --- cmd/codex-relay/main.go | |
| +++ cmd/codex-relay/main.go | |
| @@ -1136,13 +1136,12 @@ | |
| 1136 | } |
| 1137 | return match, nil |
| 1138 | } |
| 1139 | |
| 1140 | // findLatestSessionPath finds the .jsonl file in root whose first entry |
| 1141 | // timestamp is closest to notBefore — this ensures each relay latches onto |
| 1142 | // its own subprocess's session when multiple sessions share a CWD. |
| 1143 | func findLatestSessionPath(root, target string, notBefore time.Time) (string, error) { |
| 1144 | type candidate struct { |
| 1145 | path string |
| 1146 | ts time.Time |
| 1147 | } |
| @@ -1169,16 +1168,29 @@ | |
| 1168 | return "", err |
| 1169 | } |
| 1170 | if len(candidates) == 0 { |
| 1171 | return "", os.ErrNotExist |
| 1172 | } |
| 1173 | // Pick the session whose start time is closest to notBefore. |
| 1174 | // When multiple relays start near-simultaneously, each relay's |
| 1175 | // notBefore is slightly different, so each matches its own session. |
| 1176 | best := 0 |
| 1177 | bestDelta := candidates[0].ts.Sub(notBefore) |
| 1178 | for i := 1; i < len(candidates); i++ { |
| 1179 | delta := candidates[i].ts.Sub(notBefore) |
| 1180 | if delta < 0 { |
| 1181 | delta = -delta |
| 1182 | } |
| 1183 | if bestDelta < 0 { |
| 1184 | bestDelta = -bestDelta |
| 1185 | } |
| 1186 | if delta < bestDelta { |
| 1187 | best = i |
| 1188 | bestDelta = candidates[i].ts.Sub(notBefore) |
| 1189 | } |
| 1190 | } |
| 1191 | return candidates[best].path, nil |
| 1192 | } |
| 1193 | |
| 1194 | func readSessionMeta(path string) (sessionMetaPayload, time.Time, error) { |
| 1195 | file, err := os.Open(path) |
| 1196 | if err != nil { |
| 1197 |